summaryrefslogtreecommitdiffstats
path: root/oox/source/core
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:06:44 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:06:44 +0000
commited5640d8b587fbcfed7dd7967f3de04b37a76f26 (patch)
tree7a5f7c6c9d02226d7471cb3cc8fbbf631b415303 /oox/source/core
parentInitial commit. (diff)
downloadlibreoffice-ed5640d8b587fbcfed7dd7967f3de04b37a76f26.tar.xz
libreoffice-ed5640d8b587fbcfed7dd7967f3de04b37a76f26.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 'oox/source/core')
-rw-r--r--oox/source/core/binarycodec.cxx41
-rw-r--r--oox/source/core/contexthandler.cxx134
-rw-r--r--oox/source/core/contexthandler2.cxx340
-rw-r--r--oox/source/core/fastparser.cxx139
-rw-r--r--oox/source/core/fasttokenhandler.cxx82
-rw-r--r--oox/source/core/filterbase.cxx596
-rw-r--r--oox/source/core/filterdetect.cxx479
-rw-r--r--oox/source/core/fragmenthandler.cxx119
-rw-r--r--oox/source/core/fragmenthandler2.cxx155
-rw-r--r--oox/source/core/recordparser.cxx326
-rw-r--r--oox/source/core/relations.cxx158
-rw-r--r--oox/source/core/relationshandler.cxx95
-rw-r--r--oox/source/core/xmlfilterbase.cxx1235
13 files changed, 3899 insertions, 0 deletions
diff --git a/oox/source/core/binarycodec.cxx b/oox/source/core/binarycodec.cxx
new file mode 100644
index 000000000..7266e254b
--- /dev/null
+++ b/oox/source/core/binarycodec.cxx
@@ -0,0 +1,41 @@
+/* -*- 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 <oox/core/binarycodec.hxx>
+
+#include <oox/helper/attributelist.hxx>
+
+#include <osl/diagnose.h>
+
+using namespace ::com::sun::star;
+
+namespace oox::core {
+
+sal_uInt16 CodecHelper::getPasswordHash( const AttributeList& rAttribs, sal_Int32 nElement )
+{
+ sal_Int32 nPasswordHash = rAttribs.getIntegerHex( nElement, 0 );
+ OSL_ENSURE( (0 <= nPasswordHash) && (nPasswordHash <= SAL_MAX_UINT16), "CodecHelper::getPasswordHash - invalid password hash" );
+ return static_cast< sal_uInt16 >( ((0 <= nPasswordHash) && (nPasswordHash <= SAL_MAX_UINT16)) ? nPasswordHash : 0 );
+}
+
+
+
+} // namespace oox::core
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/oox/source/core/contexthandler.cxx b/oox/source/core/contexthandler.cxx
new file mode 100644
index 000000000..80e274c4a
--- /dev/null
+++ b/oox/source/core/contexthandler.cxx
@@ -0,0 +1,134 @@
+/* -*- 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 <oox/core/contexthandler.hxx>
+
+#include <oox/core/fragmenthandler.hxx>
+
+namespace oox::core {
+
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::xml::sax;
+
+ContextHandler::ContextHandler( const ContextHandler& rParent ) :
+ ContextHandler_BASE(rParent),
+ mxBaseData( rParent.mxBaseData )
+{
+}
+
+ContextHandler::ContextHandler( const FragmentBaseDataRef& rxBaseData ) :
+ mxBaseData( rxBaseData )
+{
+}
+
+ContextHandler::~ContextHandler()
+{
+}
+
+XmlFilterBase& ContextHandler::getFilter() const
+{
+ return mxBaseData->mrFilter;
+}
+
+const Relations& ContextHandler::getRelations() const
+{
+ return *mxBaseData->mxRelations;
+}
+
+const OUString& ContextHandler::getFragmentPath() const
+{
+ return mxBaseData->maFragmentPath;
+}
+
+OUString ContextHandler::getFragmentPathFromRelation( const Relation& rRelation ) const
+{
+ return mxBaseData->mxRelations->getFragmentPathFromRelation( rRelation );
+}
+
+OUString ContextHandler::getFragmentPathFromRelId( const OUString& rRelId ) const
+{
+ return mxBaseData->mxRelations->getFragmentPathFromRelId( rRelId );
+}
+
+OUString ContextHandler::getFragmentPathFromFirstType( std::u16string_view rType ) const
+{
+ return mxBaseData->mxRelations->getFragmentPathFromFirstType( rType );
+}
+
+OUString ContextHandler::getFragmentPathFromFirstTypeFromOfficeDoc( std::u16string_view rType )
+ const
+{
+ return mxBaseData->mxRelations->getFragmentPathFromFirstTypeFromOfficeDoc( rType );
+}
+
+void ContextHandler::implSetLocator( const Reference< XLocator >& rxLocator )
+{
+ mxBaseData->mxLocator = rxLocator;
+}
+
+// com.sun.star.xml.sax.XFastContextHandler interface -------------------------
+
+void ContextHandler::startFastElement( sal_Int32, const Reference< XFastAttributeList >& )
+{
+}
+
+void ContextHandler::startUnknownElement( const OUString&, const OUString&, const Reference< XFastAttributeList >& )
+{
+}
+
+void ContextHandler::endFastElement( sal_Int32 )
+{
+}
+
+void ContextHandler::endUnknownElement( const OUString&, const OUString& )
+{
+}
+
+Reference< XFastContextHandler > ContextHandler::createFastChildContext( sal_Int32, const Reference< XFastAttributeList >& )
+{
+ return nullptr;
+}
+
+Reference< XFastContextHandler > ContextHandler::createUnknownChildContext( const OUString&, const OUString&, const Reference< XFastAttributeList >& )
+{
+ return nullptr;
+}
+
+void ContextHandler::characters( const OUString& )
+{
+}
+
+// record context interface ---------------------------------------------------
+
+ContextHandlerRef ContextHandler::createRecordContext( sal_Int32, SequenceInputStream& )
+{
+ return nullptr;
+}
+
+void ContextHandler::startRecord( sal_Int32, SequenceInputStream& )
+{
+}
+
+void ContextHandler::endRecord( sal_Int32 )
+{
+}
+
+} // namespace oox::core
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/oox/source/core/contexthandler2.cxx b/oox/source/core/contexthandler2.cxx
new file mode 100644
index 000000000..1613a3d33
--- /dev/null
+++ b/oox/source/core/contexthandler2.cxx
@@ -0,0 +1,340 @@
+/* -*- 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 <oox/core/contexthandler2.hxx>
+#include <oox/core/xmlfilterbase.hxx>
+#include <oox/helper/attributelist.hxx>
+#include <oox/token/namespaces.hxx>
+#include <oox/token/tokens.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <o3tl/safeint.hxx>
+#include <osl/diagnose.h>
+#include <com/sun/star/frame/XModel.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+
+namespace oox::core {
+
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::xml::sax;
+
+/** Information about a processed element. */
+struct ElementInfo
+{
+ OUStringBuffer maChars; /// Collected element characters.
+ sal_Int32 mnElement; /// The element identifier.
+ bool mbTrimSpaces; /// True = trims leading/trailing spaces from text data.
+
+ explicit ElementInfo() : maChars( 0), mnElement( XML_TOKEN_INVALID ), mbTrimSpaces( false ) {}
+};
+
+ContextHandler2Helper::ContextHandler2Helper( bool bEnableTrimSpace, XmlFilterBase& rFilter ) :
+ mxContextStack( std::make_shared<ContextStack>() ),
+ mnRootStackSize( 0 ),
+ mbEnableTrimSpace( bEnableTrimSpace ),
+ mrFilter( rFilter )
+{
+ pushElementInfo( XML_ROOT_CONTEXT );
+}
+
+ContextHandler2Helper::ContextHandler2Helper( const ContextHandler2Helper& rParent ) :
+ mxContextStack( rParent.mxContextStack ),
+ mnRootStackSize( rParent.mxContextStack->size() ),
+ mbEnableTrimSpace( rParent.mbEnableTrimSpace ),
+ mrFilter(rParent.mrFilter)
+{
+}
+
+ContextHandler2Helper::~ContextHandler2Helper()
+{
+}
+
+sal_Int32 ContextHandler2Helper::getCurrentElementWithMce() const
+{
+ return mxContextStack->empty() ? XML_ROOT_CONTEXT : mxContextStack->back().mnElement;
+}
+
+sal_Int32 ContextHandler2Helper::getCurrentElement() const
+{
+ auto It = std::find_if(mxContextStack->rbegin(), mxContextStack->rend(),
+ [](const ElementInfo& rItem) { return getNamespace(rItem.mnElement) != NMSP_mce; });
+ if (It != mxContextStack->rend())
+ return It->mnElement;
+ return XML_ROOT_CONTEXT;
+}
+
+sal_Int32 ContextHandler2Helper::getParentElement( sal_Int32 nCountBack ) const
+{
+ if( (nCountBack < 0) || (mxContextStack->size() < o3tl::make_unsigned( nCountBack )) )
+ return XML_TOKEN_INVALID;
+ return (mxContextStack->size() == static_cast< size_t >( nCountBack )) ?
+ XML_ROOT_CONTEXT : (*mxContextStack)[ mxContextStack->size() - nCountBack - 1 ].mnElement;
+}
+
+bool ContextHandler2Helper::isRootElement() const
+{
+ return mxContextStack->size() == mnRootStackSize + 1;
+}
+
+Reference< XFastContextHandler > ContextHandler2Helper::implCreateChildContext(
+ sal_Int32 nElement, const Reference< XFastAttributeList >& rxAttribs )
+{
+ // #i76091# process collected characters (calls onCharacters() if needed)
+ processCollectedChars();
+ ContextHandlerRef xContext = onCreateContext( nElement, AttributeList( rxAttribs ) );
+ return xContext;
+}
+
+void ContextHandler2Helper::implStartElement( sal_Int32 nElement, const Reference< XFastAttributeList >& rxAttribs )
+{
+ AttributeList aAttribs( rxAttribs );
+ pushElementInfo( nElement ).mbTrimSpaces = aAttribs.getToken( XML_TOKEN( space ), XML_TOKEN_INVALID ) != XML_preserve;
+ onStartElement( aAttribs );
+}
+
+void ContextHandler2Helper::implCharacters( std::u16string_view rChars )
+{
+ // #i76091# collect characters until new element starts or this element ends
+ if( !mxContextStack->empty() )
+ mxContextStack->back().maChars.append(rChars);
+}
+
+void ContextHandler2Helper::implEndElement( sal_Int32 nElement )
+{
+ OSL_ENSURE( getCurrentElementWithMce() == nElement, "ContextHandler2Helper::implEndElement - context stack broken" );
+ if( !mxContextStack->empty() )
+ {
+ // #i76091# process collected characters (calls onCharacters() if needed)
+ processCollectedChars();
+ onEndElement();
+ popElementInfo();
+ }
+}
+
+ContextHandlerRef ContextHandler2Helper::implCreateRecordContext( sal_Int32 nRecId, SequenceInputStream& rStrm )
+{
+ return onCreateRecordContext( nRecId, rStrm );
+}
+
+void ContextHandler2Helper::implStartRecord( sal_Int32 nRecId, SequenceInputStream& rStrm )
+{
+ pushElementInfo( nRecId );
+ onStartRecord( rStrm );
+}
+
+void ContextHandler2Helper::implEndRecord( sal_Int32 nRecId )
+{
+ OSL_ENSURE( getCurrentElementWithMce() == nRecId, "ContextHandler2Helper::implEndRecord - context stack broken" );
+ if( !mxContextStack->empty() )
+ {
+ onEndRecord();
+ popElementInfo();
+ }
+}
+
+ElementInfo& ContextHandler2Helper::pushElementInfo( sal_Int32 nElement )
+{
+ mxContextStack->emplace_back();
+ ElementInfo& rInfo = mxContextStack->back();
+ rInfo.mnElement = nElement;
+ return rInfo;
+}
+
+void ContextHandler2Helper::popElementInfo()
+{
+ OSL_ENSURE( !mxContextStack->empty(), "ContextHandler2Helper::popElementInfo - context stack broken" );
+ if( !mxContextStack->empty() )
+ mxContextStack->pop_back();
+}
+
+void ContextHandler2Helper::processCollectedChars()
+{
+ OSL_ENSURE( !mxContextStack->empty(), "ContextHandler2Helper::processCollectedChars - no context info" );
+ if (mxContextStack->empty())
+ return;
+ ElementInfo& rInfo = mxContextStack->back();
+ if( !rInfo.maChars.isEmpty() )
+ {
+ OUString aChars = rInfo.maChars.makeStringAndClear();
+ if( mbEnableTrimSpace && rInfo.mbTrimSpaces )
+ aChars = aChars.trim();
+ if( !aChars.isEmpty() )
+ onCharacters( aChars );
+ }
+}
+
+ContextHandler2::ContextHandler2( ContextHandler2Helper const & rParent ) :
+ ContextHandler( dynamic_cast< ContextHandler const & >( rParent ) ),
+ ContextHandler2Helper( rParent )
+{
+}
+
+ContextHandler2::~ContextHandler2()
+{
+}
+
+// com.sun.star.xml.sax.XFastContextHandler interface -------------------------
+
+Reference< XFastContextHandler > SAL_CALL ContextHandler2::createFastChildContext(
+ sal_Int32 nElement, const Reference< XFastAttributeList >& rxAttribs )
+{
+ if( getNamespace( nElement ) == NMSP_mce ) // TODO for checking 'Ignorable'
+ {
+ if( prepareMceContext( nElement, AttributeList( rxAttribs ) ) )
+ return this;
+ return nullptr;
+ }
+
+ return implCreateChildContext( nElement, rxAttribs );
+}
+
+void SAL_CALL ContextHandler2::startFastElement(
+ sal_Int32 nElement, const Reference< XFastAttributeList >& rxAttribs )
+{
+ implStartElement( nElement, rxAttribs );
+}
+
+void SAL_CALL ContextHandler2::characters( const OUString& rChars )
+{
+ implCharacters( rChars );
+}
+
+void SAL_CALL ContextHandler2::endFastElement( sal_Int32 nElement )
+{
+ implEndElement( nElement );
+}
+
+bool ContextHandler2Helper::prepareMceContext( sal_Int32 nElement, const AttributeList& rAttribs )
+{
+ switch( nElement )
+ {
+ case MCE_TOKEN( AlternateContent ):
+ addMCEState( MCE_STATE::Started );
+ break;
+
+ case MCE_TOKEN( Choice ):
+ {
+ if (isMCEStateEmpty() || getMCEState() != MCE_STATE::Started)
+ return false;
+
+ OUString aRequires = rAttribs.getString( XML_Requires, "none" );
+
+ // At this point we can't access namespaces as the correct xml filter
+ // is long gone. For now let's decide depending on a list of supported
+ // namespaces like we do in writerfilter
+
+ std::vector<OUString> aSupportedNS =
+ {
+ "a14", // Impress needs this to import math formulas.
+ "p14",
+ "p15",
+ "x12ac",
+ "v"
+ };
+
+ Reference<XServiceInfo> xModel(getDocFilter().getModel(), UNO_QUERY);
+ if (xModel.is() && xModel->supportsService("com.sun.star.sheet.SpreadsheetDocument"))
+ {
+ // No a14 for Calc documents, it would cause duplicated shapes as-is.
+ auto it = std::find(aSupportedNS.begin(), aSupportedNS.end(), "a14");
+ if (it != aSupportedNS.end())
+ {
+ aSupportedNS.erase(it);
+ }
+ }
+
+ if (std::find(aSupportedNS.begin(), aSupportedNS.end(), aRequires) != aSupportedNS.end())
+ setMCEState( MCE_STATE::FoundChoice ) ;
+ else
+ return false;
+ }
+ break;
+
+ case MCE_TOKEN( Fallback ):
+ if( !isMCEStateEmpty() && getMCEState() == MCE_STATE::Started )
+ break;
+ return false;
+ default:
+ {
+ OUString str = rAttribs.getString( MCE_TOKEN( Ignorable ), OUString() );
+ if( !str.isEmpty() )
+ {
+ // Sequence< css::xml::FastAttribute > attrs = rAttribs.getFastAttributeList()->getFastAttributes();
+ // printf("MCE: %s\n", OUStringToOString( str, RTL_TEXTENCODING_UTF8 ).getStr() );
+ // TODO: Check & Get the namespaces in "Ignorable"
+ // printf("NS: %d : %s\n", attrs.getLength(), OUStringToOString( str, RTL_TEXTENCODING_UTF8 ).getStr() );
+ }
+ }
+ return false;
+ }
+ return true;
+}
+
+// oox.core.RecordContext interface -------------------------------------------
+
+ContextHandlerRef ContextHandler2::createRecordContext( sal_Int32 nRecId, SequenceInputStream& rStrm )
+{
+ return implCreateRecordContext( nRecId, rStrm );
+}
+
+void ContextHandler2::startRecord( sal_Int32 nRecId, SequenceInputStream& rStrm )
+{
+ implStartRecord( nRecId, rStrm );
+}
+
+void ContextHandler2::endRecord( sal_Int32 nRecId )
+{
+ implEndRecord( nRecId );
+}
+
+// oox.core.ContextHandler2Helper interface -----------------------------------
+
+ContextHandlerRef ContextHandler2::onCreateContext( sal_Int32, const AttributeList& )
+{
+ return nullptr;
+}
+
+void ContextHandler2::onStartElement( const AttributeList& )
+{
+}
+
+void ContextHandler2::onCharacters( const OUString& )
+{
+}
+
+void ContextHandler2::onEndElement()
+{
+}
+
+ContextHandlerRef ContextHandler2::onCreateRecordContext( sal_Int32, SequenceInputStream& )
+{
+ return nullptr;
+}
+
+void ContextHandler2::onStartRecord( SequenceInputStream& )
+{
+}
+
+void ContextHandler2::onEndRecord()
+{
+}
+
+} // namespace oox::core
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/oox/source/core/fastparser.cxx b/oox/source/core/fastparser.cxx
new file mode 100644
index 000000000..53e5eb78a
--- /dev/null
+++ b/oox/source/core/fastparser.cxx
@@ -0,0 +1,139 @@
+/* -*- 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 <com/sun/star/lang/IllegalArgumentException.hpp>
+#include <oox/core/fastparser.hxx>
+
+#include <oox/core/fasttokenhandler.hxx>
+#include <oox/helper/containerhelper.hxx>
+#include <oox/helper/storagebase.hxx>
+#include <oox/token/namespacemap.hxx>
+
+#include <sax/fastparser.hxx>
+
+namespace oox::core {
+
+using namespace ::com::sun::star::io;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::xml::sax;
+
+namespace {
+
+class InputStreamCloseGuard
+{
+public:
+ explicit InputStreamCloseGuard( const Reference< XInputStream >& rxInStream, bool bCloseStream );
+ ~InputStreamCloseGuard();
+private:
+ Reference< XInputStream > mxInStream;
+ bool mbCloseStream;
+};
+
+InputStreamCloseGuard::InputStreamCloseGuard( const Reference< XInputStream >& rxInStream, bool bCloseStream ) :
+ mxInStream( rxInStream ),
+ mbCloseStream( bCloseStream )
+{
+}
+
+InputStreamCloseGuard::~InputStreamCloseGuard()
+{
+ if( mxInStream.is() && mbCloseStream ) try { mxInStream->closeInput(); } catch( Exception& ) {}
+}
+
+} // namespace
+
+FastParser::FastParser() :
+ mrNamespaceMap( StaticNamespaceMap() )
+{
+ // create a fast parser instance
+ mxParser = new sax_fastparser::FastSaxParser;
+
+ // create the fast tokenhandler
+ mxTokenHandler.set( new FastTokenHandler );
+
+ // create the fast token handler based on the OOXML token list
+ mxParser->setTokenHandler( mxTokenHandler );
+}
+
+FastParser::~FastParser()
+{
+}
+
+void FastParser::registerNamespace( sal_Int32 nNamespaceId )
+{
+ if( !mxParser.is() )
+ throw RuntimeException();
+
+ // add handling for OOXML strict here
+ const OUString* pNamespaceUrl = ContainerHelper::getMapElement( mrNamespaceMap.maTransitionalNamespaceMap, nNamespaceId );
+ if( !pNamespaceUrl )
+ throw IllegalArgumentException();
+
+ mxParser->registerNamespace( *pNamespaceUrl, nNamespaceId );
+
+ //also register the OOXML strict namespaces for the same id
+ const OUString* pNamespaceStrictUrl = ContainerHelper::getMapElement( mrNamespaceMap.maStrictNamespaceMap, nNamespaceId );
+ if(pNamespaceStrictUrl && (*pNamespaceUrl != *pNamespaceStrictUrl))
+ {
+ mxParser->registerNamespace( *pNamespaceStrictUrl, nNamespaceId );
+ }
+}
+
+void FastParser::setDocumentHandler( const Reference< XFastDocumentHandler >& rxDocHandler )
+{
+ if( !mxParser.is() )
+ throw RuntimeException();
+ mxParser->setFastDocumentHandler( rxDocHandler );
+}
+
+void FastParser::clearDocumentHandler()
+{
+ if (!mxParser.is())
+ return;
+ mxParser->setFastDocumentHandler(nullptr);
+}
+
+void FastParser::parseStream( const InputSource& rInputSource, bool bCloseStream )
+{
+ // guard closing the input stream also when exceptions are thrown
+ InputStreamCloseGuard aGuard( rInputSource.aInputStream, bCloseStream );
+ if( !mxParser.is() )
+ throw RuntimeException();
+ mxParser->parseStream( rInputSource );
+}
+
+void FastParser::parseStream( const Reference< XInputStream >& rxInStream, const OUString& rStreamName )
+{
+ InputSource aInputSource;
+ aInputSource.sSystemId = rStreamName;
+ aInputSource.aInputStream = rxInStream;
+ parseStream( aInputSource );
+}
+
+void FastParser::parseStream( StorageBase& rStorage, const OUString& rStreamName )
+{
+ parseStream( rStorage.openInputStream( rStreamName ), rStreamName );
+}
+
+} // namespace oox::core
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/oox/source/core/fasttokenhandler.cxx b/oox/source/core/fasttokenhandler.cxx
new file mode 100644
index 000000000..398772df0
--- /dev/null
+++ b/oox/source/core/fasttokenhandler.cxx
@@ -0,0 +1,82 @@
+/* -*- 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 <oox/core/fasttokenhandler.hxx>
+
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <oox/token/tokenmap.hxx>
+#include <cppuhelper/supportsservice.hxx>
+
+using namespace ::com::sun::star;
+
+namespace oox::core {
+
+using namespace ::com::sun::star::uno;
+
+FastTokenHandler::FastTokenHandler() :
+ mrTokenMap( StaticTokenMap() )
+{
+}
+
+FastTokenHandler::~FastTokenHandler()
+{
+}
+
+// XServiceInfo
+OUString SAL_CALL FastTokenHandler::getImplementationName()
+{
+ return "com.sun.star.comp.oox.core.FastTokenHandler";
+}
+
+sal_Bool SAL_CALL FastTokenHandler::supportsService( const OUString& rServiceName )
+{
+ return cppu::supportsService(this, rServiceName);
+}
+
+Sequence< OUString > SAL_CALL FastTokenHandler::getSupportedServiceNames()
+{
+ Sequence<OUString> aServiceNames { "com.sun.star.xml.sax.FastTokenHandler" };
+ return aServiceNames;
+}
+
+Sequence< sal_Int8 > FastTokenHandler::getUTF8Identifier( sal_Int32 nToken )
+{
+ return mrTokenMap.getUtf8TokenName( nToken );
+}
+
+sal_Int32 FastTokenHandler::getTokenFromUTF8( const Sequence< sal_Int8 >& rIdentifier )
+{
+ return mrTokenMap.getTokenFromUtf8( rIdentifier );
+}
+
+sal_Int32 FastTokenHandler::getTokenDirect( const char *pToken, sal_Int32 nLength ) const
+{
+ return mrTokenMap.getTokenFromUTF8( pToken, nLength );
+}
+
+} // namespace oox::core
+
+extern "C" SAL_DLLPUBLIC_EXPORT uno::XInterface*
+com_sun_star_comp_oox_core_FastTokenHandler_get_implementation(
+ uno::XComponentContext* /*pCtx*/, uno::Sequence<uno::Any> const& /*rSeq*/)
+{
+ return cppu::acquire(new oox::core::FastTokenHandler());
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/oox/source/core/filterbase.cxx b/oox/source/core/filterbase.cxx
new file mode 100644
index 000000000..6ae55238c
--- /dev/null
+++ b/oox/source/core/filterbase.cxx
@@ -0,0 +1,596 @@
+/* -*- 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 <com/sun/star/container/XNameAccess.hpp>
+#include <com/sun/star/drawing/XShape.hpp>
+#include <com/sun/star/frame/XModel.hpp>
+#include <com/sun/star/io/XStream.hpp>
+#include <com/sun/star/lang/XMultiServiceFactory.hpp>
+#include <com/sun/star/task/XInteractionHandler.hpp>
+#include <com/sun/star/task/XStatusIndicator.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <cppuhelper/supportsservice.hxx>
+#include <comphelper/documentconstants.hxx>
+#include <comphelper/sequence.hxx>
+#include <comphelper/scopeguard.hxx>
+#include <unotools/mediadescriptor.hxx>
+#include <osl/diagnose.h>
+#include <rtl/uri.hxx>
+#include <memory>
+#include <mutex>
+#include <set>
+
+#include <oox/core/filterbase.hxx>
+#include <oox/helper/binaryinputstream.hxx>
+#include <oox/helper/binaryoutputstream.hxx>
+#include <oox/helper/graphichelper.hxx>
+#include <oox/helper/modelobjecthelper.hxx>
+#include <oox/ole/oleobjecthelper.hxx>
+#include <oox/ole/vbaproject.hxx>
+
+namespace oox::core {
+
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::frame;
+using namespace ::com::sun::star::graphic;
+using namespace ::com::sun::star::drawing;
+using namespace ::com::sun::star::io;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::task;
+using namespace ::com::sun::star::uno;
+
+using ::com::sun::star::container::XNameAccess;
+using utl::MediaDescriptor;
+using ::comphelper::SequenceAsHashMap;
+using ::oox::ole::OleObjectHelper;
+using ::oox::ole::VbaProject;
+
+namespace {
+
+struct UrlPool
+{
+ std::mutex maMutex;
+ ::std::set< OUString > maUrls;
+};
+
+UrlPool& StaticUrlPool()
+{
+ static UrlPool SINGLETON;
+ return SINGLETON;
+}
+
+/** This guard prevents recursive loading/saving of the same document. */
+class DocumentOpenedGuard
+{
+public:
+ explicit DocumentOpenedGuard( const OUString& rUrl );
+ ~DocumentOpenedGuard();
+ DocumentOpenedGuard(const DocumentOpenedGuard&) = delete;
+ DocumentOpenedGuard& operator=(const DocumentOpenedGuard&) = delete;
+
+ bool isValid() const { return mbValid; }
+
+private:
+ OUString maUrl;
+ bool mbValid;
+};
+
+DocumentOpenedGuard::DocumentOpenedGuard( const OUString& rUrl )
+{
+ UrlPool& rUrlPool = StaticUrlPool();
+ std::scoped_lock aGuard( rUrlPool.maMutex );
+ mbValid = rUrl.isEmpty() || (rUrlPool.maUrls.count( rUrl ) == 0);
+ if( mbValid && !rUrl.isEmpty() )
+ {
+ rUrlPool.maUrls.insert( rUrl );
+ maUrl = rUrl;
+ }
+}
+
+DocumentOpenedGuard::~DocumentOpenedGuard()
+{
+ UrlPool& rUrlPool = StaticUrlPool();
+ std::scoped_lock aGuard( rUrlPool.maMutex );
+ if( !maUrl.isEmpty() )
+ rUrlPool.maUrls.erase( maUrl );
+}
+
+/** Specifies whether this filter is an import or export filter. */
+enum FilterDirection
+{
+ FILTERDIRECTION_UNKNOWN,
+ FILTERDIRECTION_IMPORT,
+ FILTERDIRECTION_EXPORT
+};
+
+} // namespace
+
+struct FilterBaseImpl
+{
+ typedef std::shared_ptr< GraphicHelper > GraphicHelperRef;
+ typedef std::shared_ptr< ModelObjectHelper > ModelObjHelperRef;
+ typedef std::shared_ptr< OleObjectHelper > OleObjHelperRef;
+ typedef std::shared_ptr< VbaProject > VbaProjectRef;
+
+ FilterDirection meDirection;
+ SequenceAsHashMap maArguments;
+ SequenceAsHashMap maFilterData;
+ MediaDescriptor maMediaDesc;
+ OUString maFileUrl;
+ StorageRef mxStorage;
+ OoxmlVersion meVersion;
+
+ GraphicHelperRef mxGraphicHelper; /// Graphic and graphic object handling.
+ ModelObjHelperRef mxModelObjHelper; /// Tables to create new named drawing objects.
+ std::map<css::uno::Reference<css::lang::XMultiServiceFactory>, ModelObjHelperRef>
+ mxModelObjHelpers;
+ OleObjHelperRef mxOleObjHelper; /// OLE object handling.
+ VbaProjectRef mxVbaProject; /// VBA project manager.
+
+ Reference< XComponentContext > mxComponentContext;
+ Reference< XModel > mxModel;
+ Reference< XMultiServiceFactory > mxModelFactory;
+ Reference< XFrame > mxTargetFrame;
+ Reference< XInputStream > mxInStream;
+ Reference< XStream > mxOutStream;
+ Reference< XStatusIndicator > mxStatusIndicator;
+ Reference< XInteractionHandler > mxInteractionHandler;
+ Reference< XShape > mxParentShape;
+
+ bool mbExportVBA;
+
+ bool mbExportTemplate;
+
+ /// @throws RuntimeException
+ explicit FilterBaseImpl( const Reference< XComponentContext >& rxContext );
+
+ /// @throws IllegalArgumentException
+ void setDocumentModel( const Reference< XComponent >& rxComponent );
+};
+
+FilterBaseImpl::FilterBaseImpl( const Reference< XComponentContext >& rxContext ) :
+ meDirection( FILTERDIRECTION_UNKNOWN ),
+ meVersion( ECMA_DIALECT ),
+ mxComponentContext( rxContext, UNO_SET_THROW ),
+ mbExportVBA(false),
+ mbExportTemplate(false)
+{
+}
+
+void FilterBaseImpl::setDocumentModel( const Reference< XComponent >& rxComponent )
+{
+ try
+ {
+ mxModel.set( rxComponent, UNO_QUERY_THROW );
+ mxModelFactory.set( rxComponent, UNO_QUERY_THROW );
+ }
+ catch( Exception& )
+ {
+ throw IllegalArgumentException();
+ }
+}
+
+FilterBase::FilterBase( const Reference< XComponentContext >& rxContext ) :
+ mxImpl( new FilterBaseImpl( rxContext ) )
+{
+}
+
+FilterBase::~FilterBase()
+{
+}
+
+bool FilterBase::isImportFilter() const
+{
+ return mxImpl->meDirection == FILTERDIRECTION_IMPORT;
+}
+
+bool FilterBase::isExportFilter() const
+{
+ return mxImpl->meDirection == FILTERDIRECTION_EXPORT;
+}
+
+OoxmlVersion FilterBase::getVersion() const
+{
+ return mxImpl->meVersion;
+}
+
+const Reference< XComponentContext >& FilterBase::getComponentContext() const
+{
+ return mxImpl->mxComponentContext;
+}
+
+const Reference< XModel >& FilterBase::getModel() const
+{
+ return mxImpl->mxModel;
+}
+
+const Reference< XMultiServiceFactory >& FilterBase::getModelFactory() const
+{
+ return mxImpl->mxModelFactory;
+}
+
+const Reference< XFrame >& FilterBase::getTargetFrame() const
+{
+ return mxImpl->mxTargetFrame;
+}
+
+const Reference< XStatusIndicator >& FilterBase::getStatusIndicator() const
+{
+ return mxImpl->mxStatusIndicator;
+}
+
+MediaDescriptor& FilterBase::getMediaDescriptor() const
+{
+ return mxImpl->maMediaDesc;
+}
+
+SequenceAsHashMap& FilterBase::getFilterData() const
+{
+ return mxImpl->maFilterData;
+}
+
+const OUString& FilterBase::getFileUrl() const
+{
+ return mxImpl->maFileUrl;
+}
+
+namespace {
+
+bool lclIsDosDrive( const OUString& rUrl, sal_Int32 nPos = 0 )
+{
+ return
+ (rUrl.getLength() >= nPos + 3) &&
+ ((('A' <= rUrl[ nPos ]) && (rUrl[ nPos ] <= 'Z')) || (('a' <= rUrl[ nPos ]) && (rUrl[ nPos ] <= 'z'))) &&
+ (rUrl[ nPos + 1 ] == ':') &&
+ (rUrl[ nPos + 2 ] == '/');
+}
+
+} // namespace
+
+OUString FilterBase::getAbsoluteUrl( const OUString& rUrl ) const
+{
+ // handle some special cases before calling ::rtl::Uri::convertRelToAbs()
+
+ static const OUStringLiteral aFileSchema = u"file:";
+ static const OUStringLiteral aFilePrefix = u"file:///";
+ const sal_Int32 nFilePrefixLen = aFilePrefix.getLength();
+ static const OUStringLiteral aUncPrefix = u"//";
+
+ /* (1) convert all backslashes to slashes, and check that passed URL is
+ not empty. */
+ OUString aUrl = rUrl.replace( '\\', '/' );
+ if( aUrl.isEmpty() )
+ return aUrl;
+
+ /* (2) add 'file:///' to absolute Windows paths, e.g. convert
+ 'C:/path/file' to 'file:///c:/path/file'. */
+ if( lclIsDosDrive( aUrl ) )
+ return aFilePrefix + aUrl;
+
+ /* (3) add 'file:' to UNC paths, e.g. convert '//server/path/file' to
+ 'file://server/path/file'. */
+ if( aUrl.match( aUncPrefix ) )
+ return aFileSchema + aUrl;
+
+ /* (4) remove additional slashes from UNC paths, e.g. convert
+ 'file://///server/path/file' to 'file://server/path/file'. */
+ if( (aUrl.getLength() >= nFilePrefixLen + 2) &&
+ aUrl.match( aFilePrefix ) &&
+ aUrl.match( aUncPrefix, nFilePrefixLen ) )
+ {
+ return aFileSchema + aUrl.subView( nFilePrefixLen );
+ }
+
+ /* (5) handle URLs relative to current drive, e.g. the URL '/path1/file1'
+ relative to the base URL 'file:///C:/path2/file2' does not result in
+ the expected 'file:///C:/path1/file1', but in 'file:///path1/file1'. */
+ if( aUrl.startsWith("/") &&
+ mxImpl->maFileUrl.match( aFilePrefix ) &&
+ lclIsDosDrive( mxImpl->maFileUrl, nFilePrefixLen ) )
+ {
+ return OUString::Concat(mxImpl->maFileUrl.subView( 0, nFilePrefixLen + 3 )) + aUrl.subView( 1 );
+ }
+
+ try
+ {
+ return ::rtl::Uri::convertRelToAbs( mxImpl->maFileUrl, aUrl );
+ }
+ catch( ::rtl::MalformedUriException& )
+ {
+ }
+ return aUrl;
+}
+
+StorageRef const & FilterBase::getStorage() const
+{
+ return mxImpl->mxStorage;
+}
+
+Reference< XInputStream > FilterBase::openInputStream( const OUString& rStreamName ) const
+{
+ if (!mxImpl->mxStorage)
+ throw RuntimeException();
+ return mxImpl->mxStorage->openInputStream( rStreamName );
+}
+
+Reference< XOutputStream > FilterBase::openOutputStream( const OUString& rStreamName ) const
+{
+ return mxImpl->mxStorage->openOutputStream( rStreamName );
+}
+
+void FilterBase::commitStorage() const
+{
+ mxImpl->mxStorage->commit();
+}
+
+// helpers
+
+GraphicHelper& FilterBase::getGraphicHelper() const
+{
+ if( !mxImpl->mxGraphicHelper )
+ mxImpl->mxGraphicHelper.reset( implCreateGraphicHelper() );
+ return *mxImpl->mxGraphicHelper;
+}
+
+ModelObjectHelper& FilterBase::getModelObjectHelper() const
+{
+ if( !mxImpl->mxModelObjHelper )
+ mxImpl->mxModelObjHelper = std::make_shared<ModelObjectHelper>( mxImpl->mxModelFactory );
+ return *mxImpl->mxModelObjHelper;
+}
+
+ModelObjectHelper& FilterBase::getModelObjectHelperForModel(
+ const css::uno::Reference<css::lang::XMultiServiceFactory>& xFactory) const
+{
+ if (!mxImpl->mxModelObjHelpers.count(xFactory))
+ mxImpl->mxModelObjHelpers[xFactory] = std::make_shared<ModelObjectHelper>(xFactory);
+ return *mxImpl->mxModelObjHelpers[xFactory];
+}
+
+OleObjectHelper& FilterBase::getOleObjectHelper() const
+{
+ if( !mxImpl->mxOleObjHelper )
+ mxImpl->mxOleObjHelper = std::make_shared<OleObjectHelper>(mxImpl->mxModelFactory, mxImpl->mxModel);
+ return *mxImpl->mxOleObjHelper;
+}
+
+VbaProject& FilterBase::getVbaProject() const
+{
+ if( !mxImpl->mxVbaProject )
+ mxImpl->mxVbaProject.reset( implCreateVbaProject() );
+ return *mxImpl->mxVbaProject;
+}
+
+bool FilterBase::importBinaryData( StreamDataSequence & orDataSeq, const OUString& rStreamName )
+{
+ OSL_ENSURE( !rStreamName.isEmpty(), "FilterBase::importBinaryData - empty stream name" );
+ if( rStreamName.isEmpty() )
+ return false;
+
+ // try to open the stream (this may fail - do not assert)
+ BinaryXInputStream aInStrm( openInputStream( rStreamName ), true );
+ if( aInStrm.isEof() )
+ return false;
+
+ // copy the entire stream to the passed sequence
+ SequenceOutputStream aOutStrm( orDataSeq );
+ aInStrm.copyToStream( aOutStrm );
+ return true;
+}
+
+// com.sun.star.lang.XServiceInfo interface
+
+sal_Bool SAL_CALL FilterBase::supportsService( const OUString& rServiceName )
+{
+ return cppu::supportsService(this, rServiceName);
+}
+
+Sequence< OUString > SAL_CALL FilterBase::getSupportedServiceNames()
+{
+ return { "com.sun.star.document.ImportFilter", "com.sun.star.document.ExportFilter" };
+}
+
+// com.sun.star.lang.XInitialization interface
+
+void SAL_CALL FilterBase::initialize( const Sequence< Any >& rArgs )
+{
+ if( rArgs.getLength() >= 2 ) try
+ {
+ mxImpl->maArguments << rArgs[ 1 ];
+ }
+ catch( Exception& )
+ {
+ }
+
+ if (!rArgs.hasElements())
+ return;
+
+ Sequence<css::beans::PropertyValue> aSeq;
+ rArgs[0] >>= aSeq;
+ for (const auto& rVal : std::as_const(aSeq))
+ {
+ if (rVal.Name == "UserData")
+ {
+ css::uno::Sequence<OUString> aUserDataSeq;
+ rVal.Value >>= aUserDataSeq;
+ if (comphelper::findValue(aUserDataSeq, "macro-enabled") != -1)
+ mxImpl->mbExportVBA = true;
+ }
+ else if (rVal.Name == "Flags")
+ {
+ sal_Int32 nFlags(0);
+ rVal.Value >>= nFlags;
+ mxImpl->mbExportTemplate = bool(static_cast<SfxFilterFlags>(nFlags) & SfxFilterFlags::TEMPLATE);
+ }
+ }
+}
+
+// com.sun.star.document.XImporter interface
+
+void SAL_CALL FilterBase::setTargetDocument( const Reference< XComponent >& rxDocument )
+{
+ mxImpl->setDocumentModel( rxDocument );
+ mxImpl->meDirection = FILTERDIRECTION_IMPORT;
+}
+
+// com.sun.star.document.XExporter interface
+
+void SAL_CALL FilterBase::setSourceDocument( const Reference< XComponent >& rxDocument )
+{
+ mxImpl->setDocumentModel( rxDocument );
+ mxImpl->meDirection = FILTERDIRECTION_EXPORT;
+}
+
+// com.sun.star.document.XFilter interface
+
+sal_Bool SAL_CALL FilterBase::filter( const Sequence< PropertyValue >& rMediaDescSeq )
+{
+ if( !mxImpl->mxModel.is() || !mxImpl->mxModelFactory.is() || (mxImpl->meDirection == FILTERDIRECTION_UNKNOWN) )
+ throw RuntimeException();
+
+ bool bRet = false;
+ setMediaDescriptor( rMediaDescSeq );
+ DocumentOpenedGuard aOpenedGuard( mxImpl->maFileUrl );
+ if( aOpenedGuard.isValid() || mxImpl->maFileUrl.isEmpty() )
+ {
+ Reference<XModel> xTempModel = mxImpl->mxModel;
+ xTempModel->lockControllers();
+ comphelper::ScopeGuard const lockControllersGuard([xTempModel]() {
+ xTempModel->unlockControllers();
+ });
+
+ switch( mxImpl->meDirection )
+ {
+ case FILTERDIRECTION_UNKNOWN:
+ break;
+ case FILTERDIRECTION_IMPORT:
+ if( mxImpl->mxInStream.is() )
+ {
+ mxImpl->mxStorage = implCreateStorage( mxImpl->mxInStream );
+ bRet = mxImpl->mxStorage && importDocument();
+ }
+ break;
+ case FILTERDIRECTION_EXPORT:
+ if( mxImpl->mxOutStream.is() )
+ {
+ mxImpl->mxStorage = implCreateStorage( mxImpl->mxOutStream );
+ bRet = mxImpl->mxStorage && exportDocument() && implFinalizeExport( getMediaDescriptor() );
+ }
+ break;
+ }
+ }
+ return bRet;
+}
+
+void SAL_CALL FilterBase::cancel()
+{
+}
+
+// protected
+
+Reference< XInputStream > FilterBase::implGetInputStream( MediaDescriptor& rMediaDesc ) const
+{
+ return rMediaDesc.getUnpackedValueOrDefault( MediaDescriptor::PROP_INPUTSTREAM, Reference< XInputStream >() );
+}
+
+Reference< XStream > FilterBase::implGetOutputStream( MediaDescriptor& rMediaDesc ) const
+{
+ return rMediaDesc.getUnpackedValueOrDefault( MediaDescriptor::PROP_STREAMFOROUTPUT, Reference< XStream >() );
+}
+
+bool FilterBase::implFinalizeExport( MediaDescriptor& /*rMediaDescriptor*/ )
+{
+ return true;
+}
+
+Reference< XStream > const & FilterBase::getMainDocumentStream( ) const
+{
+ return mxImpl->mxOutStream;
+}
+
+// private
+
+void FilterBase::setMediaDescriptor( const Sequence< PropertyValue >& rMediaDescSeq )
+{
+ mxImpl->maMediaDesc << rMediaDescSeq;
+
+ switch( mxImpl->meDirection )
+ {
+ case FILTERDIRECTION_UNKNOWN:
+ OSL_FAIL( "FilterBase::setMediaDescriptor - invalid filter direction" );
+ break;
+ case FILTERDIRECTION_IMPORT:
+ mxImpl->maMediaDesc.addInputStream();
+ mxImpl->mxInStream = implGetInputStream( mxImpl->maMediaDesc );
+ OSL_ENSURE( mxImpl->mxInStream.is(), "FilterBase::setMediaDescriptor - missing input stream" );
+ break;
+ case FILTERDIRECTION_EXPORT:
+ mxImpl->mxOutStream = implGetOutputStream( mxImpl->maMediaDesc );
+ OSL_ENSURE( mxImpl->mxOutStream.is(), "FilterBase::setMediaDescriptor - missing output stream" );
+ break;
+ }
+
+ mxImpl->maFileUrl = mxImpl->maMediaDesc.getUnpackedValueOrDefault( MediaDescriptor::PROP_URL, OUString() );
+ mxImpl->mxTargetFrame = mxImpl->maMediaDesc.getUnpackedValueOrDefault( MediaDescriptor::PROP_FRAME, Reference< XFrame >() );
+ mxImpl->mxStatusIndicator = mxImpl->maMediaDesc.getUnpackedValueOrDefault( MediaDescriptor::PROP_STATUSINDICATOR, Reference< XStatusIndicator >() );
+ mxImpl->mxInteractionHandler = mxImpl->maMediaDesc.getUnpackedValueOrDefault( MediaDescriptor::PROP_INTERACTIONHANDLER, Reference< XInteractionHandler >() );
+ mxImpl->mxParentShape = mxImpl->maMediaDesc.getUnpackedValueOrDefault( "ParentShape", mxImpl->mxParentShape );
+ mxImpl->maFilterData = mxImpl->maMediaDesc.getUnpackedValueOrDefault( "FilterData", Sequence< PropertyValue >() );
+
+ // Check for ISO OOXML
+ OUString sFilterName = mxImpl->maMediaDesc.getUnpackedValueOrDefault( "FilterName", OUString() );
+ try
+ {
+ Reference<XMultiServiceFactory> xFactory(getComponentContext()->getServiceManager(), UNO_QUERY_THROW);
+ Reference<XNameAccess> xFilters(xFactory->createInstance("com.sun.star.document.FilterFactory" ), UNO_QUERY_THROW );
+ Any aValues = xFilters->getByName( sFilterName );
+ Sequence<PropertyValue > aPropSeq;
+ aValues >>= aPropSeq;
+ SequenceAsHashMap aProps( aPropSeq );
+
+ sal_Int32 nVersion = aProps.getUnpackedValueOrDefault( "FileFormatVersion", sal_Int32( 0 ) );
+ mxImpl->meVersion = OoxmlVersion( nVersion );
+ }
+ catch ( const Exception& )
+ {
+ // Not ISO OOXML
+ }
+}
+
+GraphicHelper* FilterBase::implCreateGraphicHelper() const
+{
+ // default: return base implementation without any special behaviour
+ return new GraphicHelper( mxImpl->mxComponentContext, mxImpl->mxTargetFrame, mxImpl->mxStorage );
+}
+
+bool FilterBase::exportVBA() const
+{
+ return mxImpl->mbExportVBA;
+}
+
+bool FilterBase::isExportTemplate() const
+{
+ return mxImpl->mbExportTemplate;
+}
+
+} // namespace oox::core
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/oox/source/core/filterdetect.cxx b/oox/source/core/filterdetect.cxx
new file mode 100644
index 000000000..455d9a8b5
--- /dev/null
+++ b/oox/source/core/filterdetect.cxx
@@ -0,0 +1,479 @@
+/* -*- 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 <oox/core/filterdetect.hxx>
+
+#include <com/sun/star/io/XStream.hpp>
+#include <comphelper/docpasswordhelper.hxx>
+#include <unotools/mediadescriptor.hxx>
+#include <cppuhelper/supportsservice.hxx>
+
+#include <oox/core/fastparser.hxx>
+#include <oox/helper/attributelist.hxx>
+#include <oox/helper/zipstorage.hxx>
+#include <oox/ole/olestorage.hxx>
+#include <oox/token/namespaces.hxx>
+#include <oox/token/tokens.hxx>
+
+#include <oox/crypto/DocumentDecryption.hxx>
+
+#include <com/sun/star/uri/UriReferenceFactory.hpp>
+#include <com/sun/star/beans/NamedValue.hpp>
+
+using namespace ::com::sun::star;
+
+namespace oox::core {
+
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::io;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::xml::sax;
+using namespace ::com::sun::star::uri;
+
+using utl::MediaDescriptor;
+using comphelper::IDocPasswordVerifier;
+using comphelper::DocPasswordVerifierResult;
+
+FilterDetectDocHandler::FilterDetectDocHandler( const Reference< XComponentContext >& rxContext, OUString& rFilterName, const OUString& rFileName ) :
+ mrFilterName( rFilterName ),
+ maFileName(rFileName),
+ maOOXMLVariant( OOXMLVariant::ECMA_Transitional ),
+ mxContext( rxContext )
+{
+ maContextStack.reserve( 2 );
+}
+
+FilterDetectDocHandler::~FilterDetectDocHandler()
+{
+}
+
+void SAL_CALL FilterDetectDocHandler::startDocument()
+{
+}
+
+void SAL_CALL FilterDetectDocHandler::endDocument()
+{
+}
+
+void SAL_CALL FilterDetectDocHandler::processingInstruction( const OUString& /*rTarget*/, const OUString& /*rData*/ )
+{
+}
+
+void SAL_CALL FilterDetectDocHandler::setDocumentLocator( const Reference<XLocator>& /*xLocator*/ )
+{
+}
+
+void SAL_CALL FilterDetectDocHandler::startFastElement(
+ sal_Int32 nElement, const Reference< XFastAttributeList >& rAttribs )
+{
+ AttributeList aAttribs( rAttribs );
+ switch ( nElement )
+ {
+ // cases for _rels/.rels
+ case PR_TOKEN( Relationships ):
+ break;
+ case PR_TOKEN( Relationship ):
+ if( !maContextStack.empty() && (maContextStack.back() == PR_TOKEN( Relationships )) )
+ parseRelationship( aAttribs );
+ break;
+
+ // cases for [Content_Types].xml
+ case PC_TOKEN( Types ):
+ break;
+ case PC_TOKEN( Default ):
+ if( !maContextStack.empty() && (maContextStack.back() == PC_TOKEN( Types )) )
+ parseContentTypesDefault( aAttribs );
+ break;
+ case PC_TOKEN( Override ):
+ if( !maContextStack.empty() && (maContextStack.back() == PC_TOKEN( Types )) )
+ parseContentTypesOverride( aAttribs );
+ break;
+ }
+ maContextStack.push_back( nElement );
+}
+
+void SAL_CALL FilterDetectDocHandler::startUnknownElement(
+ const OUString& /*Namespace*/, const OUString& /*Name*/, const Reference<XFastAttributeList>& /*Attribs*/ )
+{
+}
+
+void SAL_CALL FilterDetectDocHandler::endFastElement( sal_Int32 /*nElement*/ )
+{
+ maContextStack.pop_back();
+}
+
+void SAL_CALL FilterDetectDocHandler::endUnknownElement(
+ const OUString& /*Namespace*/, const OUString& /*Name*/ )
+{
+}
+
+Reference<XFastContextHandler> SAL_CALL FilterDetectDocHandler::createFastChildContext(
+ sal_Int32 /*Element*/, const Reference<XFastAttributeList>& /*Attribs*/ )
+{
+ return this;
+}
+
+Reference<XFastContextHandler> SAL_CALL FilterDetectDocHandler::createUnknownChildContext(
+ const OUString& /*Namespace*/, const OUString& /*Name*/, const Reference<XFastAttributeList>& /*Attribs*/)
+{
+ return this;
+}
+
+void SAL_CALL FilterDetectDocHandler::characters( const OUString& /*aChars*/ )
+{
+}
+
+void FilterDetectDocHandler::parseRelationship( const AttributeList& rAttribs )
+{
+ OUString aType = rAttribs.getString( XML_Type, OUString() );
+
+ // tdf#131936 Remember filter when opening file as 'Office Open XML Text'
+ if (aType.startsWithIgnoreAsciiCase("http://schemas.openxmlformats.org/officedocument/2006/relationships/metadata/core-properties"))
+ maOOXMLVariant = OOXMLVariant::ISO_Transitional;
+ else if (aType.startsWithIgnoreAsciiCase("http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties"))
+ maOOXMLVariant = OOXMLVariant::ECMA_Transitional;
+ else if (aType.startsWithIgnoreAsciiCase("http://purl.oclc.org/ooxml/officeDocument"))
+ maOOXMLVariant = OOXMLVariant::ISO_Strict;
+
+ if ( aType != "http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" // OOXML Transitional
+ && aType != "http://purl.oclc.org/ooxml/officeDocument/relationships/officeDocument" ) //OOXML strict
+ return;
+
+ Reference<XUriReferenceFactory> xFactory = UriReferenceFactory::create( mxContext );
+ try
+ {
+ // use '/' to representent the root of the zip package ( and provide a 'file' scheme to
+ // keep the XUriReference implementation happy )
+ Reference< XUriReference > xBase = xFactory->parse( "file:///" );
+
+ Reference< XUriReference > xPart = xFactory->parse( rAttribs.getString( XML_Target, OUString() ) );
+ Reference< XUriReference > xAbs = xFactory->makeAbsolute( xBase, xPart, true, RelativeUriExcessParentSegments_RETAIN );
+
+ if ( xAbs.is() )
+ maTargetPath = xAbs->getPath();
+ }
+ catch( const Exception& )
+ {
+ }
+}
+
+OUString FilterDetectDocHandler::getFilterNameFromContentType( std::u16string_view rContentType, const OUString& rFileName )
+{
+ bool bDocm = rFileName.endsWithIgnoreAsciiCase(".docm");
+
+ if( rContentType == u"application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml" && !bDocm )
+ {
+ switch (maOOXMLVariant)
+ {
+ case OOXMLVariant::ISO_Transitional:
+ case OOXMLVariant::ISO_Strict: // Not supported, map to ISO transitional
+ return "writer_OOXML";
+ case OOXMLVariant::ECMA_Transitional:
+ return "writer_MS_Word_2007";
+ }
+ }
+
+ if( rContentType == u"application/vnd.ms-word.document.macroEnabled.main+xml" || bDocm )
+ return "writer_MS_Word_2007_VBA";
+
+ if( rContentType == u"application/vnd.openxmlformats-officedocument.wordprocessingml.template.main+xml" ||
+ rContentType == u"application/vnd.ms-word.template.macroEnabledTemplate.main+xml" )
+ {
+ switch (maOOXMLVariant)
+ {
+ case OOXMLVariant::ISO_Transitional:
+ case OOXMLVariant::ISO_Strict: // Not supported, map to ISO transitional
+ return "writer_OOXML_Text_Template";
+ case OOXMLVariant::ECMA_Transitional:
+ return "writer_MS_Word_2007_Template";
+ }
+ }
+
+ if( rContentType == u"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml")
+ return "MS Excel 2007 XML";
+
+ if (rContentType == u"application/vnd.ms-excel.sheet.macroEnabled.main+xml")
+ return "MS Excel 2007 VBA XML";
+
+ if( rContentType == u"application/vnd.openxmlformats-officedocument.spreadsheetml.template.main+xml" ||
+ rContentType == u"application/vnd.ms-excel.template.macroEnabled.main+xml" )
+ return "MS Excel 2007 XML Template";
+
+ if ( rContentType == u"application/vnd.ms-excel.sheet.binary.macroEnabled.main" )
+ return "MS Excel 2007 Binary";
+
+ if (rContentType == u"application/vnd.openxmlformats-officedocument.presentationml.presentation.main+xml")
+ return "MS PowerPoint 2007 XML";
+
+ if (rContentType == u"application/vnd.ms-powerpoint.presentation.macroEnabled.main+xml")
+ return "MS PowerPoint 2007 XML VBA";
+
+ if( rContentType == u"application/vnd.openxmlformats-officedocument.presentationml.slideshow.main+xml" ||
+ rContentType == u"application/vnd.ms-powerpoint.slideshow.macroEnabled.main+xml" )
+ return "MS PowerPoint 2007 XML AutoPlay";
+
+ if( rContentType == u"application/vnd.openxmlformats-officedocument.presentationml.template.main+xml" ||
+ rContentType == u"application/vnd.ms-powerpoint.template.macroEnabled.main+xml" )
+ return "MS PowerPoint 2007 XML Template";
+
+ return OUString();
+}
+
+void FilterDetectDocHandler::parseContentTypesDefault( const AttributeList& rAttribs )
+{
+ // only if no overridden part name found
+ if( mrFilterName.isEmpty() )
+ {
+ // check if target path ends with extension
+ OUString aExtension = rAttribs.getString( XML_Extension, OUString() );
+ sal_Int32 nExtPos = maTargetPath.getLength() - aExtension.getLength();
+ if( (nExtPos > 0) && (maTargetPath[ nExtPos - 1 ] == '.') && maTargetPath.match( aExtension, nExtPos ) )
+ mrFilterName = getFilterNameFromContentType( rAttribs.getString( XML_ContentType, OUString() ), maFileName );
+ }
+}
+
+void FilterDetectDocHandler::parseContentTypesOverride( const AttributeList& rAttribs )
+{
+ if( rAttribs.getString( XML_PartName, OUString() ) == maTargetPath )
+ mrFilterName = getFilterNameFromContentType( rAttribs.getString( XML_ContentType, OUString() ), maFileName );
+}
+
+FilterDetect::FilterDetect( const Reference< XComponentContext >& rxContext ) :
+ mxContext( rxContext, UNO_SET_THROW )
+{
+}
+
+FilterDetect::~FilterDetect()
+{
+}
+
+namespace
+{
+
+bool lclIsZipPackage( const Reference< XComponentContext >& rxContext, const Reference< XInputStream >& rxInStrm )
+{
+ ZipStorage aZipStorage( rxContext, rxInStrm );
+ return aZipStorage.isStorage();
+}
+
+class PasswordVerifier : public IDocPasswordVerifier
+{
+public:
+ explicit PasswordVerifier( crypto::DocumentDecryption& aDecryptor );
+
+ virtual DocPasswordVerifierResult verifyPassword( const OUString& rPassword, Sequence<NamedValue>& rEncryptionData ) override;
+
+ virtual DocPasswordVerifierResult verifyEncryptionData( const Sequence<NamedValue>& rEncryptionData ) override;
+private:
+ crypto::DocumentDecryption& mDecryptor;
+};
+
+PasswordVerifier::PasswordVerifier( crypto::DocumentDecryption& aDecryptor ) :
+ mDecryptor(aDecryptor)
+{}
+
+comphelper::DocPasswordVerifierResult PasswordVerifier::verifyPassword( const OUString& rPassword, Sequence<NamedValue>& rEncryptionData )
+{
+ try
+ {
+ if (mDecryptor.generateEncryptionKey(rPassword))
+ rEncryptionData = mDecryptor.createEncryptionData(rPassword);
+ }
+ catch (...)
+ {
+ // Any exception is a reason to abort
+ return comphelper::DocPasswordVerifierResult::Abort;
+ }
+
+ return rEncryptionData.hasElements() ? comphelper::DocPasswordVerifierResult::OK : comphelper::DocPasswordVerifierResult::WrongPassword;
+}
+
+comphelper::DocPasswordVerifierResult PasswordVerifier::verifyEncryptionData( const Sequence<NamedValue>& )
+{
+ return comphelper::DocPasswordVerifierResult::WrongPassword;
+}
+
+} // namespace
+
+Reference< XInputStream > FilterDetect::extractUnencryptedPackage( MediaDescriptor& rMediaDescriptor ) const
+{
+ // try the plain input stream
+ Reference<XInputStream> xInputStream( rMediaDescriptor[ MediaDescriptor::PROP_INPUTSTREAM ], UNO_QUERY );
+ if( !xInputStream.is() || lclIsZipPackage( mxContext, xInputStream ) )
+ return xInputStream;
+
+ // check if a temporary file is passed in the 'ComponentData' property
+ Reference<XStream> xDecrypted( rMediaDescriptor.getComponentDataEntry( "DecryptedPackage" ), UNO_QUERY );
+ if( xDecrypted.is() )
+ {
+ Reference<XInputStream> xDecryptedInputStream = xDecrypted->getInputStream();
+ if( lclIsZipPackage( mxContext, xDecryptedInputStream ) )
+ return xDecryptedInputStream;
+ }
+
+ // try to decrypt an encrypted OLE package
+ oox::ole::OleStorage aOleStorage( mxContext, xInputStream, false );
+ if( aOleStorage.isStorage() )
+ {
+ try
+ {
+ crypto::DocumentDecryption aDecryptor(mxContext, aOleStorage);
+
+ if( aDecryptor.readEncryptionInfo() )
+ {
+ /* "VelvetSweatshop" is the built-in default encryption
+ password used by MS Excel for the "workbook protection"
+ feature with password. Try this first before prompting the
+ user for a password. */
+ std::vector<OUString> aDefaultPasswords;
+ aDefaultPasswords.emplace_back("VelvetSweatshop");
+
+ /* Use the comphelper password helper to request a password.
+ This helper returns either with the correct password
+ (according to the verifier), or with an empty string if
+ user has cancelled the password input dialog. */
+ PasswordVerifier aVerifier( aDecryptor );
+ Sequence<NamedValue> aEncryptionData = rMediaDescriptor.requestAndVerifyDocPassword(
+ aVerifier,
+ comphelper::DocPasswordRequestType::MS,
+ &aDefaultPasswords );
+
+ if( !aEncryptionData.hasElements() )
+ {
+ rMediaDescriptor[ MediaDescriptor::PROP_ABORTED ] <<= true;
+ }
+ else
+ {
+ // create MemoryStream for unencrypted package - rather not put this in a tempfile
+ Reference<XStream> const xTempStream(
+ mxContext->getServiceManager()->createInstanceWithContext(
+ "com.sun.star.comp.MemoryStream", mxContext),
+ UNO_QUERY_THROW);
+
+ // if decryption was unsuccessful (corrupted file or any other reason)
+ if (!aDecryptor.decrypt(xTempStream))
+ {
+ rMediaDescriptor[ MediaDescriptor::PROP_ABORTED ] <<= true;
+ }
+ else
+ {
+ // store temp file in media descriptor to keep it alive
+ rMediaDescriptor.setComponentDataEntry( "DecryptedPackage", Any( xTempStream ) );
+
+ Reference<XInputStream> xDecryptedInputStream = xTempStream->getInputStream();
+ if( lclIsZipPackage( mxContext, xDecryptedInputStream ) )
+ return xDecryptedInputStream;
+ }
+ }
+ }
+ }
+ catch( const Exception& )
+ {
+ }
+ }
+ return Reference<XInputStream>();
+}
+
+// com.sun.star.lang.XServiceInfo interface -----------------------------------
+
+OUString SAL_CALL FilterDetect::getImplementationName()
+{
+ return "com.sun.star.comp.oox.FormatDetector";
+}
+
+sal_Bool SAL_CALL FilterDetect::supportsService( const OUString& rServiceName )
+{
+ return cppu::supportsService(this, rServiceName);
+}
+
+Sequence< OUString > SAL_CALL FilterDetect::getSupportedServiceNames()
+{
+ return { "com.sun.star.frame.ExtendedTypeDetection" };
+}
+
+// com.sun.star.document.XExtendedFilterDetection interface -------------------
+
+OUString SAL_CALL FilterDetect::detect( Sequence< PropertyValue >& rMediaDescSeq )
+{
+ OUString aFilterName;
+ MediaDescriptor aMediaDescriptor( rMediaDescSeq );
+
+ try
+ {
+ aMediaDescriptor.addInputStream();
+
+ /* Get the unencrypted input stream. This may include creation of a
+ temporary file that contains the decrypted package. This temporary
+ file will be stored in the 'ComponentData' property of the media
+ descriptor. */
+ Reference< XInputStream > xInputStream( extractUnencryptedPackage( aMediaDescriptor ), UNO_SET_THROW );
+
+ // stream must be a ZIP package
+ ZipStorage aZipStorage( mxContext, xInputStream );
+ if( aZipStorage.isStorage() )
+ {
+ // create the fast parser, register the XML namespaces, set document handler
+ FastParser aParser;
+ aParser.registerNamespace( NMSP_packageRel );
+ aParser.registerNamespace( NMSP_officeRel );
+ aParser.registerNamespace( NMSP_packageContentTypes );
+
+ OUString aFileName;
+ aMediaDescriptor[utl::MediaDescriptor::PROP_URL] >>= aFileName;
+
+ aParser.setDocumentHandler( new FilterDetectDocHandler( mxContext, aFilterName, aFileName ) );
+
+ /* Parse '_rels/.rels' to get the target path and '[Content_Types].xml'
+ to determine the content type of the part at the target path. */
+ aParser.parseStream( aZipStorage, "_rels/.rels" );
+ aParser.parseStream( aZipStorage, "[Content_Types].xml" );
+ }
+ }
+ catch( const Exception& )
+ {
+ if ( aMediaDescriptor.getUnpackedValueOrDefault( MediaDescriptor::PROP_ABORTED, false ) )
+ /* The user chose to abort detection, e.g. by hitting 'Cancel' in the password input dialog,
+ so we have to return non-empty type name to abort the detection loop. The loading code is
+ supposed to check whether the "Aborted" flag is present in the descriptor, and to not attempt
+ to actually load the file then.
+
+ The returned type name is the one we got as an input, which typically was detected by the flat
+ detection (i.e. by file extension), so normally that's the correct one. Also at this point we
+ already know that the file is OLE encrypted package, so trying with other type detectors doesn't
+ make much sense anyway.
+ */
+ aFilterName = aMediaDescriptor.getUnpackedValueOrDefault( MediaDescriptor::PROP_TYPENAME, OUString() );
+ }
+
+ // write back changed media descriptor members
+ aMediaDescriptor >> rMediaDescSeq;
+ return aFilterName;
+}
+
+} // namespace oox::core
+
+extern "C" SAL_DLLPUBLIC_EXPORT uno::XInterface*
+com_sun_star_comp_oox_FormatDetector_get_implementation(uno::XComponentContext* pCtx,
+ uno::Sequence<uno::Any> const& /*rSeq*/)
+{
+ return cppu::acquire(new oox::core::FilterDetect(pCtx));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/oox/source/core/fragmenthandler.cxx b/oox/source/core/fragmenthandler.cxx
new file mode 100644
index 000000000..22319fda6
--- /dev/null
+++ b/oox/source/core/fragmenthandler.cxx
@@ -0,0 +1,119 @@
+/* -*- 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 <oox/core/fragmenthandler.hxx>
+
+#include <oox/core/xmlfilterbase.hxx>
+
+namespace oox::core {
+
+using namespace ::com::sun::star::io;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::xml::sax;
+
+FragmentBaseData::FragmentBaseData( XmlFilterBase& rFilter, const OUString& rFragmentPath, RelationsRef const & xRelations ) :
+ mrFilter( rFilter ),
+ maFragmentPath( rFragmentPath ),
+ mxRelations( xRelations )
+{
+}
+
+FragmentHandler::FragmentHandler( XmlFilterBase& rFilter, const OUString& rFragmentPath ) :
+ FragmentHandler_BASE( std::make_shared<FragmentBaseData>( rFilter, rFragmentPath, rFilter.importRelations( rFragmentPath ) ) )
+{
+}
+
+FragmentHandler::FragmentHandler( XmlFilterBase& rFilter, const OUString& rFragmentPath, RelationsRef xRelations ) :
+ FragmentHandler_BASE( std::make_shared<FragmentBaseData>( rFilter, rFragmentPath, xRelations ) )
+{
+}
+
+FragmentHandler::~FragmentHandler()
+{
+}
+
+// com.sun.star.xml.sax.XFastDocumentHandler interface ------------------------
+
+void FragmentHandler::startDocument()
+{
+}
+
+void FragmentHandler::endDocument()
+{
+}
+
+void FragmentHandler::processingInstruction( const OUString& /*rTarget*/, const OUString& /*rData*/ )
+{
+}
+
+void FragmentHandler::setDocumentLocator( const Reference< XLocator >& rxLocator )
+{
+ implSetLocator( rxLocator );
+}
+
+// com.sun.star.xml.sax.XFastContextHandler interface -------------------------
+
+void FragmentHandler::startFastElement( sal_Int32, const Reference< XFastAttributeList >& )
+{
+}
+
+void FragmentHandler::startUnknownElement( const OUString&, const OUString&, const Reference< XFastAttributeList >& )
+{
+}
+
+void FragmentHandler::endFastElement( sal_Int32 )
+{
+}
+
+void FragmentHandler::endUnknownElement( const OUString&, const OUString& )
+{
+}
+
+Reference< XFastContextHandler > FragmentHandler::createFastChildContext( sal_Int32, const Reference< XFastAttributeList >& )
+{
+ return nullptr;
+}
+
+Reference< XFastContextHandler > FragmentHandler::createUnknownChildContext( const OUString&, const OUString&, const Reference< XFastAttributeList >& )
+{
+ return nullptr;
+}
+
+void FragmentHandler::characters( const OUString& )
+{
+}
+
+// XML stream handling --------------------------------------------------------
+
+Reference< XInputStream > FragmentHandler::openFragmentStream() const
+{
+ return getFilter().openInputStream( getFragmentPath() );
+}
+
+// binary records -------------------------------------------------------------
+
+const RecordInfo* FragmentHandler::getRecordInfos() const
+{
+ // default: no support for binary records
+ return nullptr;
+}
+
+} // namespace oox::core
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/oox/source/core/fragmenthandler2.cxx b/oox/source/core/fragmenthandler2.cxx
new file mode 100644
index 000000000..e4eeef076
--- /dev/null
+++ b/oox/source/core/fragmenthandler2.cxx
@@ -0,0 +1,155 @@
+/* -*- 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 <com/sun/star/frame/XModel.hpp>
+#include <oox/core/fragmenthandler2.hxx>
+#include <oox/core/xmlfilterbase.hxx>
+#include <oox/helper/attributelist.hxx>
+#include <oox/token/namespaces.hxx>
+
+namespace oox::core {
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::xml::sax;
+
+FragmentHandler2::FragmentHandler2( XmlFilterBase& rFilter, const OUString& rFragmentPath, bool bEnableTrimSpace ) :
+ FragmentHandler( rFilter, rFragmentPath ),
+ ContextHandler2Helper( bEnableTrimSpace, rFilter )
+{
+}
+
+FragmentHandler2::~FragmentHandler2()
+{
+}
+
+// com.sun.star.xml.sax.XFastDocumentHandler interface --------------------
+
+void SAL_CALL FragmentHandler2::startDocument()
+{
+ initializeImport();
+}
+
+void SAL_CALL FragmentHandler2::endDocument()
+{
+ finalizeImport();
+}
+
+// com.sun.star.xml.sax.XFastContextHandler interface -------------------------
+
+Reference< XFastContextHandler > SAL_CALL FragmentHandler2::createFastChildContext(
+ sal_Int32 nElement, const Reference< XFastAttributeList >& rxAttribs )
+{
+ if( getNamespace( nElement ) == NMSP_mce ) // TODO for checking 'Ignorable'
+ {
+ if( prepareMceContext( nElement, AttributeList( rxAttribs ) ) )
+ return getFastContextHandler();
+ return nullptr;
+ }
+ return implCreateChildContext( nElement, rxAttribs );
+}
+
+void SAL_CALL FragmentHandler2::startFastElement(
+ sal_Int32 nElement, const Reference< XFastAttributeList >& rxAttribs )
+{
+ implStartElement( nElement, rxAttribs );
+}
+
+void SAL_CALL FragmentHandler2::characters( const OUString& rChars )
+{
+ implCharacters( rChars );
+}
+
+void SAL_CALL FragmentHandler2::endFastElement( sal_Int32 nElement )
+{
+ /* If MCE */
+ switch( nElement )
+ {
+ case MCE_TOKEN( AlternateContent ):
+ removeMCEState();
+ break;
+ }
+
+ implEndElement( nElement );
+}
+
+// oox.core.ContextHandler interface ------------------------------------------
+
+ContextHandlerRef FragmentHandler2::createRecordContext( sal_Int32 nRecId, SequenceInputStream& rStrm )
+{
+ return implCreateRecordContext( nRecId, rStrm );
+}
+
+void FragmentHandler2::startRecord( sal_Int32 nRecId, SequenceInputStream& rStrm )
+{
+ implStartRecord( nRecId, rStrm );
+}
+
+void FragmentHandler2::endRecord( sal_Int32 nRecId )
+{
+ implEndRecord( nRecId );
+}
+
+// oox.core.ContextHandler2Helper interface -----------------------------------
+
+ContextHandlerRef FragmentHandler2::onCreateContext( sal_Int32, const AttributeList& )
+{
+ return nullptr;
+}
+
+void FragmentHandler2::onStartElement( const AttributeList& )
+{
+}
+
+void FragmentHandler2::onCharacters( const OUString& )
+{
+}
+
+void FragmentHandler2::onEndElement()
+{
+}
+
+ContextHandlerRef FragmentHandler2::onCreateRecordContext( sal_Int32, SequenceInputStream& )
+{
+ return nullptr;
+}
+
+void FragmentHandler2::onStartRecord( SequenceInputStream& )
+{
+}
+
+void FragmentHandler2::onEndRecord()
+{
+}
+
+// oox.core.FragmentHandler2 interface ----------------------------------------
+
+void FragmentHandler2::initializeImport()
+{
+}
+
+void FragmentHandler2::finalizeImport()
+{
+}
+
+} // namespace oox::core
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/oox/source/core/recordparser.cxx b/oox/source/core/recordparser.cxx
new file mode 100644
index 000000000..d6f35faca
--- /dev/null
+++ b/oox/source/core/recordparser.cxx
@@ -0,0 +1,326 @@
+/* -*- 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 <oox/core/recordparser.hxx>
+
+#include <vector>
+#include <com/sun/star/io/IOException.hpp>
+#include <com/sun/star/lang/DisposedException.hpp>
+#include <com/sun/star/xml/sax/SAXException.hpp>
+#include <com/sun/star/xml/sax/XLocator.hpp>
+#include <cppuhelper/implbase.hxx>
+#include <osl/diagnose.h>
+#include <oox/core/fragmenthandler.hxx>
+
+namespace oox::core {
+
+using namespace ::com::sun::star::io;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::xml::sax;
+
+namespace prv {
+
+class Locator : public ::cppu::WeakImplHelper< XLocator >
+{
+public:
+ explicit Locator( RecordParser* pParser ) : mpParser( pParser ) {}
+
+ void dispose();
+ /// @throws css::uno::RuntimeException
+ void checkDispose();
+
+ // com.sun.star.sax.XLocator interface
+
+ virtual sal_Int32 SAL_CALL getColumnNumber() override;
+ virtual sal_Int32 SAL_CALL getLineNumber() override;
+ virtual OUString SAL_CALL getPublicId() override;
+ virtual OUString SAL_CALL getSystemId() override;
+
+private:
+ RecordParser* mpParser;
+};
+
+void Locator::dispose()
+{
+ mpParser = nullptr;
+}
+
+void Locator::checkDispose()
+{
+ if( !mpParser )
+ throw DisposedException();
+}
+
+sal_Int32 SAL_CALL Locator::getColumnNumber()
+{
+ return -1;
+}
+
+sal_Int32 SAL_CALL Locator::getLineNumber()
+{
+ return -1;
+}
+
+OUString SAL_CALL Locator::getPublicId()
+{
+ checkDispose();
+ return OUString();
+}
+
+OUString SAL_CALL Locator::getSystemId()
+{
+ checkDispose();
+ return mpParser->getInputSource().maSystemId;
+}
+
+class ContextStack
+{
+public:
+ explicit ContextStack( FragmentHandlerRef const & xHandler );
+
+ bool empty() const { return maStack.empty(); }
+
+ sal_Int32 getCurrentRecId() const;
+ bool hasCurrentEndRecId() const;
+ ContextHandlerRef getCurrentContext() const;
+
+ void pushContext( const RecordInfo& rRec, const ContextHandlerRef& rxContext );
+ void popContext();
+
+private:
+ typedef ::std::pair< RecordInfo, ContextHandlerRef > ContextInfo;
+ typedef ::std::vector< ContextInfo > ContextInfoVec;
+
+ FragmentHandlerRef mxHandler;
+ ContextInfoVec maStack;
+};
+
+ContextStack::ContextStack( FragmentHandlerRef const & xHandler ) :
+ mxHandler( xHandler )
+{
+}
+
+sal_Int32 ContextStack::getCurrentRecId() const
+{
+ return maStack.empty() ? -1 : maStack.back().first.mnStartRecId;
+}
+
+bool ContextStack::hasCurrentEndRecId() const
+{
+ return !maStack.empty() && (maStack.back().first.mnEndRecId >= 0);
+}
+
+ContextHandlerRef ContextStack::getCurrentContext() const
+{
+ if( !maStack.empty() )
+ return maStack.back().second;
+ return mxHandler;
+}
+
+void ContextStack::pushContext( const RecordInfo& rRecInfo, const ContextHandlerRef& rxContext )
+{
+ OSL_ENSURE( (rRecInfo.mnEndRecId >= 0) || maStack.empty() || hasCurrentEndRecId(),
+ "ContextStack::pushContext - nested incomplete context record identifiers" );
+ maStack.emplace_back( rRecInfo, rxContext );
+}
+
+void ContextStack::popContext()
+{
+ OSL_ENSURE( !maStack.empty(), "ContextStack::popContext - no context on stack" );
+ if( !maStack.empty() )
+ {
+ ContextInfo& rContextInfo = maStack.back();
+ if( rContextInfo.second.is() )
+ rContextInfo.second->endRecord( rContextInfo.first.mnStartRecId );
+ maStack.pop_back();
+ }
+}
+
+} // namespace oox::core::prv
+
+namespace {
+
+/** Reads a byte from the passed stream, returns true on success. */
+bool lclReadByte( sal_uInt8& ornByte, BinaryInputStream& rStrm )
+{
+ return rStrm.readMemory( &ornByte, 1 ) == 1;
+}
+
+/** Reads a compressed signed 32-bit integer from the passed stream. */
+bool lclReadCompressedInt( sal_Int32& ornValue, BinaryInputStream& rStrm )
+{
+ ornValue = 0;
+ sal_uInt8 nByte;
+ if( !lclReadByte( nByte, rStrm ) ) return false;
+ ornValue = nByte & 0x7F;
+ if( (nByte & 0x80) == 0 ) return true;
+ if( !lclReadByte( nByte, rStrm ) ) return false;
+ ornValue |= sal_Int32( nByte & 0x7F ) << 7;
+ if( (nByte & 0x80) == 0 ) return true;
+ if( !lclReadByte( nByte, rStrm ) ) return false;
+ ornValue |= sal_Int32( nByte & 0x7F ) << 14;
+ if( (nByte & 0x80) == 0 ) return true;
+ if( !lclReadByte( nByte, rStrm ) ) return false;
+ ornValue |= sal_Int32( nByte & 0x7F ) << 21;
+ return true;
+}
+
+bool lclReadRecordHeader( sal_Int32& ornRecId, sal_Int32& ornRecSize, BinaryInputStream& rStrm )
+{
+ return
+ lclReadCompressedInt( ornRecId, rStrm ) && (ornRecId >= 0) &&
+ lclReadCompressedInt( ornRecSize, rStrm ) && (ornRecSize >= 0);
+}
+
+bool lclReadNextRecord( sal_Int32& ornRecId, StreamDataSequence& orData, BinaryInputStream& rStrm )
+{
+ sal_Int32 nRecSize = 0;
+ bool bValid = lclReadRecordHeader( ornRecId, nRecSize, rStrm );
+ if( bValid )
+ {
+ orData.realloc( nRecSize );
+ bValid = (nRecSize == 0) || (rStrm.readData( orData, nRecSize ) == nRecSize);
+ }
+ return bValid;
+}
+
+} // namespace
+
+RecordParser::RecordParser()
+{
+ mxLocator.set( new prv::Locator( this ) );
+}
+
+RecordParser::~RecordParser()
+{
+ if( mxLocator.is() )
+ mxLocator->dispose();
+}
+
+void RecordParser::setFragmentHandler( const ::rtl::Reference< FragmentHandler >& rxHandler )
+{
+ mxHandler = rxHandler;
+
+ // build record infos
+ maStartMap.clear();
+ maEndMap.clear();
+ const RecordInfo* pRecs = mxHandler.is() ? mxHandler->getRecordInfos() : nullptr;
+ OSL_ENSURE( pRecs, "RecordInfoProvider::RecordInfoProvider - missing record list" );
+ for( ; pRecs && pRecs->mnStartRecId >= 0; ++pRecs )
+ {
+ maStartMap[ pRecs->mnStartRecId ] = *pRecs;
+ if( pRecs->mnEndRecId >= 0 )
+ maEndMap[ pRecs->mnEndRecId ] = *pRecs;
+ }
+}
+
+void RecordParser::parseStream( const RecordInputSource& rInputSource )
+{
+ maSource = rInputSource;
+
+ if( !maSource.mxInStream || maSource.mxInStream->isEof() )
+ throw IOException();
+ if( !mxHandler.is() )
+ throw SAXException();
+
+ // start the document
+ mxHandler->setDocumentLocator( mxLocator );
+ mxHandler->startDocument();
+
+ // parse the stream
+ mxStack.reset( new prv::ContextStack( mxHandler ) );
+ sal_Int32 nRecId = 0;
+ StreamDataSequence aRecData;
+ while( lclReadNextRecord( nRecId, aRecData, *maSource.mxInStream ) )
+ {
+ // create record stream object from imported record data
+ SequenceInputStream aRecStrm( aRecData );
+ // try to leave a context, there may be other incomplete contexts on the stack
+ if( const RecordInfo* pEndRecInfo = getEndRecordInfo( nRecId ) )
+ {
+ // finalize contexts without record identifier for context end
+ while( !mxStack->empty() && !mxStack->hasCurrentEndRecId() )
+ mxStack->popContext();
+ // finalize the current context and pop context info from stack
+ OSL_ENSURE( mxStack->getCurrentRecId() == pEndRecInfo->mnStartRecId, "RecordParser::parseStream - context records mismatch" );
+ ContextHandlerRef xCurrContext = mxStack->getCurrentContext();
+ if( xCurrContext.is() )
+ {
+ // context end record may contain some data, handle it as simple record
+ aRecStrm.seekToStart();
+ xCurrContext->startRecord( nRecId, aRecStrm );
+ xCurrContext->endRecord( nRecId );
+ }
+ mxStack->popContext();
+ }
+ else
+ {
+ // end context with incomplete record id, if the same id comes again
+ if( (mxStack->getCurrentRecId() == nRecId) && !mxStack->hasCurrentEndRecId() )
+ mxStack->popContext();
+ // try to start a new context
+ ContextHandlerRef xCurrContext = mxStack->getCurrentContext();
+ if( xCurrContext.is() )
+ {
+ aRecStrm.seekToStart();
+ xCurrContext = xCurrContext->createRecordContext( nRecId, aRecStrm );
+ }
+ // track all context identifiers on the stack (do not push simple records)
+ const RecordInfo* pStartRecInfo = getStartRecordInfo( nRecId );
+ if( pStartRecInfo )
+ mxStack->pushContext( *pStartRecInfo, xCurrContext );
+ // import the record
+ if( xCurrContext.is() )
+ {
+ // import the record
+ aRecStrm.seekToStart();
+ xCurrContext->startRecord( nRecId, aRecStrm );
+ // end simple records (context records are finished in ContextStack::popContext)
+ if( !pStartRecInfo )
+ xCurrContext->endRecord( nRecId );
+ }
+ }
+ }
+ // close remaining contexts (missing context end records or stream error)
+ while( !mxStack->empty() )
+ mxStack->popContext();
+ mxStack.reset();
+
+ // finish document
+ mxHandler->endDocument();
+
+ maSource = RecordInputSource();
+}
+
+const RecordInfo* RecordParser::getStartRecordInfo( sal_Int32 nRecId ) const
+{
+ RecordInfoMap::const_iterator aIt = maStartMap.find( nRecId );
+ return (aIt == maStartMap.end()) ? nullptr : &aIt->second;
+}
+
+const RecordInfo* RecordParser::getEndRecordInfo( sal_Int32 nRecId ) const
+{
+ RecordInfoMap::const_iterator aIt = maEndMap.find( nRecId );
+ return (aIt == maEndMap.end()) ? nullptr : &aIt->second;
+}
+
+} // namespace oox::core
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/oox/source/core/relations.cxx b/oox/source/core/relations.cxx
new file mode 100644
index 000000000..f927c542c
--- /dev/null
+++ b/oox/source/core/relations.cxx
@@ -0,0 +1,158 @@
+/* -*- 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 <algorithm>
+#include <string_view>
+
+#include <oox/core/relations.hxx>
+
+namespace oox::core {
+
+namespace {
+
+std::u16string_view lclRemoveFileName( std::u16string_view rPath )
+{
+ size_t idx = rPath.rfind( '/' );
+ if (idx == std::u16string_view::npos)
+ return std::u16string_view();
+ return rPath.substr( 0, idx );
+}
+
+OUString lclAppendFileName( std::u16string_view rPath, const OUString& rFileName )
+{
+ return rPath.empty() ? rFileName :
+ rPath + OUStringChar('/') + rFileName;
+}
+
+OUString createOfficeDocRelationTypeTransitional(std::u16string_view rType)
+{
+ return OUString::Concat("http://schemas.openxmlformats.org/officeDocument/2006/relationships/")
+ + rType;
+}
+
+OUString createOfficeDocRelationTypeStrict(std::u16string_view rType)
+{
+ return OUString::Concat("http://purl.oclc.org/ooxml/officeDocument/relationships/") + rType;
+}
+
+}
+
+Relations::Relations( const OUString& rFragmentPath )
+ : maFragmentPath( rFragmentPath )
+{
+}
+
+const Relation* Relations::getRelationFromRelId( const OUString& rId ) const
+{
+ ::std::map< OUString, Relation >::const_iterator aIt = maMap.find( rId );
+ return (aIt == maMap.end()) ? nullptr : &aIt->second;
+}
+
+const Relation* Relations::getRelationFromFirstType( std::u16string_view rType ) const
+{
+ for (auto const& elem : maMap)
+ if( elem.second.maType.equalsIgnoreAsciiCase( rType ) )
+ return &elem.second;
+ return nullptr;
+}
+
+RelationsRef Relations::getRelationsFromTypeFromOfficeDoc( std::u16string_view rType ) const
+{
+ RelationsRef xRelations = std::make_shared<Relations>( maFragmentPath );
+ for (auto const& elem : maMap)
+ if( elem.second.maType.equalsIgnoreAsciiCase( createOfficeDocRelationTypeTransitional(rType) ) ||
+ elem.second.maType.equalsIgnoreAsciiCase( createOfficeDocRelationTypeStrict(rType) ))
+ xRelations->maMap[ elem.first ] = elem.second;
+ return xRelations;
+}
+
+OUString Relations::getExternalTargetFromRelId( const OUString& rRelId ) const
+{
+ const Relation* pRelation = getRelationFromRelId( rRelId );
+ return (pRelation && pRelation->mbExternal) ? pRelation->maTarget : OUString();
+}
+
+OUString Relations::getInternalTargetFromRelId( const OUString& rRelId ) const
+{
+ const Relation* pRelation = getRelationFromRelId( rRelId );
+ return (pRelation && !pRelation->mbExternal) ? pRelation->maTarget : OUString();
+}
+
+OUString Relations::getFragmentPathFromRelation( const Relation& rRelation ) const
+{
+ // no target, no fragment path
+ if( rRelation.mbExternal || rRelation.maTarget.isEmpty() )
+ return OUString();
+
+ // absolute target: return it without leading slash (#i100978)
+ if( rRelation.maTarget[ 0 ] == '/' )
+ return rRelation.maTarget.copy( 1 );
+
+ // empty fragment path: return target
+ if( maFragmentPath.isEmpty() )
+ return rRelation.maTarget;
+
+ // resolve relative target path according to base path
+ OUString aPath( lclRemoveFileName( maFragmentPath ) );
+ sal_Int32 nStartPos = 0;
+ while( nStartPos < rRelation.maTarget.getLength() )
+ {
+ sal_Int32 nSepPos = rRelation.maTarget.indexOf( '/', nStartPos );
+ if( nSepPos < 0 ) nSepPos = rRelation.maTarget.getLength();
+ // append next directory name from aTarget to aPath, or remove last directory on '../'
+ if( (nStartPos + 2 == nSepPos) && (rRelation.maTarget[ nStartPos ] == '.') && (rRelation.maTarget[ nStartPos + 1 ] == '.') )
+ aPath = lclRemoveFileName( aPath );
+ else
+ aPath = lclAppendFileName( aPath, rRelation.maTarget.copy( nStartPos, nSepPos - nStartPos ) );
+ // move nStartPos to next directory name
+ nStartPos = nSepPos + 1;
+ }
+
+ return aPath;
+}
+
+OUString Relations::getFragmentPathFromRelId( const OUString& rRelId ) const
+{
+ const Relation* pRelation = getRelationFromRelId( rRelId );
+ return pRelation ? getFragmentPathFromRelation( *pRelation ) : OUString();
+}
+
+OUString Relations::getFragmentPathFromFirstType( std::u16string_view rType ) const
+{
+ const Relation* pRelation = getRelationFromFirstType( rType );
+ return pRelation ? getFragmentPathFromRelation( *pRelation ) : OUString();
+}
+
+OUString Relations::getFragmentPathFromFirstTypeFromOfficeDoc( std::u16string_view rType ) const
+{
+ OUString aTransitionalType(createOfficeDocRelationTypeTransitional(rType));
+ const Relation* pRelation = getRelationFromFirstType( aTransitionalType );
+ if(!pRelation)
+ {
+ OUString aStrictType = createOfficeDocRelationTypeStrict(rType);
+ pRelation = getRelationFromFirstType( aStrictType );
+ }
+ return pRelation ? getFragmentPathFromRelation( *pRelation ) : OUString();
+}
+
+} // namespace oox::core
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/oox/source/core/relationshandler.cxx b/oox/source/core/relationshandler.cxx
new file mode 100644
index 000000000..90f3ebcef
--- /dev/null
+++ b/oox/source/core/relationshandler.cxx
@@ -0,0 +1,95 @@
+/* -*- 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 <string_view>
+
+#include <oox/core/relationshandler.hxx>
+
+#include <sal/log.hxx>
+#include <oox/helper/attributelist.hxx>
+#include <oox/token/namespaces.hxx>
+#include <oox/token/tokens.hxx>
+
+namespace oox::core {
+
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::xml::sax;
+
+namespace {
+
+/* Build path to relations file from passed fragment path, e.g.:
+ 'path/path/file.xml' -> 'path/path/_rels/file.xml.rels'
+ 'file.xml' -> '_rels/file.xml.rels'
+ '' -> '_rels/.rels'
+ */
+OUString lclGetRelationsPath( const OUString& rFragmentPath )
+{
+ sal_Int32 nPathLen = ::std::max< sal_Int32 >( rFragmentPath.lastIndexOf( '/' ) + 1, 0 );
+ return OUString::Concat(std::u16string_view(rFragmentPath.getStr(), nPathLen )) + // file path including slash
+ "_rels/" + // additional '_rels/' path
+ std::u16string_view(rFragmentPath.getStr() + nPathLen) + // file name after path
+ ".rels"; // '.rels' suffix
+}
+
+} // namespace
+
+RelationsFragment::RelationsFragment( XmlFilterBase& rFilter, const RelationsRef& xRelations ) :
+ FragmentHandler( rFilter, lclGetRelationsPath( xRelations->getFragmentPath() ), xRelations ),
+ mxRelations( xRelations )
+{
+}
+
+Reference< XFastContextHandler > RelationsFragment::createFastChildContext(
+ sal_Int32 nElement, const Reference< XFastAttributeList >& rxAttribs )
+{
+ Reference< XFastContextHandler > xRet;
+ AttributeList aAttribs( rxAttribs );
+ switch( nElement )
+ {
+ case PR_TOKEN( Relationship ):
+ {
+ Relation aRelation;
+ aRelation.maId = aAttribs.getString( XML_Id, OUString() );
+ aRelation.maType = aAttribs.getString( XML_Type, OUString() );
+ aRelation.maTarget = aAttribs.getString( XML_Target, OUString() );
+ if( !aRelation.maId.isEmpty() && !aRelation.maType.isEmpty() && !aRelation.maTarget.isEmpty() )
+ {
+ sal_Int32 nTargetMode = aAttribs.getToken( XML_TargetMode, XML_Internal );
+ SAL_WARN_IF( (nTargetMode != XML_Internal) && (nTargetMode != XML_External), "oox",
+ "RelationsFragment::createFastChildContext - unexpected target mode, assuming external" );
+ aRelation.mbExternal = nTargetMode != XML_Internal;
+
+ SAL_WARN_IF( mxRelations->count( aRelation.maId ) != 0, "oox",
+ "RelationsFragment::createFastChildContext - relation identifier exists already" );
+ mxRelations->emplace( aRelation.maId, aRelation );
+ }
+ }
+ break;
+ case PR_TOKEN( Relationships ):
+ xRet = getFastContextHandler();
+ break;
+ }
+ return xRet;
+}
+
+} // namespace oox::core
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/oox/source/core/xmlfilterbase.cxx b/oox/source/core/xmlfilterbase.cxx
new file mode 100644
index 000000000..e8e1a1389
--- /dev/null
+++ b/oox/source/core/xmlfilterbase.cxx
@@ -0,0 +1,1235 @@
+/* -*- 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 <oox/core/xmlfilterbase.hxx>
+
+#include <cstdio>
+#include <string_view>
+
+#include <com/sun/star/beans/XPropertyAccess.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/beans/Pair.hpp>
+#include <com/sun/star/embed/XRelationshipAccess.hpp>
+#include <com/sun/star/frame/XModel.hpp>
+#include <com/sun/star/xml/sax/XFastSAXSerializable.hpp>
+#include <com/sun/star/xml/sax/XSAXSerializable.hpp>
+#include <com/sun/star/xml/sax/Writer.hpp>
+#include <o3tl/any.hxx>
+#include <unotools/mediadescriptor.hxx>
+#include <unotools/docinfohelper.hxx>
+#include <sax/fshelper.hxx>
+#include <rtl/strbuf.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <osl/diagnose.h>
+#include <sal/log.hxx>
+#include <i18nlangtag/languagetag.hxx>
+#include <oox/core/fastparser.hxx>
+#include <oox/core/fragmenthandler.hxx>
+#include <oox/core/recordparser.hxx>
+#include <oox/core/relationshandler.hxx>
+#include <oox/helper/propertyset.hxx>
+#include <oox/helper/zipstorage.hxx>
+#include <oox/ole/olestorage.hxx>
+#include <oox/token/namespaces.hxx>
+#include <oox/token/relationship.hxx>
+#include <oox/token/properties.hxx>
+#include <oox/token/tokens.hxx>
+#include <com/sun/star/document/XDocumentPropertiesSupplier.hpp>
+#include <com/sun/star/document/XOOXMLDocumentPropertiesImporter.hpp>
+#include <com/sun/star/xml/dom/DocumentBuilder.hpp>
+#include <comphelper/processfactory.hxx>
+#include <oox/core/filterdetect.hxx>
+#include <comphelper/stl_types.hxx>
+#include <comphelper/storagehelper.hxx>
+#include <comphelper/sequence.hxx>
+#include <comphelper/ofopxmlhelper.hxx>
+
+#include <oox/crypto/DocumentEncryption.hxx>
+#include <tools/urlobj.hxx>
+#include <com/sun/star/util/Date.hpp>
+#include <com/sun/star/util/Duration.hpp>
+#include <sax/tools/converter.hxx>
+#include <oox/token/namespacemap.hxx>
+#include <editeng/unoprnms.hxx>
+#include <o3tl/sorted_vector.hxx>
+
+using ::com::sun::star::xml::dom::DocumentBuilder;
+using ::com::sun::star::xml::dom::XDocument;
+using ::com::sun::star::xml::dom::XDocumentBuilder;
+
+namespace oox::core {
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::container;
+using namespace ::com::sun::star::document;
+using namespace ::com::sun::star::embed;
+using namespace ::com::sun::star::io;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::xml::sax;
+
+using utl::MediaDescriptor;
+using ::sax_fastparser::FSHelperPtr;
+using ::sax_fastparser::FastSerializerHelper;
+
+namespace {
+
+const Sequence< beans::Pair< OUString, sal_Int32 > >& NamespaceIds()
+{
+ static const Sequence< beans::Pair< OUString, sal_Int32 > > SINGLETON
+ {
+ {"http://www.w3.org/XML/1998/namespace", NMSP_xml},
+ {"http://schemas.openxmlformats.org/package/2006/relationships",
+ NMSP_packageRel},
+ {"http://schemas.openxmlformats.org/officeDocument/2006/relationships",
+ NMSP_officeRel},
+ {"http://purl.oclc.org/ooxml/officeDocument/relationships",
+ NMSP_officeRel},
+ {"http://schemas.openxmlformats.org/drawingml/2006/main", NMSP_dml},
+ {"http://purl.oclc.org/ooxml/drawingml/main", NMSP_dml},
+ {"http://schemas.openxmlformats.org/drawingml/2006/diagram",
+ NMSP_dmlDiagram},
+ {"http://purl.oclc.org/ooxml/drawingml/diagram", NMSP_dmlDiagram},
+ {"http://schemas.openxmlformats.org/drawingml/2006/chart",
+ NMSP_dmlChart},
+ {"http://schemas.openxmlformats.org/drawingml/2006/chartDrawing",
+ NMSP_dmlChartDr},
+ {"urn:schemas-microsoft-com:vml", NMSP_vml},
+ {"urn:schemas-microsoft-com:office:office", NMSP_vmlOffice},
+ {"urn:schemas-microsoft-com:office:word", NMSP_vmlWord},
+ {"urn:schemas-microsoft-com:office:excel", NMSP_vmlExcel},
+ {"urn:schemas-microsoft-com:office:powerpoint", NMSP_vmlPowerpoint},
+ {"http://schemas.microsoft.com/office/2006/activeX", NMSP_ax},
+ {"http://schemas.openxmlformats.org/spreadsheetml/2006/main",
+ NMSP_xls},
+ {"http://schemas.openxmlformats.org/drawingml/2006/spreadsheetDrawing",
+ NMSP_xm},
+ {"http://schemas.microsoft.com/office/excel/2006/main",
+ NMSP_dmlSpreadDr},
+ {"http://schemas.openxmlformats.org/presentationml/2006/main",
+ NMSP_ppt},
+ {"http://schemas.openxmlformats.org/markup-compatibility/2006",
+ NMSP_mce},
+ {"http://schemas.openxmlformats.org/spreadsheetml/2006/main/v2",
+ NMSP_mceTest},
+ {"http://schemas.openxmlformats.org/officeDocument/2006/math",
+ NMSP_officeMath},
+ {"http://schemas.microsoft.com/office/drawing/2008/diagram",
+ NMSP_dsp},
+ {"http://schemas.microsoft.com/office/spreadsheetml/2009/9/main",
+ NMSP_xls14Lst},
+ {"http://schemas.libreoffice.org/", NMSP_loext},
+ {"http://schemas.microsoft.com/office/drawing/2010/main",
+ NMSP_a14},
+ {"http://schemas.microsoft.com/office/powerpoint/2010/main",
+ NMSP_p14},
+ {"http://schemas.microsoft.com/office/powerpoint/2012/main",
+ NMSP_p15},
+ {"http://schemas.microsoft.com/office/spreadsheetml/2011/1/ac",
+ NMSP_x12ac},
+ {"http://schemas.microsoft.com/office/drawing/2012/chart",
+ NMSP_c15},
+ {"http://schemas.microsoft.com/office/spreadsheetml/2015/revision2",
+ NMSP_xr2},
+ };
+ return SINGLETON;
+};
+
+void registerNamespaces( FastParser& rParser )
+{
+ const Sequence< beans::Pair<OUString, sal_Int32> >& ids = NamespaceIds();
+
+ // Filter out duplicates: a namespace can have multiple URLs, think of
+ // strict vs transitional.
+ o3tl::sorted_vector<sal_Int32> aSet;
+ aSet.reserve(ids.getLength());
+ for (const auto& rId : ids)
+ aSet.insert(rId.Second);
+
+ for (auto const& elem : aSet)
+ rParser.registerNamespace(elem);
+}
+
+} // namespace
+
+struct XmlFilterBaseImpl
+{
+ typedef RefMap< OUString, Relations > RelationsMap;
+
+ FastParser maFastParser;
+ RelationsMap maRelationsMap;
+ TextFieldStack maTextFieldStack;
+ const NamespaceMap& mrNamespaceMap;
+ NamedShapePairs* mpDiagramFontHeights = nullptr;
+
+ /// @throws RuntimeException
+ explicit XmlFilterBaseImpl();
+};
+
+constexpr OUStringLiteral gaBinSuffix( u".bin" );
+
+XmlFilterBaseImpl::XmlFilterBaseImpl() :
+ mrNamespaceMap(StaticNamespaceMap())
+{
+ // register XML namespaces
+ registerNamespaces(maFastParser);
+}
+
+XmlFilterBase::XmlFilterBase( const Reference< XComponentContext >& rxContext ) :
+ FilterBase( rxContext ),
+ mxImpl( new XmlFilterBaseImpl ),
+ mnRelId( 1 ),
+ mnMaxDocId( 0 ),
+ mbMSO2007(false),
+ mbMSO(false),
+ mbMissingExtDrawing(false)
+{
+}
+
+XmlFilterBase::~XmlFilterBase()
+{
+ // #i118640# Reset the DocumentHandler at the FastSaxParser manually; this is
+ // needed since the mechanism is that instances of FragmentHandler execute
+ // their stuff (creating objects, setting attributes, ...) on being destroyed.
+ // They get destroyed by setting a new DocumentHandler. This also happens in
+ // the following implicit destruction chain of ~XmlFilterBaseImpl, but in that
+ // case it's member RelationsMap maRelationsMap will be destroyed, but maybe
+ // still be used by ~FragmentHandler -> crash.
+ mxImpl->maFastParser.clearDocumentHandler();
+}
+
+std::shared_ptr<::oox::drawingml::Theme> XmlFilterBase::getCurrentThemePtr() const
+{
+ // default returns empty ptr
+ return std::shared_ptr<::oox::drawingml::Theme>();
+}
+
+void XmlFilterBase::checkDocumentProperties(const Reference<XDocumentProperties>& xDocProps)
+{
+ mbMSO2007 = mbMSO = false;
+ if (!xDocProps->getGenerator().startsWithIgnoreAsciiCase("Microsoft"))
+ return;
+ mbMSO = true;
+
+ uno::Reference<beans::XPropertyAccess> xUserDefProps(xDocProps->getUserDefinedProperties(), uno::UNO_QUERY);
+ if (!xUserDefProps.is())
+ return;
+
+ comphelper::SequenceAsHashMap aUserDefinedProperties(xUserDefProps->getPropertyValues());
+ comphelper::SequenceAsHashMap::iterator it = aUserDefinedProperties.find("AppVersion");
+ if (it == aUserDefinedProperties.end())
+ return;
+
+ OUString aValue;
+ if (!(it->second >>= aValue))
+ return;
+
+ if (!aValue.startsWithIgnoreAsciiCase("12."))
+ return;
+
+ SAL_INFO("oox", "a MSO 2007 document");
+ mbMSO2007 = true;
+}
+
+void XmlFilterBase::putPropertiesToDocumentGrabBag(const css::uno::Reference<css::lang::XComponent>& xDstDoc,
+ const comphelper::SequenceAsHashMap& rProperties)
+{
+ try
+ {
+ uno::Reference<beans::XPropertySet> xDocProps(xDstDoc, uno::UNO_QUERY);
+ if (xDocProps.is())
+ {
+ uno::Reference<beans::XPropertySetInfo> xPropsInfo = xDocProps->getPropertySetInfo();
+
+ static constexpr OUStringLiteral aGrabBagPropName = u"InteropGrabBag";
+ if (xPropsInfo.is() && xPropsInfo->hasPropertyByName(aGrabBagPropName))
+ {
+ // get existing grab bag
+ comphelper::SequenceAsHashMap aGrabBag(xDocProps->getPropertyValue(aGrabBagPropName));
+
+ // put the new items
+ aGrabBag.update(rProperties);
+
+ // put it back to the document
+ xDocProps->setPropertyValue(aGrabBagPropName, uno::Any(aGrabBag.getAsConstPropertyValueList()));
+ }
+ }
+ }
+ catch (const uno::Exception&)
+ {
+ SAL_WARN("oox","Failed to save documents grab bag");
+ }
+}
+
+void XmlFilterBase::importDocumentProperties()
+{
+ MediaDescriptor aMediaDesc( getMediaDescriptor() );
+ Reference< XInputStream > xInputStream;
+ Reference< XComponentContext > xContext = getComponentContext();
+ rtl::Reference< ::oox::core::FilterDetect > xDetector( new ::oox::core::FilterDetect( xContext ) );
+ xInputStream = xDetector->extractUnencryptedPackage( aMediaDesc );
+ Reference< XComponent > xModel = getModel();
+ Reference< XStorage > xDocumentStorage (
+ ::comphelper::OStorageHelper::GetStorageOfFormatFromInputStream( OFOPXML_STORAGE_FORMAT_STRING, xInputStream ) );
+ Reference< XInterface > xTemp = xContext->getServiceManager()->createInstanceWithContext(
+ "com.sun.star.document.OOXMLDocumentPropertiesImporter",
+ xContext);
+ Reference< XOOXMLDocumentPropertiesImporter > xImporter( xTemp, UNO_QUERY );
+ Reference< XDocumentPropertiesSupplier > xPropSupplier( xModel, UNO_QUERY);
+ Reference< XDocumentProperties > xDocProps = xPropSupplier->getDocumentProperties();
+ xImporter->importProperties( xDocumentStorage, xDocProps );
+ checkDocumentProperties(xDocProps);
+
+ importCustomFragments(xDocumentStorage);
+}
+
+FastParser* XmlFilterBase::createParser()
+{
+ FastParser* pParser = new FastParser;
+ registerNamespaces(*pParser);
+ return pParser;
+}
+
+namespace {
+
+OUString getTransitionalRelationshipOfficeDocType(std::u16string_view rPart)
+{
+ return OUString::Concat("http://schemas.openxmlformats.org/officeDocument/2006/relationships/")
+ + rPart;
+}
+
+OUString getStrictRelationshipOfficeDocType(std::u16string_view rPart)
+{
+ return OUString::Concat("http://purl.oclc.org/ooxml/officeDocument/relationships/") + rPart;
+}
+
+}
+
+OUString XmlFilterBase::getFragmentPathFromFirstTypeFromOfficeDoc( std::u16string_view rPart )
+{
+ // importRelations() caches the relations map for subsequence calls
+ const OUString aTransitionalRelationshipType = getTransitionalRelationshipOfficeDocType(rPart);
+ OUString aFragment = importRelations( OUString() )->getFragmentPathFromFirstType( aTransitionalRelationshipType );
+ if(aFragment.isEmpty())
+ {
+ const OUString aStrictRelationshipType = getStrictRelationshipOfficeDocType(rPart);
+ aFragment = importRelations( OUString() )->getFragmentPathFromFirstType( aStrictRelationshipType );
+ }
+
+ return aFragment;
+}
+
+bool XmlFilterBase::importFragment( const rtl::Reference<FragmentHandler>& rxHandler )
+{
+ FastParser aParser;
+ registerNamespaces(aParser);
+ return importFragment(rxHandler, aParser);
+}
+
+bool XmlFilterBase::importFragment( const rtl::Reference<FragmentHandler>& rxHandler, FastParser& rParser )
+{
+ OSL_ENSURE( rxHandler.is(), "XmlFilterBase::importFragment - missing fragment handler" );
+ if( !rxHandler.is() )
+ return false;
+
+ // fragment handler must contain path to fragment stream
+ OUString aFragmentPath = rxHandler->getFragmentPath();
+ OSL_ENSURE( !aFragmentPath.isEmpty(), "XmlFilterBase::importFragment - missing fragment path" );
+ if( aFragmentPath.isEmpty() )
+ return false;
+
+ // try to import binary streams (fragment extension must be '.bin')
+ if (aFragmentPath.endsWith(gaBinSuffix))
+ {
+ try
+ {
+ // try to open the fragment stream (this may fail - do not assert)
+ Reference< XInputStream > xInStrm( openInputStream( aFragmentPath ), UNO_SET_THROW );
+
+ // create the record parser
+ RecordParser aParser;
+ aParser.setFragmentHandler( rxHandler );
+
+ // create the input source and parse the stream
+ RecordInputSource aSource;
+ aSource.mxInStream = std::make_shared<BinaryXInputStream>( xInStrm, true );
+ aSource.maSystemId = aFragmentPath;
+ aParser.parseStream( aSource );
+ return true;
+ }
+ catch( Exception& )
+ {
+ }
+ return false;
+ }
+
+ // get the XFastDocumentHandler interface from the fragment handler
+ if( !rxHandler.is() )
+ return false;
+
+ // try to import XML stream
+ try
+ {
+ /* Try to open the fragment stream (may fail, do not throw/assert).
+ Using the virtual function openFragmentStream() allows a document
+ handler to create specialized input streams, e.g. VML streams that
+ have to preprocess the raw input data. */
+ Reference< XInputStream > xInStrm = rxHandler->openFragmentStream();
+ /* tdf#100084 Check again the aFragmentPath route with lowercase file name
+ TODO: complete handling of case-insensitive file paths */
+ if ( !xInStrm.is() )
+ {
+ sal_Int32 nPathLen = aFragmentPath.lastIndexOf('/') + 1;
+ OUString fileName = aFragmentPath.copy(nPathLen);
+ OUString sLowerCaseFileName = fileName.toAsciiLowerCase();
+ if ( fileName != sLowerCaseFileName )
+ {
+ aFragmentPath = aFragmentPath.subView(0, nPathLen) + sLowerCaseFileName;
+ xInStrm = openInputStream(aFragmentPath);
+ }
+ }
+
+ // own try/catch block for showing parser failure assertion with fragment path
+ if( xInStrm.is() ) try
+ {
+ rParser.setDocumentHandler(rxHandler);
+ rParser.parseStream(xInStrm, aFragmentPath);
+ return true;
+ }
+ catch( Exception& )
+ {
+ OSL_FAIL( OStringBuffer( "XmlFilterBase::importFragment - XML parser failed in fragment '" +
+ OUStringToOString( aFragmentPath, RTL_TEXTENCODING_ASCII_US ) + "'" ).getStr() );
+ }
+ }
+ catch( Exception& )
+ {
+ }
+ return false;
+}
+
+Reference<XDocument> XmlFilterBase::importFragment( const OUString& aFragmentPath )
+{
+ Reference<XDocument> xRet;
+
+ // path to fragment stream valid?
+ OSL_ENSURE( !aFragmentPath.isEmpty(), "XmlFilterBase::importFragment - empty fragment path" );
+ if( aFragmentPath.isEmpty() )
+ return xRet;
+
+ // try to open the fragment stream (this may fail - do not assert)
+ Reference< XInputStream > xInStrm = openInputStream( aFragmentPath );
+ if( !xInStrm.is() )
+ return xRet;
+
+ // binary streams (fragment extension is '.bin') currently not supported
+ if (aFragmentPath.endsWith(gaBinSuffix))
+ return xRet;
+
+ // try to import XML stream
+ try
+ {
+ // create the dom parser
+ Reference<XDocumentBuilder> xDomBuilder( DocumentBuilder::create( getComponentContext() ) );
+
+ // create DOM from fragment
+ xRet = xDomBuilder->parse(xInStrm);
+ }
+ catch( Exception& )
+ {
+ }
+
+ return xRet;
+}
+
+bool XmlFilterBase::importFragment( const ::rtl::Reference< FragmentHandler >& rxHandler,
+ const Reference< XFastSAXSerializable >& rxSerializer )
+{
+ if( !rxHandler.is() )
+ return false;
+
+ // try to import XML stream
+ try
+ {
+ rxSerializer->fastSerialize( rxHandler,
+ mxImpl->maFastParser.getTokenHandler(),
+ Sequence< StringPair >(),
+ NamespaceIds() );
+ return true;
+ }
+ catch( Exception& )
+ {}
+
+ return false;
+}
+
+RelationsRef XmlFilterBase::importRelations( const OUString& rFragmentPath )
+{
+ // try to find cached relations
+ RelationsRef& rxRelations = mxImpl->maRelationsMap[ rFragmentPath ];
+ if( !rxRelations )
+ {
+ // import and cache relations
+ rxRelations = std::make_shared<Relations>( rFragmentPath );
+ importFragment( new RelationsFragment( *this, rxRelations ) );
+ }
+ return rxRelations;
+}
+
+Reference< XOutputStream > XmlFilterBase::openFragmentStream( const OUString& rStreamName, const OUString& rMediaType )
+{
+ Reference< XOutputStream > xOutputStream = openOutputStream( rStreamName );
+ PropertySet aPropSet( xOutputStream );
+ aPropSet.setProperty( PROP_MediaType, rMediaType );
+ return xOutputStream;
+}
+
+FSHelperPtr XmlFilterBase::openFragmentStreamWithSerializer( const OUString& rStreamName, const OUString& rMediaType )
+{
+ const bool bWriteHeader = rMediaType.indexOf( "vml" ) < 0 || rMediaType.indexOf( "+xml" ) >= 0;
+ return std::make_shared<FastSerializerHelper>( openFragmentStream( rStreamName, rMediaType ), bWriteHeader );
+}
+
+TextFieldStack& XmlFilterBase::getTextFieldStack() const
+{
+ return mxImpl->maTextFieldStack;
+}
+
+namespace {
+
+OUString lclAddRelation( const Reference< XRelationshipAccess >& rRelations, sal_Int32 nId, const OUString& rType, std::u16string_view rTarget, bool bExternal )
+{
+ OUString sId = "rId" + OUString::number( nId );
+
+ Sequence< StringPair > aEntry( bExternal ? 3 : 2 );
+ auto pEntry = aEntry.getArray();
+ pEntry[0].First = "Type";
+ pEntry[0].Second = rType;
+ pEntry[1].First = "Target";
+ pEntry[1].Second = INetURLObject::decode(rTarget, INetURLObject::DecodeMechanism::ToIUri, RTL_TEXTENCODING_UTF8);
+ if( bExternal )
+ {
+ pEntry[2].First = "TargetMode";
+ pEntry[2].Second = "External";
+ }
+ rRelations->insertRelationshipByID( sId, aEntry, true );
+
+ return sId;
+}
+
+} // namespace
+
+OUString XmlFilterBase::addRelation( const OUString& rType, std::u16string_view rTarget )
+{
+ Reference< XRelationshipAccess > xRelations( getStorage()->getXStorage(), UNO_QUERY );
+ if( xRelations.is() )
+ return lclAddRelation( xRelations, mnRelId ++, rType, rTarget, false/*bExternal*/ );
+
+ return OUString();
+}
+
+OUString XmlFilterBase::addRelation( const Reference< XOutputStream >& rOutputStream, const OUString& rType, std::u16string_view rTarget, bool bExternal )
+{
+ sal_Int32 nId = 0;
+
+ PropertySet aPropSet( rOutputStream );
+ if( aPropSet.is() )
+ aPropSet.getProperty( nId, PROP_RelId );
+ else
+ nId = mnRelId++;
+
+ Reference< XRelationshipAccess > xRelations( rOutputStream, UNO_QUERY );
+ if( xRelations.is() )
+ return lclAddRelation( xRelations, nId, rType, rTarget, bExternal );
+
+ return OUString();
+}
+
+static void
+writeElement( const FSHelperPtr& pDoc, sal_Int32 nXmlElement, std::u16string_view sValue )
+{
+ pDoc->startElement(nXmlElement);
+ pDoc->writeEscaped( sValue );
+ pDoc->endElement( nXmlElement );
+}
+
+static void
+writeElement( const FSHelperPtr& pDoc, sal_Int32 nXmlElement, const sal_Int32 nValue )
+{
+ pDoc->startElement(nXmlElement);
+ pDoc->write( nValue );
+ pDoc->endElement( nXmlElement );
+}
+
+static void
+writeElement( const FSHelperPtr& pDoc, sal_Int32 nXmlElement, const util::DateTime& rTime )
+{
+ if( rTime.Year == 0 )
+ return;
+
+ if ( ( nXmlElement >> 16 ) != XML_dcterms )
+ pDoc->startElement(nXmlElement);
+ else
+ pDoc->startElement(nXmlElement, FSNS(XML_xsi, XML_type), "dcterms:W3CDTF");
+
+ char pStr[200];
+ snprintf( pStr, sizeof( pStr ), "%d-%02d-%02dT%02d:%02d:%02dZ",
+ rTime.Year, rTime.Month, rTime.Day,
+ rTime.Hours, rTime.Minutes, rTime.Seconds );
+
+ pDoc->write( pStr );
+
+ pDoc->endElement( nXmlElement );
+}
+
+static void
+writeElement( const FSHelperPtr& pDoc, sal_Int32 nXmlElement, const Sequence< OUString >& aItems )
+{
+ if( !aItems.hasElements() )
+ return;
+
+ OUStringBuffer sRep;
+ // tdf#143175 - join elements including a delimiter using a standard iterator
+ ::comphelper::intersperse(aItems.begin(), aItems.end(),
+ ::comphelper::OUStringBufferAppender(sRep), OUString(" "));
+
+ writeElement( pDoc, nXmlElement, sRep.makeStringAndClear() );
+}
+
+static void
+writeElement( const FSHelperPtr& pDoc, sal_Int32 nXmlElement, const LanguageTag& rLanguageTag )
+{
+ // dc:language, Dublin Core recommends "such as RFC 4646", which is BCP 47
+ // and obsoleted by RFC 5646, see
+ // http://dublincore.org/documents/dcmi-terms/#terms-language
+ // http://dublincore.org/documents/dcmi-terms/#elements-language
+ writeElement( pDoc, nXmlElement, rLanguageTag.getBcp47MS() );
+}
+
+static void
+writeCoreProperties( XmlFilterBase& rSelf, const Reference< XDocumentProperties >& xProperties )
+{
+ OUString sValue;
+ if( rSelf.getVersion() == oox::core::ISOIEC_29500_2008 )
+ {
+ // The lowercase "officedocument" is intentional and according to the spec
+ // (although most other places are written "officeDocument")
+ sValue = "http://schemas.openxmlformats.org/officedocument/2006/relationships/metadata/core-properties";
+ }
+ else
+ sValue = "http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties";
+
+ rSelf.addRelation( sValue, u"docProps/core.xml" );
+ FSHelperPtr pCoreProps = rSelf.openFragmentStreamWithSerializer(
+ "docProps/core.xml",
+ "application/vnd.openxmlformats-package.core-properties+xml" );
+ pCoreProps->startElementNS( XML_cp, XML_coreProperties,
+ FSNS(XML_xmlns, XML_cp), rSelf.getNamespaceURL(OOX_NS(packageMetaCorePr)),
+ FSNS(XML_xmlns, XML_dc), rSelf.getNamespaceURL(OOX_NS(dc)),
+ FSNS(XML_xmlns, XML_dcterms), rSelf.getNamespaceURL(OOX_NS(dcTerms)),
+ FSNS(XML_xmlns, XML_dcmitype), rSelf.getNamespaceURL(OOX_NS(dcmiType)),
+ FSNS(XML_xmlns, XML_xsi), rSelf.getNamespaceURL(OOX_NS(xsi)));
+
+ uno::Reference<beans::XPropertyAccess> xUserDefinedProperties(xProperties->getUserDefinedProperties(), uno::UNO_QUERY);
+ comphelper::SequenceAsHashMap aUserDefinedProperties(xUserDefinedProperties->getPropertyValues());
+ comphelper::SequenceAsHashMap::iterator it;
+
+ it = aUserDefinedProperties.find("OOXMLCorePropertyCategory");
+ if (it != aUserDefinedProperties.end())
+ {
+ OUString aValue;
+ if (it->second >>= aValue)
+ writeElement( pCoreProps, FSNS( XML_cp, XML_category ), aValue );
+ }
+
+ it = aUserDefinedProperties.find("OOXMLCorePropertyContentStatus");
+ if (it != aUserDefinedProperties.end())
+ {
+ OUString aValue;
+ if (it->second >>= aValue)
+ writeElement( pCoreProps, FSNS( XML_cp, XML_contentStatus ), aValue );
+ }
+
+ it = aUserDefinedProperties.find("OOXMLCorePropertyContentType");
+ if (it != aUserDefinedProperties.end())
+ {
+ OUString aValue;
+ if (it->second >>= aValue)
+ writeElement( pCoreProps, FSNS( XML_cp, XML_contentType ), aValue );
+ }
+ writeElement( pCoreProps, FSNS( XML_dcterms, XML_created ), xProperties->getCreationDate() );
+ writeElement( pCoreProps, FSNS( XML_dc, XML_creator ), xProperties->getAuthor() );
+ writeElement( pCoreProps, FSNS( XML_dc, XML_description ), xProperties->getDescription() );
+
+ it = aUserDefinedProperties.find("OOXMLCorePropertyIdentifier");
+ if (it != aUserDefinedProperties.end())
+ {
+ OUString aValue;
+ if (it->second >>= aValue)
+ writeElement( pCoreProps, FSNS( XML_dc, XML_identifier ), aValue );
+ }
+ writeElement( pCoreProps, FSNS( XML_cp, XML_keywords ), xProperties->getKeywords() );
+ writeElement( pCoreProps, FSNS( XML_dc, XML_language ), LanguageTag( xProperties->getLanguage()) );
+ writeElement( pCoreProps, FSNS( XML_cp, XML_lastModifiedBy ), xProperties->getModifiedBy() );
+ writeElement( pCoreProps, FSNS( XML_cp, XML_lastPrinted ), xProperties->getPrintDate() );
+ writeElement( pCoreProps, FSNS( XML_dcterms, XML_modified ), xProperties->getModificationDate() );
+ writeElement( pCoreProps, FSNS( XML_cp, XML_revision ), xProperties->getEditingCycles() );
+ writeElement( pCoreProps, FSNS( XML_dc, XML_subject ), xProperties->getSubject() );
+ writeElement( pCoreProps, FSNS( XML_dc, XML_title ), xProperties->getTitle() );
+
+ it = aUserDefinedProperties.find("OOXMLCorePropertyVersion");
+ if (it != aUserDefinedProperties.end())
+ {
+ OUString aValue;
+ if (it->second >>= aValue)
+ writeElement( pCoreProps, FSNS( XML_cp, XML_version ), aValue );
+ }
+
+ pCoreProps->endElementNS( XML_cp, XML_coreProperties );
+}
+
+static void
+writeAppProperties( XmlFilterBase& rSelf, const Reference< XDocumentProperties >& xProperties )
+{
+ rSelf.addRelation(
+ "http://schemas.openxmlformats.org/officeDocument/2006/relationships/extended-properties",
+ u"docProps/app.xml" );
+ FSHelperPtr pAppProps = rSelf.openFragmentStreamWithSerializer(
+ "docProps/app.xml",
+ "application/vnd.openxmlformats-officedocument.extended-properties+xml" );
+ pAppProps->startElement( XML_Properties,
+ XML_xmlns, rSelf.getNamespaceURL(OOX_NS(officeExtPr)),
+ FSNS(XML_xmlns, XML_vt), rSelf.getNamespaceURL(OOX_NS(officeDocPropsVT)));
+
+ uno::Reference<beans::XPropertyAccess> xUserDefinedProperties(xProperties->getUserDefinedProperties(), uno::UNO_QUERY);
+ comphelper::SequenceAsHashMap aUserDefinedProperties(xUserDefinedProperties->getPropertyValues());
+ comphelper::SequenceAsHashMap::iterator it;
+
+ writeElement( pAppProps, XML_Template, xProperties->getTemplateName() );
+
+ it = aUserDefinedProperties.find("Manager");
+ if (it != aUserDefinedProperties.end())
+ {
+ OUString aValue;
+ if (it->second >>= aValue)
+ writeElement( pAppProps, XML_Manager, aValue );
+ }
+
+#ifdef OOXTODO
+ writeElement( pAppProps, XML_PresentationFormat, "presentation format" );
+ writeElement( pAppProps, XML_Lines, "lines" );
+ writeElement( pAppProps, XML_Slides, "slides" );
+ writeElement( pAppProps, XML_Notes, "notes" );
+#endif /* def OOXTODO */
+ // EditingDuration is in seconds, TotalTime is in minutes.
+ writeElement( pAppProps, XML_TotalTime, xProperties->getEditingDuration() / 60 );
+#ifdef OOXTODO
+ writeElement( pAppProps, XML_HiddenSlides, "hidden slides" );
+ writeElement( pAppProps, XML_MMClips, "mm clips" );
+ writeElement( pAppProps, XML_ScaleCrop, "scale crop" );
+ writeElement( pAppProps, XML_HeadingPairs, "heading pairs" );
+ writeElement( pAppProps, XML_TitlesOfParts, "titles of parts" );
+ writeElement( pAppProps, XML_LinksUpToDate, "links up-to-date" );
+ writeElement( pAppProps, XML_SharedDoc, "shared doc" );
+ writeElement( pAppProps, XML_HLinks, "hlinks" );
+ writeElement( pAppProps, XML_HyperlinksChanged, "hyperlinks changed" );
+ writeElement( pAppProps, XML_DigSig, "digital signature" );
+#endif /* def OOXTODO */
+ writeElement( pAppProps, XML_Application, utl::DocInfoHelper::GetGeneratorString() );
+
+ it = aUserDefinedProperties.find("HyperlinkBase");
+ if (it != aUserDefinedProperties.end())
+ {
+ OUString aValue;
+ if (it->second >>= aValue)
+ writeElement( pAppProps, XML_HyperlinkBase, aValue );
+ }
+ // AppVersion specifies the version of the application which produced document
+ // It is strictly connected with MS Office versions:
+ // * 12: [Office 2007] [LO < 7.0]
+ // * 14: [Office 2010]
+ // * 15: [Office 2013/2016/2019] [LO >= 7.0]
+ // The LibreOffice is application on 2013/2016/2019 level
+ writeElement( pAppProps, XML_AppVersion, u"15.0000" );
+
+ // OOXTODO Calculate DocSecurity value based on security (password, read-only etc.)
+ it = aUserDefinedProperties.find("DocSecurity");
+ if (it != aUserDefinedProperties.end())
+ {
+ sal_Int32 nValue;
+ if (it->second >>= nValue)
+ writeElement( pAppProps, XML_DocSecurity, nValue );
+ }
+
+ comphelper::SequenceAsHashMap aStats = xProperties->getDocumentStatistics();
+ sal_Int32 nValue = 0;
+
+ it = aStats.find("PageCount");
+ if (it != aStats.end())
+ {
+ if (it->second >>= nValue)
+ writeElement(pAppProps, XML_Pages, nValue);
+ }
+
+ it = aStats.find("WordCount");
+ if (it != aStats.end())
+ {
+ if (it->second >>= nValue)
+ writeElement(pAppProps, XML_Words, nValue);
+ }
+
+ it = aStats.find("NonWhitespaceCharacterCount");
+ if (it != aStats.end())
+ {
+ if (it->second >>= nValue)
+ writeElement(pAppProps, XML_Characters, nValue);
+ }
+
+ it = aStats.find("CharacterCount");
+ if (it != aStats.end())
+ {
+ if (it->second >>= nValue)
+ writeElement(pAppProps, XML_CharactersWithSpaces, nValue);
+ }
+
+ it = aStats.find("ParagraphCount");
+ if (it != aStats.end())
+ {
+ if (it->second >>= nValue)
+ writeElement(pAppProps, XML_Paragraphs, nValue);
+ }
+
+ it = aUserDefinedProperties.find("Company");
+ if (it != aUserDefinedProperties.end())
+ {
+ OUString aValue;
+ if (it->second >>= aValue)
+ writeElement(pAppProps, XML_Company, aValue);
+ }
+
+ pAppProps->endElement( XML_Properties );
+}
+
+static void
+writeCustomProperties( XmlFilterBase& rSelf, const Reference< XDocumentProperties >& xProperties, bool bSecurityOptOpenReadOnly )
+{
+ uno::Reference<beans::XPropertyAccess> xUserDefinedProperties( xProperties->getUserDefinedProperties(), uno::UNO_QUERY );
+ auto aprop = comphelper::sequenceToContainer< std::vector<beans::PropertyValue> >(xUserDefinedProperties->getPropertyValues());
+ sal_Int32 nbCustomProperties = aprop.size();
+ // tdf#89791 : if no custom properties, no need to add docProps/custom.x
+ // tdf#107690: except the case of read-only documents, because that
+ // is handled by the _MarkAsFinal custom property in MSO.
+ if (!nbCustomProperties && !bSecurityOptOpenReadOnly)
+ return;
+
+ if (bSecurityOptOpenReadOnly)
+ {
+ PropertyValue aPropertyValue;
+ // MSO custom property for read-only documents
+ aPropertyValue.Name = "_MarkAsFinal";
+ aPropertyValue.Value <<= true;
+ aprop.push_back(aPropertyValue);
+ }
+
+ rSelf.addRelation(
+ "http://schemas.openxmlformats.org/officeDocument/2006/relationships/custom-properties",
+ u"docProps/custom.xml" );
+ FSHelperPtr pAppProps = rSelf.openFragmentStreamWithSerializer(
+ "docProps/custom.xml",
+ "application/vnd.openxmlformats-officedocument.custom-properties+xml" );
+ pAppProps->startElement( XML_Properties,
+ XML_xmlns, rSelf.getNamespaceURL(OOX_NS(officeCustomPr)),
+ FSNS(XML_xmlns, XML_vt), rSelf.getNamespaceURL(OOX_NS(officeDocPropsVT)));
+
+ size_t nIndex = 0;
+ for (const auto& rProp : aprop)
+ {
+ if ( !rProp.Name.isEmpty() )
+ {
+ // Skip storing these values in Custom Properties as it will be stored in Core/Extended Properties
+ if (( rProp.Name == "OOXMLCorePropertyCategory" ) || // stored in cp:category
+ ( rProp.Name == "OOXMLCorePropertyContentStatus" ) || // stored in cp:contentStatus
+ ( rProp.Name == "OOXMLCorePropertyContentType" ) || // stored in cp:contentType
+ ( rProp.Name == "OOXMLCorePropertyIdentifier" ) || // stored in dc:identifier
+ ( rProp.Name == "OOXMLCorePropertyVersion" ) || // stored in cp:version
+ ( rProp.Name == "HyperlinkBase" ) || // stored in Extended File Properties
+ ( rProp.Name == "AppVersion" ) || // stored in Extended File Properties
+ ( rProp.Name == "DocSecurity" ) || // stored in Extended File Properties
+ ( rProp.Name == "Manager" ) || // stored in Extended File Properties
+ ( rProp.Name == "Company" )) // stored in Extended File Properties
+ continue;
+
+ // pid starts from 2 not from 1 as MS supports pid from 2
+ pAppProps->startElement( XML_property ,
+ XML_fmtid, "{D5CDD505-2E9C-101B-9397-08002B2CF9AE}",
+ XML_pid, OString::number(nIndex + 2),
+ XML_name, rProp.Name);
+
+ switch ( rProp.Value.getValueTypeClass() )
+ {
+ case TypeClass_STRING:
+ {
+ OUString aValue;
+ rProp.Value >>= aValue;
+ writeElement( pAppProps, FSNS( XML_vt, XML_lpwstr ), aValue );
+ }
+ break;
+ case TypeClass_BOOLEAN:
+ {
+ bool val = *o3tl::forceAccess<bool>(rProp.Value);
+ writeElement( pAppProps, FSNS( XML_vt, XML_bool ), val ? 1 : 0);
+ }
+ break;
+ case TypeClass_DOUBLE:
+ {
+ double num = {}; // spurious -Werror=maybe-uninitialized
+ if ( rProp.Value >>= num )
+ {
+ // r8 - 8-byte real number
+ writeElement( pAppProps, FSNS( XML_vt, XML_r8 ), OUString::number(num) );
+ }
+ }
+ break;
+ default:
+ {
+ double num = {}; // spurious -Werror=maybe-uninitialized
+ util::Date aDate;
+ util::Duration aDuration;
+ util::DateTime aDateTime;
+ if ( rProp.Value >>= num )
+ {
+ // i4 - 4-byte signed integer
+ writeElement( pAppProps, FSNS( XML_vt, XML_i4 ), num );
+ }
+ else if ( rProp.Value >>= aDate )
+ {
+ aDateTime = util::DateTime( 0, 0 , 0, 0, aDate.Day, aDate.Month, aDate.Year, true );
+ writeElement( pAppProps, FSNS( XML_vt, XML_filetime ), aDateTime);
+ }
+ else if ( rProp.Value >>= aDuration )
+ {
+ OUStringBuffer buf;
+ ::sax::Converter::convertDuration( buf, aDuration );
+ OUString aDurationStr = buf.makeStringAndClear();
+ writeElement( pAppProps, FSNS( XML_vt, XML_lpwstr ), aDurationStr );
+ }
+ else if ( rProp.Value >>= aDateTime )
+ writeElement( pAppProps, FSNS( XML_vt, XML_filetime ), aDateTime );
+ else
+ //no other options
+ OSL_FAIL( "XMLFilterBase::writeCustomProperties unsupported value type!" );
+ }
+ break;
+ }
+ pAppProps->endElement( XML_property );
+ }
+ ++nIndex;
+ }
+ pAppProps->endElement( XML_Properties );
+}
+
+void XmlFilterBase::exportDocumentProperties( const Reference< XDocumentProperties >& xProperties, bool bSecurityOptOpenReadOnly )
+{
+ if( xProperties.is() )
+ {
+ writeCoreProperties( *this, xProperties );
+ writeAppProperties( *this, xProperties );
+ writeCustomProperties( *this, xProperties, bSecurityOptOpenReadOnly );
+ }
+}
+
+// protected ------------------------------------------------------------------
+
+Reference< XInputStream > XmlFilterBase::implGetInputStream( MediaDescriptor& rMediaDesc ) const
+{
+ /* Get the input stream directly from the media descriptor, or decrypt the
+ package again. The latter is needed e.g. when the document is reloaded.
+ All this is implemented in the detector service. */
+ rtl::Reference< FilterDetect > xDetector( new FilterDetect( getComponentContext() ) );
+ return xDetector->extractUnencryptedPackage( rMediaDesc );
+}
+
+Reference<XStream> XmlFilterBase::implGetOutputStream( MediaDescriptor& rMediaDescriptor ) const
+{
+ const Sequence< NamedValue > aMediaEncData = rMediaDescriptor.getUnpackedValueOrDefault(
+ MediaDescriptor::PROP_ENCRYPTIONDATA,
+ Sequence< NamedValue >() );
+
+ if (aMediaEncData.getLength() == 0)
+ {
+ return FilterBase::implGetOutputStream( rMediaDescriptor );
+ }
+ else // We need to encrypt the stream so create a memory stream
+ {
+ Reference< XComponentContext > xContext = getComponentContext();
+ return Reference< XStream > (
+ xContext->getServiceManager()->createInstanceWithContext("com.sun.star.comp.MemoryStream", xContext),
+ uno::UNO_QUERY_THROW );
+ }
+}
+
+bool XmlFilterBase::implFinalizeExport( MediaDescriptor& rMediaDescriptor )
+{
+ bool bRet = true;
+
+ const Sequence< NamedValue > aMediaEncData = rMediaDescriptor.getUnpackedValueOrDefault(
+ MediaDescriptor::PROP_ENCRYPTIONDATA,
+ Sequence< NamedValue >() );
+
+ if (aMediaEncData.getLength())
+ {
+ commitStorage();
+
+ Reference< XStream> xDocumentStream (FilterBase::implGetOutputStream(rMediaDescriptor));
+ oox::ole::OleStorage aOleStorage( getComponentContext(), xDocumentStream, true );
+ crypto::DocumentEncryption encryptor( getComponentContext(), getMainDocumentStream(), aOleStorage, aMediaEncData );
+ bRet = encryptor.encrypt();
+ if (bRet)
+ aOleStorage.commit();
+ }
+
+ return bRet;
+}
+
+// private --------------------------------------------------------------------
+
+StorageRef XmlFilterBase::implCreateStorage( const Reference< XInputStream >& rxInStream ) const
+{
+ return std::make_shared<ZipStorage>( getComponentContext(), rxInStream );
+}
+
+StorageRef XmlFilterBase::implCreateStorage( const Reference< XStream >& rxOutStream ) const
+{
+ return std::make_shared<ZipStorage>( getComponentContext(), rxOutStream );
+}
+
+bool XmlFilterBase::isMSO2007Document() const
+{
+ return mbMSO2007;
+}
+
+bool XmlFilterBase::isMSODocument() const
+{
+ return mbMSO;
+}
+
+void XmlFilterBase::setMissingExtDrawing()
+{
+ mbMissingExtDrawing = true;
+}
+
+void XmlFilterBase::setDiagramFontHeights(NamedShapePairs* pDiagramFontHeights)
+{
+ mxImpl->mpDiagramFontHeights = pDiagramFontHeights;
+}
+
+NamedShapePairs* XmlFilterBase::getDiagramFontHeights() { return mxImpl->mpDiagramFontHeights; }
+
+OUString XmlFilterBase::getNamespaceURL(sal_Int32 nNSID) const
+{
+ auto itr = mxImpl->mrNamespaceMap.maTransitionalNamespaceMap.find(nNSID);
+ if (itr == mxImpl->mrNamespaceMap.maTransitionalNamespaceMap.end())
+ {
+ SAL_WARN("oox", "missing namespace in the namespace map for : " << nNSID);
+ return OUString();
+ }
+
+ return itr->second;
+}
+
+void XmlFilterBase::importCustomFragments(css::uno::Reference<css::embed::XStorage> const & xDocumentStorage)
+{
+ Reference<XRelationshipAccess> xRelations(xDocumentStorage, UNO_QUERY);
+ if (!xRelations.is())
+ return;
+
+ const uno::Sequence<uno::Sequence<beans::StringPair>> aSeqs = xRelations->getAllRelationships();
+
+ std::vector<StreamDataSequence> aCustomFragments;
+ std::vector<OUString> aCustomFragmentTypes;
+ std::vector<OUString> aCustomFragmentTargets;
+ for (const uno::Sequence<beans::StringPair>& aSeq : aSeqs)
+ {
+ OUString sType;
+ OUString sTarget;
+ for (const beans::StringPair& aPair : aSeq)
+ {
+ if (aPair.First == "Target")
+ sTarget = aPair.Second;
+ else if (aPair.First == "Type")
+ sType = aPair.Second;
+ }
+
+ // Preserve non-standard (i.e. custom) entries.
+ if (!sType.match("http://schemas.openxmlformats.org") // OOXML/ECMA Transitional
+ && !sType.match("http://purl.oclc.org")) // OOXML Strict
+ {
+ StreamDataSequence aDataSeq;
+ if (importBinaryData(aDataSeq, sTarget))
+ {
+ aCustomFragments.emplace_back(aDataSeq);
+ aCustomFragmentTypes.emplace_back(sType);
+ aCustomFragmentTargets.emplace_back(sTarget);
+ }
+ }
+ }
+
+ // Adding the saved custom xml DOM
+ comphelper::SequenceAsHashMap aGrabBagProperties;
+ aGrabBagProperties["OOXCustomFragments"] <<= comphelper::containerToSequence(aCustomFragments);
+ aGrabBagProperties["OOXCustomFragmentTypes"] <<= comphelper::containerToSequence(aCustomFragmentTypes);
+ aGrabBagProperties["OOXCustomFragmentTargets"] <<= comphelper::containerToSequence(aCustomFragmentTargets);
+
+ std::vector<uno::Reference<xml::dom::XDocument>> aCustomXmlDomList;
+ std::vector<uno::Reference<xml::dom::XDocument>> aCustomXmlDomPropsList;
+ //FIXME: Ideally, we should get these the relations, but it seems that is not consistently set.
+ // In some cases it's stored in the workbook relationships, which is unexpected. So we discover them directly.
+ for (int i = 1; ; ++i)
+ {
+ Reference<XDocument> xCustDoc = importFragment("customXml/item" + OUString::number(i) + ".xml");
+ Reference<XDocument> xCustDocProps = importFragment("customXml/itemProps" + OUString::number(i) + ".xml");
+ if (xCustDoc && xCustDocProps)
+ {
+ aCustomXmlDomList.emplace_back(xCustDoc);
+ aCustomXmlDomPropsList.emplace_back(xCustDocProps);
+ }
+ else
+ break;
+ }
+
+ // Adding the saved custom xml DOM
+ aGrabBagProperties["OOXCustomXml"] <<= comphelper::containerToSequence(aCustomXmlDomList);
+ aGrabBagProperties["OOXCustomXmlProps"] <<= comphelper::containerToSequence(aCustomXmlDomPropsList);
+
+ // Save the [Content_Types].xml after parsing.
+ uno::Sequence<uno::Sequence<beans::StringPair>> aContentTypeInfo;
+ uno::Reference<io::XInputStream> xInputStream = openInputStream("[Content_Types].xml");
+ if (xInputStream.is())
+ aContentTypeInfo = comphelper::OFOPXMLHelper::ReadContentTypeSequence(xInputStream, getComponentContext());
+
+ aGrabBagProperties["OOXContentTypes"] <<= aContentTypeInfo;
+
+ Reference<XComponent> xModel = getModel();
+ oox::core::XmlFilterBase::putPropertiesToDocumentGrabBag(xModel, aGrabBagProperties);
+}
+
+void XmlFilterBase::exportCustomFragments()
+{
+ Reference<XComponent> xModel = getModel();
+ uno::Reference<beans::XPropertySet> xPropSet(xModel, uno::UNO_QUERY_THROW);
+
+ uno::Reference<beans::XPropertySetInfo> xPropSetInfo = xPropSet->getPropertySetInfo();
+ if (!xPropSetInfo->hasPropertyByName(UNO_NAME_MISC_OBJ_INTEROPGRABBAG))
+ return;
+
+ uno::Sequence<uno::Reference<xml::dom::XDocument>> customXmlDomlist;
+ uno::Sequence<uno::Reference<xml::dom::XDocument>> customXmlDomPropslist;
+ uno::Sequence<StreamDataSequence> customFragments;
+ uno::Sequence<OUString> customFragmentTypes;
+ uno::Sequence<OUString> customFragmentTargets;
+ uno::Sequence<uno::Sequence<beans::StringPair>> aContentTypes;
+
+ uno::Sequence<beans::PropertyValue> propList;
+ xPropSet->getPropertyValue(UNO_NAME_MISC_OBJ_INTEROPGRABBAG) >>= propList;
+ for (const auto& rProp : std::as_const(propList))
+ {
+ const OUString propName = rProp.Name;
+ if (propName == "OOXCustomXml")
+ {
+ rProp.Value >>= customXmlDomlist;
+ }
+ else if (propName == "OOXCustomXmlProps")
+ {
+ rProp.Value >>= customXmlDomPropslist;
+ }
+ else if (propName == "OOXCustomFragments")
+ {
+ rProp.Value >>= customFragments;
+ }
+ else if (propName == "OOXCustomFragmentTypes")
+ {
+ rProp.Value >>= customFragmentTypes;
+ }
+ else if (propName == "OOXCustomFragmentTargets")
+ {
+ rProp.Value >>= customFragmentTargets;
+ }
+ else if (propName == "OOXContentTypes")
+ {
+ rProp.Value >>= aContentTypes;
+ }
+ }
+
+ // Expect customXmlDomPropslist.getLength() == customXmlDomlist.getLength().
+ for (sal_Int32 j = 0; j < customXmlDomlist.getLength(); j++)
+ {
+ uno::Reference<xml::dom::XDocument> customXmlDom = customXmlDomlist[j];
+ uno::Reference<xml::dom::XDocument> customXmlDomProps = customXmlDomPropslist[j];
+ const OUString fragmentPath = "customXml/item" + OUString::number(j+1) + ".xml";
+ if (customXmlDom.is())
+ {
+ addRelation(oox::getRelationship(Relationship::CUSTOMXML), OUStringConcatenation("../" + fragmentPath));
+
+ uno::Reference<xml::sax::XSAXSerializable> serializer(customXmlDom, uno::UNO_QUERY);
+ uno::Reference<xml::sax::XWriter> writer = xml::sax::Writer::create(comphelper::getProcessComponentContext());
+ writer->setOutputStream(openFragmentStream(fragmentPath, "application/xml"));
+ serializer->serialize(uno::Reference<xml::sax::XDocumentHandler>(writer, uno::UNO_QUERY_THROW),
+ uno::Sequence<beans::StringPair>());
+ }
+
+ if (customXmlDomProps.is())
+ {
+ uno::Reference<xml::sax::XSAXSerializable> serializer(customXmlDomProps, uno::UNO_QUERY);
+ uno::Reference<xml::sax::XWriter> writer = xml::sax::Writer::create(comphelper::getProcessComponentContext());
+ writer->setOutputStream(openFragmentStream("customXml/itemProps"+OUString::number(j+1)+".xml",
+ "application/vnd.openxmlformats-officedocument.customXmlProperties+xml"));
+ serializer->serialize(uno::Reference<xml::sax::XDocumentHandler>(writer, uno::UNO_QUERY_THROW),
+ uno::Sequence<beans::StringPair>());
+
+ // Adding itemprops's relationship entry to item.xml.rels file
+ addRelation(openFragmentStream(fragmentPath, "application/xml"),
+ oox::getRelationship(Relationship::CUSTOMXMLPROPS),
+ OUStringConcatenation("itemProps"+OUString::number(j+1)+".xml"));
+ }
+ }
+
+ // Expect customFragments.getLength() == customFragmentTypes.getLength() == customFragmentTargets.getLength().
+ for (sal_Int32 j = 0; j < customFragments.getLength(); j++)
+ {
+ addRelation(customFragmentTypes[j], customFragmentTargets[j]);
+ const OUString aFilename = customFragmentTargets[j];
+ Reference<XOutputStream> xOutStream = openOutputStream(aFilename);
+ if (xOutStream.is())
+ {
+ xOutStream->writeBytes(customFragments[j]);
+ uno::Reference<XPropertySet> xProps(xOutStream, uno::UNO_QUERY);
+ if (xProps.is())
+ {
+ const OUString aType = comphelper::OFOPXMLHelper::GetContentTypeByName(aContentTypes, aFilename);
+ const OUString aContentType = (aType.getLength() ? aType : OUString("application/octet-stream"));
+ xProps->setPropertyValue("MediaType", uno::Any(aContentType));
+ }
+ }
+ }
+}
+
+} // namespace oox::core
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */