diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:06:44 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:06:44 +0000 |
commit | ed5640d8b587fbcfed7dd7967f3de04b37a76f26 (patch) | |
tree | 7a5f7c6c9d02226d7471cb3cc8fbbf631b415303 /svx/source/xml | |
parent | Initial commit. (diff) | |
download | libreoffice-upstream.tar.xz libreoffice-upstream.zip |
Adding upstream version 4:7.4.7.upstream/4%7.4.7upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r-- | svx/source/xml/xmleohlp.cxx | 717 | ||||
-rw-r--r-- | svx/source/xml/xmlexport.cxx | 239 | ||||
-rw-r--r-- | svx/source/xml/xmlgrhlp.cxx | 1190 | ||||
-rw-r--r-- | svx/source/xml/xmlxtexp.cxx | 485 | ||||
-rw-r--r-- | svx/source/xml/xmlxtimp.cxx | 461 |
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: */ |