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/Any.hxx>
15 #include <comphelper/processfactory.hxx>
16 #include <comphelper/seekableinput.hxx>
18 #include <rtl/string.hxx>
20 #include <sot/storage.hxx>
22 #include <tools/stream.hxx>
23 #include <unotools/streamwrap.hxx>
24 #include <unotools/ucbstreamhelper.hxx>
29 #include <boost/noncopyable.hpp>
30 #include <boost/scoped_ptr.hpp>
31 #include <unordered_map>
33 namespace writerperfect
36 using namespace ::com::sun::star::uno
;
37 using namespace ::com::sun::star::io
;
39 namespace container
= com::sun::star::container
;
40 namespace lang
= com::sun::star::lang
;
41 namespace packages
= com::sun::star::packages
;
46 class PositionHolder
: boost::noncopyable
49 explicit PositionHolder(const Reference
<XSeekable
> &rxSeekable
);
53 const Reference
<XSeekable
> mxSeekable
;
54 const sal_uInt64 mnPosition
;
57 PositionHolder::PositionHolder(const Reference
<XSeekable
> &rxSeekable
)
58 : mxSeekable(rxSeekable
)
59 , mnPosition(rxSeekable
->getPosition())
63 PositionHolder::~PositionHolder() try
65 mxSeekable
->seek(mnPosition
);
71 } // anonymous namespace
75 tools::SvRef
<SotStorage
> ref
;
76 } SotStorageRefWrapper
;
80 tools::SvRef
<SotStorageStream
> ref
;
81 } SotStorageStreamRefWrapper
;
86 rtl::OUString
lcl_normalizeSubStreamPath(const rtl::OUString
&rPath
)
88 // accept paths which begin by '/'
89 // TODO: maybe this should to a full normalization
90 if (rPath
.startsWith("/") && rPath
.getLength() >= 2)
100 const rtl::OUString
concatPath(const rtl::OUString
&lhs
, const rtl::OUString
&rhs
)
104 return lhs
+ "/" + rhs
;
109 explicit OLEStreamData(const rtl::OString
&rName
);
111 SotStorageStreamRefWrapper stream
;
113 /** Name of the stream.
115 * This is not @c rtl::OUString, because we need to be able to
116 * produce const char* from it.
121 typedef std::unordered_map
<rtl::OUString
, std::size_t, rtl::OUStringHash
> NameMap_t
;
122 typedef std::unordered_map
<rtl::OUString
, SotStorageRefWrapper
, rtl::OUStringHash
> OLEStorageMap_t
;
124 /** Representation of an OLE2 storage.
126 * This class tries to bring a bit of sanity to use of SotStorage with
127 * respect to the needs of @c librevenge::RVNGInputStream API. It
128 * holds all nested storages for the whole lifetime (more precisely,
129 * since initialization, which is performed by calling @c
130 * initialize()), thus ensuring that no created stream is destroyed
131 * just because its parent storage went out of scope. It also holds a
132 * bidirectional map of stream names to their indexes (index of a
133 * stream is determined by deep-first traversal), which is also
134 * populated during initialization (member variables @c maStreams and
137 * Streams are created on demand (and saved, for the same reason as
140 struct OLEStorageImpl
144 void initialize(SvStream
*pStream
);
146 tools::SvRef
<SotStorageStream
> getStream(const rtl::OUString
&rPath
);
147 tools::SvRef
<SotStorageStream
> getStream(std::size_t nId
);
150 void traverse(const tools::SvRef
<SotStorage
> &rStorage
, const rtl::OUString
&rPath
);
152 tools::SvRef
<SotStorageStream
> createStream(const rtl::OUString
&rPath
);
155 SotStorageRefWrapper mxRootStorage
; //< root storage of the OLE2
156 OLEStorageMap_t maStorageMap
; //< map of all sub storages by name
157 ::std::vector
< OLEStreamData
> maStreams
; //< list of streams and their names
158 NameMap_t maNameMap
; //< map of stream names to indexes (into @c maStreams)
162 OLEStreamData::OLEStreamData(const rtl::OString
&rName
)
168 OLEStorageImpl::OLEStorageImpl()
173 , mbInitialized(false)
177 void OLEStorageImpl::initialize(SvStream
*const pStream
)
182 mxRootStorage
.ref
= new SotStorage(pStream
, true);
184 traverse(mxRootStorage
.ref
, "");
186 mbInitialized
= true;
189 tools::SvRef
<SotStorageStream
> OLEStorageImpl::getStream(const rtl::OUString
&rPath
)
191 const rtl::OUString
aPath(lcl_normalizeSubStreamPath(rPath
));
192 NameMap_t::iterator aIt
= maNameMap
.find(aPath
);
194 // For the while don't return stream in this situation.
195 // Later, given how libcdr's zip stream implementation behaves,
196 // return the first stream in the storage if there is one.
197 if (maNameMap
.end() == aIt
)
198 return tools::SvRef
<SotStorageStream
>();
200 if (!maStreams
[aIt
->second
].stream
.ref
.Is())
201 maStreams
[aIt
->second
].stream
.ref
= createStream(aPath
);
203 return maStreams
[aIt
->second
].stream
.ref
;
206 tools::SvRef
<SotStorageStream
> OLEStorageImpl::getStream(const std::size_t nId
)
208 if (!maStreams
[nId
].stream
.ref
.Is())
209 maStreams
[nId
].stream
.ref
= createStream(rtl::OStringToOUString(maStreams
[nId
].name
, RTL_TEXTENCODING_UTF8
));
211 return maStreams
[nId
].stream
.ref
;
214 void OLEStorageImpl::traverse(const tools::SvRef
<SotStorage
> &rStorage
, const rtl::OUString
&rPath
)
216 SvStorageInfoList infos
;
218 rStorage
->FillInfoList(&infos
);
220 for (SvStorageInfoList::const_iterator aIt
= infos
.begin(); infos
.end() != aIt
; ++aIt
)
224 maStreams
.push_back(OLEStreamData(rtl::OUStringToOString(concatPath(rPath
, aIt
->GetName()), RTL_TEXTENCODING_UTF8
)));
225 maNameMap
[concatPath(rPath
, aIt
->GetName())] = maStreams
.size() - 1;
227 else if (aIt
->IsStorage())
229 const rtl::OUString aPath
= concatPath(rPath
, aIt
->GetName());
230 SotStorageRefWrapper xStorage
;
231 xStorage
.ref
= rStorage
->OpenSotStorage(aIt
->GetName(), STREAM_STD_READ
);
232 maStorageMap
[aPath
] = xStorage
;
234 // deep-first traversal
235 traverse(xStorage
.ref
, aPath
);
239 SAL_WARN("writerperfect", "OLEStorageImpl::traverse: invalid storage entry, neither stream nor file");
244 tools::SvRef
<SotStorageStream
> OLEStorageImpl::createStream(const rtl::OUString
&rPath
)
246 const sal_Int32 nDelim
= rPath
.lastIndexOf(sal_Unicode('/'));
249 return mxRootStorage
.ref
->OpenSotStream(rPath
, STREAM_STD_READ
);
251 const rtl::OUString aDir
= rPath
.copy(0, nDelim
);
252 const rtl::OUString aName
= rPath
.copy(nDelim
+ 1);
254 const OLEStorageMap_t::const_iterator aIt
= maStorageMap
.find(aDir
);
256 if (maStorageMap
.end() == aIt
)
259 return aIt
->second
.ref
->OpenSotStream(aName
, STREAM_STD_READ
);
269 explicit ZipStreamData(const rtl::OString
&rName
);
271 Reference
<XInputStream
> xStream
;
273 /** Name of the stream.
275 * This is not @c rtl::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 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 rtl::OUString
&rPath
);
299 Reference
<XInputStream
> getStream(std::size_t nId
);
302 void traverse(const Reference
<container::XNameAccess
> &rxEnum
);
304 Reference
<XInputStream
> createStream(const rtl::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(const rtl::OString
&rName
)
319 ZipStorageImpl::ZipStorageImpl(const Reference
<container::XNameAccess
> &rxContainer
)
320 : mxContainer(rxContainer
)
323 , mbInitialized(false)
325 assert(mxContainer
.is());
328 void ZipStorageImpl::initialize()
330 traverse(mxContainer
);
332 mbInitialized
= true;
335 Reference
<XInputStream
> ZipStorageImpl::getStream(const rtl::OUString
&rPath
)
337 const rtl::OUString
aPath(lcl_normalizeSubStreamPath(rPath
));
338 NameMap_t::iterator aIt
= maNameMap
.find(aPath
);
340 // For the while don't return stream in this situation.
341 // Later, given how libcdr's zip stream implementation behaves,
342 // return the first stream in the storage if there is one.
343 if (maNameMap
.end() == aIt
)
344 return Reference
<XInputStream
>();
346 if (!maStreams
[aIt
->second
].xStream
.is())
347 maStreams
[aIt
->second
].xStream
= createStream(aPath
);
349 return maStreams
[aIt
->second
].xStream
;
352 Reference
<XInputStream
> ZipStorageImpl::getStream(const std::size_t nId
)
354 if (!maStreams
[nId
].xStream
.is())
355 maStreams
[nId
].xStream
= createStream(rtl::OStringToOUString(maStreams
[nId
].aName
, RTL_TEXTENCODING_UTF8
));
357 return maStreams
[nId
].xStream
;
360 void ZipStorageImpl::traverse(const Reference
<container::XNameAccess
> &rxContainer
)
362 const Sequence
<rtl::OUString
> lNames
= rxContainer
->getElementNames();
364 maStreams
.reserve(lNames
.getLength());
366 for (sal_Int32 n
= 0; n
< lNames
.getLength(); ++n
)
368 if (!lNames
[n
].endsWith("/")) // skip dirs
370 maStreams
.push_back(ZipStreamData(rtl::OUStringToOString(lNames
[n
], RTL_TEXTENCODING_UTF8
)));
371 maNameMap
[lNames
[n
]] = maStreams
.size() - 1;
376 Reference
<XInputStream
> ZipStorageImpl::createStream(const rtl::OUString
&rPath
)
378 Reference
<XInputStream
> xStream
;
382 const Reference
<XInputStream
> xInputStream(mxContainer
->getByName(rPath
), UNO_QUERY_THROW
);
383 const Reference
<XSeekable
> xSeekable(xInputStream
, UNO_QUERY
);
386 xStream
= xInputStream
;
388 xStream
.set(new comphelper::OSeekableInputWrapper(xInputStream
, comphelper::getProcessComponentContext()));
390 catch (const Exception
&)
400 class WPXSvInputStreamImpl
403 WPXSvInputStreamImpl(::com::sun::star::uno::Reference
<
404 ::com::sun::star::io::XInputStream
> xStream
);
405 ~WPXSvInputStreamImpl();
408 unsigned subStreamCount();
409 const char *subStreamName(unsigned id
);
410 bool existsSubStream(const char *name
);
411 librevenge::RVNGInputStream
*getSubStreamByName(const char *name
);
412 librevenge::RVNGInputStream
*getSubStreamById(unsigned id
);
414 const unsigned char *read(unsigned long numBytes
, unsigned long &numBytesRead
);
415 int seek(long offset
);
419 void invalidateReadBuffer();
423 void ensureOLEIsInitialized();
426 void ensureZipIsInitialized();
428 static librevenge::RVNGInputStream
*createWPXStream(const tools::SvRef
<SotStorageStream
> &rxStorage
);
429 static librevenge::RVNGInputStream
*createWPXStream(const Reference
<XInputStream
> &rxStream
);
432 ::com::sun::star::uno::Reference
< ::com::sun::star::io::XInputStream
> mxStream
;
433 ::com::sun::star::uno::Reference
< ::com::sun::star::io::XSeekable
> mxSeekable
;
434 ::com::sun::star::uno::Sequence
< sal_Int8
> maData
;
435 boost::scoped_ptr
< OLEStorageImpl
> mpOLEStorage
;
436 boost::scoped_ptr
< ZipStorageImpl
> mpZipStorage
;
441 const unsigned char *mpReadBuffer
;
442 unsigned long mnReadBufferLength
;
443 unsigned long mnReadBufferPos
;
446 WPXSvInputStreamImpl::WPXSvInputStreamImpl(Reference
< XInputStream
> xStream
) :
448 mxSeekable(xStream
, UNO_QUERY
),
456 mnReadBufferLength(0),
459 if (!xStream
.is() || !mxStream
.is())
463 if (!mxSeekable
.is())
469 mnLength
= mxSeekable
->getLength();
470 if (0 < mxSeekable
->getPosition())
475 SAL_WARN("writerperfect", "mnLength = mxSeekable->getLength() threw exception");
482 WPXSvInputStreamImpl::~WPXSvInputStreamImpl()
486 const unsigned char *WPXSvInputStreamImpl::read(unsigned long numBytes
, unsigned long &numBytesRead
)
490 if (numBytes
== 0 || isEnd())
493 numBytesRead
= mxStream
->readSomeBytes(maData
, numBytes
);
494 if (numBytesRead
== 0)
497 return reinterpret_cast<const unsigned char *>(maData
.getConstArray());
500 long WPXSvInputStreamImpl::tell()
502 if ((mnLength
== 0) || !mxStream
.is() || !mxSeekable
.is())
506 sal_Int64 tmpPosition
= mxSeekable
->getPosition();
507 if ((tmpPosition
< 0) || (tmpPosition
> LONG_MAX
))
509 return (long)tmpPosition
;
513 int WPXSvInputStreamImpl::seek(long offset
)
515 if ((mnLength
== 0) || !mxStream
.is() || !mxSeekable
.is())
518 sal_Int64 tmpPosition
= mxSeekable
->getPosition();
519 if ((tmpPosition
< 0) || (tmpPosition
> LONG_MAX
))
524 mxSeekable
->seek(offset
);
529 SAL_WARN("writerperfect", "mxSeekable->seek(offset) threw exception");
534 bool WPXSvInputStreamImpl::isEnd()
536 if ((mnLength
== 0) || !mxStream
.is() || !mxSeekable
.is())
538 return (mxSeekable
->getPosition() >= mnLength
);
541 bool WPXSvInputStreamImpl::isStructured()
543 if ((mnLength
== 0) || !mxStream
.is() || !mxSeekable
.is())
546 PositionHolder
pos(mxSeekable
);
557 unsigned WPXSvInputStreamImpl::subStreamCount()
559 if ((mnLength
== 0) || !mxStream
.is() || !mxSeekable
.is())
562 PositionHolder
pos(mxSeekable
);
567 ensureOLEIsInitialized();
569 return mpOLEStorage
->maStreams
.size();
576 ensureZipIsInitialized();
578 return mpZipStorage
->maStreams
.size();
584 const char *WPXSvInputStreamImpl::subStreamName(const unsigned id
)
586 if ((mnLength
== 0) || !mxStream
.is() || !mxSeekable
.is())
589 PositionHolder
pos(mxSeekable
);
594 ensureOLEIsInitialized();
596 if (mpOLEStorage
->maStreams
.size() <= id
)
599 return mpOLEStorage
->maStreams
[id
].name
.getStr();
606 ensureZipIsInitialized();
608 if (mpZipStorage
->maStreams
.size() <= id
)
611 return mpZipStorage
->maStreams
[id
].aName
.getStr();
617 bool WPXSvInputStreamImpl::existsSubStream(const char *const name
)
622 if ((mnLength
== 0) || !mxStream
.is() || !mxSeekable
.is())
625 PositionHolder
pos(mxSeekable
);
628 const rtl::OUString
aName(rtl::OStringToOUString(rtl::OString(name
), RTL_TEXTENCODING_UTF8
));
632 ensureOLEIsInitialized();
633 return mpOLEStorage
->maNameMap
.end() != mpOLEStorage
->maNameMap
.find(aName
);
640 ensureZipIsInitialized();
641 return mpZipStorage
->maNameMap
.end() != mpZipStorage
->maNameMap
.find(aName
);
647 librevenge::RVNGInputStream
*WPXSvInputStreamImpl::getSubStreamByName(const char *const name
)
652 if ((mnLength
== 0) || !mxStream
.is() || !mxSeekable
.is())
655 PositionHolder
pos(mxSeekable
);
658 const rtl::OUString
aName(rtl::OStringToOUString(rtl::OString(name
), RTL_TEXTENCODING_UTF8
));
662 ensureOLEIsInitialized();
663 return createWPXStream(mpOLEStorage
->getStream(aName
));
670 ensureZipIsInitialized();
674 return createWPXStream(mpZipStorage
->getStream(aName
));
676 catch (const Exception
&)
685 librevenge::RVNGInputStream
*WPXSvInputStreamImpl::getSubStreamById(const unsigned id
)
687 if ((mnLength
== 0) || !mxStream
.is() || !mxSeekable
.is())
690 PositionHolder
pos(mxSeekable
);
695 ensureOLEIsInitialized();
697 if (mpOLEStorage
->maStreams
.size() <= id
)
700 return createWPXStream(mpOLEStorage
->getStream(id
));
707 ensureZipIsInitialized();
709 if (mpZipStorage
->maStreams
.size() <= id
)
714 return createWPXStream(mpZipStorage
->getStream(id
));
716 catch (const Exception
&)
724 void WPXSvInputStreamImpl::invalidateReadBuffer()
728 seek((long) tell() + (long)mnReadBufferPos
- (long)mnReadBufferLength
);
731 mnReadBufferLength
= 0;
735 librevenge::RVNGInputStream
*WPXSvInputStreamImpl::createWPXStream(const tools::SvRef
<SotStorageStream
> &rxStorage
)
739 Reference
< XInputStream
> xContents(new utl::OSeekableInputStreamWrapper(rxStorage
));
740 return new WPXSvInputStream(xContents
);
745 librevenge::RVNGInputStream
*WPXSvInputStreamImpl::createWPXStream(const Reference
<XInputStream
> &rxStream
)
748 return new WPXSvInputStream(rxStream
);
753 bool WPXSvInputStreamImpl::isOLE()
757 assert(0 == mxSeekable
->getPosition());
759 boost::scoped_ptr
<SvStream
> pStream(utl::UcbStreamHelper::CreateStream(mxStream
));
760 if (pStream
&& SotStorage::IsOLEStorage(pStream
.get()))
761 mpOLEStorage
.reset(new OLEStorageImpl());
766 return bool(mpOLEStorage
);
769 bool WPXSvInputStreamImpl::isZip()
773 assert(0 == mxSeekable
->getPosition());
777 Sequence
<Any
> aArgs(1);
778 aArgs
[0] <<= mxStream
;
780 const Reference
<XComponentContext
> xContext(comphelper::getProcessComponentContext(), UNO_QUERY_THROW
);
781 const Reference
<packages::zip::XZipFileAccess2
> xZip(
782 xContext
->getServiceManager()->createInstanceWithArgumentsAndContext("com.sun.star.packages.zip.ZipFileAccess", aArgs
, xContext
),
784 mpZipStorage
.reset(new ZipStorageImpl(xZip
));
786 catch (const Exception
&)
794 return bool(mpZipStorage
);
797 void WPXSvInputStreamImpl::ensureOLEIsInitialized()
799 assert(mpOLEStorage
);
801 if (!mpOLEStorage
->mbInitialized
)
802 mpOLEStorage
->initialize(utl::UcbStreamHelper::CreateStream(mxStream
));
805 void WPXSvInputStreamImpl::ensureZipIsInitialized()
807 assert(mpZipStorage
);
809 if (!mpZipStorage
->mbInitialized
)
810 mpZipStorage
->initialize();
813 WPXSvInputStream::WPXSvInputStream(Reference
< XInputStream
> xStream
) :
814 mpImpl(new WPXSvInputStreamImpl(xStream
))
818 WPXSvInputStream::~WPXSvInputStream()
824 #define BUFFER_MAX 65536
826 const unsigned char *WPXSvInputStream::read(unsigned long numBytes
, unsigned long &numBytesRead
)
830 if (numBytes
== 0 || numBytes
> (std::numeric_limits
<unsigned long>::max
)()/2)
833 if (mpImpl
->mpReadBuffer
)
835 if ((mpImpl
->mnReadBufferPos
+ numBytes
> mpImpl
->mnReadBufferPos
) && (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
= (unsigned long) mpImpl
->tell();
847 if (curpos
== (unsigned long)-1) // returned ERROR
850 if ((curpos
+ numBytes
< curpos
) /*overflow*/ ||
851 (curpos
+ numBytes
>= (sal_uInt64
)mpImpl
->mnLength
)) /*reading more than available*/
853 numBytes
= mpImpl
->mnLength
- curpos
;
856 if (numBytes
< BUFFER_MAX
)
858 if (BUFFER_MAX
< mpImpl
->mnLength
- curpos
)
859 mpImpl
->mnReadBufferLength
= BUFFER_MAX
;
860 else /* BUFFER_MAX >= mpImpl->mnLength - curpos */
861 mpImpl
->mnReadBufferLength
= mpImpl
->mnLength
- curpos
;
864 mpImpl
->mnReadBufferLength
= numBytes
;
866 unsigned long tmpNumBytes(0);
867 mpImpl
->mpReadBuffer
= mpImpl
->read(mpImpl
->mnReadBufferLength
, tmpNumBytes
);
868 if (tmpNumBytes
!= mpImpl
->mnReadBufferLength
)
869 mpImpl
->mnReadBufferLength
= tmpNumBytes
;
871 mpImpl
->mnReadBufferPos
= 0;
872 if (!mpImpl
->mnReadBufferLength
)
875 numBytesRead
= numBytes
;
877 mpImpl
->mnReadBufferPos
+= numBytesRead
;
878 return mpImpl
->mpReadBuffer
;
881 long WPXSvInputStream::tell()
883 long retVal
= mpImpl
->tell();
884 return retVal
- (long)mpImpl
->mnReadBufferLength
+ (long)mpImpl
->mnReadBufferPos
;
887 int WPXSvInputStream::seek(long offset
, librevenge::RVNG_SEEK_TYPE seekType
)
889 sal_Int64 tmpOffset
= offset
;
890 if (seekType
== librevenge::RVNG_SEEK_CUR
)
892 if (seekType
== librevenge::RVNG_SEEK_END
)
893 tmpOffset
+= mpImpl
->mnLength
;
901 if (tmpOffset
> mpImpl
->mnLength
)
903 tmpOffset
= mpImpl
->mnLength
;
907 if (tmpOffset
< mpImpl
->tell() && (unsigned long)tmpOffset
>= (unsigned long)mpImpl
->tell() - mpImpl
->mnReadBufferLength
)
909 mpImpl
->mnReadBufferPos
= (unsigned long)(tmpOffset
+ (long) mpImpl
->mnReadBufferLength
- (long) mpImpl
->tell());
913 mpImpl
->invalidateReadBuffer();
915 if (mpImpl
->seek(tmpOffset
))
920 bool WPXSvInputStream::isEnd()
922 return mpImpl
->isEnd() && mpImpl
->mnReadBufferPos
== mpImpl
->mnReadBufferLength
;
925 bool WPXSvInputStream::isStructured()
927 mpImpl
->invalidateReadBuffer();
928 return mpImpl
->isStructured();
931 unsigned WPXSvInputStream::subStreamCount()
933 mpImpl
->invalidateReadBuffer();
934 return mpImpl
->subStreamCount();
937 const char *WPXSvInputStream::subStreamName(const unsigned id
)
939 mpImpl
->invalidateReadBuffer();
940 return mpImpl
->subStreamName(id
);
943 bool WPXSvInputStream::existsSubStream(const char *const name
)
945 mpImpl
->invalidateReadBuffer();
946 return mpImpl
->existsSubStream(name
);
949 librevenge::RVNGInputStream
*WPXSvInputStream::getSubStreamByName(const char *name
)
951 mpImpl
->invalidateReadBuffer();
952 return mpImpl
->getSubStreamByName(name
);
955 librevenge::RVNGInputStream
*WPXSvInputStream::getSubStreamById(const unsigned id
)
957 mpImpl
->invalidateReadBuffer();
958 return mpImpl
->getSubStreamById(id
);
963 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */