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 <unordered_map>
33 namespace writerperfect
35 using namespace ::com::sun::star::uno
;
36 using namespace ::com::sun::star::io
;
38 namespace container
= com::sun::star::container
;
39 namespace packages
= com::sun::star::packages
;
46 explicit PositionHolder(const Reference
<XSeekable
>& rxSeekable
);
48 PositionHolder(const PositionHolder
&) = delete;
49 PositionHolder
& operator=(const PositionHolder
&) = delete;
52 const Reference
<XSeekable
> mxSeekable
;
53 const sal_uInt64 mnPosition
;
56 PositionHolder::PositionHolder(const Reference
<XSeekable
>& rxSeekable
)
57 : mxSeekable(rxSeekable
)
58 , mnPosition(rxSeekable
->getPosition())
62 PositionHolder::~PositionHolder()
66 mxSeekable
->seek(mnPosition
);
73 } // anonymous namespace
77 OUString
lcl_normalizeSubStreamPath(const OUString
& rPath
)
79 // accept paths which begin by '/'
80 // TODO: maybe this should do a full normalization
81 if (rPath
.startsWith("/") && rPath
.getLength() >= 2)
89 OUString
concatPath(const OUString
& lhs
, const OUString
& rhs
)
93 return lhs
+ "/" + rhs
;
98 OLEStreamData(const OString
& rName
, const OString
& rvngName
);
100 tools::SvRef
<SotStorageStream
> stream
;
102 /** Name of the stream.
104 * This is not @c OUString, because we need to be able to
105 * produce const char* from it.
108 /** librevenge name of the stream.
110 * This is not @c OUString, because we need to be able to
111 * produce const char* from it.
116 typedef std::unordered_map
<OUString
, std::size_t> NameMap_t
;
117 typedef std::unordered_map
<OUString
, tools::SvRef
<SotStorage
>> OLEStorageMap_t
;
119 /** Representation of an OLE2 storage.
121 * This class tries to bring a bit of sanity to use of SotStorage with
122 * respect to the needs of @c librevenge::RVNGInputStream API. It
123 * holds all nested storages for the whole lifetime (more precisely,
124 * since initialization, which is performed by calling @c
125 * initialize()), thus ensuring that no created stream is destroyed
126 * just because its parent storage went out of scope. It also holds a
127 * bidirectional map of stream names to their indexes (index of a
128 * stream is determined by deep-first traversal), which is also
129 * populated during initialization (member variables @c maStreams and
132 * Streams are created on demand (and saved, for the same reason as
135 struct OLEStorageImpl
139 void initialize(std::unique_ptr
<SvStream
> pStream
);
141 tools::SvRef
<SotStorageStream
> getStream(const OUString
& rPath
);
142 tools::SvRef
<SotStorageStream
> const& getStream(std::size_t nId
);
145 void traverse(const tools::SvRef
<SotStorage
>& rStorage
, const OUString
& rPath
);
147 tools::SvRef
<SotStorageStream
> createStream(const OUString
& rPath
);
150 tools::SvRef
<SotStorage
> mxRootStorage
; //< root storage of the OLE2
151 OLEStorageMap_t maStorageMap
; //< map of all sub storages by name
152 ::std::vector
<OLEStreamData
> maStreams
; //< list of streams and their names
153 NameMap_t maNameMap
; //< map of stream names to indexes (into @c maStreams)
157 OLEStreamData::OLEStreamData(const OString
& rName
, const OString
& rvngName
)
164 OLEStorageImpl::OLEStorageImpl()
169 , mbInitialized(false)
173 void OLEStorageImpl::initialize(std::unique_ptr
<SvStream
> pStream
)
178 mxRootStorage
= new SotStorage(pStream
.release(), true);
180 traverse(mxRootStorage
, "");
182 mbInitialized
= true;
185 tools::SvRef
<SotStorageStream
> OLEStorageImpl::getStream(const OUString
& rPath
)
187 const OUString
aPath(lcl_normalizeSubStreamPath(rPath
));
188 NameMap_t::iterator aIt
= maNameMap
.find(aPath
);
190 // For the while don't return stream in this situation.
191 // Later, given how libcdr's zip stream implementation behaves,
192 // return the first stream in the storage if there is one.
193 if (maNameMap
.end() == aIt
)
194 return tools::SvRef
<SotStorageStream
>();
196 if (!maStreams
[aIt
->second
].stream
.is())
197 maStreams
[aIt
->second
].stream
198 = createStream(OStringToOUString(maStreams
[aIt
->second
].name
, RTL_TEXTENCODING_UTF8
));
200 return maStreams
[aIt
->second
].stream
;
203 tools::SvRef
<SotStorageStream
> const& OLEStorageImpl::getStream(const std::size_t nId
)
205 if (!maStreams
[nId
].stream
.is())
206 maStreams
[nId
].stream
207 = createStream(OStringToOUString(maStreams
[nId
].name
, RTL_TEXTENCODING_UTF8
));
209 return maStreams
[nId
].stream
;
212 void OLEStorageImpl::traverse(const tools::SvRef
<SotStorage
>& rStorage
, const OUString
& rPath
)
214 SvStorageInfoList infos
;
216 rStorage
->FillInfoList(&infos
);
218 for (const auto& info
: infos
)
222 OUString baseName
= info
.GetName(), rvngName
= baseName
;
223 // librevenge::RVNGOLEStream ignores the first character when is a control code, so ...
224 if (!rvngName
.isEmpty() && rvngName
.toChar() < 32)
225 rvngName
= rvngName
.copy(1);
226 maStreams
.emplace_back(
227 OUStringToOString(concatPath(rPath
, baseName
), RTL_TEXTENCODING_UTF8
),
228 OUStringToOString(concatPath(rPath
, rvngName
), RTL_TEXTENCODING_UTF8
));
229 maNameMap
[concatPath(rPath
, rvngName
)] = maStreams
.size() - 1;
231 else if (info
.IsStorage())
233 const OUString aPath
= concatPath(rPath
, info
.GetName());
234 tools::SvRef
<SotStorage
> aStorage
235 = rStorage
->OpenSotStorage(info
.GetName(), StreamMode::STD_READ
);
236 maStorageMap
[aPath
] = aStorage
;
238 // deep-first traversal
239 traverse(aStorage
, aPath
);
243 SAL_WARN("writerperfect",
244 "OLEStorageImpl::traverse: invalid storage entry, neither stream nor file");
249 tools::SvRef
<SotStorageStream
> OLEStorageImpl::createStream(const OUString
& rPath
)
251 const sal_Int32 nDelim
= rPath
.lastIndexOf(u
'/');
254 return mxRootStorage
->OpenSotStream(rPath
, StreamMode::STD_READ
);
256 const OUString aDir
= rPath
.copy(0, nDelim
);
257 const OUString aName
= rPath
.copy(nDelim
+ 1);
259 const OLEStorageMap_t::const_iterator aIt
= maStorageMap
.find(aDir
);
261 if (maStorageMap
.end() == aIt
)
264 return aIt
->second
->OpenSotStream(aName
, StreamMode::STD_READ
);
272 explicit ZipStreamData(const OString
& rName
);
274 Reference
<XInputStream
> xStream
;
276 /** Name of the stream.
278 * This is not @c OUString, because we need to be able to
279 * produce const char* from it.
284 /** Representation of a Zip storage.
286 * This is quite similar to OLEStorageImpl, except that we do not need
287 * to keep all storages (folders) open.
289 struct ZipStorageImpl
291 explicit ZipStorageImpl(const Reference
<container::XNameAccess
>& rxContainer
);
293 /** Initialize for access.
295 * This creates a bidirectional map of stream names to their
296 * indexes (index of a stream is determined by deep-first
301 Reference
<XInputStream
> getStream(const OUString
& rPath
);
302 Reference
<XInputStream
> const& getStream(std::size_t nId
);
305 void traverse(const Reference
<container::XNameAccess
>& rxEnum
);
307 Reference
<XInputStream
> createStream(const OUString
& rPath
);
310 Reference
<container::XNameAccess
> mxContainer
; //< root of the Zip
311 ::std::vector
<ZipStreamData
> maStreams
; //< list of streams and their names
312 NameMap_t maNameMap
; //< map of stream names to indexes (into @c maStreams)
316 ZipStreamData::ZipStreamData(const OString
& rName
)
322 ZipStorageImpl::ZipStorageImpl(const Reference
<container::XNameAccess
>& rxContainer
)
323 : mxContainer(rxContainer
)
326 , mbInitialized(false)
328 assert(mxContainer
.is());
331 void ZipStorageImpl::initialize()
333 traverse(mxContainer
);
335 mbInitialized
= true;
338 Reference
<XInputStream
> ZipStorageImpl::getStream(const OUString
& rPath
)
340 const OUString
aPath(lcl_normalizeSubStreamPath(rPath
));
341 NameMap_t::iterator aIt
= maNameMap
.find(aPath
);
343 // For the while don't return stream in this situation.
344 // Later, given how libcdr's zip stream implementation behaves,
345 // return the first stream in the storage if there is one.
346 if (maNameMap
.end() == aIt
)
347 return Reference
<XInputStream
>();
349 if (!maStreams
[aIt
->second
].xStream
.is())
350 maStreams
[aIt
->second
].xStream
= createStream(aPath
);
352 return maStreams
[aIt
->second
].xStream
;
355 Reference
<XInputStream
> const& ZipStorageImpl::getStream(const std::size_t nId
)
357 if (!maStreams
[nId
].xStream
.is())
358 maStreams
[nId
].xStream
359 = createStream(OStringToOUString(maStreams
[nId
].aName
, RTL_TEXTENCODING_UTF8
));
361 return maStreams
[nId
].xStream
;
364 void ZipStorageImpl::traverse(const Reference
<container::XNameAccess
>& rxContainer
)
366 const Sequence
<OUString
> lNames
= rxContainer
->getElementNames();
368 maStreams
.reserve(lNames
.getLength());
370 for (const auto& rName
: lNames
)
372 if (!rName
.endsWith("/")) // skip dirs
374 maStreams
.emplace_back(OUStringToOString(rName
, RTL_TEXTENCODING_UTF8
));
375 maNameMap
[rName
] = maStreams
.size() - 1;
380 Reference
<XInputStream
> ZipStorageImpl::createStream(const OUString
& rPath
)
382 Reference
<XInputStream
> xStream
;
386 const Reference
<XInputStream
> xInputStream(mxContainer
->getByName(rPath
), UNO_QUERY_THROW
);
387 const Reference
<XSeekable
> xSeekable(xInputStream
, UNO_QUERY
);
390 xStream
= xInputStream
;
392 xStream
.set(new comphelper::OSeekableInputWrapper(
393 xInputStream
, comphelper::getProcessComponentContext()));
395 catch (const Exception
&)
404 class WPXSvInputStreamImpl
407 explicit WPXSvInputStreamImpl(css::uno::Reference
<css::io::XInputStream
> const& xStream
);
410 unsigned subStreamCount();
411 const char* subStreamName(unsigned id
);
412 bool existsSubStream(const char* name
);
413 librevenge::RVNGInputStream
* getSubStreamByName(const char* name
);
414 librevenge::RVNGInputStream
* getSubStreamById(unsigned id
);
416 const unsigned char* read(unsigned long numBytes
, unsigned long& numBytesRead
);
417 int seek(tools::Long offset
);
421 void invalidateReadBuffer();
425 void ensureOLEIsInitialized();
428 void ensureZipIsInitialized();
430 static librevenge::RVNGInputStream
*
431 createWPXStream(const tools::SvRef
<SotStorageStream
>& rxStorage
);
432 static librevenge::RVNGInputStream
* createWPXStream(const Reference
<XInputStream
>& rxStream
);
435 css::uno::Reference
<css::io::XInputStream
> mxStream
;
436 css::uno::Reference
<css::io::XSeekable
> mxSeekable
;
437 css::uno::Sequence
<sal_Int8
> maData
;
438 std::unique_ptr
<OLEStorageImpl
> mpOLEStorage
;
439 std::unique_ptr
<ZipStorageImpl
> mpZipStorage
;
445 const unsigned char* mpReadBuffer
;
446 unsigned long mnReadBufferLength
;
447 unsigned long mnReadBufferPos
;
450 WPXSvInputStreamImpl::WPXSvInputStreamImpl(Reference
<XInputStream
> const& xStream
)
452 , mxSeekable(xStream
, UNO_QUERY
)
454 , mbCheckedOLE(false)
455 , mbCheckedZip(false)
457 , mpReadBuffer(nullptr)
458 , mnReadBufferLength(0)
461 if (!xStream
.is() || !mxStream
.is())
465 if (!mxSeekable
.is())
471 mnLength
= mxSeekable
->getLength();
472 if (0 < mxSeekable
->getPosition())
477 SAL_WARN("writerperfect", "mnLength = mxSeekable->getLength() threw exception");
484 const unsigned char* WPXSvInputStreamImpl::read(unsigned long numBytes
, unsigned long& numBytesRead
)
488 if (numBytes
== 0 || isEnd())
491 numBytesRead
= mxStream
->readSomeBytes(maData
, numBytes
);
492 if (numBytesRead
== 0)
495 return reinterpret_cast<const unsigned char*>(maData
.getConstArray());
498 tools::Long
WPXSvInputStreamImpl::tell()
500 if ((mnLength
== 0) || !mxStream
.is() || !mxSeekable
.is())
504 const sal_Int64 tmpPosition
= mxSeekable
->getPosition();
505 if ((tmpPosition
< 0) || (tmpPosition
> LONG_MAX
))
507 return static_cast<tools::Long
>(tmpPosition
);
511 int WPXSvInputStreamImpl::seek(tools::Long offset
)
513 if ((mnLength
== 0) || !mxStream
.is() || !mxSeekable
.is())
516 const sal_Int64 tmpPosition
= mxSeekable
->getPosition();
517 if ((tmpPosition
< 0) || (tmpPosition
> LONG_MAX
))
522 mxSeekable
->seek(offset
);
527 SAL_WARN("writerperfect", "mxSeekable->seek(offset) threw exception");
532 bool WPXSvInputStreamImpl::isEnd()
534 if ((mnLength
== 0) || !mxStream
.is() || !mxSeekable
.is())
536 return (mxSeekable
->getPosition() >= mnLength
);
539 bool WPXSvInputStreamImpl::isStructured()
541 if ((mnLength
== 0) || !mxStream
.is() || !mxSeekable
.is())
544 PositionHolder
pos(mxSeekable
);
555 unsigned WPXSvInputStreamImpl::subStreamCount()
557 if ((mnLength
== 0) || !mxStream
.is() || !mxSeekable
.is())
560 PositionHolder
pos(mxSeekable
);
565 ensureOLEIsInitialized();
567 return mpOLEStorage
->maStreams
.size();
574 ensureZipIsInitialized();
576 return mpZipStorage
->maStreams
.size();
582 const char* WPXSvInputStreamImpl::subStreamName(const unsigned id
)
584 if ((mnLength
== 0) || !mxStream
.is() || !mxSeekable
.is())
587 PositionHolder
pos(mxSeekable
);
592 ensureOLEIsInitialized();
594 if (mpOLEStorage
->maStreams
.size() <= id
)
597 return mpOLEStorage
->maStreams
[id
].RVNGname
.getStr();
604 ensureZipIsInitialized();
606 if (mpZipStorage
->maStreams
.size() <= id
)
609 return mpZipStorage
->maStreams
[id
].aName
.getStr();
615 bool WPXSvInputStreamImpl::existsSubStream(const char* const name
)
620 if ((mnLength
== 0) || !mxStream
.is() || !mxSeekable
.is())
623 PositionHolder
pos(mxSeekable
);
626 const OUString
aName(OStringToOUString(OString(name
), RTL_TEXTENCODING_UTF8
));
630 ensureOLEIsInitialized();
631 return mpOLEStorage
->maNameMap
.end() != mpOLEStorage
->maNameMap
.find(aName
);
638 ensureZipIsInitialized();
639 return mpZipStorage
->maNameMap
.end() != mpZipStorage
->maNameMap
.find(aName
);
645 librevenge::RVNGInputStream
* WPXSvInputStreamImpl::getSubStreamByName(const char* const name
)
650 if ((mnLength
== 0) || !mxStream
.is() || !mxSeekable
.is())
653 PositionHolder
pos(mxSeekable
);
656 const OUString
aName(OStringToOUString(OString(name
), RTL_TEXTENCODING_UTF8
));
660 ensureOLEIsInitialized();
661 return createWPXStream(mpOLEStorage
->getStream(aName
));
668 ensureZipIsInitialized();
672 return createWPXStream(mpZipStorage
->getStream(aName
));
674 catch (const Exception
&)
683 librevenge::RVNGInputStream
* WPXSvInputStreamImpl::getSubStreamById(const unsigned id
)
685 if ((mnLength
== 0) || !mxStream
.is() || !mxSeekable
.is())
688 PositionHolder
pos(mxSeekable
);
693 ensureOLEIsInitialized();
695 if (mpOLEStorage
->maStreams
.size() <= id
)
698 return createWPXStream(mpOLEStorage
->getStream(id
));
705 ensureZipIsInitialized();
707 if (mpZipStorage
->maStreams
.size() <= id
)
712 return createWPXStream(mpZipStorage
->getStream(id
));
714 catch (const Exception
&)
722 void WPXSvInputStreamImpl::invalidateReadBuffer()
726 seek(tell() + static_cast<tools::Long
>(mnReadBufferPos
)
727 - static_cast<tools::Long
>(mnReadBufferLength
));
728 mpReadBuffer
= nullptr;
730 mnReadBufferLength
= 0;
734 librevenge::RVNGInputStream
*
735 WPXSvInputStreamImpl::createWPXStream(const tools::SvRef
<SotStorageStream
>& rxStorage
)
739 Reference
<XInputStream
> xContents(new utl::OSeekableInputStreamWrapper(rxStorage
.get()));
740 return new WPXSvInputStream(xContents
);
745 librevenge::RVNGInputStream
*
746 WPXSvInputStreamImpl::createWPXStream(const Reference
<XInputStream
>& rxStream
)
749 return new WPXSvInputStream(rxStream
);
754 bool WPXSvInputStreamImpl::isOLE()
758 assert(0 == mxSeekable
->getPosition());
760 std::unique_ptr
<SvStream
> pStream(utl::UcbStreamHelper::CreateStream(mxStream
));
761 if (pStream
&& SotStorage::IsOLEStorage(pStream
.get()))
762 mpOLEStorage
.reset(new OLEStorageImpl());
767 return bool(mpOLEStorage
);
770 bool WPXSvInputStreamImpl::isZip()
774 assert(0 == mxSeekable
->getPosition());
778 Sequence
<Any
> aArgs(1);
779 aArgs
[0] <<= mxStream
;
781 const Reference
<XComponentContext
> xContext(comphelper::getProcessComponentContext(),
783 const Reference
<packages::zip::XZipFileAccess2
> xZip(
784 xContext
->getServiceManager()->createInstanceWithArgumentsAndContext(
785 "com.sun.star.packages.zip.ZipFileAccess", aArgs
, xContext
),
787 mpZipStorage
.reset(new ZipStorageImpl(xZip
));
789 catch (const Exception
&)
797 return bool(mpZipStorage
);
800 void WPXSvInputStreamImpl::ensureOLEIsInitialized()
802 assert(mpOLEStorage
);
804 if (!mpOLEStorage
->mbInitialized
)
805 mpOLEStorage
->initialize(utl::UcbStreamHelper::CreateStream(mxStream
));
808 void WPXSvInputStreamImpl::ensureZipIsInitialized()
810 assert(mpZipStorage
);
812 if (!mpZipStorage
->mbInitialized
)
813 mpZipStorage
->initialize();
816 WPXSvInputStream::WPXSvInputStream(Reference
<XInputStream
> const& xStream
)
817 : mpImpl(new WPXSvInputStreamImpl(xStream
))
821 WPXSvInputStream::~WPXSvInputStream() {}
823 #define BUFFER_MAX 65536
825 const unsigned char* WPXSvInputStream::read(unsigned long numBytes
, unsigned long& numBytesRead
)
829 if (numBytes
== 0 || numBytes
> std::numeric_limits
<unsigned long>::max() / 2)
832 if (mpImpl
->mpReadBuffer
)
834 if ((mpImpl
->mnReadBufferPos
+ numBytes
> mpImpl
->mnReadBufferPos
)
835 && (mpImpl
->mnReadBufferPos
+ numBytes
<= mpImpl
->mnReadBufferLength
))
837 const unsigned char* pTmp
= mpImpl
->mpReadBuffer
+ mpImpl
->mnReadBufferPos
;
838 mpImpl
->mnReadBufferPos
+= numBytes
;
839 numBytesRead
= numBytes
;
843 mpImpl
->invalidateReadBuffer();
846 unsigned long curpos
= static_cast<unsigned long>(mpImpl
->tell());
847 if (curpos
== static_cast<unsigned long>(-1)) // returned ERROR
850 if ((curpos
+ numBytes
< curpos
) /*overflow*/
851 || (curpos
+ numBytes
852 >= o3tl::make_unsigned(mpImpl
->mnLength
))) /*reading more than available*/
854 numBytes
= mpImpl
->mnLength
- curpos
;
857 if (numBytes
< BUFFER_MAX
)
859 if (BUFFER_MAX
< mpImpl
->mnLength
- curpos
)
860 mpImpl
->mnReadBufferLength
= BUFFER_MAX
;
861 else /* BUFFER_MAX >= mpImpl->mnLength - curpos */
862 mpImpl
->mnReadBufferLength
= mpImpl
->mnLength
- curpos
;
865 mpImpl
->mnReadBufferLength
= numBytes
;
867 unsigned long tmpNumBytes(0);
868 mpImpl
->mpReadBuffer
= mpImpl
->read(mpImpl
->mnReadBufferLength
, tmpNumBytes
);
869 if (tmpNumBytes
!= mpImpl
->mnReadBufferLength
)
870 mpImpl
->mnReadBufferLength
= tmpNumBytes
;
872 mpImpl
->mnReadBufferPos
= 0;
873 if (!mpImpl
->mnReadBufferLength
)
876 if (numBytes
<= mpImpl
->mnReadBufferLength
)
877 numBytesRead
= numBytes
;
879 numBytesRead
= mpImpl
->mnReadBufferLength
;
881 mpImpl
->mnReadBufferPos
+= numBytesRead
;
882 return mpImpl
->mpReadBuffer
;
885 long WPXSvInputStream::tell()
887 tools::Long retVal
= mpImpl
->tell();
888 return retVal
- static_cast<tools::Long
>(mpImpl
->mnReadBufferLength
)
889 + static_cast<tools::Long
>(mpImpl
->mnReadBufferPos
);
892 int WPXSvInputStream::seek(long offset
, librevenge::RVNG_SEEK_TYPE seekType
)
894 sal_Int64 tmpOffset
= offset
;
895 if (seekType
== librevenge::RVNG_SEEK_CUR
)
897 if (seekType
== librevenge::RVNG_SEEK_END
)
898 tmpOffset
+= mpImpl
->mnLength
;
906 if (tmpOffset
> mpImpl
->mnLength
)
908 tmpOffset
= mpImpl
->mnLength
;
912 if (tmpOffset
< mpImpl
->tell()
913 && o3tl::make_unsigned(tmpOffset
)
914 >= static_cast<unsigned long>(mpImpl
->tell()) - mpImpl
->mnReadBufferLength
)
916 mpImpl
->mnReadBufferPos
= static_cast<unsigned long>(
917 tmpOffset
+ static_cast<tools::Long
>(mpImpl
->mnReadBufferLength
) - mpImpl
->tell());
921 mpImpl
->invalidateReadBuffer();
923 if (mpImpl
->seek(tmpOffset
))
928 bool WPXSvInputStream::isEnd()
930 return mpImpl
->isEnd() && mpImpl
->mnReadBufferPos
== mpImpl
->mnReadBufferLength
;
933 bool WPXSvInputStream::isStructured()
935 mpImpl
->invalidateReadBuffer();
936 return mpImpl
->isStructured();
939 unsigned WPXSvInputStream::subStreamCount()
941 mpImpl
->invalidateReadBuffer();
942 return mpImpl
->subStreamCount();
945 const char* WPXSvInputStream::subStreamName(const unsigned id
)
947 mpImpl
->invalidateReadBuffer();
948 return mpImpl
->subStreamName(id
);
951 bool WPXSvInputStream::existsSubStream(const char* const name
)
953 mpImpl
->invalidateReadBuffer();
954 return mpImpl
->existsSubStream(name
);
957 librevenge::RVNGInputStream
* WPXSvInputStream::getSubStreamByName(const char* name
)
959 mpImpl
->invalidateReadBuffer();
960 return mpImpl
->getSubStreamByName(name
);
963 librevenge::RVNGInputStream
* WPXSvInputStream::getSubStreamById(const unsigned id
)
965 mpImpl
->invalidateReadBuffer();
966 return mpImpl
->getSubStreamById(id
);
970 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */