summaryrefslogtreecommitdiffstats
path: root/svx/source/xml
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--svx/source/xml/xmleohlp.cxx717
-rw-r--r--svx/source/xml/xmlexport.cxx239
-rw-r--r--svx/source/xml/xmlgrhlp.cxx1190
-rw-r--r--svx/source/xml/xmlxtexp.cxx485
-rw-r--r--svx/source/xml/xmlxtimp.cxx461
5 files changed, 3092 insertions, 0 deletions
diff --git a/svx/source/xml/xmleohlp.cxx b/svx/source/xml/xmleohlp.cxx
new file mode 100644
index 000000000..7fd88e492
--- /dev/null
+++ b/svx/source/xml/xmleohlp.cxx
@@ -0,0 +1,717 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <com/sun/star/io/XStream.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/embed/XTransactedObject.hpp>
+#include <com/sun/star/embed/ElementModes.hpp>
+#include <com/sun/star/embed/XEmbeddedObject.hpp>
+#include <com/sun/star/embed/XEmbedPersist.hpp>
+#include <com/sun/star/embed/EmbedStates.hpp>
+#include <com/sun/star/embed/Aspects.hpp>
+#include <com/sun/star/lang/WrappedTargetRuntimeException.hpp>
+#include <osl/diagnose.h>
+#include <sot/storage.hxx>
+#include <tools/debug.hxx>
+#include <sal/log.hxx>
+#include <unotools/streamwrap.hxx>
+#include <unotools/tempfile.hxx>
+
+#include <svtools/embedhlp.hxx>
+#include <unotools/ucbstreamhelper.hxx>
+#include <comphelper/propertyvalue.hxx>
+#include <comphelper/storagehelper.hxx>
+#include <comphelper/embeddedobjectcontainer.hxx>
+
+#include <comphelper/fileformat.h>
+#include <cppuhelper/exc_hlp.hxx>
+#include <cppuhelper/implbase.hxx>
+#include <svx/xmleohlp.hxx>
+#include <map>
+#include <memory>
+#include <mutex>
+
+using namespace ::osl;
+using namespace ::cppu;
+using namespace ::utl;
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::document;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::container;
+using namespace ::com::sun::star::io;
+using namespace ::com::sun::star::lang;
+
+constexpr OUStringLiteral XML_CONTAINERSTORAGE_NAME_60 = u"Pictures";
+constexpr OUStringLiteral XML_CONTAINERSTORAGE_NAME = u"ObjectReplacements";
+constexpr OUStringLiteral XML_EMBEDDEDOBJECT_URL_BASE = u"vnd.sun.star.EmbeddedObject:";
+constexpr OUStringLiteral XML_EMBEDDEDOBJECTGRAPHIC_URL_BASE = u"vnd.sun.star.GraphicObject:";
+
+
+class OutputStorageWrapper_Impl : public ::cppu::WeakImplHelper<XOutputStream>
+{
+ std::mutex maMutex;
+ Reference < XOutputStream > xOut;
+ TempFile aTempFile;
+ bool bStreamClosed : 1;
+ SvStream* pStream;
+
+public:
+ OutputStorageWrapper_Impl();
+
+// css::io::XOutputStream
+ virtual void SAL_CALL writeBytes(const Sequence< sal_Int8 >& aData) override;
+ virtual void SAL_CALL flush() override;
+ virtual void SAL_CALL closeOutput() override;
+
+ SvStream* GetStream();
+};
+
+OutputStorageWrapper_Impl::OutputStorageWrapper_Impl()
+ : bStreamClosed( false )
+ , pStream(nullptr)
+{
+ aTempFile.EnableKillingFile();
+ pStream = aTempFile.GetStream( StreamMode::READWRITE );
+ xOut = new OOutputStreamWrapper( *pStream );
+}
+
+SvStream *OutputStorageWrapper_Impl::GetStream()
+{
+ if( bStreamClosed )
+ return pStream;
+ return nullptr;
+}
+
+void SAL_CALL OutputStorageWrapper_Impl::writeBytes(
+ const Sequence< sal_Int8 >& aData)
+{
+ std::scoped_lock aGuard( maMutex );
+ xOut->writeBytes( aData );
+}
+
+void SAL_CALL OutputStorageWrapper_Impl::flush()
+{
+ std::scoped_lock aGuard( maMutex );
+ xOut->flush();
+}
+
+void SAL_CALL OutputStorageWrapper_Impl::closeOutput()
+{
+ std::scoped_lock aGuard( maMutex );
+ xOut->closeOutput();
+ bStreamClosed = true;
+}
+
+SvXMLEmbeddedObjectHelper::SvXMLEmbeddedObjectHelper() :
+ mpDocPersist( nullptr ),
+ meCreateMode( SvXMLEmbeddedObjectHelperMode::Read )
+{
+}
+
+SvXMLEmbeddedObjectHelper::SvXMLEmbeddedObjectHelper( ::comphelper::IEmbeddedHelper& rDocPersist, SvXMLEmbeddedObjectHelperMode eCreateMode ) :
+ mpDocPersist( nullptr ),
+ meCreateMode( SvXMLEmbeddedObjectHelperMode::Read )
+{
+ Init( nullptr, rDocPersist, eCreateMode );
+}
+
+SvXMLEmbeddedObjectHelper::~SvXMLEmbeddedObjectHelper()
+{
+}
+
+void SvXMLEmbeddedObjectHelper::disposing(std::unique_lock<std::mutex>&)
+{
+ if( mxTempStorage.is() )
+ {
+ mxTempStorage->dispose();
+ mxTempStorage.clear();
+ }
+}
+
+void SvXMLEmbeddedObjectHelper::splitObjectURL(const OUString& _aURLNoPar,
+ OUString& rContainerStorageName,
+ OUString& rObjectStorageName)
+{
+ DBG_ASSERT(_aURLNoPar.isEmpty() || '#' != _aURLNoPar[0], "invalid object URL" );
+ OUString aURLNoPar = _aURLNoPar;
+
+ sal_Int32 _nPos = aURLNoPar.lastIndexOf( '/' );
+ if( -1 == _nPos )
+ {
+ rContainerStorageName.clear();
+ rObjectStorageName = aURLNoPar;
+ }
+ else
+ {
+ //eliminate 'superfluous' slashes at start and end
+ //#i103076# load objects with all allowed xlink:href syntaxes
+ {
+ //eliminate './' at start
+ sal_Int32 nStart = 0;
+ sal_Int32 nCount = aURLNoPar.getLength();
+ if( aURLNoPar.startsWith( "./" ) )
+ {
+ nStart = 2;
+ nCount -= 2;
+ }
+
+ //eliminate '/' at end
+ sal_Int32 nEnd = aURLNoPar.lastIndexOf( '/' );
+ if( nEnd == aURLNoPar.getLength()-1 && nEnd != (nStart-1) )
+ nCount--;
+
+ aURLNoPar = aURLNoPar.copy( nStart, nCount );
+ }
+
+ _nPos = aURLNoPar.lastIndexOf( '/' );
+ if( _nPos >= 0 )
+ rContainerStorageName = aURLNoPar.copy( 0, _nPos );
+ rObjectStorageName = aURLNoPar.copy( _nPos+1 );
+ }
+}
+
+bool SvXMLEmbeddedObjectHelper::ImplGetStorageNames(
+ const OUString& rURLStr,
+ OUString& rContainerStorageName,
+ OUString& rObjectStorageName,
+ bool bInternalToExternal,
+ bool *pGraphicRepl,
+ bool *pOasisFormat ) const
+{
+ // internal URL: vnd.sun.star.EmbeddedObject:<object-name>
+ // or: vnd.sun.star.EmbeddedObject:<path>/<object-name>
+ // internal replacement images:
+ // vnd.sun.star.EmbeddedObjectGraphic:<object-name>
+ // or: vnd.sun.star.EmbeddedObjectGraphic:<path>/<object-name>
+ // external URL: ./<path>/<object-name>
+ // or: <path>/<object-name>
+ // or: <object-name>
+ // currently, path may only consist of a single directory name
+ // it is also possible to have additional arguments at the end of URL: <main URL>[?<name>=<value>[,<name>=<value>]*]
+
+ if( pGraphicRepl )
+ *pGraphicRepl = false;
+
+ if( pOasisFormat )
+ *pOasisFormat = true; // the default value
+
+ if( rURLStr.isEmpty() )
+ return false;
+
+ // get rid of arguments
+ sal_Int32 nPos = rURLStr.indexOf( '?' );
+ OUString aURLNoPar;
+ if ( nPos == -1 )
+ aURLNoPar = rURLStr;
+ else
+ {
+ aURLNoPar = rURLStr.copy( 0, nPos );
+
+ // check the arguments
+ nPos++;
+ while( nPos >= 0 && nPos < rURLStr.getLength() )
+ {
+ OUString aToken = rURLStr.getToken( 0, ',', nPos );
+ if ( aToken.equalsIgnoreAsciiCase( "oasis=false" ) )
+ {
+ if ( pOasisFormat )
+ *pOasisFormat = false;
+ break;
+ }
+ else
+ {
+ SAL_WARN( "svx", "invalid arguments was found in URL!" );
+ }
+ }
+ }
+
+ if( bInternalToExternal )
+ {
+ nPos = aURLNoPar.indexOf( ':' );
+ if( -1 == nPos )
+ return false;
+ bool bObjUrl = aURLNoPar.startsWith( XML_EMBEDDEDOBJECT_URL_BASE );
+ bool bGrUrl = !bObjUrl &&
+ aURLNoPar.startsWith( XML_EMBEDDEDOBJECTGRAPHIC_URL_BASE );
+ if( !(bObjUrl || bGrUrl) )
+ return false;
+
+ sal_Int32 nPathStart = nPos + 1;
+ nPos = aURLNoPar.lastIndexOf( '/' );
+ if( -1 == nPos )
+ {
+ rContainerStorageName.clear();
+ rObjectStorageName = aURLNoPar.copy( nPathStart );
+ }
+ else if( nPos > nPathStart )
+ {
+ rContainerStorageName = aURLNoPar.copy( nPathStart, nPos-nPathStart);
+ rObjectStorageName = aURLNoPar.copy( nPos+1 );
+ }
+ else
+ return false;
+
+ if( bGrUrl )
+ {
+ bool bOASIS = mxRootStorage.is() &&
+ ( SotStorage::GetVersion( mxRootStorage ) > SOFFICE_FILEFORMAT_60 );
+ if (bOASIS)
+ rContainerStorageName = XML_CONTAINERSTORAGE_NAME;
+ else
+ rContainerStorageName = XML_CONTAINERSTORAGE_NAME_60;
+
+ if( pGraphicRepl )
+ *pGraphicRepl = true;
+ }
+
+
+ }
+ else
+ {
+ splitObjectURL(aURLNoPar, rContainerStorageName, rObjectStorageName);
+ }
+
+ if( -1 != rContainerStorageName.indexOf( '/' ) )
+ {
+ OSL_FAIL( "SvXMLEmbeddedObjectHelper: invalid path name" );
+ return false;
+ }
+
+ return true;
+}
+
+uno::Reference < embed::XStorage > const & SvXMLEmbeddedObjectHelper::ImplGetContainerStorage(
+ const OUString& rStorageName )
+{
+ DBG_ASSERT( -1 == rStorageName.indexOf( '/' ) &&
+ -1 == rStorageName.indexOf( '\\' ),
+ "nested embedded storages aren't supported" );
+ if( !mxContainerStorage.is() ||
+ ( rStorageName != maCurContainerStorageName ) )
+ {
+ if( mxContainerStorage.is() &&
+ !maCurContainerStorageName.isEmpty() &&
+ SvXMLEmbeddedObjectHelperMode::Write == meCreateMode )
+ {
+ uno::Reference < embed::XTransactedObject > xTrans( mxContainerStorage, uno::UNO_QUERY );
+ if ( xTrans.is() )
+ xTrans->commit();
+ }
+
+ if( !rStorageName.isEmpty() && mxRootStorage.is() )
+ {
+ sal_Int32 nMode = SvXMLEmbeddedObjectHelperMode::Write == meCreateMode
+ ? ::embed::ElementModes::READWRITE
+ : ::embed::ElementModes::READ;
+ mxContainerStorage = mxRootStorage->openStorageElement( rStorageName,
+ nMode );
+ }
+ else
+ {
+ mxContainerStorage = mxRootStorage;
+ }
+ maCurContainerStorageName = rStorageName;
+ }
+
+ return mxContainerStorage;
+}
+
+void SvXMLEmbeddedObjectHelper::ImplReadObject(
+ const OUString& rContainerStorageName,
+ OUString& rObjName,
+ const SvGlobalName *, // pClassId, see "TODO/LATER" below
+ SvStream* pTemp )
+{
+ uno::Reference < embed::XStorage > xDocStor( mpDocPersist->getStorage() );
+ uno::Reference < embed::XStorage > xCntnrStor( ImplGetContainerStorage( rContainerStorageName ) );
+
+ if( !xCntnrStor.is() && !pTemp )
+ return;
+
+ OUString aSrcObjName( rObjName );
+ comphelper::EmbeddedObjectContainer& rContainer = mpDocPersist->getEmbeddedObjectContainer();
+
+ // Is the object name unique?
+ // if the object is already instantiated by GetEmbeddedObject
+ // that means that the duplication is being loaded
+ bool bDuplicate = rContainer.HasInstantiatedEmbeddedObject( rObjName );
+ DBG_ASSERT( !bDuplicate, "An object in the document is referenced twice!" );
+
+ if( xDocStor != xCntnrStor || pTemp || bDuplicate )
+ {
+ // TODO/LATER: make this altogether a method in the EmbeddedObjectContainer
+
+ // create a unique name for the duplicate object
+ if( bDuplicate )
+ rObjName = rContainer.CreateUniqueObjectName();
+
+ if( pTemp )
+ {
+ try
+ {
+ pTemp->Seek( 0 );
+ uno::Reference < io::XStream > xStm = xDocStor->openStreamElement( rObjName,
+ embed::ElementModes::READWRITE | embed::ElementModes::TRUNCATE );
+ std::unique_ptr<SvStream> pStream(::utl::UcbStreamHelper::CreateStream( xStm ));
+ pTemp->ReadStream( *pStream );
+ pStream.reset();
+
+ // TODO/LATER: what to do when other types of objects are based on substream persistence?
+ // This is an ole object
+ uno::Reference< beans::XPropertySet > xProps( xStm, uno::UNO_QUERY_THROW );
+ xProps->setPropertyValue(
+ "MediaType",
+ uno::Any( OUString( "application/vnd.sun.star.oleobject" ) ) );
+
+ xStm->getOutputStream()->closeOutput();
+ }
+ catch ( uno::Exception& )
+ {
+ return;
+ }
+ }
+ else
+ {
+ try
+ {
+ xCntnrStor->copyElementTo( aSrcObjName, xDocStor, rObjName );
+ }
+ catch ( uno::Exception& )
+ {
+ return;
+ }
+ }
+ }
+
+ // make object known to the container
+ // TODO/LATER: could be done a little bit more efficient!
+ OUString aName( rObjName );
+
+ // TODO/LATER: The provided pClassId is ignored for now.
+ // The stream contains OLE storage internally and this storage already has a class id specifying the
+ // server that was used to create the object. pClassId could be used to specify the server that should
+ // be used for the next opening, but this information seems to be out of the file format responsibility
+ // area.
+ OUString const baseURL(mpDocPersist->getDocumentBaseURL());
+ rContainer.GetEmbeddedObject(aName, &baseURL);
+}
+
+OUString SvXMLEmbeddedObjectHelper::ImplInsertEmbeddedObjectURL(
+ const OUString& rURLStr )
+{
+ OUString sRetURL;
+
+ OUString aContainerStorageName, aObjectStorageName;
+ if( !ImplGetStorageNames( rURLStr, aContainerStorageName,
+ aObjectStorageName,
+ SvXMLEmbeddedObjectHelperMode::Write == meCreateMode ) )
+ return sRetURL;
+
+ if( SvXMLEmbeddedObjectHelperMode::Read == meCreateMode )
+ {
+ OutputStorageWrapper_Impl *pOut = nullptr;
+ std::map< OUString, rtl::Reference<OutputStorageWrapper_Impl> >::iterator aIter;
+
+ if( mxStreamMap )
+ {
+ aIter = mxStreamMap->find( rURLStr );
+ if( aIter != mxStreamMap->end() && aIter->second.is() )
+ pOut = aIter->second.get();
+ }
+
+ SvGlobalName aClassId, *pClassId = nullptr;
+ sal_Int32 nPos = aObjectStorageName.lastIndexOf( '!' );
+ if( -1 != nPos && aClassId.MakeId( aObjectStorageName.subView( nPos+1 ) ) )
+ {
+ aObjectStorageName = aObjectStorageName.copy( 0, nPos );
+ pClassId = &aClassId;
+ }
+
+ ImplReadObject( aContainerStorageName, aObjectStorageName, pClassId, pOut ? pOut->GetStream() : nullptr );
+ sRetURL = XML_EMBEDDEDOBJECT_URL_BASE + aObjectStorageName;
+
+ if( pOut )
+ {
+ mxStreamMap->erase( aIter );
+ }
+ }
+ else
+ {
+ // Objects are written using ::comphelper::IEmbeddedHelper::SaveAs
+ sRetURL = "./";
+ if( !aContainerStorageName.isEmpty() )
+ {
+ sRetURL += aContainerStorageName + "/";
+ }
+ sRetURL += aObjectStorageName;
+ }
+
+ return sRetURL;
+}
+
+uno::Reference< io::XInputStream > SvXMLEmbeddedObjectHelper::ImplGetReplacementImage(
+ const uno::Reference< embed::XEmbeddedObject >& xObj )
+{
+ uno::Reference< io::XInputStream > xStream;
+
+ if( xObj.is() )
+ {
+ try
+ {
+ bool bSwitchBackToLoaded = false;
+ sal_Int32 nCurState = xObj->getCurrentState();
+ if ( nCurState == embed::EmbedStates::LOADED || nCurState == embed::EmbedStates::RUNNING )
+ {
+ // means that the object is not active
+ // copy replacement image from old to new container
+ OUString aMediaType;
+ xStream = mpDocPersist->getEmbeddedObjectContainer().GetGraphicStream( xObj, &aMediaType );
+ }
+
+ if ( !xStream.is() )
+ {
+ // the image must be regenerated
+ // TODO/LATER: another aspect could be used
+ if ( nCurState == embed::EmbedStates::LOADED )
+ bSwitchBackToLoaded = true;
+
+ OUString aMediaType;
+ xStream = svt::EmbeddedObjectRef::GetGraphicReplacementStream(
+ embed::Aspects::MSOLE_CONTENT,
+ xObj,
+ &aMediaType );
+ }
+
+ if ( bSwitchBackToLoaded )
+ // switch back to loaded state; that way we have a minimum cache confusion
+ xObj->changeState( embed::EmbedStates::LOADED );
+ }
+ catch( uno::Exception& )
+ {}
+ }
+
+ return xStream;
+}
+
+void SvXMLEmbeddedObjectHelper::Init(
+ const uno::Reference < embed::XStorage >& rRootStorage,
+ ::comphelper::IEmbeddedHelper& rPersist,
+ SvXMLEmbeddedObjectHelperMode eCreateMode )
+{
+ mxRootStorage = rRootStorage;
+ mpDocPersist = &rPersist;
+ meCreateMode = eCreateMode;
+}
+
+rtl::Reference<SvXMLEmbeddedObjectHelper> SvXMLEmbeddedObjectHelper::Create(
+ const uno::Reference < embed::XStorage >& rRootStorage,
+ ::comphelper::IEmbeddedHelper& rDocPersist,
+ SvXMLEmbeddedObjectHelperMode eCreateMode )
+{
+ rtl::Reference<SvXMLEmbeddedObjectHelper> pThis(new SvXMLEmbeddedObjectHelper);
+
+ pThis->Init( rRootStorage, rDocPersist, eCreateMode );
+
+ return pThis;
+}
+
+rtl::Reference<SvXMLEmbeddedObjectHelper> SvXMLEmbeddedObjectHelper::Create(
+ ::comphelper::IEmbeddedHelper& rDocPersist,
+ SvXMLEmbeddedObjectHelperMode eCreateMode )
+{
+ rtl::Reference<SvXMLEmbeddedObjectHelper> pThis(new SvXMLEmbeddedObjectHelper);
+
+ pThis->Init( nullptr, rDocPersist, eCreateMode );
+
+ return pThis;
+}
+
+OUString SAL_CALL SvXMLEmbeddedObjectHelper::resolveEmbeddedObjectURL(const OUString& rURL)
+{
+ std::unique_lock aGuard( m_aMutex );
+
+ OUString sRet;
+ try
+ {
+ sRet = ImplInsertEmbeddedObjectURL(rURL);
+ }
+ catch (const RuntimeException&)
+ {
+ throw;
+ }
+ catch (const Exception&)
+ {
+ css::uno::Any anyEx = cppu::getCaughtException();
+ throw WrappedTargetRuntimeException(
+ "SvXMLEmbeddedObjectHelper::resolveEmbeddedObjectURL non-RuntimeException",
+ static_cast<uno::XWeak*>(this), anyEx);
+ }
+ return sRet;
+}
+
+// XNameAccess: alien objects!
+Any SAL_CALL SvXMLEmbeddedObjectHelper::getByName(
+ const OUString& rURLStr )
+{
+ std::unique_lock aGuard( m_aMutex );
+ Any aRet;
+ if( SvXMLEmbeddedObjectHelperMode::Read == meCreateMode )
+ {
+ Reference < XOutputStream > xStrm;
+ if( mxStreamMap )
+ {
+ auto aIter = mxStreamMap->find( rURLStr );
+ if( aIter != mxStreamMap->end() && aIter->second.is() )
+ xStrm = aIter->second.get();
+ }
+ if( !xStrm.is() )
+ {
+ rtl::Reference<OutputStorageWrapper_Impl> xOut = new OutputStorageWrapper_Impl;
+ if( !mxStreamMap )
+ mxStreamMap.emplace();
+ (*mxStreamMap)[rURLStr] = xOut;
+ xStrm = xOut.get();
+ }
+
+ aRet <<= xStrm;
+ }
+ else
+ {
+ bool bGraphicRepl = false;
+ bool bOasisFormat = true;
+ Reference < XInputStream > xStrm;
+ OUString aContainerStorageName, aObjectStorageName;
+ if( ImplGetStorageNames( rURLStr, aContainerStorageName,
+ aObjectStorageName,
+ true,
+ &bGraphicRepl,
+ &bOasisFormat ) )
+ {
+ try
+ {
+ comphelper::EmbeddedObjectContainer& rContainer =
+ mpDocPersist->getEmbeddedObjectContainer();
+
+ Reference < embed::XEmbeddedObject > xObj = rContainer.GetEmbeddedObject( aObjectStorageName );
+ DBG_ASSERT( xObj.is(), "Didn't get object" );
+
+ if( xObj.is() )
+ {
+ if( bGraphicRepl )
+ {
+ xStrm = ImplGetReplacementImage( xObj );
+ }
+ else
+ {
+ Reference < embed::XEmbedPersist > xPersist( xObj, UNO_QUERY );
+ if( xPersist.is() )
+ {
+ if( !mxTempStorage.is() )
+ mxTempStorage =
+ comphelper::OStorageHelper::GetTemporaryStorage();
+ Sequence < beans::PropertyValue > aDummy,
+ aEmbDescr{ comphelper::makePropertyValue("StoreVisualReplacement",
+ !bOasisFormat) };
+ if ( !bOasisFormat )
+ {
+ uno::Reference< io::XInputStream > xGrInStream = ImplGetReplacementImage( xObj );
+ if ( xGrInStream.is() )
+ {
+ aEmbDescr.realloc( 2 );
+ auto pEmbDescr = aEmbDescr.getArray();
+ pEmbDescr[1].Name = "VisualReplacement";
+ pEmbDescr[1].Value <<= xGrInStream;
+ }
+ }
+
+ xPersist->storeToEntry( mxTempStorage, aObjectStorageName,
+ aDummy, aEmbDescr );
+ Reference < io::XStream > xStream =
+ mxTempStorage->openStreamElement(
+ aObjectStorageName,
+ embed::ElementModes::READ);
+ if( xStream.is() )
+ xStrm = xStream->getInputStream();
+ }
+ }
+ }
+ }
+ catch ( uno::Exception& )
+ {
+ }
+ }
+
+ aRet <<= xStrm;
+ }
+
+ return aRet;
+}
+
+Sequence< OUString > SAL_CALL SvXMLEmbeddedObjectHelper::getElementNames()
+{
+ return {};
+}
+
+sal_Bool SAL_CALL SvXMLEmbeddedObjectHelper::hasByName( const OUString& rURLStr )
+{
+ std::unique_lock aGuard( m_aMutex );
+ if( SvXMLEmbeddedObjectHelperMode::Read == meCreateMode )
+ {
+ return true;
+ }
+ else
+ {
+ OUString aContainerStorageName, aObjectStorageName;
+ if( !ImplGetStorageNames( rURLStr, aContainerStorageName,
+ aObjectStorageName,
+ true ) )
+ return false;
+
+ comphelper::EmbeddedObjectContainer& rContainer = mpDocPersist->getEmbeddedObjectContainer();
+ return !aObjectStorageName.isEmpty() &&
+ rContainer.HasEmbeddedObject( aObjectStorageName );
+ }
+}
+
+// XNameAccess
+Type SAL_CALL SvXMLEmbeddedObjectHelper::getElementType()
+{
+ std::unique_lock aGuard( m_aMutex );
+ if( SvXMLEmbeddedObjectHelperMode::Read == meCreateMode )
+ return cppu::UnoType<XOutputStream>::get();
+ else
+ return cppu::UnoType<XInputStream>::get();
+}
+
+sal_Bool SAL_CALL SvXMLEmbeddedObjectHelper::hasElements()
+{
+ std::unique_lock aGuard( m_aMutex );
+ if( SvXMLEmbeddedObjectHelperMode::Read == meCreateMode )
+ {
+ return true;
+ }
+ else
+ {
+ comphelper::EmbeddedObjectContainer& rContainer = mpDocPersist->getEmbeddedObjectContainer();
+ return rContainer.HasEmbeddedObjects();
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/xml/xmlexport.cxx b/svx/source/xml/xmlexport.cxx
new file mode 100644
index 000000000..1900a9e0b
--- /dev/null
+++ b/svx/source/xml/xmlexport.cxx
@@ -0,0 +1,239 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <tools/diagnose_ex.h>
+#include <com/sun/star/xml/sax/InputSource.hpp>
+#include <com/sun/star/xml/sax/XDocumentHandler.hpp>
+#include <com/sun/star/xml/sax/Writer.hpp>
+#include <com/sun/star/lang/XComponent.hpp>
+#include <com/sun/star/xml/sax/XFastParser.hpp>
+#include <com/sun/star/io/XOutputStream.hpp>
+#include <com/sun/star/document/XFilter.hpp>
+#include <com/sun/star/document/XExporter.hpp>
+#include <com/sun/star/document/XImporter.hpp>
+#include <comphelper/processfactory.hxx>
+#include <svx/svdmodel.hxx>
+#include <svx/xmleohlp.hxx>
+#include <svx/xmlgrhlp.hxx>
+
+#include <svx/unomodel.hxx>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+
+bool SvxDrawingLayerExport( SdrModel* pModel, const uno::Reference<io::XOutputStream>& xOut, const Reference< lang::XComponent >& xComponent )
+{
+ return SvxDrawingLayerExport( pModel, xOut, xComponent, "com.sun.star.comp.DrawingLayer.XMLExporter" );
+}
+
+bool SvxDrawingLayerExport( SdrModel* pModel, const uno::Reference<io::XOutputStream>& xOut, const Reference< lang::XComponent >& xComponent, const char* pExportService )
+{
+ bool bDocRet = xOut.is();
+
+ uno::Reference<document::XGraphicStorageHandler> xGraphicStorageHandler;
+ rtl::Reference<SvXMLGraphicHelper> xGraphicHelper;
+
+ Reference< document::XEmbeddedObjectResolver > xObjectResolver;
+ rtl::Reference<SvXMLEmbeddedObjectHelper> xObjectHelper;
+
+ Reference< lang::XComponent > xSourceDoc( xComponent );
+ try
+ {
+ if( !xSourceDoc.is() )
+ {
+ xSourceDoc = new SvxUnoDrawingModel( pModel );
+ pModel->setUnoModel( Reference< XInterface >::query( xSourceDoc ) );
+ }
+
+ uno::Reference< uno::XComponentContext> xContext( ::comphelper::getProcessComponentContext() );
+
+ if( bDocRet )
+ {
+ uno::Reference< xml::sax::XWriter > xWriter = xml::sax::Writer::create( xContext );
+
+ ::comphelper::IEmbeddedHelper *pPersist = pModel->GetPersist();
+ if( pPersist )
+ {
+ xObjectHelper = SvXMLEmbeddedObjectHelper::Create( *pPersist, SvXMLEmbeddedObjectHelperMode::Write );
+ xObjectResolver = xObjectHelper.get();
+ }
+
+ xGraphicHelper = SvXMLGraphicHelper::Create( SvXMLGraphicHelperMode::Write );
+ xGraphicStorageHandler = xGraphicHelper.get();
+
+ uno::Reference<xml::sax::XDocumentHandler> xHandler = xWriter;
+
+ // doc export
+ xWriter->setOutputStream( xOut );
+
+ uno::Sequence< uno::Any > aArgs( xObjectResolver.is() ? 3 : 2 );
+ auto pArgs = aArgs.getArray();
+ pArgs[0] <<= xHandler;
+ pArgs[1] <<= xGraphicStorageHandler;
+ if( xObjectResolver.is() )
+ pArgs[2] <<= xObjectResolver;
+
+ uno::Reference< document::XFilter > xFilter( xContext->getServiceManager()->createInstanceWithArgumentsAndContext( OUString::createFromAscii( pExportService ), aArgs, xContext ), uno::UNO_QUERY );
+ if( !xFilter.is() )
+ {
+ OSL_FAIL( "com.sun.star.comp.Draw.XMLExporter service missing" );
+ bDocRet = false;
+ }
+
+ if( bDocRet )
+ {
+ uno::Reference< document::XExporter > xExporter( xFilter, uno::UNO_QUERY );
+ if( xExporter.is() )
+ {
+ xExporter->setSourceDocument( xSourceDoc );
+
+ uno::Sequence< beans::PropertyValue > aDescriptor( 0 );
+ bDocRet = xFilter->filter( aDescriptor );
+ }
+ }
+ }
+ }
+ catch(uno::Exception const&)
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ bDocRet = false;
+ }
+
+ if( xGraphicHelper )
+ xGraphicHelper->dispose();
+ xGraphicHelper.clear();
+ xGraphicStorageHandler = nullptr;
+
+ if( xObjectHelper.is() )
+ xObjectHelper->dispose();
+
+ return bDocRet;
+}
+
+bool SvxDrawingLayerExport( SdrModel* pModel, const uno::Reference<io::XOutputStream>& xOut )
+{
+ Reference< lang::XComponent > xComponent;
+ return SvxDrawingLayerExport( pModel, xOut, xComponent );
+}
+
+//-
+
+bool SvxDrawingLayerImport( SdrModel* pModel, const uno::Reference<io::XInputStream>& xInputStream, const Reference< lang::XComponent >& xComponent )
+{
+ return SvxDrawingLayerImport( pModel, xInputStream, xComponent, "com.sun.star.comp.Draw.XMLOasisImporter" );
+}
+
+bool SvxDrawingLayerImport( SdrModel* pModel, const uno::Reference<io::XInputStream>& xInputStream, const Reference< lang::XComponent >& xComponent, const char* pImportService )
+{
+ bool bRet = true;
+
+ uno::Reference<document::XGraphicStorageHandler> xGraphicStorageHandler;
+ rtl::Reference<SvXMLGraphicHelper> xGraphicHelper;
+
+ Reference< document::XEmbeddedObjectResolver > xObjectResolver;
+ rtl::Reference<SvXMLEmbeddedObjectHelper> xObjectHelper;
+
+ Reference< lang::XComponent > xTargetDocument( xComponent );
+ if( !xTargetDocument.is() )
+ {
+ xTargetDocument = new SvxUnoDrawingModel( pModel );
+ pModel->setUnoModel( Reference< XInterface >::query( xTargetDocument ) );
+ }
+
+ Reference< frame::XModel > xTargetModel( xTargetDocument, UNO_QUERY );
+
+ try
+ {
+ // Get service factory
+ Reference< uno::XComponentContext > xContext = comphelper::getProcessComponentContext();
+
+ if ( xTargetModel.is() )
+ xTargetModel->lockControllers();
+
+
+ xGraphicHelper = SvXMLGraphicHelper::Create( SvXMLGraphicHelperMode::Read );
+ xGraphicStorageHandler = xGraphicHelper.get();
+
+ ::comphelper::IEmbeddedHelper *pPersist = pModel->GetPersist();
+ if( pPersist )
+ {
+ xObjectHelper = SvXMLEmbeddedObjectHelper::Create(
+ *pPersist,
+ SvXMLEmbeddedObjectHelperMode::Read );
+ xObjectResolver = xObjectHelper.get();
+ }
+
+ // parse
+ // prepare ParserInputSource
+ xml::sax::InputSource aParserInput;
+ aParserInput.aInputStream = xInputStream;
+
+ // prepare filter arguments
+ Sequence<Any> aFilterArgs( 2 );
+ Any *pArgs = aFilterArgs.getArray();
+ *pArgs++ <<= xGraphicStorageHandler;
+ *pArgs++ <<= xObjectResolver;
+
+ // get filter
+ Reference< XInterface > xFilter = xContext->getServiceManager()->createInstanceWithArgumentsAndContext( OUString::createFromAscii( pImportService ), aFilterArgs, xContext);
+ SAL_WARN_IF( !xFilter, "svx", "Can't instantiate filter component " << pImportService);
+ uno::Reference< xml::sax::XFastParser > xFastParser( xFilter, UNO_QUERY );
+ assert(xFastParser);
+
+ bRet = false;
+ if( xFastParser.is() )
+ {
+ // connect model and filter
+ uno::Reference < document::XImporter > xImporter( xFilter, UNO_QUERY );
+ xImporter->setTargetDocument( xTargetDocument );
+
+ // finally, parser the stream
+ xFastParser->parseStream( aParserInput );
+
+ bRet = true;
+ }
+ }
+ catch( uno::Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+
+ if( xGraphicHelper )
+ xGraphicHelper->dispose();
+ xGraphicHelper.clear();
+ xGraphicStorageHandler = nullptr;
+
+ if( xObjectHelper.is() )
+ xObjectHelper->dispose();
+ xObjectHelper.clear();
+ xObjectResolver = nullptr;
+
+ if ( xTargetModel.is() )
+ xTargetModel->unlockControllers();
+
+ return bRet;
+}
+
+bool SvxDrawingLayerImport( SdrModel* pModel, const uno::Reference<io::XInputStream>& xInputStream )
+{
+ Reference< lang::XComponent > xComponent;
+ return SvxDrawingLayerImport( pModel, xInputStream, xComponent );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/xml/xmlgrhlp.cxx b/svx/source/xml/xmlgrhlp.cxx
new file mode 100644
index 000000000..4815c9e8a
--- /dev/null
+++ b/svx/source/xml/xmlgrhlp.cxx
@@ -0,0 +1,1190 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+#include <sal/log.hxx>
+
+#include <com/sun/star/embed/XTransactedObject.hpp>
+#include <com/sun/star/embed/ElementModes.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/io/NotConnectedException.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/lang/XInitialization.hpp>
+#include <com/sun/star/lang/XMultiServiceFactory.hpp>
+#include <com/sun/star/util/XCancellable.hpp>
+#include <com/sun/star/embed/XHierarchicalStorageAccess.hpp>
+#include <comphelper/fileformat.h>
+#include <comphelper/graphicmimetype.hxx>
+#include <comphelper/compbase.hxx>
+#include <cppuhelper/implbase.hxx>
+#include <cppuhelper/supportsservice.hxx>
+
+#include <rtl/ref.hxx>
+#include <unotools/ucbstreamhelper.hxx>
+#include <unotools/streamwrap.hxx>
+#include <unotools/tempfile.hxx>
+#include <unotools/saveopt.hxx>
+#include <vcl/filter/SvmWriter.hxx>
+#include <vcl/gfxlink.hxx>
+#include <vcl/metaact.hxx>
+#include <tools/zcodec.hxx>
+#include <tools/diagnose_ex.h>
+
+#include <vcl/GraphicObject.hxx>
+#include <vcl/graphicfilter.hxx>
+#include <svx/xmlgrhlp.hxx>
+#include <svx/xmleohlp.hxx>
+
+#include <algorithm>
+#include <memory>
+#include <string_view>
+#include <utility>
+
+using namespace com::sun::star;
+using namespace com::sun::star::uno;
+using namespace com::sun::star::io;
+
+namespace com::sun::star::uno { class XComponentContext; }
+
+constexpr OUStringLiteral XML_GRAPHICSTORAGE_NAME = u"Pictures";
+constexpr OUStringLiteral XML_GRAPHICOBJECT_URL_BASE = u"vnd.sun.star.GraphicObject:";
+
+namespace {
+
+const MetaCommentAction* ImplCheckForEPS( GDIMetaFile const & rMtf )
+{
+ const MetaCommentAction* pComment = nullptr;
+
+ if ( rMtf.GetActionSize() >= 2
+ && rMtf.GetAction(0)->GetType() == MetaActionType::EPS
+ && rMtf.GetAction(1)->GetType() == MetaActionType::COMMENT
+ && ( static_cast<const MetaCommentAction*>(rMtf.GetAction( 1 ))->GetComment() == "EPSReplacementGraphic" ) )
+ pComment = static_cast<const MetaCommentAction*>(rMtf.GetAction( 1 ));
+
+ return pComment;
+}
+
+class GraphicInputStream : public cppu::WeakImplHelper<XInputStream>
+{
+private:
+ virtual sal_Int32 SAL_CALL readBytes(Sequence<sal_Int8> & aData, sal_Int32 nBytesToRead) override;
+ virtual sal_Int32 SAL_CALL readSomeBytes(Sequence<sal_Int8> & aData, sal_Int32 nMaxBytesToRead) override;
+ virtual void SAL_CALL skipBytes(sal_Int32 nBytesToSkip) override;
+ virtual sal_Int32 SAL_CALL available() override;
+ virtual void SAL_CALL closeInput() override;
+
+private:
+ utl::TempFile maTempFile;
+ Reference<XInputStream> mxStreamWrapper;
+
+public:
+
+ explicit GraphicInputStream(GraphicObject const & raGraphicObject, const OUString & rMimeType);
+ GraphicInputStream(const GraphicInputStream&) = delete;
+
+ GraphicInputStream& operator=(const GraphicInputStream&) = delete;
+
+ bool exists() const
+ {
+ return mxStreamWrapper.is();
+ }
+};
+
+
+GraphicInputStream::GraphicInputStream(GraphicObject const & aGraphicObject, const OUString & rMimeType)
+{
+ maTempFile.EnableKillingFile();
+
+ if (aGraphicObject.GetType() == GraphicType::NONE)
+ return;
+
+ std::unique_ptr<SvStream> pStream = ::utl::UcbStreamHelper::CreateStream(maTempFile.GetURL(), StreamMode::WRITE | StreamMode::TRUNC);
+
+ if (!pStream)
+ return;
+
+ const Graphic& aGraphic(aGraphicObject.GetGraphic());
+ const GfxLink aGfxLink(aGraphic.GetGfxLink());
+ bool bRet = false;
+
+ if (aGfxLink.GetDataSize() && aGfxLink.GetData())
+ {
+ if (rMimeType.isEmpty())
+ {
+ pStream->WriteBytes(aGfxLink.GetData(), aGfxLink.GetDataSize());
+ bRet = (pStream->GetError() == ERRCODE_NONE);
+ }
+ else
+ {
+ GraphicFilter &rFilter = GraphicFilter::GetGraphicFilter();
+ bRet = (rFilter.ExportGraphic(aGraphic, u"", *pStream, rFilter.GetExportFormatNumberForMediaType(rMimeType)) == ERRCODE_NONE);
+ }
+ }
+ else
+ {
+ if (aGraphic.GetType() == GraphicType::Bitmap)
+ {
+ GraphicFilter & rFilter = GraphicFilter::GetGraphicFilter();
+ OUString aFormat = rMimeType;
+
+ if (aGraphic.IsAnimated())
+ aFormat = "image/gif";
+ else if (aFormat.isEmpty())
+ aFormat = "image/png";
+
+ bRet = (rFilter.ExportGraphic(aGraphic, u"", *pStream, rFilter.GetExportFormatNumberForMediaType(aFormat)) == ERRCODE_NONE);
+ }
+ else if (rMimeType.isEmpty() && aGraphic.GetType() == GraphicType::GdiMetafile)
+ {
+ pStream->SetVersion(SOFFICE_FILEFORMAT_8);
+ pStream->SetCompressMode(SvStreamCompressFlags::ZBITMAP);
+ SvmWriter aWriter(*pStream);
+ aWriter.Write(aGraphic.GetGDIMetaFile());
+ bRet = (pStream->GetError() == ERRCODE_NONE);
+ }
+ else if (!rMimeType.isEmpty())
+ {
+ GraphicFilter & rFilter = GraphicFilter::GetGraphicFilter();
+ bRet = ( rFilter.ExportGraphic( aGraphic, u"", *pStream, rFilter.GetExportFormatNumberForMediaType( rMimeType ) ) == ERRCODE_NONE );
+ }
+ }
+
+ if (bRet)
+ {
+ pStream->Seek( 0 );
+ mxStreamWrapper = new ::utl::OInputStreamWrapper(std::move(pStream));
+ }
+}
+
+sal_Int32 SAL_CALL GraphicInputStream::readBytes(Sequence<sal_Int8> & rData, sal_Int32 nBytesToRead)
+{
+ if (!mxStreamWrapper.is())
+ throw NotConnectedException();
+
+ return mxStreamWrapper->readBytes(rData, nBytesToRead);
+}
+
+sal_Int32 SAL_CALL GraphicInputStream::readSomeBytes(Sequence<sal_Int8>& rData, sal_Int32 nMaxBytesToRead )
+{
+ if (!mxStreamWrapper.is())
+ throw NotConnectedException() ;
+
+ return mxStreamWrapper->readSomeBytes(rData, nMaxBytesToRead);
+}
+
+void SAL_CALL GraphicInputStream::skipBytes(sal_Int32 nBytesToSkip)
+{
+ if (!mxStreamWrapper.is())
+ throw NotConnectedException();
+
+ mxStreamWrapper->skipBytes(nBytesToSkip);
+}
+
+sal_Int32 SAL_CALL GraphicInputStream::available()
+{
+ if (!mxStreamWrapper.is())
+ throw NotConnectedException();
+
+ return mxStreamWrapper->available();
+}
+
+void SAL_CALL GraphicInputStream::closeInput()
+{
+ if (!mxStreamWrapper.is())
+ throw NotConnectedException();
+
+ mxStreamWrapper->closeInput();
+}
+
+class SvXMLGraphicOutputStream:
+ public cppu::WeakImplHelper<XOutputStream>
+{
+private:
+
+ // XOutputStream
+ virtual void SAL_CALL writeBytes( const Sequence< sal_Int8 >& rData ) override;
+ virtual void SAL_CALL flush() override;
+ virtual void SAL_CALL closeOutput() override;
+
+private:
+
+ std::unique_ptr<::utl::TempFile> mpTmp;
+ std::unique_ptr<SvStream> mpOStm;
+ Reference< XOutputStream > mxStmWrapper;
+ std::unique_ptr<GraphicObject> mxGrfObj;
+ bool mbClosed;
+
+public:
+
+ SvXMLGraphicOutputStream();
+ virtual ~SvXMLGraphicOutputStream() override;
+ SvXMLGraphicOutputStream(const SvXMLGraphicOutputStream&) = delete;
+ SvXMLGraphicOutputStream& operator=(const SvXMLGraphicOutputStream&) = delete;
+
+ bool Exists() const { return mxStmWrapper.is(); }
+ const GraphicObject& GetGraphicObject();
+ Graphic GetGraphic();
+};
+
+SvXMLGraphicOutputStream::SvXMLGraphicOutputStream()
+ : mpTmp(new ::utl::TempFile)
+ , mxGrfObj(new GraphicObject)
+ , mbClosed(false)
+{
+ mpTmp->EnableKillingFile();
+
+ mpOStm = ::utl::UcbStreamHelper::CreateStream( mpTmp->GetURL(), StreamMode::WRITE | StreamMode::TRUNC );
+
+ if( mpOStm )
+ mxStmWrapper = new ::utl::OOutputStreamWrapper( *mpOStm );
+}
+
+SvXMLGraphicOutputStream::~SvXMLGraphicOutputStream()
+{
+ mpTmp.reset();
+ mpOStm.reset();
+}
+
+void SAL_CALL SvXMLGraphicOutputStream::writeBytes( const Sequence< sal_Int8 >& rData )
+{
+ if( !mxStmWrapper.is() )
+ throw NotConnectedException() ;
+
+ mxStmWrapper->writeBytes( rData );
+}
+
+void SAL_CALL SvXMLGraphicOutputStream::flush()
+{
+ if( !mxStmWrapper.is() )
+ throw NotConnectedException() ;
+
+ mxStmWrapper->flush();
+}
+
+void SAL_CALL SvXMLGraphicOutputStream::closeOutput()
+{
+ if( !mxStmWrapper.is() )
+ throw NotConnectedException() ;
+
+ mxStmWrapper->closeOutput();
+ mxStmWrapper.clear();
+
+ mbClosed = true;
+}
+
+Graphic SvXMLGraphicOutputStream::GetGraphic()
+{
+ Graphic aGraphic;
+
+ if (mbClosed && mxGrfObj->GetType() == GraphicType::NONE && mpOStm)
+ {
+ mpOStm->Seek( 0 );
+ sal_uInt16 nFormat = GRFILTER_FORMAT_DONTKNOW;
+ sal_uInt16 nDeterminedFormat = GRFILTER_FORMAT_DONTKNOW;
+ GraphicFilter::GetGraphicFilter().ImportGraphic( aGraphic, u"", *mpOStm ,nFormat,&nDeterminedFormat);
+
+ if (nDeterminedFormat == GRFILTER_FORMAT_DONTKNOW)
+ {
+ //Read the first two byte to check whether it is a gzipped stream, is so it may be in wmz or emz format
+ //unzip them and try again
+
+ sal_uInt8 sFirstBytes[ 2 ];
+
+ sal_uInt64 nStreamLen = mpOStm->TellEnd();
+ mpOStm->Seek( 0 );
+
+ if ( nStreamLen == 0 )
+ {
+ SvLockBytes* pLockBytes = mpOStm->GetLockBytes();
+ if ( pLockBytes )
+ pLockBytes->SetSynchronMode();
+
+ nStreamLen = mpOStm->TellEnd();
+ mpOStm->Seek( 0 );
+ }
+ if( nStreamLen >= 2 )
+ {
+ //read two byte
+ mpOStm->ReadBytes(sFirstBytes, 2);
+
+ if( sFirstBytes[0] == 0x1f && sFirstBytes[1] == 0x8b )
+ {
+ SvMemoryStream aDest;
+ ZCodec aZCodec( 0x8000, 0x8000 );
+ aZCodec.BeginCompression(ZCODEC_DEFAULT_COMPRESSION, /*gzLib*/true);
+ mpOStm->Seek( 0 );
+ aZCodec.Decompress( *mpOStm, aDest );
+
+ if (aZCodec.EndCompression())
+ {
+ sal_uInt64 nStreamLen_ = aDest.TellEnd();
+ if (nStreamLen_ > 0)
+ {
+ aDest.Seek(0);
+ GraphicFilter::GetGraphicFilter().ImportGraphic( aGraphic, u"", aDest ,nFormat,&nDeterminedFormat );
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if (aGraphic.GetType() != GraphicType::NONE)
+ {
+ mpOStm.reset();
+ mpTmp.reset();
+ }
+ return aGraphic;
+}
+
+const GraphicObject& SvXMLGraphicOutputStream::GetGraphicObject()
+{
+ Graphic aGraphic(GetGraphic());
+ if (aGraphic.GetType() != GraphicType::NONE)
+ {
+ mxGrfObj.reset(new GraphicObject(aGraphic));
+ }
+ return *mxGrfObj;
+}
+
+}
+
+SvXMLGraphicHelper::SvXMLGraphicHelper(SvXMLGraphicHelperMode eCreateMode)
+{
+ Init( nullptr, eCreateMode );
+}
+
+SvXMLGraphicHelper::SvXMLGraphicHelper()
+ : meCreateMode(SvXMLGraphicHelperMode::Read)
+{
+}
+
+SvXMLGraphicHelper::~SvXMLGraphicHelper()
+{
+}
+
+bool SvXMLGraphicHelper::ImplGetStreamNames( const OUString& rURLStr,
+ OUString& rPictureStorageName,
+ OUString& rPictureStreamName )
+{
+ if (rURLStr.isEmpty())
+ return false;
+
+ const OUString aURLStr {rURLStr.copy(rURLStr.lastIndexOf(':')+1)};
+
+ if( !aURLStr.isEmpty() && aURLStr.indexOf('/')<0 ) // just one token?
+ {
+ rPictureStorageName = OUString();
+ rPictureStreamName = aURLStr;
+ }
+ else
+ SvXMLEmbeddedObjectHelper::splitObjectURL(aURLStr, rPictureStorageName, rPictureStreamName);
+
+ SAL_WARN_IF(rPictureStreamName.isEmpty(), "svx", "SvXMLGraphicHelper::ImplInsertGraphicURL: invalid scheme: " << rURLStr);
+
+ return !rPictureStreamName.isEmpty();
+}
+
+uno::Reference < embed::XStorage > SvXMLGraphicHelper::ImplGetGraphicStorage( const OUString& rStorageName )
+{
+ uno::Reference < embed::XStorage > xRetStorage;
+ if( mxRootStorage.is() )
+ {
+ try
+ {
+ maCurStorageName = rStorageName;
+ xRetStorage = mxRootStorage->openStorageElement(
+ maCurStorageName,
+ ( SvXMLGraphicHelperMode::Write == meCreateMode )
+ ? embed::ElementModes::READWRITE
+ : embed::ElementModes::READ );
+ }
+ catch ( uno::Exception& )
+ {
+ }
+ //#i43196# try again to open the storage element - this time readonly
+ if(!xRetStorage.is())
+ {
+ try
+ {
+ maCurStorageName = rStorageName;
+ xRetStorage = mxRootStorage->openStorageElement( maCurStorageName, embed::ElementModes::READ );
+ }
+ catch ( uno::Exception& )
+ {
+ }
+ }
+ }
+
+ return xRetStorage;
+}
+
+SvxGraphicHelperStream_Impl SvXMLGraphicHelper::ImplGetGraphicStream( const OUString& rPictureStorageName,
+ const OUString& rPictureStreamName )
+{
+ SvxGraphicHelperStream_Impl aRet;
+ if (!rPictureStorageName.isEmpty())
+ aRet.xStorage = ImplGetGraphicStorage(rPictureStorageName);
+ else
+ aRet.xStorage = mxRootStorage;
+
+ sal_Int32 nMode = embed::ElementModes::READ;
+ if (SvXMLGraphicHelperMode::Write == meCreateMode)
+ {
+ nMode = embed::ElementModes::READWRITE;
+ }
+
+ if (aRet.xStorage.is())
+ {
+ aRet.xStream = aRet.xStorage->openStreamElement( rPictureStreamName, nMode );
+ }
+ else if (rPictureStorageName.indexOf('/') != -1)
+ {
+ uno::Reference<embed::XHierarchicalStorageAccess> xHierRootStorage(mxRootStorage,
+ uno::UNO_QUERY);
+ if (xHierRootStorage.is())
+ {
+ try
+ {
+ aRet.xStream = xHierRootStorage->openStreamElementByHierarchicalName(
+ rPictureStorageName + "/" + rPictureStreamName, nMode);
+ aRet.xStorage = mxRootStorage;
+ }
+ catch (const uno::Exception&)
+ {
+ TOOLS_WARN_EXCEPTION("svx",
+ "SvXMLGraphicHelper::ImplGetGraphicStream: failed to open "
+ << rPictureStreamName);
+ }
+ }
+ }
+
+ if (aRet.xStream.is() && (SvXMLGraphicHelperMode::Write == meCreateMode))
+ {
+ uno::Reference<beans::XPropertySet> xProps(aRet.xStream, uno::UNO_QUERY);
+ xProps->setPropertyValue("UseCommonStoragePasswordEncryption", uno::Any(true));
+ }
+
+ return aRet;
+}
+
+OUString SvXMLGraphicHelper::ImplGetGraphicMimeType( const OUString& rFileName )
+{
+ if( ( rFileName.getLength() >= 4 ) && ( rFileName[ rFileName.getLength() - 4 ] == '.' ) )
+ {
+ const OString aExt(OUStringToOString(rFileName.subView(rFileName.getLength() - 3),
+ RTL_TEXTENCODING_ASCII_US));
+ return comphelper::GraphicMimeTypeHelper::GetMimeTypeForExtension( aExt );
+ }
+
+ return OUString();
+}
+
+Graphic SvXMLGraphicHelper::ImplReadGraphic( const OUString& rPictureStorageName,
+ const OUString& rPictureStreamName )
+{
+ Graphic aReturnGraphic;
+ SvxGraphicHelperStream_Impl aStream( ImplGetGraphicStream( rPictureStorageName, rPictureStreamName ) );
+ if (aStream.xStream.is())
+ {
+ GraphicFilter& rGraphicFilter = GraphicFilter::GetGraphicFilter();
+ std::unique_ptr<SvStream> pStream(utl::UcbStreamHelper::CreateStream(aStream.xStream));
+ Graphic aGraphic = rGraphicFilter.ImportUnloadedGraphic(*pStream);
+ if (!aGraphic.IsNone())
+ aReturnGraphic = aGraphic;
+ else
+ rGraphicFilter.ImportGraphic(aReturnGraphic, u"", *pStream);
+ }
+
+ return aReturnGraphic;
+}
+
+void SvXMLGraphicHelper::Init( const uno::Reference < embed::XStorage >& rXMLStorage,
+ SvXMLGraphicHelperMode eCreateMode,
+ const OUString& rGraphicMimeType )
+{
+ mxRootStorage = rXMLStorage;
+ meCreateMode = eCreateMode;
+ maOutputMimeType = rGraphicMimeType;
+}
+
+rtl::Reference<SvXMLGraphicHelper> SvXMLGraphicHelper::Create( const uno::Reference < embed::XStorage >& rXMLStorage,
+ SvXMLGraphicHelperMode eCreateMode )
+{
+ rtl::Reference<SvXMLGraphicHelper> pThis = new SvXMLGraphicHelper;
+ pThis->Init( rXMLStorage, eCreateMode, OUString() );
+
+ return pThis;
+}
+
+rtl::Reference<SvXMLGraphicHelper> SvXMLGraphicHelper::Create( SvXMLGraphicHelperMode eCreateMode,
+ const OUString& rGraphicMimeType )
+{
+ rtl::Reference<SvXMLGraphicHelper> pThis = new SvXMLGraphicHelper;
+
+ pThis->Init( nullptr, eCreateMode, rGraphicMimeType );
+
+ return pThis;
+}
+
+namespace
+{
+
+void splitUserDataFromURL(OUString const & rWholeURL, OUString & rJustURL, OUString & rUserData)
+{
+ sal_Int32 nUser = rWholeURL.indexOf('?');
+ if (nUser >= 0)
+ {
+ rJustURL = rWholeURL.copy(0, nUser);
+ nUser++;
+ rUserData = rWholeURL.copy(nUser);
+ }
+ else
+ {
+ rJustURL = rWholeURL;
+ }
+}
+
+} // end anonymous namespace
+
+// XGraphicObjectResolver
+OUString SAL_CALL SvXMLGraphicHelper::resolveGraphicObjectURL( const OUString& /*rURL*/ )
+{
+ throw uno::RuntimeException("XGraphicObjectResolver has been removed in LibreOffice 6.1");
+}
+
+// XGraphicStorageHandler
+uno::Reference<graphic::XGraphic> SAL_CALL SvXMLGraphicHelper::loadGraphic(OUString const & rURL)
+{
+ std::unique_lock aGuard(m_aMutex);
+
+ uno::Reference<graphic::XGraphic> xGraphic;
+
+ OUString aURLOnly;
+ OUString aUserData;
+ splitUserDataFromURL(rURL, aURLOnly, aUserData);
+
+ auto aIterator = maGraphicObjects.find(aURLOnly);
+ if (aIterator != maGraphicObjects.end())
+ {
+ return aIterator->second;
+ }
+
+ OUString aPictureStorageName, aPictureStreamName;
+
+ if (ImplGetStreamNames(aURLOnly, aPictureStorageName, aPictureStreamName))
+ {
+ const GraphicObject aGraphicObject(ImplReadGraphic(aPictureStorageName, aPictureStreamName));
+
+ if (aGraphicObject.GetType() != GraphicType::NONE)
+ {
+ xGraphic = aGraphicObject.GetGraphic().GetXGraphic();
+ maGraphicObjects[aURLOnly] = xGraphic;
+ }
+ }
+
+ return xGraphic;
+}
+
+uno::Reference<graphic::XGraphic> SAL_CALL SvXMLGraphicHelper::loadGraphicFromOutputStream(uno::Reference<io::XOutputStream> const & rxOutputStream)
+{
+ std::unique_lock aGuard(m_aMutex);
+
+ uno::Reference<graphic::XGraphic> xGraphic;
+
+ if ((SvXMLGraphicHelperMode::Read == meCreateMode) && rxOutputStream.is())
+ {
+
+ SvXMLGraphicOutputStream* pGraphicOutputStream = static_cast<SvXMLGraphicOutputStream*>(rxOutputStream.get());
+ if (pGraphicOutputStream)
+ {
+ xGraphic = pGraphicOutputStream->GetGraphic().GetXGraphic();
+ }
+ }
+ return xGraphic;
+}
+
+OUString SAL_CALL SvXMLGraphicHelper::saveGraphicByName(css::uno::Reference<css::graphic::XGraphic> const & rxGraphic,
+ OUString & rOutSavedMimeType, OUString const & rRequestName)
+{
+ return implSaveGraphic(rxGraphic, rOutSavedMimeType, rRequestName);
+}
+
+OUString SAL_CALL SvXMLGraphicHelper::saveGraphic(css::uno::Reference<css::graphic::XGraphic> const & rxGraphic)
+{
+ OUString aOutMimeType;
+ return implSaveGraphic(rxGraphic, aOutMimeType, std::u16string_view());
+}
+
+OUString SvXMLGraphicHelper::implSaveGraphic(css::uno::Reference<css::graphic::XGraphic> const & rxGraphic,
+ OUString & rOutSavedMimeType, std::u16string_view rRequestName)
+{
+ Graphic aGraphic(rxGraphic);
+
+ auto aIterator = maExportGraphics.find(aGraphic);
+ if (aIterator != maExportGraphics.end())
+ {
+ auto const & aURLAndMimePair = aIterator->second;
+ rOutSavedMimeType = aURLAndMimePair.second;
+ return aURLAndMimePair.first;
+ }
+
+ GraphicObject aGraphicObject(aGraphic);
+
+ if (aGraphicObject.GetType() != GraphicType::NONE)
+ {
+ const GfxLink aGfxLink(aGraphic.GetGfxLink());
+ OUString aExtension;
+ bool bUseGfxLink = true;
+
+ if (aGfxLink.GetDataSize())
+ {
+ switch (aGfxLink.GetType())
+ {
+ case GfxLinkType::EpsBuffer: aExtension = ".eps"; break;
+ case GfxLinkType::NativeGif: aExtension = ".gif"; break;
+ // #i15508# added BMP type for better exports (checked, works)
+ case GfxLinkType::NativeBmp: aExtension = ".bmp"; break;
+ case GfxLinkType::NativeJpg: aExtension = ".jpg"; break;
+ case GfxLinkType::NativePng: aExtension = ".png"; break;
+ case GfxLinkType::NativeTif: aExtension = ".tif"; break;
+ case GfxLinkType::NativeWmf:
+ if (aGfxLink.IsEMF())
+ aExtension = ".emf";
+ else
+ aExtension = ".wmf";
+ break;
+ case GfxLinkType::NativeMet: aExtension = ".met"; break;
+ case GfxLinkType::NativePct: aExtension = ".pct"; break;
+ case GfxLinkType::NativeSvg:
+ {
+ // backward-compat kludge: since no released OOo
+ // version to date can handle svg properly, wrap it up
+ // into an svm. slight catch22 here, since strict ODF
+ // conformance _recommends_ svg - then again, most old
+ // ODF consumers are believed to be OOo
+ auto nSaneVersion = GetODFSaneDefaultVersion();
+ if ( nSaneVersion < SvtSaveOptions::ODFSVER_012
+ || nSaneVersion == SvtSaveOptions::ODFSVER_012_EXT_COMPAT)
+ {
+ bUseGfxLink = false;
+ aExtension = ".svm";
+ }
+ else
+ {
+ aExtension = ".svg";
+ }
+ break;
+ }
+ case GfxLinkType::NativePdf: aExtension = ".pdf"; break;
+ case GfxLinkType::NativeWebp: aExtension = ".webp"; break;
+
+ default:
+ aExtension = ".grf";
+ break;
+ }
+ }
+ else
+ {
+ if (aGraphicObject.GetType() == GraphicType::Bitmap)
+ {
+ if (aGraphicObject.IsAnimated())
+ aExtension = ".gif";
+ else
+ aExtension = ".png";
+ }
+ else if (aGraphicObject.GetType() == GraphicType::GdiMetafile)
+ {
+ // SJ: first check if this metafile is just an eps file, then we will store the eps instead of svm
+ GDIMetaFile& rMetafile(const_cast<GDIMetaFile&>(aGraphic.GetGDIMetaFile()));
+
+ if (ImplCheckForEPS(rMetafile))
+ aExtension = ".eps";
+ else
+ aExtension = ".svm";
+ }
+ }
+
+ OUString rPictureStreamName;
+ if (!rRequestName.empty())
+ {
+ rPictureStreamName = rRequestName + aExtension;
+ }
+ else
+ {
+ OUString sId = OStringToOUString(aGraphicObject.GetUniqueID(), RTL_TEXTENCODING_ASCII_US);
+ rPictureStreamName = sId + aExtension;
+ }
+
+ SvxGraphicHelperStream_Impl aStream(ImplGetGraphicStream(XML_GRAPHICSTORAGE_NAME, rPictureStreamName));
+
+ if (aStream.xStream.is())
+ {
+ const OUString aMimeType(ImplGetGraphicMimeType(rPictureStreamName));
+ uno::Reference<beans::XPropertySet> xProps(aStream.xStream, uno::UNO_QUERY);
+
+ // set stream properties (MediaType/Compression)
+ if (!aMimeType.isEmpty())
+ {
+ xProps->setPropertyValue("MediaType", uno::Any(aMimeType));
+ }
+
+ // picture formats that actually _do_ benefit from zip
+ // storage compression
+ // .svm pics gets compressed via ZBITMAP old-style stream
+ // option below
+ static const char* aCompressiblePics[] =
+ {
+ "image/svg+xml",
+ "image/x-emf",
+ "image/x-wmf",
+ "image/tiff",
+ "image/x-eps",
+ "image/bmp",
+ "image/x-pict"
+ };
+
+ bool bSuccess = false;
+
+ bool bCompressed = aMimeType.isEmpty();
+ if( !bCompressed )
+ {
+ for(const char* p : aCompressiblePics)
+ {
+ if( aMimeType.equalsIgnoreAsciiCaseAscii(p) )
+ {
+ bCompressed = true;
+ break;
+ }
+ }
+ }
+
+ xProps->setPropertyValue("Compressed", Any(bCompressed));
+
+ std::unique_ptr<SvStream> pStream(utl::UcbStreamHelper::CreateStream(aStream.xStream));
+ if (bUseGfxLink && aGfxLink.GetDataSize() && aGfxLink.GetData())
+ {
+ pStream->WriteBytes(aGfxLink.GetData(), aGfxLink.GetDataSize());
+ rOutSavedMimeType = aMimeType;
+ bSuccess = (pStream->GetError() == ERRCODE_NONE);
+ }
+ else
+ {
+ if (aGraphic.GetType() == GraphicType::Bitmap)
+ {
+ GraphicFilter& rFilter = GraphicFilter::GetGraphicFilter();
+ OUString aFormat;
+
+ if (aGraphic.IsAnimated())
+ {
+ aFormat = "gif";
+ }
+ else
+ {
+ aFormat = "png";
+ }
+ rOutSavedMimeType = comphelper::GraphicMimeTypeHelper::GetMimeTypeForExtension(aFormat.toUtf8());
+
+ bSuccess = (rFilter.ExportGraphic(aGraphic, u"", *pStream, rFilter.GetExportFormatNumberForShortName(aFormat)) == ERRCODE_NONE);
+ }
+ else if (aGraphic.GetType() == GraphicType::GdiMetafile)
+ {
+ pStream->SetVersion(SOFFICE_FILEFORMAT_8);
+ pStream->SetCompressMode(SvStreamCompressFlags::ZBITMAP);
+ rOutSavedMimeType = comphelper::GraphicMimeTypeHelper::GetMimeTypeForExtension("svm");
+
+ // SJ: first check if this metafile is just an eps file, then we will store the eps instead of svm
+ GDIMetaFile& rMtf(const_cast<GDIMetaFile&>(aGraphic.GetGDIMetaFile()));
+ const MetaCommentAction* pComment = ImplCheckForEPS(rMtf);
+ if (pComment)
+ {
+ sal_uInt32 nSize = pComment->GetDataSize();
+ const sal_uInt8* pData = pComment->GetData();
+ if (nSize && pData)
+ pStream->WriteBytes(pData, nSize);
+
+ const MetaEPSAction* pAct = static_cast<const MetaEPSAction*>(rMtf.FirstAction());
+ const GfxLink& rLink = pAct->GetLink();
+
+ pStream->WriteBytes(rLink.GetData(), rLink.GetDataSize());
+ }
+ else
+ {
+ SvmWriter aWriter(*pStream);
+ aWriter.Write(rMtf);
+ }
+
+ bSuccess = (pStream->GetError() == ERRCODE_NONE);
+ }
+ }
+
+ if (!bSuccess)
+ return OUString();
+
+ uno::Reference<embed::XTransactedObject> xStorage(aStream.xStorage, uno::UNO_QUERY);
+ pStream.reset();
+ aStream.xStream->getOutputStream()->closeOutput();
+ if (xStorage.is())
+ xStorage->commit();
+
+ OUString aStoragePath = "Pictures/" + rPictureStreamName;
+
+ // put into cache
+ maExportGraphics[aGraphic] = std::make_pair(aStoragePath, rOutSavedMimeType);
+
+ return aStoragePath;
+ }
+ }
+
+ return OUString();
+}
+
+uno::Reference<io::XInputStream> SAL_CALL SvXMLGraphicHelper::createInputStream(uno::Reference<graphic::XGraphic> const & rxGraphic)
+{
+ Reference<XInputStream> xInputStream;
+
+ Graphic aGraphic(rxGraphic);
+ GraphicObject aGraphicObject(aGraphic);
+
+ if (SvXMLGraphicHelperMode::Write == meCreateMode)
+ {
+ OUString sMimeType = comphelper::GraphicMimeTypeHelper::GetMimeTypeForExtension(OUStringToOString(maOutputMimeType, RTL_TEXTENCODING_ASCII_US));
+ rtl::Reference<GraphicInputStream> pInputStream(new GraphicInputStream(aGraphicObject, sMimeType));
+
+ // We release the pointer from unique_ptr and assign it to the input stream return type.
+ // In case the stream doesn't exists, unique_ptr will delete the pointer when we go out of scope.
+ if (pInputStream->exists())
+ xInputStream = pInputStream.get();
+ }
+
+ return xInputStream;
+}
+
+// XBinaryStreamResolver
+Reference< XInputStream > SAL_CALL SvXMLGraphicHelper::getInputStream( const OUString& /*rURL*/ )
+{
+ Reference<XInputStream> xRet;
+ return xRet;
+}
+
+Reference< XOutputStream > SAL_CALL SvXMLGraphicHelper::createOutputStream()
+{
+ Reference< XOutputStream > xRet;
+
+ if( SvXMLGraphicHelperMode::Read == meCreateMode )
+ {
+ rtl::Reference<SvXMLGraphicOutputStream> pOutputStream(new SvXMLGraphicOutputStream);
+
+ if( pOutputStream->Exists() )
+ {
+ xRet = pOutputStream.get();
+ maGrfStms.push_back( xRet );
+ }
+ }
+
+ return xRet;
+}
+
+OUString SAL_CALL SvXMLGraphicHelper::resolveOutputStream( const Reference< XOutputStream >& rxBinaryStream )
+{
+ OUString aRet;
+
+ if( ( SvXMLGraphicHelperMode::Read == meCreateMode ) && rxBinaryStream.is() )
+ {
+ if( ::std::find( maGrfStms.begin(), maGrfStms.end(), rxBinaryStream ) != maGrfStms.end() )
+ {
+ SvXMLGraphicOutputStream* pOStm = static_cast< SvXMLGraphicOutputStream* >( rxBinaryStream.get() );
+
+ if( pOStm )
+ {
+ const GraphicObject& rGrfObj = pOStm->GetGraphicObject();
+ const OUString aId(OStringToOUString(
+ rGrfObj.GetUniqueID(), RTL_TEXTENCODING_ASCII_US));
+
+ if( !aId.isEmpty() )
+ {
+ aRet = XML_GRAPHICOBJECT_URL_BASE + aId;
+ }
+ }
+ }
+ }
+
+ return aRet;
+}
+
+// for instantiation via service manager
+namespace {
+
+namespace impl
+{
+typedef comphelper::WeakComponentImplHelper<lang::XInitialization,
+ document::XGraphicObjectResolver,
+ document::XGraphicStorageHandler,
+ document::XBinaryStreamResolver,
+ lang::XServiceInfo>
+ SvXMLGraphicImportExportHelper_Base;
+
+} // namespace impl
+
+class SvXMLGraphicImportExportHelper :
+ public impl::SvXMLGraphicImportExportHelper_Base
+{
+public:
+ explicit SvXMLGraphicImportExportHelper( SvXMLGraphicHelperMode eMode );
+
+protected:
+ // is called from WeakComponentImplHelper when XComponent::dispose() was
+ // called from outside
+ virtual void disposing(std::unique_lock<std::mutex>&) override;
+
+ // ____ XInitialization ____
+ // one argument is allowed, which is the XStorage
+ virtual void SAL_CALL initialize( const Sequence< Any >& aArguments ) override;
+
+ // ____ XGraphicObjectResolver ____
+ virtual OUString SAL_CALL resolveGraphicObjectURL( const OUString& aURL ) override;
+
+ // ____ XGraphicStorageHandler ____
+ virtual css::uno::Reference<css::graphic::XGraphic> SAL_CALL
+ loadGraphic(const OUString& aURL) override;
+
+ virtual css::uno::Reference<css::graphic::XGraphic> SAL_CALL
+ loadGraphicFromOutputStream(css::uno::Reference<css::io::XOutputStream> const & rxOutputStream) override;
+
+ virtual OUString SAL_CALL
+ saveGraphic(css::uno::Reference<css::graphic::XGraphic> const & rxGraphic) override;
+
+ virtual OUString SAL_CALL
+ saveGraphicByName(css::uno::Reference<css::graphic::XGraphic> const & rxGraphic, OUString & rOutSavedMimeType, OUString const & rRequestName) override;
+
+ virtual css::uno::Reference<css::io::XInputStream> SAL_CALL
+ createInputStream(css::uno::Reference<css::graphic::XGraphic> const & rxGraphic) override;
+
+ // ____ XBinaryStreamResolver ____
+ virtual Reference< io::XInputStream > SAL_CALL getInputStream( const OUString& aURL ) override;
+ virtual Reference< io::XOutputStream > SAL_CALL createOutputStream() override;
+ virtual OUString SAL_CALL resolveOutputStream( const Reference< io::XOutputStream >& aBinaryStream ) override;
+
+ // ____ XServiceInfo ____
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override;
+ virtual Sequence< OUString > SAL_CALL getSupportedServiceNames() override;
+
+private:
+ SvXMLGraphicHelperMode m_eGraphicHelperMode;
+ rtl::Reference<SvXMLGraphicHelper> m_xXMLGraphicHelper;
+};
+
+SvXMLGraphicImportExportHelper::SvXMLGraphicImportExportHelper( SvXMLGraphicHelperMode eMode ) :
+ m_eGraphicHelperMode( eMode )
+{}
+
+void SvXMLGraphicImportExportHelper::disposing(std::unique_lock<std::mutex>&)
+{
+ if (m_xXMLGraphicHelper)
+ {
+ m_xXMLGraphicHelper->dispose();
+ m_xXMLGraphicHelper.clear();
+ }
+}
+
+// ____ XInitialization ____
+void SAL_CALL SvXMLGraphicImportExportHelper::initialize(
+ const Sequence< Any >& aArguments )
+{
+ Reference< embed::XStorage > xStorage;
+ if( aArguments.hasElements() )
+ aArguments[0] >>= xStorage;
+
+ m_xXMLGraphicHelper = SvXMLGraphicHelper::Create( xStorage, m_eGraphicHelperMode );
+}
+
+// ____ XGraphicObjectResolver ____
+OUString SAL_CALL SvXMLGraphicImportExportHelper::resolveGraphicObjectURL( const OUString& aURL )
+{
+ return m_xXMLGraphicHelper->resolveGraphicObjectURL( aURL );
+}
+
+// ____ XGraphicStorageHandler ____
+uno::Reference<graphic::XGraphic> SAL_CALL SvXMLGraphicImportExportHelper::loadGraphic(OUString const & rURL)
+{
+ return m_xXMLGraphicHelper->loadGraphic(rURL);
+}
+
+uno::Reference<graphic::XGraphic> SAL_CALL SvXMLGraphicImportExportHelper::loadGraphicFromOutputStream(uno::Reference<io::XOutputStream> const & rxOutputStream)
+{
+ return m_xXMLGraphicHelper->loadGraphicFromOutputStream(rxOutputStream);
+}
+
+OUString SAL_CALL SvXMLGraphicImportExportHelper::saveGraphic(css::uno::Reference<css::graphic::XGraphic> const & rxGraphic)
+{
+ return m_xXMLGraphicHelper->saveGraphic(rxGraphic);
+}
+
+OUString SAL_CALL SvXMLGraphicImportExportHelper::saveGraphicByName(css::uno::Reference<css::graphic::XGraphic> const & rxGraphic,
+ OUString & rOutSavedMimeType, OUString const & rRequestName)
+{
+ return m_xXMLGraphicHelper->saveGraphicByName(rxGraphic, rOutSavedMimeType, rRequestName);
+}
+
+uno::Reference<io::XInputStream> SAL_CALL SvXMLGraphicImportExportHelper::createInputStream(uno::Reference<graphic::XGraphic> const & rxGraphic)
+{
+ return m_xXMLGraphicHelper->createInputStream(rxGraphic);
+}
+
+// ____ XBinaryStreamResolver ____
+Reference< io::XInputStream > SAL_CALL SvXMLGraphicImportExportHelper::getInputStream( const OUString& aURL )
+{
+ return m_xXMLGraphicHelper->getInputStream( aURL );
+}
+Reference< io::XOutputStream > SAL_CALL SvXMLGraphicImportExportHelper::createOutputStream()
+{
+ return m_xXMLGraphicHelper->createOutputStream();
+}
+OUString SAL_CALL SvXMLGraphicImportExportHelper::resolveOutputStream( const Reference< io::XOutputStream >& aBinaryStream )
+{
+ return m_xXMLGraphicHelper->resolveOutputStream( aBinaryStream );
+}
+
+// ____ XServiceInfo ____
+OUString SAL_CALL SvXMLGraphicImportExportHelper::getImplementationName()
+{
+ if( m_eGraphicHelperMode == SvXMLGraphicHelperMode::Read )
+ return "com.sun.star.comp.Svx.GraphicImportHelper";
+ return "com.sun.star.comp.Svx.GraphicExportHelper";
+}
+
+sal_Bool SAL_CALL SvXMLGraphicImportExportHelper::supportsService( const OUString& ServiceName )
+{
+ return cppu::supportsService(this, ServiceName);
+}
+
+Sequence< OUString > SAL_CALL SvXMLGraphicImportExportHelper::getSupportedServiceNames()
+{
+ return { "com.sun.star.document.GraphicObjectResolver",
+ "com.sun.star.document.GraphicStorageHandler",
+ "com.sun.star.document.BinaryStreamResolver" };
+}
+
+}
+
+/** Create this with createInstanceWithArguments. service name
+ "com.sun.star.comp.Svx.GraphicImportHelper", one argument which is the
+ XStorage. Without arguments no helper class is created. With an empty
+ argument the helper class is created and initialized like in the CTOR to
+ SvXMLGraphicHelper that only gets the create mode.
+
+ You should call dispose after you no longer need this component.
+
+ uses eCreateMode == SvXMLGraphicHelperMode::Read, bDirect == sal_True in
+ SvXMLGraphicHelper
+ */
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
+com_sun_star_comp_Svx_GraphicImportHelper_get_implementation(
+ css::uno::XComponentContext *,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new SvXMLGraphicImportExportHelper(SvXMLGraphicHelperMode::Read));
+}
+
+/** Create this with createInstanceWithArguments. service name
+ "com.sun.star.comp.Svx.GraphicExportHelper", one argument which is the
+ XStorage. Without arguments no helper class is created. With an empty
+ argument the helper class is created and initialized like in the CTOR to
+ SvXMLGraphicHelper that only gets the create mode
+
+ To write the Pictures stream, you have to call dispose at this component.
+ Make sure you call dispose before you commit the parent storage.
+
+ uses eCreateMode == SvXMLGraphicHelperMode::Write, bDirect == sal_True in
+ SvXMLGraphicHelper
+ */
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
+com_sun_star_comp_Svx_GraphicExportHelper_get_implementation(
+ css::uno::XComponentContext *,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new SvXMLGraphicImportExportHelper(SvXMLGraphicHelperMode::Write));
+}
+
+namespace svx {
+
+ void DropUnusedNamedItems(css::uno::Reference<css::uno::XInterface> const& xModel)
+ {
+ uno::Reference<lang::XMultiServiceFactory> const xModelFactory(xModel, uno::UNO_QUERY);
+ assert(xModelFactory.is());
+ try
+ {
+ uno::Reference<util::XCancellable> const xGradient(
+ xModelFactory->createInstance("com.sun.star.drawing.GradientTable"),
+ uno::UNO_QUERY );
+ if (xGradient.is())
+ {
+ xGradient->cancel();
+ }
+
+ uno::Reference<util::XCancellable> const xHatch(
+ xModelFactory->createInstance("com.sun.star.drawing.HatchTable"),
+ uno::UNO_QUERY );
+ if (xHatch.is())
+ {
+ xHatch->cancel();
+ }
+
+ uno::Reference<util::XCancellable> const xBitmap(
+ xModelFactory->createInstance("com.sun.star.drawing.BitmapTable"),
+ uno::UNO_QUERY );
+ if (xBitmap.is())
+ {
+ xBitmap->cancel();
+ }
+
+ uno::Reference<util::XCancellable> const xTransGradient(
+ xModelFactory->createInstance("com.sun.star.drawing.TransparencyGradientTable"),
+ uno::UNO_QUERY );
+ if (xTransGradient.is())
+ {
+ xTransGradient->cancel();
+ }
+
+ uno::Reference<util::XCancellable> const xMarker(
+ xModelFactory->createInstance("com.sun.star.drawing.MarkerTable"),
+ uno::UNO_QUERY );
+ if (xMarker.is())
+ {
+ xMarker->cancel();
+ }
+
+ uno::Reference<util::XCancellable> const xDashes(
+ xModelFactory->createInstance("com.sun.star.drawing.DashTable"),
+ uno::UNO_QUERY );
+ if (xDashes.is())
+ {
+ xDashes->cancel();
+ }
+ }
+ catch (const Exception&)
+ {
+ TOOLS_WARN_EXCEPTION("svx", "dropUnusedNamedItems(): exception during clearing of unused named items");
+ }
+ }
+
+} // namespace svx
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/xml/xmlxtexp.cxx b/svx/source/xml/xmlxtexp.cxx
new file mode 100644
index 000000000..103752b05
--- /dev/null
+++ b/svx/source/xml/xmlxtexp.cxx
@@ -0,0 +1,485 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <tools/diagnose_ex.h>
+#include <tools/urlobj.hxx>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/container/XNameContainer.hpp>
+#include <com/sun/star/xml/sax/Writer.hpp>
+#include <com/sun/star/uno/Sequence.hxx>
+#include <com/sun/star/drawing/PolyPolygonBezierCoords.hpp>
+#include <com/sun/star/drawing/LineDash.hpp>
+#include <com/sun/star/awt/Gradient.hpp>
+#include <com/sun/star/awt/XBitmap.hpp>
+#include <com/sun/star/drawing/Hatch.hpp>
+#include <com/sun/star/embed/ElementModes.hpp>
+
+#include <sax/tools/converter.hxx>
+#include <sfx2/docfile.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <xmloff/xmlnamespace.hxx>
+#include <xmloff/namespacemap.hxx>
+
+#include <xmloff/xmltoken.hxx>
+#include <xmloff/xmlmetae.hxx>
+#include <xmloff/DashStyle.hxx>
+#include <xmloff/GradientStyle.hxx>
+#include <xmloff/HatchStyle.hxx>
+#include <xmloff/ImageStyle.hxx>
+#include <xmloff/MarkerStyle.hxx>
+#include <com/sun/star/embed/XTransactedObject.hpp>
+#include <comphelper/processfactory.hxx>
+#include <unotools/streamwrap.hxx>
+#include <svx/xmlgrhlp.hxx>
+
+#include <xmlxtexp.hxx>
+
+#include <comphelper/storagehelper.hxx>
+#include <memory>
+
+using namespace com::sun::star;
+using namespace com::sun::star::container;
+using namespace com::sun::star::document;
+using namespace com::sun::star::uno;
+using namespace com::sun::star::awt;
+using namespace com::sun::star::lang;
+using namespace com::sun::star::xml::sax;
+using namespace ::xmloff::token;
+using namespace cppu;
+
+using com::sun::star::embed::XTransactedObject;
+
+namespace {
+
+class SvxXMLTableEntryExporter
+{
+public:
+ explicit SvxXMLTableEntryExporter( SvXMLExport& rExport ) : mrExport( rExport ) {}
+ virtual ~SvxXMLTableEntryExporter();
+
+ virtual void exportEntry( const OUString& rStrName, const Any& rValue ) = 0;
+
+protected:
+ SvXMLExport& mrExport;
+};
+
+class SvxXMLColorEntryExporter : public SvxXMLTableEntryExporter
+{
+public:
+ explicit SvxXMLColorEntryExporter( SvXMLExport& rExport );
+
+ virtual void exportEntry( const OUString& rStrName, const Any& rValue ) override;
+};
+
+class SvxXMLLineEndEntryExporter : public SvxXMLTableEntryExporter
+{
+public:
+ explicit SvxXMLLineEndEntryExporter( SvXMLExport& rExport );
+
+ virtual void exportEntry( const OUString& rStrName, const Any& rValue ) override;
+private:
+ XMLMarkerStyleExport maMarkerStyle;
+};
+
+class SvxXMLDashEntryExporter : public SvxXMLTableEntryExporter
+{
+public:
+ explicit SvxXMLDashEntryExporter( SvXMLExport& rExport );
+
+ virtual void exportEntry( const OUString& rStrName, const Any& rValue ) override;
+
+private:
+ XMLDashStyleExport maDashStyle;
+};
+
+class SvxXMLHatchEntryExporter : public SvxXMLTableEntryExporter
+{
+public:
+ explicit SvxXMLHatchEntryExporter( SvXMLExport& rExport );
+
+ virtual void exportEntry( const OUString& rStrName, const Any& rValue ) override;
+private:
+ XMLHatchStyleExport maHatchStyle;
+};
+
+class SvxXMLGradientEntryExporter : public SvxXMLTableEntryExporter
+{
+public:
+ explicit SvxXMLGradientEntryExporter( SvXMLExport& rExport );
+
+ virtual void exportEntry( const OUString& rStrName, const Any& rValue ) override;
+private:
+ XMLGradientStyleExport maGradientStyle;
+};
+
+class SvxXMLBitmapEntryExporter : public SvxXMLTableEntryExporter
+{
+public:
+ explicit SvxXMLBitmapEntryExporter( SvXMLExport& rExport );
+
+ virtual void exportEntry( const OUString& rStrName, const Any& rValue ) override;
+};
+
+}
+
+SvxXMLXTableExportComponent::SvxXMLXTableExportComponent(
+ const css::uno::Reference< css::uno::XComponentContext >& rContext,
+ const uno::Reference<xml::sax::XDocumentHandler> & rHandler,
+ const uno::Reference<container::XNameContainer >& xTable,
+ uno::Reference<document::XGraphicStorageHandler> const & xGraphicStorageHandler)
+: SvXMLExport(rContext, "", /*rFileName*/"", rHandler, nullptr, FieldUnit::MM_100TH, SvXMLExportFlags::NONE),
+ mxTable( xTable )
+{
+
+ GetNamespaceMap_().Add( GetXMLToken(XML_NP_OOO), GetXMLToken(XML_N_OOO), XML_NAMESPACE_OOO );
+ GetNamespaceMap_().Add( GetXMLToken(XML_NP_OFFICE), GetXMLToken(XML_N_OFFICE), XML_NAMESPACE_OFFICE );
+ GetNamespaceMap_().Add( GetXMLToken(XML_NP_DRAW), GetXMLToken(XML_N_DRAW), XML_NAMESPACE_DRAW );
+ GetNamespaceMap_().Add( GetXMLToken(XML_NP_XLINK), GetXMLToken(XML_N_XLINK), XML_NAMESPACE_XLINK );
+ GetNamespaceMap_().Add( GetXMLToken(XML_NP_SVG), GetXMLToken(XML_N_SVG), XML_NAMESPACE_SVG );
+ SetGraphicStorageHandler(xGraphicStorageHandler);
+}
+
+SvxXMLXTableExportComponent::~SvxXMLXTableExportComponent()
+{
+}
+
+static void initializeStreamMetadata( const uno::Reference< uno::XInterface > &xOut )
+{
+ uno::Reference< beans::XPropertySet > xProps( xOut, uno::UNO_QUERY );
+ if( !xProps.is() )
+ {
+ OSL_FAIL( "Missing stream metadata interface" );
+ return;
+ }
+
+ try
+ {
+ xProps->setPropertyValue("MediaType", uno::Any( OUString( "text/xml" ) ) );
+
+ // use stock encryption
+ xProps->setPropertyValue("UseCommonStoragePasswordEncryption", uno::Any( true ) );
+ } catch ( const uno::Exception & )
+ {
+ TOOLS_WARN_EXCEPTION("svx", "exception setting stream metadata");
+ }
+}
+
+static void createStorageStream( uno::Reference < io::XOutputStream > *xOut,
+ rtl::Reference<SvXMLGraphicHelper>& rxGraphicHelper,
+ const uno::Reference < embed::XStorage >& xSubStorage )
+{
+ uno::Reference < io::XStream > xStream = xSubStorage->openStreamElement(
+ "Content.xml",
+ embed::ElementModes::WRITE );
+ rxGraphicHelper = SvXMLGraphicHelper::Create( xSubStorage, SvXMLGraphicHelperMode::Write );
+ initializeStreamMetadata( xStream );
+ *xOut = xStream->getOutputStream();
+}
+
+bool SvxXMLXTableExportComponent::save(
+ const OUString& rURL,
+ const uno::Reference<container::XNameContainer >& xTable,
+ const uno::Reference<embed::XStorage >& xStorage,
+ OUString *pOptName )
+{
+ bool bRet = false;
+ std::unique_ptr<SfxMedium> pMedium;
+ rtl::Reference<SvXMLGraphicHelper> xGraphicHelper;
+
+ INetURLObject aURLObj( rURL );
+ bool bToStorage = aURLObj.GetProtocol() == INetProtocol::NotValid; // a relative path
+ bool bSaveAsStorage = xTable->getElementType() == cppu::UnoType<awt::XBitmap>::get();
+
+ if( pOptName )
+ *pOptName = rURL;
+
+ try
+ {
+ uno::Reference< uno::XComponentContext> xContext( ::comphelper::getProcessComponentContext() );
+
+ uno::Reference< xml::sax::XWriter > xWriter = xml::sax::Writer::create( xContext );
+
+ uno::Reference < io::XStream > xStream;
+ uno::Reference < io::XOutputStream > xOut;
+ uno::Reference<embed::XStorage > xSubStorage;
+ uno::Reference<XGraphicStorageHandler> xGraphicStorageHandler;
+ const sal_Int32 eCreate = embed::ElementModes::WRITE | embed::ElementModes::TRUNCATE;
+
+ if( !bToStorage || !xStorage.is() )
+ { // local URL -> SfxMedium route
+ if( bSaveAsStorage )
+ xSubStorage = ::comphelper::OStorageHelper::GetStorageFromURL( rURL, eCreate );
+ else
+ {
+ pMedium.reset(new SfxMedium( rURL, StreamMode::WRITE | StreamMode::TRUNC ));
+
+ SvStream* pStream = pMedium->GetOutStream();
+ if( !pStream )
+ {
+ OSL_FAIL( "no output stream!" );
+ return false;
+ }
+
+ xOut = new utl::OOutputStreamWrapper( *pStream );
+ }
+ }
+ else // save into the xSubStorage
+ {
+ OUString aPath = rURL;
+
+ if( bSaveAsStorage )
+ {
+ try {
+ xSubStorage = xStorage->openStorageElement( aPath, eCreate );
+ } catch (uno::Exception &) {
+ OSL_FAIL( "no output storage!" );
+ return false;
+ }
+ }
+ else
+ {
+ aPath += ".xml";
+ try {
+ xStream = xStorage->openStreamElement( aPath, eCreate );
+ if( !xStream.is() )
+ return false;
+ initializeStreamMetadata( xStream );
+ xOut = xStream->getOutputStream();
+ } catch (uno::Exception &) {
+ OSL_FAIL( "no output stream!" );
+ return false;
+ }
+ if( pOptName )
+ *pOptName = aPath;
+ }
+ }
+
+ if( !xOut.is() && xSubStorage.is() )
+ createStorageStream( &xOut, xGraphicHelper, xSubStorage );
+ if( !xOut.is() )
+ return false;
+
+ xWriter->setOutputStream( xOut );
+ if( xGraphicHelper.is() )
+ xGraphicStorageHandler = xGraphicHelper.get();
+
+ // Finally do the export
+ rtl::Reference< SvxXMLXTableExportComponent > xExporter( new SvxXMLXTableExportComponent( xContext, xWriter, xTable, xGraphicStorageHandler ) );
+ bRet = xExporter->exportTable();
+
+ if( xGraphicHelper )
+ xGraphicHelper->dispose();
+ xGraphicHelper.clear();
+
+ if( xSubStorage.is() )
+ {
+ uno::Reference< XTransactedObject > xTrans( xSubStorage, UNO_QUERY );
+ if( xTrans.is() )
+ xTrans->commit();
+
+ xSubStorage->dispose();
+ }
+ }
+ catch( uno::Exception& )
+ {
+ bRet = false;
+ }
+
+ if( pMedium )
+ pMedium->Commit();
+
+ return bRet;
+}
+
+bool SvxXMLXTableExportComponent::exportTable() noexcept
+{
+ bool bRet = false;
+
+ try
+ {
+ GetDocHandler()->startDocument();
+
+ addChaffWhenEncryptedStorage();
+
+ // export namespaces
+ sal_uInt16 nPos = GetNamespaceMap().GetFirstKey();
+ while( USHRT_MAX != nPos )
+ {
+ GetAttrList().AddAttribute( GetNamespaceMap().GetAttrNameByKey( nPos ), GetNamespaceMap().GetNameByKey( nPos ) );
+ nPos = GetNamespaceMap().GetNextKey( nPos );
+ }
+
+ do
+ {
+ if( !mxTable.is() )
+ break;
+
+ char const* pEleName;
+ Type aExportType = mxTable->getElementType();
+ std::unique_ptr<SvxXMLTableEntryExporter> pExporter;
+
+ if( aExportType == cppu::UnoType<sal_Int32>::get() )
+ {
+ pExporter.reset(new SvxXMLColorEntryExporter(*this));
+ pEleName = "color-table";
+ }
+ else if( aExportType == cppu::UnoType< drawing::PolyPolygonBezierCoords >::get() )
+ {
+ pExporter.reset(new SvxXMLLineEndEntryExporter(*this));
+ pEleName = "marker-table";
+ }
+ else if( aExportType == cppu::UnoType< drawing::LineDash >::get() )
+ {
+ pExporter.reset(new SvxXMLDashEntryExporter(*this));
+ pEleName = "dash-table";
+ }
+ else if( aExportType == cppu::UnoType< drawing::Hatch >::get() )
+ {
+ pExporter.reset(new SvxXMLHatchEntryExporter(*this));
+ pEleName = "hatch-table";
+ }
+ else if( aExportType == cppu::UnoType< awt::Gradient >::get() )
+ {
+ pExporter.reset(new SvxXMLGradientEntryExporter(*this));
+ pEleName = "gradient-table";
+ }
+ else if( aExportType == cppu::UnoType<awt::XBitmap>::get())
+ {
+ pExporter.reset(new SvxXMLBitmapEntryExporter(*this));
+ pEleName = "bitmap-table";
+ }
+ else
+ {
+ OSL_FAIL( "unknown type for export");
+ break;
+ }
+
+ SvXMLElementExport aElem( *this, XML_NAMESPACE_OOO, pEleName, true, true );
+
+ const Sequence< OUString > aNames = mxTable->getElementNames();
+ Any aAny;
+
+ for( const OUString& rName : aNames )
+ {
+ aAny = mxTable->getByName( rName );
+ pExporter->exportEntry( rName, aAny );
+ }
+
+ bRet = true;
+ }
+ while(false);
+
+ GetDocHandler()->endDocument();
+ }
+ catch( Exception const& )
+ {
+ bRet = false;
+ }
+
+ return bRet;
+}
+
+// methods without content:
+void SvxXMLXTableExportComponent::ExportAutoStyles_() {}
+void SvxXMLXTableExportComponent::ExportMasterStyles_() {}
+void SvxXMLXTableExportComponent::ExportContent_() {}
+
+
+SvxXMLTableEntryExporter::~SvxXMLTableEntryExporter()
+{
+}
+
+
+SvxXMLColorEntryExporter::SvxXMLColorEntryExporter( SvXMLExport& rExport )
+: SvxXMLTableEntryExporter( rExport )
+{
+}
+
+void SvxXMLColorEntryExporter::exportEntry( const OUString& rStrName, const Any& rValue )
+{
+ mrExport.AddAttribute( XML_NAMESPACE_DRAW, XML_NAME, rStrName );
+
+ sal_Int32 nColor = 0;
+ rValue >>= nColor;
+
+ OUStringBuffer aOut;
+ ::sax::Converter::convertColor( aOut, nColor );
+ mrExport.AddAttribute( XML_NAMESPACE_DRAW, XML_COLOR, aOut.makeStringAndClear() );
+
+ SvXMLElementExport aElem( mrExport, XML_NAMESPACE_DRAW, XML_COLOR, true, true );
+}
+
+
+SvxXMLLineEndEntryExporter::SvxXMLLineEndEntryExporter( SvXMLExport& rExport )
+: SvxXMLTableEntryExporter( rExport ), maMarkerStyle( rExport )
+{
+}
+
+void SvxXMLLineEndEntryExporter::exportEntry( const OUString& rStrName, const Any& rValue )
+{
+ maMarkerStyle.exportXML( rStrName, rValue );
+}
+
+
+SvxXMLDashEntryExporter::SvxXMLDashEntryExporter( SvXMLExport& rExport )
+: SvxXMLTableEntryExporter( rExport ), maDashStyle( rExport )
+{
+}
+
+void SvxXMLDashEntryExporter::exportEntry( const OUString& rStrName, const Any& rValue )
+{
+ maDashStyle.exportXML( rStrName, rValue );
+}
+
+
+SvxXMLHatchEntryExporter::SvxXMLHatchEntryExporter( SvXMLExport& rExport )
+: SvxXMLTableEntryExporter( rExport ), maHatchStyle( rExport )
+{
+}
+
+void SvxXMLHatchEntryExporter::exportEntry( const OUString& rStrName, const Any& rValue )
+{
+ maHatchStyle.exportXML( rStrName, rValue );
+}
+
+
+SvxXMLGradientEntryExporter::SvxXMLGradientEntryExporter( SvXMLExport& rExport )
+: SvxXMLTableEntryExporter( rExport ), maGradientStyle( rExport )
+{
+}
+
+void SvxXMLGradientEntryExporter::exportEntry( const OUString& rStrName, const Any& rValue )
+{
+ maGradientStyle.exportXML( rStrName, rValue );
+}
+
+
+SvxXMLBitmapEntryExporter::SvxXMLBitmapEntryExporter( SvXMLExport& rExport )
+: SvxXMLTableEntryExporter( rExport )
+{
+}
+
+void SvxXMLBitmapEntryExporter::exportEntry( const OUString& rStrName, const Any& rValue )
+{
+ XMLImageStyle::exportXML(rStrName, rValue, mrExport);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/xml/xmlxtimp.cxx b/svx/source/xml/xmlxtimp.cxx
new file mode 100644
index 000000000..9fd5481d5
--- /dev/null
+++ b/svx/source/xml/xmlxtimp.cxx
@@ -0,0 +1,461 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <tools/urlobj.hxx>
+#include <com/sun/star/document/XGraphicStorageHandler.hpp>
+#include <com/sun/star/embed/ElementModes.hpp>
+#include <com/sun/star/container/XNameContainer.hpp>
+#include <com/sun/star/xml/sax/XDocumentHandler.hpp>
+#include <com/sun/star/drawing/PolyPolygonBezierCoords.hpp>
+#include <com/sun/star/drawing/LineDash.hpp>
+#include <com/sun/star/awt/Gradient.hpp>
+#include <com/sun/star/awt/XBitmap.hpp>
+#include <com/sun/star/drawing/Hatch.hpp>
+#include <com/sun/star/io/XSeekable.hpp>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/storagehelper.hxx>
+#include <sax/tools/converter.hxx>
+#include <sfx2/docfile.hxx>
+#include <xmloff/xmlnamespace.hxx>
+#include <xmloff/namespacemap.hxx>
+
+#include <xmloff/xmltoken.hxx>
+#include <xmloff/DashStyle.hxx>
+#include <xmloff/GradientStyle.hxx>
+#include <xmloff/HatchStyle.hxx>
+#include <xmloff/ImageStyle.hxx>
+#include <xmloff/MarkerStyle.hxx>
+#include <xmloff/xmlictxt.hxx>
+#include <svx/xmlgrhlp.hxx>
+
+#include <xmlxtimp.hxx>
+#include <tools/diagnose_ex.h>
+
+using namespace com::sun::star;
+using namespace com::sun::star::container;
+using namespace com::sun::star::document;
+using namespace com::sun::star::uno;
+using namespace com::sun::star::awt;
+using namespace com::sun::star::lang;
+using namespace com::sun::star::xml::sax;
+using namespace ::xmloff::token;
+using namespace cppu;
+
+namespace {
+
+enum class SvxXMLTableImportContextEnum { Color, Marker, Dash, Hatch, Gradient, Bitmap };
+
+class SvxXMLTableImportContext : public SvXMLImportContext
+{
+public:
+ SvxXMLTableImportContext( SvXMLImport& rImport, SvxXMLTableImportContextEnum eContext, const uno::Reference< XNameContainer >& xTable,
+ bool bOOoFormat );
+
+ virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL
+ createFastChildContext(sal_Int32 Element,
+ const css::uno::Reference< css::xml::sax::XFastAttributeList > & Attribs) override;
+
+protected:
+ static void importColor( const uno::Reference< XFastAttributeList >& xAttrList, Any& rAny, OUString& rName );
+ void importMarker( const uno::Reference< XFastAttributeList >& xAttrList, Any& rAny, OUString& rName );
+ void importDash( const uno::Reference< XFastAttributeList >& xAttrList, Any& rAny, OUString& rName );
+ void importHatch( const uno::Reference< XFastAttributeList >& xAttrList, Any& rAny, OUString& rName );
+ void importGradient( const uno::Reference< XFastAttributeList >& xAttrList, Any& rAny, OUString& rName );
+ void importBitmap( const uno::Reference< XFastAttributeList >& xAttrList, Any& rAny, OUString& rName );
+
+private:
+ uno::Reference< XNameContainer > mxTable;
+ SvxXMLTableImportContextEnum meContext;
+ bool mbOOoFormat;
+};
+
+}
+
+SvxXMLTableImportContext::SvxXMLTableImportContext( SvXMLImport& rImport, SvxXMLTableImportContextEnum eContext, const uno::Reference< XNameContainer >& xTable, bool bOOoFormat )
+: SvXMLImportContext( rImport ), mxTable( xTable ), meContext( eContext ),
+ mbOOoFormat( bOOoFormat )
+{
+}
+
+css::uno::Reference< css::xml::sax::XFastContextHandler >
+ SvxXMLTableImportContext::createFastChildContext(sal_Int32 nElement,
+ const css::uno::Reference< css::xml::sax::XFastAttributeList > & rAttrList)
+{
+ if( !(IsTokenInNamespace(nElement, XML_NAMESPACE_DRAW) ||
+ IsTokenInNamespace(nElement, XML_NAMESPACE_DRAW_OOO) ))
+ return nullptr;
+
+ std::vector<std::pair<sal_Int32, OString>> aTmpAttrList;
+ for (auto& aIter : sax_fastparser::castToFastAttributeList( rAttrList ))
+ aTmpAttrList.push_back({aIter.getToken(), OString(aIter.toCString())});
+ if( mbOOoFormat &&
+ (SvxXMLTableImportContextEnum::Dash == meContext || SvxXMLTableImportContextEnum::Hatch == meContext ||
+ SvxXMLTableImportContextEnum::Bitmap == meContext) )
+ {
+ for( auto & aIter : aTmpAttrList )
+ {
+ sal_Int32 aLocalAttr = aIter.first & TOKEN_MASK;
+ if( aIter.first == XML_ELEMENT(XLINK, XML_HREF) &&
+ SvxXMLTableImportContextEnum::Bitmap == meContext )
+ {
+ OString& rValue = aIter.second;
+ if( !rValue.isEmpty() && '#' == rValue[0] )
+ rValue = rValue.copy( 1 );
+ }
+ else if( (IsTokenInNamespace(aIter.first, XML_NAMESPACE_DRAW) || IsTokenInNamespace(aIter.first, XML_NAMESPACE_DRAW_OOO)) &&
+ ( ( SvxXMLTableImportContextEnum::Dash == meContext &&
+ ( aLocalAttr == XML_DOTS1_LENGTH ||
+ aLocalAttr == XML_DOTS2_LENGTH ||
+ aLocalAttr == XML_DISTANCE ) ) ||
+ ( SvxXMLTableImportContextEnum::Hatch == meContext &&
+ ( aLocalAttr == XML_DISTANCE ) ) ) )
+ {
+ OString& rValue = aIter.second;
+ sal_Int32 nPos = rValue.getLength();
+ while( nPos && rValue[nPos-1] <= ' ' )
+ --nPos;
+ if( nPos > 2 &&
+ ('c'==rValue[nPos-2] || 'C'==rValue[nPos-2]) &&
+ ('h'==rValue[nPos-1] || 'H'==rValue[nPos-1]) )
+ {
+ rValue = rValue.copy( 0, nPos-2 );
+ }
+ }
+ }
+ }
+ try
+ {
+ rtl::Reference<sax_fastparser::FastAttributeList> xFastList = new sax_fastparser::FastAttributeList(nullptr);
+ for (const auto& aIter : aTmpAttrList)
+ xFastList->add(aIter.first, aIter.second);
+
+ Any aAny;
+ OUString aName;
+
+ switch( meContext )
+ {
+ case SvxXMLTableImportContextEnum::Color:
+ importColor( xFastList, aAny, aName );
+ break;
+ case SvxXMLTableImportContextEnum::Marker:
+ importMarker( xFastList, aAny, aName );
+ break;
+ case SvxXMLTableImportContextEnum::Dash:
+ importDash( xFastList, aAny, aName );
+ break;
+ case SvxXMLTableImportContextEnum::Hatch:
+ importHatch( xFastList, aAny, aName );
+ break;
+ case SvxXMLTableImportContextEnum::Gradient:
+ importGradient( xFastList, aAny, aName );
+ break;
+ case SvxXMLTableImportContextEnum::Bitmap:
+ importBitmap( xFastList, aAny, aName );
+ break;
+ }
+
+ if( !aName.isEmpty() && aAny.hasValue() )
+ {
+ if( mxTable->hasByName( aName ) )
+ {
+ mxTable->replaceByName( aName, aAny );
+ }
+ else
+ {
+ mxTable->insertByName( aName, aAny );
+ }
+ }
+ }
+ catch (const uno::Exception&)
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+ return new SvXMLImportContext( GetImport() );
+}
+
+void SvxXMLTableImportContext::importColor( const uno::Reference< XFastAttributeList >& xAttrList, Any& rAny, OUString& rName )
+{
+ for (auto& aIter : sax_fastparser::castToFastAttributeList( xAttrList ))
+ {
+ switch (aIter.getToken())
+ {
+ case XML_ELEMENT(DRAW, XML_NAME):
+ case XML_ELEMENT(DRAW_OOO, XML_NAME):
+ rName = aIter.toString();
+ break;
+ case XML_ELEMENT(DRAW, XML_COLOR):
+ case XML_ELEMENT(DRAW_OOO, XML_COLOR):
+ {
+ sal_Int32 nColor(0);
+ ::sax::Converter::convertColor(nColor, aIter.toView());
+ rAny <<= nColor;
+ break;
+ }
+ default:
+ XMLOFF_WARN_UNKNOWN("xmloff", aIter);
+ }
+ }
+}
+
+void SvxXMLTableImportContext::importMarker( const uno::Reference< XFastAttributeList >& xAttrList, Any& rAny, OUString& rName )
+{
+ try
+ {
+ XMLMarkerStyleImport aMarkerStyle( GetImport() );
+ aMarkerStyle.importXML( xAttrList, rAny, rName );
+ }
+ catch (const Exception&)
+ {
+ TOOLS_WARN_EXCEPTION("svx", "");
+ }
+}
+
+void SvxXMLTableImportContext::importDash( const uno::Reference< XFastAttributeList >& xAttrList, Any& rAny, OUString& rName )
+{
+ try
+ {
+ XMLDashStyleImport aDashStyle( GetImport() );
+ aDashStyle.importXML( xAttrList, rAny, rName );
+ }
+ catch (const Exception&)
+ {
+ TOOLS_WARN_EXCEPTION("svx", "");
+ }
+}
+
+void SvxXMLTableImportContext::importHatch( const uno::Reference< XFastAttributeList >& xAttrList, Any& rAny, OUString& rName )
+{
+ try
+ {
+ XMLHatchStyleImport aHatchStyle( GetImport() );
+ aHatchStyle.importXML( xAttrList, rAny, rName );
+ }
+ catch (const Exception&)
+ {
+ TOOLS_WARN_EXCEPTION("svx", "");
+ }
+}
+
+void SvxXMLTableImportContext::importGradient( const uno::Reference< XFastAttributeList >& xAttrList, Any& rAny, OUString& rName )
+{
+ try
+ {
+ XMLGradientStyleImport aGradientStyle( GetImport() );
+ aGradientStyle.importXML( xAttrList, rAny, rName );
+ }
+ catch (const Exception&)
+ {
+ TOOLS_WARN_EXCEPTION("svx", "");
+ }
+}
+
+void SvxXMLTableImportContext::importBitmap( const uno::Reference< XFastAttributeList >& xAttrList, Any& rAny, OUString& rName )
+{
+ try
+ {
+ uno::Any aGraphicAny;
+ XMLImageStyle::importXML(xAttrList, aGraphicAny, rName, GetImport());
+ if (aGraphicAny.has<uno::Reference<graphic::XGraphic>>())
+ {
+ auto xGraphic = aGraphicAny.get<uno::Reference<graphic::XGraphic>>();
+ uno::Reference<awt::XBitmap> xBitmap(xGraphic, uno::UNO_QUERY);
+ if (xBitmap.is())
+ rAny <<= xBitmap;
+ }
+ }
+ catch (const Exception&)
+ {
+ TOOLS_WARN_EXCEPTION("svx", "");
+ }
+}
+
+
+SvxXMLXTableImport::SvxXMLXTableImport(
+ const css::uno::Reference< css::uno::XComponentContext >& rContext,
+ const uno::Reference< XNameContainer > & rTable,
+ uno::Reference<XGraphicStorageHandler> const & xGraphicStorageHandler)
+: SvXMLImport(rContext, "", SvXMLImportFlags::NONE),
+ mrTable( rTable )
+{
+ SetGraphicStorageHandler(xGraphicStorageHandler);
+
+ GetNamespaceMap().Add( GetXMLToken(XML_NP_OOO), GetXMLToken(XML_N_OOO), XML_NAMESPACE_OOO);
+ GetNamespaceMap().Add( GetXMLToken(XML_NP_OFFICE), GetXMLToken(XML_N_OFFICE), XML_NAMESPACE_OFFICE);
+ GetNamespaceMap().Add( GetXMLToken(XML_NP_DRAW), GetXMLToken(XML_N_DRAW), XML_NAMESPACE_DRAW);
+ GetNamespaceMap().Add( GetXMLToken(XML_NP_XLINK), GetXMLToken(XML_N_XLINK), XML_NAMESPACE_XLINK);
+
+ GetNamespaceMap().Add( "__ooo", GetXMLToken(XML_N_OOO), XML_NAMESPACE_OOO );
+ GetNamespaceMap().Add( "__xlink", GetXMLToken(XML_N_XLINK), XML_NAMESPACE_XLINK );
+
+ // OOo namespaces for reading OOo 1.1 files
+ GetNamespaceMap().Add( "___office",
+ GetXMLToken(XML_N_OFFICE_OOO),
+ XML_NAMESPACE_OFFICE );
+ GetNamespaceMap().Add( "___draw",
+ GetXMLToken(XML_N_DRAW_OOO),
+ XML_NAMESPACE_DRAW );
+}
+
+SvxXMLXTableImport::~SvxXMLXTableImport() noexcept
+{
+}
+
+static void openStorageStream( xml::sax::InputSource *pParserInput,
+ rtl::Reference<SvXMLGraphicHelper>& rxGraphicHelper,
+ const uno::Reference < embed::XStorage >& xStorage )
+{
+ uno::Reference < io::XStream > xIStm( xStorage->openStreamElement( "Content.xml", embed::ElementModes::READ ), uno::UNO_SET_THROW );
+ pParserInput->aInputStream = xIStm->getInputStream();
+ rxGraphicHelper = SvXMLGraphicHelper::Create( xStorage, SvXMLGraphicHelperMode::Read );
+}
+
+bool SvxXMLXTableImport::load( const OUString &rPath, const OUString &rReferer,
+ const uno::Reference < embed::XStorage > &xStorage,
+ const uno::Reference< XNameContainer >& xTable,
+ bool *bOptLoadedFromStorage ) noexcept
+{
+ bool bRet = true;
+ rtl::Reference<SvXMLGraphicHelper> xGraphicHelper;
+
+ INetURLObject aURLObj( rPath );
+ bool bUseStorage = aURLObj.GetProtocol() == INetProtocol::NotValid; // a relative path
+
+ try
+ {
+ uno::Reference<uno::XComponentContext> xContext( ::comphelper::getProcessComponentContext() );
+
+ xml::sax::InputSource aParserInput;
+ comphelper::LifecycleProxy aNasty;
+
+ if( !bUseStorage || !xStorage.is() )
+ {
+ SfxMedium aMedium( rPath, rReferer, StreamMode::READ | StreamMode::NOCREATE );
+ aParserInput.sSystemId = aMedium.GetName();
+
+ if( aMedium.IsStorage() )
+ {
+ uno::Reference < embed::XStorage > xMediumStorage( aMedium.GetStorage( false ), uno::UNO_SET_THROW );
+ openStorageStream( &aParserInput, xGraphicHelper, xMediumStorage );
+ }
+ else
+ aParserInput.aInputStream = aMedium.GetInputStream();
+ }
+ else // relative URL into a storage
+ {
+ uno::Reference< embed::XStorage > xSubStorage;
+ try
+ {
+ xSubStorage = comphelper::OStorageHelper::GetStorageAtPath(
+ xStorage, rPath, embed::ElementModes::READ, aNasty );
+ }
+ catch (const uno::Exception&)
+ {
+ }
+ if( xSubStorage.is() )
+ openStorageStream( &aParserInput, xGraphicHelper, xSubStorage );
+ else
+ {
+ css::uno::Reference< css::io::XStream > xStream = comphelper::OStorageHelper::GetStreamAtPath(
+ xStorage, rPath, embed::ElementModes::READ, aNasty );
+ if( !xStream.is() )
+ return false;
+ aParserInput.aInputStream = xStream->getInputStream();
+ }
+ if( bOptLoadedFromStorage )
+ *bOptLoadedFromStorage = true;
+ }
+
+ uno::Reference<XGraphicStorageHandler> xGraphicStorageHandler;
+ if (xGraphicHelper.is())
+ xGraphicStorageHandler = xGraphicHelper.get();
+
+ try
+ {
+ uno::Reference< io::XSeekable > xSeek( aParserInput.aInputStream, uno::UNO_QUERY_THROW );
+ xSeek->seek( 0 );
+ }
+ catch (const uno::Exception&)
+ {
+ }
+
+ rtl::Reference<SvxXMLXTableImport> xImport(new SvxXMLXTableImport(xContext, xTable, xGraphicStorageHandler));
+ xImport->parseStream( aParserInput );
+
+ if( xGraphicHelper )
+ xGraphicHelper->dispose();
+ }
+ catch (...)
+ {
+// thrown each time you load a document with property tables that are not
+// on the current machine. FIXME: would be better to check a file exists
+// before importing ...
+ bRet = false;
+ }
+
+ return bRet;
+}
+
+SvXMLImportContext *SvxXMLXTableImport::CreateFastContext( sal_Int32 nElement,
+ const ::css::uno::Reference< ::css::xml::sax::XFastAttributeList >& /*xAttrList*/ )
+{
+ if( IsTokenInNamespace(nElement, XML_NAMESPACE_OOO) ||
+ IsTokenInNamespace(nElement, XML_NAMESPACE_OFFICE) ||
+ IsTokenInNamespace(nElement, XML_NAMESPACE_OFFICE_OOO) )
+ {
+ bool bOOoFormat = IsTokenInNamespace(nElement, XML_NAMESPACE_OFFICE) ||
+ IsTokenInNamespace(nElement, XML_NAMESPACE_OFFICE_OOO);
+ Type aType = mrTable->getElementType();
+ sal_Int32 nToken = nElement & TOKEN_MASK;
+
+ if ( nToken == XML_COLOR_TABLE )
+ {
+ if( aType == ::cppu::UnoType<sal_Int32>::get() )
+ return new SvxXMLTableImportContext( *this, SvxXMLTableImportContextEnum::Color, mrTable, bOOoFormat );
+ }
+ else if ( nToken == XML_MARKER_TABLE )
+ {
+ if( aType == cppu::UnoType<drawing::PolyPolygonBezierCoords>::get())
+ return new SvxXMLTableImportContext( *this, SvxXMLTableImportContextEnum::Marker, mrTable, bOOoFormat );
+ }
+ else if ( nToken == XML_DASH_TABLE )
+ {
+ if( aType == cppu::UnoType<drawing::LineDash>::get())
+ return new SvxXMLTableImportContext( *this, SvxXMLTableImportContextEnum::Dash, mrTable, bOOoFormat );
+ }
+ else if ( nToken == XML_HATCH_TABLE )
+ {
+ if( aType == cppu::UnoType<drawing::Hatch>::get())
+ return new SvxXMLTableImportContext( *this, SvxXMLTableImportContextEnum::Hatch, mrTable, bOOoFormat );
+ }
+ else if ( nToken == XML_GRADIENT_TABLE )
+ {
+ if( aType == cppu::UnoType<awt::Gradient>::get())
+ return new SvxXMLTableImportContext( *this, SvxXMLTableImportContextEnum::Gradient, mrTable, bOOoFormat );
+ }
+ else if ( nToken == XML_BITMAP_TABLE )
+ {
+ if( aType == ::cppu::UnoType<awt::XBitmap>::get())
+ return new SvxXMLTableImportContext( *this, SvxXMLTableImportContextEnum::Bitmap, mrTable, bOOoFormat );
+ }
+ }
+
+ return nullptr;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */