Version 7.6.3.2-android, tag libreoffice-7.6.3.2-android
[LibreOffice.git] / svx / source / xml / xmleohlp.cxx
bloba7392cdeefe0b4f8aa244bc20ab9fe081720bbaf
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
21 #include <com/sun/star/io/XStream.hpp>
22 #include <com/sun/star/beans/XPropertySet.hpp>
23 #include <com/sun/star/embed/XTransactedObject.hpp>
24 #include <com/sun/star/embed/ElementModes.hpp>
25 #include <com/sun/star/embed/XEmbeddedObject.hpp>
26 #include <com/sun/star/embed/XEmbedPersist.hpp>
27 #include <com/sun/star/embed/EmbedStates.hpp>
28 #include <com/sun/star/embed/Aspects.hpp>
29 #include <com/sun/star/lang/WrappedTargetRuntimeException.hpp>
30 #include <osl/diagnose.h>
31 #include <sot/storage.hxx>
32 #include <tools/debug.hxx>
33 #include <sal/log.hxx>
34 #include <unotools/streamwrap.hxx>
35 #include <unotools/tempfile.hxx>
37 #include <svtools/embedhlp.hxx>
38 #include <unotools/ucbstreamhelper.hxx>
39 #include <comphelper/propertyvalue.hxx>
40 #include <comphelper/storagehelper.hxx>
41 #include <comphelper/embeddedobjectcontainer.hxx>
43 #include <comphelper/fileformat.h>
44 #include <cppuhelper/exc_hlp.hxx>
45 #include <cppuhelper/implbase.hxx>
46 #include <svx/xmleohlp.hxx>
47 #include <map>
48 #include <memory>
49 #include <mutex>
51 using namespace ::osl;
52 using namespace ::cppu;
53 using namespace ::utl;
54 using namespace ::com::sun::star;
55 using namespace ::com::sun::star::document;
56 using namespace ::com::sun::star::uno;
57 using namespace ::com::sun::star::container;
58 using namespace ::com::sun::star::io;
59 using namespace ::com::sun::star::lang;
61 constexpr OUStringLiteral XML_CONTAINERSTORAGE_NAME_60 = u"Pictures";
62 constexpr OUStringLiteral XML_CONTAINERSTORAGE_NAME = u"ObjectReplacements";
63 constexpr OUStringLiteral XML_EMBEDDEDOBJECT_URL_BASE = u"vnd.sun.star.EmbeddedObject:";
64 constexpr OUStringLiteral XML_EMBEDDEDOBJECTGRAPHIC_URL_BASE = u"vnd.sun.star.GraphicObject:";
67 class OutputStorageWrapper_Impl : public ::cppu::WeakImplHelper<XOutputStream>
69 std::mutex maMutex;
70 Reference < XOutputStream > xOut;
71 TempFileFast aTempFile;
72 bool bStreamClosed : 1;
73 SvStream* pStream;
75 public:
76 OutputStorageWrapper_Impl();
78 // css::io::XOutputStream
79 virtual void SAL_CALL writeBytes(const Sequence< sal_Int8 >& aData) override;
80 virtual void SAL_CALL flush() override;
81 virtual void SAL_CALL closeOutput() override;
83 SvStream* GetStream();
86 OutputStorageWrapper_Impl::OutputStorageWrapper_Impl()
87 : bStreamClosed( false )
88 , pStream(nullptr)
90 pStream = aTempFile.GetStream( StreamMode::READWRITE );
91 xOut = new OOutputStreamWrapper( *pStream );
94 SvStream *OutputStorageWrapper_Impl::GetStream()
96 if( bStreamClosed )
97 return pStream;
98 return nullptr;
101 void SAL_CALL OutputStorageWrapper_Impl::writeBytes(
102 const Sequence< sal_Int8 >& aData)
104 std::scoped_lock aGuard( maMutex );
105 xOut->writeBytes( aData );
108 void SAL_CALL OutputStorageWrapper_Impl::flush()
110 std::scoped_lock aGuard( maMutex );
111 xOut->flush();
114 void SAL_CALL OutputStorageWrapper_Impl::closeOutput()
116 std::scoped_lock aGuard( maMutex );
117 xOut->closeOutput();
118 bStreamClosed = true;
121 SvXMLEmbeddedObjectHelper::SvXMLEmbeddedObjectHelper() :
122 mpDocPersist( nullptr ),
123 meCreateMode( SvXMLEmbeddedObjectHelperMode::Read )
127 SvXMLEmbeddedObjectHelper::SvXMLEmbeddedObjectHelper( ::comphelper::IEmbeddedHelper& rDocPersist, SvXMLEmbeddedObjectHelperMode eCreateMode ) :
128 mpDocPersist( nullptr ),
129 meCreateMode( SvXMLEmbeddedObjectHelperMode::Read )
131 Init( nullptr, rDocPersist, eCreateMode );
134 SvXMLEmbeddedObjectHelper::~SvXMLEmbeddedObjectHelper()
138 void SvXMLEmbeddedObjectHelper::disposing(std::unique_lock<std::mutex>&)
140 if( mxTempStorage.is() )
142 mxTempStorage->dispose();
143 mxTempStorage.clear();
147 void SvXMLEmbeddedObjectHelper::splitObjectURL(const OUString& _aURLNoPar,
148 OUString& rContainerStorageName,
149 OUString& rObjectStorageName)
151 DBG_ASSERT(_aURLNoPar.isEmpty() || '#' != _aURLNoPar[0], "invalid object URL" );
152 OUString aURLNoPar = _aURLNoPar;
154 sal_Int32 _nPos = aURLNoPar.lastIndexOf( '/' );
155 if( -1 == _nPos )
157 rContainerStorageName.clear();
158 rObjectStorageName = aURLNoPar;
160 else
162 //eliminate 'superfluous' slashes at start and end
163 //#i103076# load objects with all allowed xlink:href syntaxes
165 //eliminate './' at start
166 sal_Int32 nStart = 0;
167 sal_Int32 nCount = aURLNoPar.getLength();
168 if( aURLNoPar.startsWith( "./" ) )
170 nStart = 2;
171 nCount -= 2;
174 //eliminate '/' at end
175 sal_Int32 nEnd = aURLNoPar.lastIndexOf( '/' );
176 if( nEnd == aURLNoPar.getLength()-1 && nEnd != (nStart-1) )
177 nCount--;
179 aURLNoPar = aURLNoPar.copy( nStart, nCount );
182 _nPos = aURLNoPar.lastIndexOf( '/' );
183 if( _nPos >= 0 )
184 rContainerStorageName = aURLNoPar.copy( 0, _nPos );
185 rObjectStorageName = aURLNoPar.copy( _nPos+1 );
189 bool SvXMLEmbeddedObjectHelper::ImplGetStorageNames(
190 const OUString& rURLStr,
191 OUString& rContainerStorageName,
192 OUString& rObjectStorageName,
193 bool bInternalToExternal,
194 bool *pGraphicRepl,
195 bool *pOasisFormat ) const
197 // internal URL: vnd.sun.star.EmbeddedObject:<object-name>
198 // or: vnd.sun.star.EmbeddedObject:<path>/<object-name>
199 // internal replacement images:
200 // vnd.sun.star.EmbeddedObjectGraphic:<object-name>
201 // or: vnd.sun.star.EmbeddedObjectGraphic:<path>/<object-name>
202 // external URL: ./<path>/<object-name>
203 // or: <path>/<object-name>
204 // or: <object-name>
205 // currently, path may only consist of a single directory name
206 // it is also possible to have additional arguments at the end of URL: <main URL>[?<name>=<value>[,<name>=<value>]*]
208 if( pGraphicRepl )
209 *pGraphicRepl = false;
211 if( pOasisFormat )
212 *pOasisFormat = true; // the default value
214 if( rURLStr.isEmpty() )
215 return false;
217 // get rid of arguments
218 sal_Int32 nPos = rURLStr.indexOf( '?' );
219 OUString aURLNoPar;
220 if ( nPos == -1 )
221 aURLNoPar = rURLStr;
222 else
224 aURLNoPar = rURLStr.copy( 0, nPos );
226 // check the arguments
227 nPos++;
228 while( nPos >= 0 && nPos < rURLStr.getLength() )
230 OUString aToken = rURLStr.getToken( 0, ',', nPos );
231 if ( aToken.equalsIgnoreAsciiCase( "oasis=false" ) )
233 if ( pOasisFormat )
234 *pOasisFormat = false;
235 break;
237 else
239 SAL_WARN( "svx", "invalid arguments was found in URL!" );
244 if( bInternalToExternal )
246 nPos = aURLNoPar.indexOf( ':' );
247 if( -1 == nPos )
248 return false;
249 bool bObjUrl = aURLNoPar.startsWith( XML_EMBEDDEDOBJECT_URL_BASE );
250 bool bGrUrl = !bObjUrl &&
251 aURLNoPar.startsWith( XML_EMBEDDEDOBJECTGRAPHIC_URL_BASE );
252 if( !(bObjUrl || bGrUrl) )
253 return false;
255 sal_Int32 nPathStart = nPos + 1;
256 nPos = aURLNoPar.lastIndexOf( '/' );
257 if( -1 == nPos )
259 rContainerStorageName.clear();
260 rObjectStorageName = aURLNoPar.copy( nPathStart );
262 else if( nPos > nPathStart )
264 rContainerStorageName = aURLNoPar.copy( nPathStart, nPos-nPathStart);
265 rObjectStorageName = aURLNoPar.copy( nPos+1 );
267 else
268 return false;
270 if( bGrUrl )
272 bool bOASIS = mxRootStorage.is() &&
273 ( SotStorage::GetVersion( mxRootStorage ) > SOFFICE_FILEFORMAT_60 );
274 if (bOASIS)
275 rContainerStorageName = XML_CONTAINERSTORAGE_NAME;
276 else
277 rContainerStorageName = XML_CONTAINERSTORAGE_NAME_60;
279 if( pGraphicRepl )
280 *pGraphicRepl = true;
285 else
287 splitObjectURL(aURLNoPar, rContainerStorageName, rObjectStorageName);
290 if( -1 != rContainerStorageName.indexOf( '/' ) )
292 OSL_FAIL( "SvXMLEmbeddedObjectHelper: invalid path name" );
293 return false;
296 return true;
299 uno::Reference < embed::XStorage > const & SvXMLEmbeddedObjectHelper::ImplGetContainerStorage(
300 const OUString& rStorageName )
302 DBG_ASSERT( -1 == rStorageName.indexOf( '/' ) &&
303 -1 == rStorageName.indexOf( '\\' ),
304 "nested embedded storages aren't supported" );
305 if( !mxContainerStorage.is() ||
306 ( rStorageName != maCurContainerStorageName ) )
308 if( mxContainerStorage.is() &&
309 !maCurContainerStorageName.isEmpty() &&
310 SvXMLEmbeddedObjectHelperMode::Write == meCreateMode )
312 uno::Reference < embed::XTransactedObject > xTrans( mxContainerStorage, uno::UNO_QUERY );
313 if ( xTrans.is() )
314 xTrans->commit();
317 if( !rStorageName.isEmpty() && mxRootStorage.is() )
319 sal_Int32 nMode = SvXMLEmbeddedObjectHelperMode::Write == meCreateMode
320 ? ::embed::ElementModes::READWRITE
321 : ::embed::ElementModes::READ;
322 mxContainerStorage = mxRootStorage->openStorageElement( rStorageName,
323 nMode );
325 else
327 mxContainerStorage = mxRootStorage;
329 maCurContainerStorageName = rStorageName;
332 return mxContainerStorage;
335 void SvXMLEmbeddedObjectHelper::ImplReadObject(
336 const OUString& rContainerStorageName,
337 OUString& rObjName,
338 const SvGlobalName *, // pClassId, see "TODO/LATER" below
339 SvStream* pTemp )
341 uno::Reference < embed::XStorage > xDocStor( mpDocPersist->getStorage() );
342 uno::Reference < embed::XStorage > xCntnrStor( ImplGetContainerStorage( rContainerStorageName ) );
344 if( !xCntnrStor.is() && !pTemp )
345 return;
347 OUString aSrcObjName( rObjName );
348 comphelper::EmbeddedObjectContainer& rContainer = mpDocPersist->getEmbeddedObjectContainer();
350 // Is the object name unique?
351 // if the object is already instantiated by GetEmbeddedObject
352 // that means that the duplication is being loaded
353 bool bDuplicate = rContainer.HasInstantiatedEmbeddedObject( rObjName );
354 DBG_ASSERT( !bDuplicate, "An object in the document is referenced twice!" );
356 if( xDocStor != xCntnrStor || pTemp || bDuplicate )
358 // TODO/LATER: make this altogether a method in the EmbeddedObjectContainer
360 // create a unique name for the duplicate object
361 if( bDuplicate )
362 rObjName = rContainer.CreateUniqueObjectName();
364 if( pTemp )
368 pTemp->Seek( 0 );
369 uno::Reference < io::XStream > xStm = xDocStor->openStreamElement( rObjName,
370 embed::ElementModes::READWRITE | embed::ElementModes::TRUNCATE );
371 std::unique_ptr<SvStream> pStream(::utl::UcbStreamHelper::CreateStream( xStm ));
372 pTemp->ReadStream( *pStream );
373 pStream.reset();
375 // TODO/LATER: what to do when other types of objects are based on substream persistence?
376 // This is an ole object
377 uno::Reference< beans::XPropertySet > xProps( xStm, uno::UNO_QUERY_THROW );
378 xProps->setPropertyValue(
379 "MediaType",
380 uno::Any( OUString( "application/vnd.sun.star.oleobject" ) ) );
382 xStm->getOutputStream()->closeOutput();
384 catch ( uno::Exception& )
386 return;
389 else
393 xCntnrStor->copyElementTo( aSrcObjName, xDocStor, rObjName );
395 catch ( uno::Exception& )
397 return;
402 // make object known to the container
403 // TODO/LATER: could be done a little bit more efficient!
404 OUString aName( rObjName );
406 // TODO/LATER: The provided pClassId is ignored for now.
407 // The stream contains OLE storage internally and this storage already has a class id specifying the
408 // server that was used to create the object. pClassId could be used to specify the server that should
409 // be used for the next opening, but this information seems to be out of the file format responsibility
410 // area.
411 OUString const baseURL(mpDocPersist->getDocumentBaseURL());
412 rContainer.GetEmbeddedObject(aName, &baseURL);
415 OUString SvXMLEmbeddedObjectHelper::ImplInsertEmbeddedObjectURL(
416 const OUString& rURLStr )
418 OUString sRetURL;
420 OUString aContainerStorageName, aObjectStorageName;
421 if( !ImplGetStorageNames( rURLStr, aContainerStorageName,
422 aObjectStorageName,
423 SvXMLEmbeddedObjectHelperMode::Write == meCreateMode ) )
424 return sRetURL;
426 if( SvXMLEmbeddedObjectHelperMode::Read == meCreateMode )
428 OutputStorageWrapper_Impl *pOut = nullptr;
429 std::map< OUString, rtl::Reference<OutputStorageWrapper_Impl> >::iterator aIter;
431 if( mxStreamMap )
433 aIter = mxStreamMap->find( rURLStr );
434 if( aIter != mxStreamMap->end() && aIter->second.is() )
435 pOut = aIter->second.get();
438 SvGlobalName aClassId, *pClassId = nullptr;
439 sal_Int32 nPos = aObjectStorageName.lastIndexOf( '!' );
440 if( -1 != nPos && aClassId.MakeId( aObjectStorageName.subView( nPos+1 ) ) )
442 aObjectStorageName = aObjectStorageName.copy( 0, nPos );
443 pClassId = &aClassId;
446 ImplReadObject( aContainerStorageName, aObjectStorageName, pClassId, pOut ? pOut->GetStream() : nullptr );
447 sRetURL = XML_EMBEDDEDOBJECT_URL_BASE + aObjectStorageName;
449 if( pOut )
451 mxStreamMap->erase( aIter );
454 else
456 // Objects are written using ::comphelper::IEmbeddedHelper::SaveAs
457 sRetURL = "./";
458 if( !aContainerStorageName.isEmpty() )
460 sRetURL += aContainerStorageName + "/";
462 sRetURL += aObjectStorageName;
465 return sRetURL;
468 uno::Reference< io::XInputStream > SvXMLEmbeddedObjectHelper::ImplGetReplacementImage(
469 const uno::Reference< embed::XEmbeddedObject >& xObj )
471 uno::Reference< io::XInputStream > xStream;
473 if( xObj.is() )
477 bool bSwitchBackToLoaded = false;
478 sal_Int32 nCurState = xObj->getCurrentState();
479 if ( nCurState == embed::EmbedStates::LOADED || nCurState == embed::EmbedStates::RUNNING )
481 // means that the object is not active
482 // copy replacement image from old to new container
483 OUString aMediaType;
484 xStream = mpDocPersist->getEmbeddedObjectContainer().GetGraphicStream( xObj, &aMediaType );
487 if ( !xStream.is() )
489 // the image must be regenerated
490 // TODO/LATER: another aspect could be used
491 if ( nCurState == embed::EmbedStates::LOADED )
492 bSwitchBackToLoaded = true;
494 OUString aMediaType;
495 xStream = svt::EmbeddedObjectRef::GetGraphicReplacementStream(
496 embed::Aspects::MSOLE_CONTENT,
497 xObj,
498 &aMediaType );
501 if ( bSwitchBackToLoaded )
502 // switch back to loaded state; that way we have a minimum cache confusion
503 xObj->changeState( embed::EmbedStates::LOADED );
505 catch( uno::Exception& )
509 return xStream;
512 void SvXMLEmbeddedObjectHelper::Init(
513 const uno::Reference < embed::XStorage >& rRootStorage,
514 ::comphelper::IEmbeddedHelper& rPersist,
515 SvXMLEmbeddedObjectHelperMode eCreateMode )
517 mxRootStorage = rRootStorage;
518 mpDocPersist = &rPersist;
519 meCreateMode = eCreateMode;
522 rtl::Reference<SvXMLEmbeddedObjectHelper> SvXMLEmbeddedObjectHelper::Create(
523 const uno::Reference < embed::XStorage >& rRootStorage,
524 ::comphelper::IEmbeddedHelper& rDocPersist,
525 SvXMLEmbeddedObjectHelperMode eCreateMode )
527 rtl::Reference<SvXMLEmbeddedObjectHelper> pThis(new SvXMLEmbeddedObjectHelper);
529 pThis->Init( rRootStorage, rDocPersist, eCreateMode );
531 return pThis;
534 rtl::Reference<SvXMLEmbeddedObjectHelper> SvXMLEmbeddedObjectHelper::Create(
535 ::comphelper::IEmbeddedHelper& rDocPersist,
536 SvXMLEmbeddedObjectHelperMode eCreateMode )
538 rtl::Reference<SvXMLEmbeddedObjectHelper> pThis(new SvXMLEmbeddedObjectHelper);
540 pThis->Init( nullptr, rDocPersist, eCreateMode );
542 return pThis;
545 OUString SAL_CALL SvXMLEmbeddedObjectHelper::resolveEmbeddedObjectURL(const OUString& rURL)
547 std::unique_lock aGuard( m_aMutex );
549 OUString sRet;
552 sRet = ImplInsertEmbeddedObjectURL(rURL);
554 catch (const RuntimeException&)
556 throw;
558 catch (const Exception&)
560 css::uno::Any anyEx = cppu::getCaughtException();
561 throw WrappedTargetRuntimeException(
562 "SvXMLEmbeddedObjectHelper::resolveEmbeddedObjectURL non-RuntimeException",
563 static_cast<uno::XWeak*>(this), anyEx);
565 return sRet;
568 // XNameAccess: alien objects!
569 Any SAL_CALL SvXMLEmbeddedObjectHelper::getByName(
570 const OUString& rURLStr )
572 std::unique_lock aGuard( m_aMutex );
573 Any aRet;
574 if( SvXMLEmbeddedObjectHelperMode::Read == meCreateMode )
576 Reference < XOutputStream > xStrm;
577 if( mxStreamMap )
579 auto aIter = mxStreamMap->find( rURLStr );
580 if( aIter != mxStreamMap->end() && aIter->second.is() )
581 xStrm = aIter->second.get();
583 if( !xStrm.is() )
585 rtl::Reference<OutputStorageWrapper_Impl> xOut = new OutputStorageWrapper_Impl;
586 if( !mxStreamMap )
587 mxStreamMap.emplace();
588 (*mxStreamMap)[rURLStr] = xOut;
589 xStrm = xOut.get();
592 aRet <<= xStrm;
594 else
596 bool bGraphicRepl = false;
597 bool bOasisFormat = true;
598 Reference < XInputStream > xStrm;
599 OUString aContainerStorageName, aObjectStorageName;
600 if( ImplGetStorageNames( rURLStr, aContainerStorageName,
601 aObjectStorageName,
602 true,
603 &bGraphicRepl,
604 &bOasisFormat ) )
608 comphelper::EmbeddedObjectContainer& rContainer =
609 mpDocPersist->getEmbeddedObjectContainer();
611 Reference < embed::XEmbeddedObject > xObj = rContainer.GetEmbeddedObject( aObjectStorageName );
612 DBG_ASSERT( xObj.is(), "Didn't get object" );
614 if( xObj.is() )
616 if( bGraphicRepl )
618 xStrm = ImplGetReplacementImage( xObj );
620 else
622 Reference < embed::XEmbedPersist > xPersist( xObj, UNO_QUERY );
623 if( xPersist.is() )
625 if( !mxTempStorage.is() )
626 mxTempStorage =
627 comphelper::OStorageHelper::GetTemporaryStorage();
628 Sequence < beans::PropertyValue > aDummy,
629 aEmbDescr{ comphelper::makePropertyValue("StoreVisualReplacement",
630 !bOasisFormat) };
631 if ( !bOasisFormat )
633 uno::Reference< io::XInputStream > xGrInStream = ImplGetReplacementImage( xObj );
634 if ( xGrInStream.is() )
636 aEmbDescr.realloc( 2 );
637 auto pEmbDescr = aEmbDescr.getArray();
638 pEmbDescr[1].Name = "VisualReplacement";
639 pEmbDescr[1].Value <<= xGrInStream;
643 xPersist->storeToEntry( mxTempStorage, aObjectStorageName,
644 aDummy, aEmbDescr );
645 Reference < io::XStream > xStream =
646 mxTempStorage->openStreamElement(
647 aObjectStorageName,
648 embed::ElementModes::READ);
649 if( xStream.is() )
650 xStrm = xStream->getInputStream();
655 catch ( uno::Exception& )
660 aRet <<= xStrm;
663 return aRet;
666 Sequence< OUString > SAL_CALL SvXMLEmbeddedObjectHelper::getElementNames()
668 return {};
671 sal_Bool SAL_CALL SvXMLEmbeddedObjectHelper::hasByName( const OUString& rURLStr )
673 std::unique_lock aGuard( m_aMutex );
674 if( SvXMLEmbeddedObjectHelperMode::Read == meCreateMode )
676 return true;
678 else
680 OUString aContainerStorageName, aObjectStorageName;
681 if( !ImplGetStorageNames( rURLStr, aContainerStorageName,
682 aObjectStorageName,
683 true ) )
684 return false;
686 comphelper::EmbeddedObjectContainer& rContainer = mpDocPersist->getEmbeddedObjectContainer();
687 return !aObjectStorageName.isEmpty() &&
688 rContainer.HasEmbeddedObject( aObjectStorageName );
692 // XNameAccess
693 Type SAL_CALL SvXMLEmbeddedObjectHelper::getElementType()
695 std::unique_lock aGuard( m_aMutex );
696 if( SvXMLEmbeddedObjectHelperMode::Read == meCreateMode )
697 return cppu::UnoType<XOutputStream>::get();
698 else
699 return cppu::UnoType<XInputStream>::get();
702 sal_Bool SAL_CALL SvXMLEmbeddedObjectHelper::hasElements()
704 std::unique_lock aGuard( m_aMutex );
705 if( SvXMLEmbeddedObjectHelperMode::Read == meCreateMode )
707 return true;
709 else
711 comphelper::EmbeddedObjectContainer& rContainer = mpDocPersist->getEmbeddedObjectContainer();
712 return rContainer.HasEmbeddedObjects();
716 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */