1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
10 #include <WPXSvInputStream.hxx>
12 #include <com/sun/star/packages/zip/XZipFileAccess2.hpp>
13 #include <com/sun/star/uno/XComponentContext.hpp>
15 #include <comphelper/processfactory.hxx>
16 #include <comphelper/seekableinput.hxx>
17 #include <o3tl/safeint.hxx>
18 #include <rtl/string.hxx>
19 #include <sal/log.hxx>
21 #include <sot/storage.hxx>
23 #include <tools/stream.hxx>
24 #include <unotools/streamwrap.hxx>
25 #include <unotools/ucbstreamhelper.hxx>
30 #include <string_view>
31 #include <unordered_map>
35 namespace writerperfect
37 using namespace ::com::sun::star::uno
;
38 using namespace ::com::sun::star::io
;
40 namespace container
= com::sun::star::container
;
41 namespace packages
= com::sun::star::packages
;
48 explicit PositionHolder(const Reference
<XSeekable
>& rxSeekable
);
50 PositionHolder(const PositionHolder
&) = delete;
51 PositionHolder
& operator=(const PositionHolder
&) = delete;
54 const Reference
<XSeekable
> mxSeekable
;
55 const sal_uInt64 mnPosition
;
58 PositionHolder::PositionHolder(const Reference
<XSeekable
>& rxSeekable
)
59 : mxSeekable(rxSeekable
)
60 , mnPosition(rxSeekable
->getPosition())
64 PositionHolder::~PositionHolder()
68 mxSeekable
->seek(mnPosition
);
75 } // anonymous namespace
79 OUString
lcl_normalizeSubStreamPath(const OUString
& rPath
)
81 // accept paths which begin by '/'
82 // TODO: maybe this should do a full normalization
83 if (rPath
.startsWith("/") && rPath
.getLength() >= 2)
91 OUString
concatPath(std::u16string_view lhs
, const OUString
& rhs
)
95 return OUString::Concat(lhs
) + "/" + rhs
;
100 OLEStreamData(OString aName
, OString rvngName
);
102 tools::SvRef
<SotStorageStream
> stream
;
104 /** Name of the stream.
106 * This is not @c OUString, because we need to be able to
107 * produce const char* from it.
110 /** librevenge name of the stream.
112 * This is not @c OUString, because we need to be able to
113 * produce const char* from it.
118 typedef std::unordered_map
<OUString
, std::size_t> NameMap_t
;
119 typedef std::unordered_map
<OUString
, tools::SvRef
<SotStorage
>> OLEStorageMap_t
;
121 /** Representation of an OLE2 storage.
123 * This class tries to bring a bit of sanity to use of SotStorage with
124 * respect to the needs of @c librevenge::RVNGInputStream API. It
125 * holds all nested storages for the whole lifetime (more precisely,
126 * since initialization, which is performed by calling @c
127 * initialize()), thus ensuring that no created stream is destroyed
128 * just because its parent storage went out of scope. It also holds a
129 * bidirectional map of stream names to their indexes (index of a
130 * stream is determined by deep-first traversal), which is also
131 * populated during initialization (member variables @c maStreams and
134 * Streams are created on demand (and saved, for the same reason as
137 struct OLEStorageImpl
141 void initialize(std::unique_ptr
<SvStream
> pStream
);
143 tools::SvRef
<SotStorageStream
> getStream(const OUString
& rPath
);
144 tools::SvRef
<SotStorageStream
> const& getStream(std::size_t nId
);
147 void traverse(const tools::SvRef
<SotStorage
>& rStorage
, std::u16string_view rPath
);
149 tools::SvRef
<SotStorageStream
> createStream(const OUString
& rPath
);
152 tools::SvRef
<SotStorage
> mxRootStorage
; //< root storage of the OLE2
153 OLEStorageMap_t maStorageMap
; //< map of all sub storages by name
154 ::std::vector
<OLEStreamData
> maStreams
; //< list of streams and their names
155 NameMap_t maNameMap
; //< map of stream names to indexes (into @c maStreams)
159 OLEStreamData::OLEStreamData(OString aName
, OString rvngName
)
160 : name(std::move(aName
))
161 , RVNGname(std::move(rvngName
))
165 OLEStorageImpl::OLEStorageImpl()
166 : mbInitialized(false)
170 void OLEStorageImpl::initialize(std::unique_ptr
<SvStream
> pStream
)
175 mxRootStorage
= new SotStorage(pStream
.release(), true);
177 traverse(mxRootStorage
, u
"");
179 mbInitialized
= true;
182 tools::SvRef
<SotStorageStream
> OLEStorageImpl::getStream(const OUString
& rPath
)
184 const OUString
aPath(lcl_normalizeSubStreamPath(rPath
));
185 NameMap_t::iterator aIt
= maNameMap
.find(aPath
);
187 // For the while don't return stream in this situation.
188 // Later, given how libcdr's zip stream implementation behaves,
189 // return the first stream in the storage if there is one.
190 if (maNameMap
.end() == aIt
)
191 return tools::SvRef
<SotStorageStream
>();
193 if (!maStreams
[aIt
->second
].stream
.is())
194 maStreams
[aIt
->second
].stream
195 = createStream(OStringToOUString(maStreams
[aIt
->second
].name
, RTL_TEXTENCODING_UTF8
));
197 return maStreams
[aIt
->second
].stream
;
200 tools::SvRef
<SotStorageStream
> const& OLEStorageImpl::getStream(const std::size_t nId
)
202 if (!maStreams
[nId
].stream
.is())
203 maStreams
[nId
].stream
204 = createStream(OStringToOUString(maStreams
[nId
].name
, RTL_TEXTENCODING_UTF8
));
206 return maStreams
[nId
].stream
;
209 void OLEStorageImpl::traverse(const tools::SvRef
<SotStorage
>& rStorage
, std::u16string_view rPath
)
211 SvStorageInfoList infos
;
213 rStorage
->FillInfoList(&infos
);
215 for (const auto& info
: infos
)
219 OUString baseName
= info
.GetName(), rvngName
= baseName
;
220 // librevenge::RVNGOLEStream ignores the first character when is a control code, so ...
221 if (!rvngName
.isEmpty() && rvngName
.toChar() < 32)
222 rvngName
= rvngName
.copy(1);
223 maStreams
.emplace_back(
224 OUStringToOString(concatPath(rPath
, baseName
), RTL_TEXTENCODING_UTF8
),
225 OUStringToOString(concatPath(rPath
, rvngName
), RTL_TEXTENCODING_UTF8
));
226 maNameMap
[concatPath(rPath
, rvngName
)] = maStreams
.size() - 1;
228 else if (info
.IsStorage())
230 const OUString aPath
= concatPath(rPath
, info
.GetName());
231 tools::SvRef
<SotStorage
> aStorage
232 = rStorage
->OpenSotStorage(info
.GetName(), StreamMode::STD_READ
);
233 maStorageMap
[aPath
] = aStorage
;
235 // deep-first traversal
236 traverse(aStorage
, aPath
);
240 SAL_WARN("writerperfect",
241 "OLEStorageImpl::traverse: invalid storage entry, neither stream nor file");
246 tools::SvRef
<SotStorageStream
> OLEStorageImpl::createStream(const OUString
& rPath
)
248 const sal_Int32 nDelim
= rPath
.lastIndexOf(u
'/');
251 return mxRootStorage
->OpenSotStream(rPath
, StreamMode::STD_READ
);
253 const OUString aDir
= rPath
.copy(0, nDelim
);
254 const OUString aName
= rPath
.copy(nDelim
+ 1);
256 const OLEStorageMap_t::const_iterator aIt
= maStorageMap
.find(aDir
);
258 if (maStorageMap
.end() == aIt
)
261 return aIt
->second
->OpenSotStream(aName
, StreamMode::STD_READ
);
269 explicit ZipStreamData(OString aName
);
271 Reference
<XInputStream
> xStream
;
273 /** Name of the stream.
275 * This is not @c OUString, because we need to be able to
276 * produce const char* from it.
281 /** Representation of a Zip storage.
283 * This is quite similar to OLEStorageImpl, except that we do not need
284 * to keep all storages (folders) open.
286 struct ZipStorageImpl
288 explicit ZipStorageImpl(const Reference
<container::XNameAccess
>& rxContainer
);
290 /** Initialize for access.
292 * This creates a bidirectional map of stream names to their
293 * indexes (index of a stream is determined by deep-first
298 Reference
<XInputStream
> getStream(const OUString
& rPath
);
299 Reference
<XInputStream
> const& getStream(std::size_t nId
);
302 void traverse(const Reference
<container::XNameAccess
>& rxEnum
);
304 Reference
<XInputStream
> createStream(const OUString
& rPath
);
307 Reference
<container::XNameAccess
> mxContainer
; //< root of the Zip
308 ::std::vector
<ZipStreamData
> maStreams
; //< list of streams and their names
309 NameMap_t maNameMap
; //< map of stream names to indexes (into @c maStreams)
313 ZipStreamData::ZipStreamData(OString _aName
)
314 : aName(std::move(_aName
))
318 ZipStorageImpl::ZipStorageImpl(const Reference
<container::XNameAccess
>& rxContainer
)
319 : mxContainer(rxContainer
)
320 , mbInitialized(false)
322 assert(mxContainer
.is());
325 void ZipStorageImpl::initialize()
327 traverse(mxContainer
);
329 mbInitialized
= true;
332 Reference
<XInputStream
> ZipStorageImpl::getStream(const OUString
& rPath
)
334 const OUString
aPath(lcl_normalizeSubStreamPath(rPath
));
335 NameMap_t::iterator aIt
= maNameMap
.find(aPath
);
337 // For the while don't return stream in this situation.
338 // Later, given how libcdr's zip stream implementation behaves,
339 // return the first stream in the storage if there is one.
340 if (maNameMap
.end() == aIt
)
341 return Reference
<XInputStream
>();
343 if (!maStreams
[aIt
->second
].xStream
.is())
344 maStreams
[aIt
->second
].xStream
= createStream(aPath
);
346 return maStreams
[aIt
->second
].xStream
;
349 Reference
<XInputStream
> const& ZipStorageImpl::getStream(const std::size_t nId
)
351 if (!maStreams
[nId
].xStream
.is())
352 maStreams
[nId
].xStream
353 = createStream(OStringToOUString(maStreams
[nId
].aName
, RTL_TEXTENCODING_UTF8
));
355 return maStreams
[nId
].xStream
;
358 void ZipStorageImpl::traverse(const Reference
<container::XNameAccess
>& rxContainer
)
360 const Sequence
<OUString
> lNames
= rxContainer
->getElementNames();
362 maStreams
.reserve(lNames
.getLength());
364 for (const auto& rName
: lNames
)
366 if (!rName
.endsWith("/")) // skip dirs
368 maStreams
.emplace_back(OUStringToOString(rName
, RTL_TEXTENCODING_UTF8
));
369 maNameMap
[rName
] = maStreams
.size() - 1;
374 Reference
<XInputStream
> ZipStorageImpl::createStream(const OUString
& rPath
)
376 Reference
<XInputStream
> xStream
;
380 const Reference
<XInputStream
> xInputStream(mxContainer
->getByName(rPath
), UNO_QUERY_THROW
);
381 const Reference
<XSeekable
> xSeekable(xInputStream
, UNO_QUERY
);
384 xStream
= xInputStream
;
386 xStream
.set(new comphelper::OSeekableInputWrapper(
387 xInputStream
, comphelper::getProcessComponentContext()));
389 catch (const Exception
&)
398 class WPXSvInputStreamImpl
401 explicit WPXSvInputStreamImpl(css::uno::Reference
<css::io::XInputStream
> const& xStream
);
404 unsigned subStreamCount();
405 const char* subStreamName(unsigned id
);
406 bool existsSubStream(const char* name
);
407 librevenge::RVNGInputStream
* getSubStreamByName(const char* name
);
408 librevenge::RVNGInputStream
* getSubStreamById(unsigned id
);
410 const unsigned char* read(unsigned long numBytes
, unsigned long& numBytesRead
);
411 int seek(tools::Long offset
);
415 void invalidateReadBuffer();
419 void ensureOLEIsInitialized();
422 void ensureZipIsInitialized();
424 static librevenge::RVNGInputStream
*
425 createWPXStream(const tools::SvRef
<SotStorageStream
>& rxStorage
);
426 static librevenge::RVNGInputStream
* createWPXStream(const Reference
<XInputStream
>& rxStream
);
429 css::uno::Reference
<css::io::XInputStream
> mxStream
;
430 css::uno::Reference
<css::io::XSeekable
> mxSeekable
;
431 css::uno::Sequence
<sal_Int8
> maData
;
432 std::unique_ptr
<OLEStorageImpl
> mpOLEStorage
;
433 std::unique_ptr
<ZipStorageImpl
> mpZipStorage
;
439 const unsigned char* mpReadBuffer
;
440 unsigned long mnReadBufferLength
;
441 unsigned long mnReadBufferPos
;
444 WPXSvInputStreamImpl::WPXSvInputStreamImpl(Reference
<XInputStream
> const& xStream
)
446 , mxSeekable(xStream
, UNO_QUERY
)
448 , mbCheckedOLE(false)
449 , mbCheckedZip(false)
451 , mpReadBuffer(nullptr)
452 , mnReadBufferLength(0)
455 if (!xStream
.is() || !mxStream
.is())
459 if (!mxSeekable
.is())
465 mnLength
= mxSeekable
->getLength();
466 if (0 < mxSeekable
->getPosition())
471 SAL_WARN("writerperfect", "mnLength = mxSeekable->getLength() threw exception");
478 const unsigned char* WPXSvInputStreamImpl::read(unsigned long numBytes
, unsigned long& numBytesRead
)
482 if (numBytes
== 0 || isEnd())
485 numBytesRead
= mxStream
->readSomeBytes(maData
, numBytes
);
486 if (numBytesRead
== 0)
489 return reinterpret_cast<const unsigned char*>(maData
.getConstArray());
492 tools::Long
WPXSvInputStreamImpl::tell()
494 if ((mnLength
== 0) || !mxStream
.is() || !mxSeekable
.is())
498 const sal_Int64 tmpPosition
= mxSeekable
->getPosition();
499 if ((tmpPosition
< 0) || (tmpPosition
> LONG_MAX
))
501 return static_cast<tools::Long
>(tmpPosition
);
505 int WPXSvInputStreamImpl::seek(tools::Long offset
)
507 if ((mnLength
== 0) || !mxStream
.is() || !mxSeekable
.is())
510 const sal_Int64 tmpPosition
= mxSeekable
->getPosition();
511 if ((tmpPosition
< 0) || (tmpPosition
> LONG_MAX
))
516 mxSeekable
->seek(offset
);
521 SAL_WARN("writerperfect", "mxSeekable->seek(offset) threw exception");
526 bool WPXSvInputStreamImpl::isEnd()
528 if ((mnLength
== 0) || !mxStream
.is() || !mxSeekable
.is())
530 return (mxSeekable
->getPosition() >= mnLength
);
533 bool WPXSvInputStreamImpl::isStructured()
535 if ((mnLength
== 0) || !mxStream
.is() || !mxSeekable
.is())
538 PositionHolder
pos(mxSeekable
);
549 unsigned WPXSvInputStreamImpl::subStreamCount()
551 if ((mnLength
== 0) || !mxStream
.is() || !mxSeekable
.is())
554 PositionHolder
pos(mxSeekable
);
559 ensureOLEIsInitialized();
561 return mpOLEStorage
->maStreams
.size();
568 ensureZipIsInitialized();
570 return mpZipStorage
->maStreams
.size();
576 const char* WPXSvInputStreamImpl::subStreamName(const unsigned id
)
578 if ((mnLength
== 0) || !mxStream
.is() || !mxSeekable
.is())
581 PositionHolder
pos(mxSeekable
);
586 ensureOLEIsInitialized();
588 if (mpOLEStorage
->maStreams
.size() <= id
)
591 return mpOLEStorage
->maStreams
[id
].RVNGname
.getStr();
598 ensureZipIsInitialized();
600 if (mpZipStorage
->maStreams
.size() <= id
)
603 return mpZipStorage
->maStreams
[id
].aName
.getStr();
609 bool WPXSvInputStreamImpl::existsSubStream(const char* const name
)
614 if ((mnLength
== 0) || !mxStream
.is() || !mxSeekable
.is())
617 PositionHolder
pos(mxSeekable
);
620 const OUString
aName(OStringToOUString(std::string_view(name
), RTL_TEXTENCODING_UTF8
));
624 ensureOLEIsInitialized();
625 return mpOLEStorage
->maNameMap
.end() != mpOLEStorage
->maNameMap
.find(aName
);
632 ensureZipIsInitialized();
633 return mpZipStorage
->maNameMap
.end() != mpZipStorage
->maNameMap
.find(aName
);
639 librevenge::RVNGInputStream
* WPXSvInputStreamImpl::getSubStreamByName(const char* const name
)
644 if ((mnLength
== 0) || !mxStream
.is() || !mxSeekable
.is())
647 PositionHolder
pos(mxSeekable
);
650 const OUString
aName(OStringToOUString(std::string_view(name
), RTL_TEXTENCODING_UTF8
));
654 ensureOLEIsInitialized();
655 return createWPXStream(mpOLEStorage
->getStream(aName
));
662 ensureZipIsInitialized();
666 return createWPXStream(mpZipStorage
->getStream(aName
));
668 catch (const Exception
&)
677 librevenge::RVNGInputStream
* WPXSvInputStreamImpl::getSubStreamById(const unsigned id
)
679 if ((mnLength
== 0) || !mxStream
.is() || !mxSeekable
.is())
682 PositionHolder
pos(mxSeekable
);
687 ensureOLEIsInitialized();
689 if (mpOLEStorage
->maStreams
.size() <= id
)
692 return createWPXStream(mpOLEStorage
->getStream(id
));
699 ensureZipIsInitialized();
701 if (mpZipStorage
->maStreams
.size() <= id
)
706 return createWPXStream(mpZipStorage
->getStream(id
));
708 catch (const Exception
&)
716 void WPXSvInputStreamImpl::invalidateReadBuffer()
720 seek(tell() + static_cast<tools::Long
>(mnReadBufferPos
)
721 - static_cast<tools::Long
>(mnReadBufferLength
));
722 mpReadBuffer
= nullptr;
724 mnReadBufferLength
= 0;
728 librevenge::RVNGInputStream
*
729 WPXSvInputStreamImpl::createWPXStream(const tools::SvRef
<SotStorageStream
>& rxStorage
)
733 Reference
<XInputStream
> xContents(new utl::OSeekableInputStreamWrapper(rxStorage
.get()));
734 return new WPXSvInputStream(xContents
);
739 librevenge::RVNGInputStream
*
740 WPXSvInputStreamImpl::createWPXStream(const Reference
<XInputStream
>& rxStream
)
743 return new WPXSvInputStream(rxStream
);
748 bool WPXSvInputStreamImpl::isOLE()
752 assert(0 == mxSeekable
->getPosition());
754 std::unique_ptr
<SvStream
> pStream(utl::UcbStreamHelper::CreateStream(mxStream
));
755 if (pStream
&& SotStorage::IsOLEStorage(pStream
.get()))
756 mpOLEStorage
.reset(new OLEStorageImpl());
761 return bool(mpOLEStorage
);
764 bool WPXSvInputStreamImpl::isZip()
768 assert(0 == mxSeekable
->getPosition());
772 const Reference
<XComponentContext
> xContext(comphelper::getProcessComponentContext(),
774 const Reference
<packages::zip::XZipFileAccess2
> xZip(
775 xContext
->getServiceManager()->createInstanceWithArgumentsAndContext(
776 "com.sun.star.packages.zip.ZipFileAccess", { Any(mxStream
) }, xContext
),
778 mpZipStorage
.reset(new ZipStorageImpl(xZip
));
780 catch (const Exception
&)
788 return bool(mpZipStorage
);
791 void WPXSvInputStreamImpl::ensureOLEIsInitialized()
793 assert(mpOLEStorage
);
795 if (!mpOLEStorage
->mbInitialized
)
796 mpOLEStorage
->initialize(utl::UcbStreamHelper::CreateStream(mxStream
));
799 void WPXSvInputStreamImpl::ensureZipIsInitialized()
801 assert(mpZipStorage
);
803 if (!mpZipStorage
->mbInitialized
)
804 mpZipStorage
->initialize();
807 WPXSvInputStream::WPXSvInputStream(Reference
<XInputStream
> const& xStream
)
808 : mpImpl(new WPXSvInputStreamImpl(xStream
))
812 WPXSvInputStream::~WPXSvInputStream() {}
814 #define BUFFER_MAX 65536
816 const unsigned char* WPXSvInputStream::read(unsigned long numBytes
, unsigned long& numBytesRead
)
820 if (numBytes
== 0 || numBytes
> std::numeric_limits
<unsigned long>::max() / 2)
823 if (mpImpl
->mpReadBuffer
)
825 if ((mpImpl
->mnReadBufferPos
+ numBytes
> mpImpl
->mnReadBufferPos
)
826 && (mpImpl
->mnReadBufferPos
+ numBytes
<= mpImpl
->mnReadBufferLength
))
828 const unsigned char* pTmp
= mpImpl
->mpReadBuffer
+ mpImpl
->mnReadBufferPos
;
829 mpImpl
->mnReadBufferPos
+= numBytes
;
830 numBytesRead
= numBytes
;
834 mpImpl
->invalidateReadBuffer();
837 unsigned long curpos
= static_cast<unsigned long>(mpImpl
->tell());
838 if (curpos
== static_cast<unsigned long>(-1)) // returned ERROR
841 if ((curpos
+ numBytes
< curpos
) /*overflow*/
842 || (curpos
+ numBytes
843 >= o3tl::make_unsigned(mpImpl
->mnLength
))) /*reading more than available*/
845 numBytes
= mpImpl
->mnLength
- curpos
;
848 if (numBytes
< BUFFER_MAX
)
850 if (BUFFER_MAX
< mpImpl
->mnLength
- curpos
)
851 mpImpl
->mnReadBufferLength
= BUFFER_MAX
;
852 else /* BUFFER_MAX >= mpImpl->mnLength - curpos */
853 mpImpl
->mnReadBufferLength
= mpImpl
->mnLength
- curpos
;
856 mpImpl
->mnReadBufferLength
= numBytes
;
858 unsigned long tmpNumBytes(0);
859 mpImpl
->mpReadBuffer
= mpImpl
->read(mpImpl
->mnReadBufferLength
, tmpNumBytes
);
860 if (tmpNumBytes
!= mpImpl
->mnReadBufferLength
)
861 mpImpl
->mnReadBufferLength
= tmpNumBytes
;
863 mpImpl
->mnReadBufferPos
= 0;
864 if (!mpImpl
->mnReadBufferLength
)
867 if (numBytes
<= mpImpl
->mnReadBufferLength
)
868 numBytesRead
= numBytes
;
870 numBytesRead
= mpImpl
->mnReadBufferLength
;
872 mpImpl
->mnReadBufferPos
+= numBytesRead
;
873 return mpImpl
->mpReadBuffer
;
876 long WPXSvInputStream::tell()
878 tools::Long retVal
= mpImpl
->tell();
879 return retVal
- static_cast<tools::Long
>(mpImpl
->mnReadBufferLength
)
880 + static_cast<tools::Long
>(mpImpl
->mnReadBufferPos
);
883 int WPXSvInputStream::seek(long offset
, librevenge::RVNG_SEEK_TYPE seekType
)
885 sal_Int64 tmpOffset
= offset
;
886 if (seekType
== librevenge::RVNG_SEEK_CUR
)
888 if (seekType
== librevenge::RVNG_SEEK_END
)
889 tmpOffset
+= mpImpl
->mnLength
;
897 if (tmpOffset
> mpImpl
->mnLength
)
899 tmpOffset
= mpImpl
->mnLength
;
903 if (tmpOffset
< mpImpl
->tell()
904 && o3tl::make_unsigned(tmpOffset
)
905 >= static_cast<unsigned long>(mpImpl
->tell()) - mpImpl
->mnReadBufferLength
)
907 mpImpl
->mnReadBufferPos
= static_cast<unsigned long>(
908 tmpOffset
+ static_cast<tools::Long
>(mpImpl
->mnReadBufferLength
) - mpImpl
->tell());
912 mpImpl
->invalidateReadBuffer();
914 if (mpImpl
->seek(tmpOffset
))
919 bool WPXSvInputStream::isEnd()
921 return mpImpl
->isEnd() && mpImpl
->mnReadBufferPos
== mpImpl
->mnReadBufferLength
;
924 bool WPXSvInputStream::isStructured()
926 mpImpl
->invalidateReadBuffer();
927 return mpImpl
->isStructured();
930 unsigned WPXSvInputStream::subStreamCount()
932 mpImpl
->invalidateReadBuffer();
933 return mpImpl
->subStreamCount();
936 const char* WPXSvInputStream::subStreamName(const unsigned id
)
938 mpImpl
->invalidateReadBuffer();
939 return mpImpl
->subStreamName(id
);
942 bool WPXSvInputStream::existsSubStream(const char* const name
)
944 mpImpl
->invalidateReadBuffer();
945 return mpImpl
->existsSubStream(name
);
948 librevenge::RVNGInputStream
* WPXSvInputStream::getSubStreamByName(const char* name
)
950 mpImpl
->invalidateReadBuffer();
951 return mpImpl
->getSubStreamByName(name
);
954 librevenge::RVNGInputStream
* WPXSvInputStream::getSubStreamById(const unsigned id
)
956 mpImpl
->invalidateReadBuffer();
957 return mpImpl
->getSubStreamById(id
);
961 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */