sw/qa/extras/ooxmlexport/data/tdf108269.docm |binary sw/qa/extras/ooxmlexport/ooxmlexport9.cxx | 12 +++ sw/source/filter/ww8/docxexport.cxx | 75 ++++++++++++++++++++++++ sw/source/filter/ww8/docxexport.hxx | 3 writerfilter/inc/ooxml/OOXMLDocument.hxx | 1 writerfilter/source/filter/WriterFilter.cxx | 3 writerfilter/source/ooxml/OOXMLDocumentImpl.cxx | 75 ++++++++++++++++++++++++ writerfilter/source/ooxml/OOXMLDocumentImpl.hxx | 4 + writerfilter/source/ooxml/OOXMLStreamImpl.hxx | 3 9 files changed, 176 insertions(+)
New commits: commit 8a59b30bb1af55f7afd8b98e4b60234f98d84c76 Author: Miklos Vajna <vmik...@collabora.co.uk> Date: Fri Jun 2 18:41:38 2017 +0200 Related: tdf#108269 DOCM filter: preserve VBA stream This means 2 new streams when roundtripping DOCM files that actually have macros: word/vbaProject.bin and word/vbaData.xml (+ the relation pointing to the second from the first). Change-Id: Iba24eea4c5bca8f743a53027c71ed2aae48f1934 Reviewed-on: https://gerrit.libreoffice.org/38360 Tested-by: Jenkins <c...@libreoffice.org> Reviewed-by: Miklos Vajna <vmik...@collabora.co.uk> diff --git a/sw/qa/extras/ooxmlexport/data/tdf108269.docm b/sw/qa/extras/ooxmlexport/data/tdf108269.docm new file mode 100644 index 000000000000..44e943531d30 Binary files /dev/null and b/sw/qa/extras/ooxmlexport/data/tdf108269.docm differ diff --git a/sw/qa/extras/ooxmlexport/ooxmlexport9.cxx b/sw/qa/extras/ooxmlexport/ooxmlexport9.cxx index 0191b8a38023..a7aadf16549f 100644 --- a/sw/qa/extras/ooxmlexport/ooxmlexport9.cxx +++ b/sw/qa/extras/ooxmlexport/ooxmlexport9.cxx @@ -93,6 +93,18 @@ DECLARE_SW_ROUNDTRIP_TEST(testBadDocm, "bad.docm", nullptr, DocmTest) CPPUNIT_ASSERT_EQUAL(OUString("MS Word 2007 XML VBA"), pTextDoc->GetDocShell()->GetMedium()->GetFilter()->GetName()); } +DECLARE_SW_ROUNDTRIP_TEST(testTdf108269, "tdf108269.docm", nullptr, DocmTest) +{ + if (!mbExported) + return; + + uno::Reference<packages::zip::XZipFileAccess2> xNameAccess = packages::zip::ZipFileAccess::createWithURL(comphelper::getComponentContext(m_xSFactory), maTempFile.GetURL()); + // This failed: VBA streams were not roundtripped via the doc-level + // grab-bag. + CPPUNIT_ASSERT(xNameAccess->hasByName("word/vbaProject.bin")); + CPPUNIT_ASSERT(xNameAccess->hasByName("word/vbaData.xml")); +} + DECLARE_OOXMLEXPORT_TEST(testTdf92045, "tdf92045.docx") { // This was true, <w:effect w:val="none"/> resulted in setting the blinking font effect. diff --git a/sw/source/filter/ww8/docxexport.cxx b/sw/source/filter/ww8/docxexport.cxx index 57432c9add87..a57eb02915c8 100644 --- a/sw/source/filter/ww8/docxexport.cxx +++ b/sw/source/filter/ww8/docxexport.cxx @@ -77,6 +77,7 @@ #include <comphelper/string.hxx> #include <rtl/ustrbuf.hxx> #include <vcl/font.hxx> +#include <unotools/ucbstreamhelper.hxx> using namespace sax_fastparser; using namespace ::comphelper; @@ -464,6 +465,8 @@ void DocxExport::ExportDocument_Impl() WriteEmbeddings(); + WriteVBA(); + m_aLinkedTextboxesHelper.clear(); //final cleanup delete m_pStyles; m_pStyles = nullptr; @@ -1251,6 +1254,78 @@ void DocxExport::WriteActiveX() } } +void DocxExport::WriteVBA() +{ + uno::Reference<beans::XPropertySet> xPropertySet(m_pDoc->GetDocShell()->GetBaseModel(), uno::UNO_QUERY); + if (!xPropertySet.is()) + return; + + uno::Reference<beans::XPropertySetInfo> xPropertySetInfo = xPropertySet->getPropertySetInfo(); + if (!xPropertySetInfo->hasPropertyByName(UNO_NAME_MISC_OBJ_INTEROPGRABBAG)) + return; + + uno::Sequence<beans::PropertyValue> aGrabBag; + xPropertySet->getPropertyValue(UNO_NAME_MISC_OBJ_INTEROPGRABBAG) >>= aGrabBag; + uno::Sequence<beans::PropertyValue> aVBA; + for (const auto& rProperty : aGrabBag) + { + if (rProperty.Name == "OOXVBA") + rProperty.Value >>= aVBA; + } + if (!aVBA.hasElements()) + return; + + uno::Reference<io::XOutputStream> xProjectStream; + for (const auto& rProperty : aVBA) + { + if (rProperty.Name == "ProjectStream") + { + // First check for the project stream, this sets xProjectStream. + uno::Reference<io::XStream> xInputStream; + rProperty.Value >>= xInputStream; + if (!xInputStream.is()) + return; + std::unique_ptr<SvStream> pIn(utl::UcbStreamHelper::CreateStream(xInputStream)); + + xProjectStream = GetFilter().openFragmentStream("word/vbaProject.bin", "application/vnd.ms-office.vbaProject"); + uno::Reference<io::XStream> xOutputStream(xProjectStream, uno::UNO_QUERY); + if (!xOutputStream.is()) + return; + std::unique_ptr<SvStream> pOut(utl::UcbStreamHelper::CreateStream(xOutputStream)); + + // Write the stream. + pOut->WriteStream(*pIn); + + // Write the relationship. + m_pFilter->addRelation(m_pDocumentFS->getOutputStream(), "http://schemas.microsoft.com/office/2006/relationships/vbaProject", "vbaProject.bin"); + } + else if (rProperty.Name == "DataStream") + { + // Then the data stream, which wants to work with an already set + // xProjectStream. + uno::Reference<io::XStream> xInputStream; + rProperty.Value >>= xInputStream; + if (!xInputStream.is()) + return; + std::unique_ptr<SvStream> pIn(utl::UcbStreamHelper::CreateStream(xInputStream)); + + uno::Reference<io::XStream> xOutputStream(GetFilter().openFragmentStream("word/vbaData.xml", "application/vnd.ms-word.vbaData+xml"), uno::UNO_QUERY); + if (!xOutputStream.is()) + return; + std::unique_ptr<SvStream> pOut(utl::UcbStreamHelper::CreateStream(xOutputStream)); + + // Write the stream. + pOut->WriteStream(*pIn); + + // Write the relationship. + if (!xProjectStream.is()) + return; + + m_pFilter->addRelation(xProjectStream, "http://schemas.microsoft.com/office/2006/relationships/wordVbaData", "vbaData.xml"); + } + } +} + void DocxExport::WriteEmbeddings() { uno::Reference< beans::XPropertySet > xPropSet( m_pDoc->GetDocShell()->GetBaseModel(), uno::UNO_QUERY_THROW ); diff --git a/sw/source/filter/ww8/docxexport.hxx b/sw/source/filter/ww8/docxexport.hxx index 454e0742cee1..b3627639eb96 100644 --- a/sw/source/filter/ww8/docxexport.hxx +++ b/sw/source/filter/ww8/docxexport.hxx @@ -244,6 +244,9 @@ private: /// Write word/embeddings/Worksheet[n].xlsx void WriteEmbeddings(); + /// Writes word/vbaProject.bin. + void WriteVBA(); + /// return true if Page Layout is set as Mirrored bool isMirroredMargin(); diff --git a/writerfilter/inc/ooxml/OOXMLDocument.hxx b/writerfilter/inc/ooxml/OOXMLDocument.hxx index 27ecf84559b5..009aeba949cc 100644 --- a/writerfilter/inc/ooxml/OOXMLDocument.hxx +++ b/writerfilter/inc/ooxml/OOXMLDocument.hxx @@ -229,6 +229,7 @@ public: virtual css::uno::Sequence<css::uno::Reference<css::xml::dom::XDocument> > getActiveXDomList( ) = 0; virtual css::uno::Sequence<css::uno::Reference<css::io::XInputStream> > getActiveXBinList() = 0; virtual css::uno::Sequence<css::beans::PropertyValue > getEmbeddingsList() = 0; + virtual css::uno::Sequence<css::beans::PropertyValue > getVBA() = 0; }; diff --git a/writerfilter/source/filter/WriterFilter.cxx b/writerfilter/source/filter/WriterFilter.cxx index ff2586c7d3a3..55c70e11eabc 100644 --- a/writerfilter/source/filter/WriterFilter.cxx +++ b/writerfilter/source/filter/WriterFilter.cxx @@ -236,6 +236,9 @@ sal_Bool WriterFilter::filter(const uno::Sequence< beans::PropertyValue >& aDesc // Adding the saved embedding document to document's grab bag aGrabBagProperties["OOXEmbeddings"] <<= pDocument->getEmbeddingsList(); + if (pDocument->getVBA().hasElements()) + aGrabBagProperties["OOXVBA"] <<= pDocument->getVBA(); + putPropertiesToDocumentGrabBag(aGrabBagProperties); writerfilter::ooxml::OOXMLStream::Pointer_t pVBAProjectStream(writerfilter::ooxml::OOXMLDocumentFactory::createStream(pDocStream, writerfilter::ooxml::OOXMLStream::VBAPROJECT)); diff --git a/writerfilter/source/ooxml/OOXMLDocumentImpl.cxx b/writerfilter/source/ooxml/OOXMLDocumentImpl.cxx index 089c0e2a8f26..550820be018e 100644 --- a/writerfilter/source/ooxml/OOXMLDocumentImpl.cxx +++ b/writerfilter/source/ooxml/OOXMLDocumentImpl.cxx @@ -39,6 +39,7 @@ #include <svx/dialogs.hrc> #include <comphelper/sequence.hxx> #include <unotools/mediadescriptor.hxx> +#include <comphelper/propertysequence.hxx> #include <iostream> #include "sfx2/objsh.hxx" @@ -496,6 +497,9 @@ void OOXMLDocumentImpl::resolve(Stream & rStream) resolveActiveXStream(rStream); + if (!mbIsSubstream) + preserveVBA(); + resolveFastSubStream(rStream, OOXMLStream::FONTTABLE); resolveFastSubStream(rStream, OOXMLStream::STYLES); resolveFastSubStream(rStream, OOXMLStream::NUMBERING); @@ -808,6 +812,72 @@ void OOXMLDocumentImpl::resolveEmbeddingsStream(const OOXMLStream::Pointer_t& pS mxEmbeddingsList = comphelper::containerToSequence(aEmbeddings); } +namespace +{ +/// Returns the target string for rType in xRelationshipAccess. +OUString getTypeTarget(const uno::Reference<embed::XRelationshipAccess>& xRelationshipAccess, const OUString& rType) +{ + uno::Sequence< uno::Sequence<beans::StringPair> > aRelations = xRelationshipAccess->getAllRelationships(); + for (const auto& rRelation : aRelations) + { + OUString aType; + OUString aTarget; + for (const auto& rPair : rRelation) + { + if (rPair.First == "Type") + aType = rPair.Second; + else if (rPair.First == "Target") + aTarget = rPair.Second; + } + + if (aType == rType) + return aTarget; + } + + return OUString(); +} +} + +void OOXMLDocumentImpl::preserveVBA() +{ + auto pOOXMLStream = dynamic_cast<OOXMLStreamImpl*>(mpStream.get()); + if (!pOOXMLStream) + return; + + uno::Reference<embed::XRelationshipAccess> xRelationshipAccess(pOOXMLStream->accessDocumentStream(), uno::UNO_QUERY); + if (!xRelationshipAccess.is()) + return; + + OUString aVBAStreamName = getTypeTarget(xRelationshipAccess, "http://schemas.microsoft.com/office/2006/relationships/vbaProject"); + if (aVBAStreamName.isEmpty()) + return; + + uno::Reference<embed::XHierarchicalStorageAccess> xStorage(pOOXMLStream->getStorage(), uno::UNO_QUERY); + if (!xStorage.is()) + return; + + OUString aPath = pOOXMLStream->getPath(); + uno::Reference<io::XStream> xStream(xStorage->openStreamElementByHierarchicalName(aPath + aVBAStreamName, embed::ElementModes::SEEKABLEREAD), uno::UNO_QUERY); + if (!xStream.is()) + return; + + xRelationshipAccess.set(xStream, uno::UNO_QUERY); + uno::Reference<io::XStream> xDataStream; + if (xRelationshipAccess.is()) + { + // Check if there is a vbaData.xml for the vbaProject.bin. + OUString aVBAData = getTypeTarget(xRelationshipAccess, "http://schemas.microsoft.com/office/2006/relationships/wordVbaData"); + if (!aVBAData.isEmpty()) + xDataStream.set(xStorage->openStreamElementByHierarchicalName(aPath + aVBAData, embed::ElementModes::SEEKABLEREAD), uno::UNO_QUERY); + } + + maVBA = comphelper::InitPropertySequence( + { + {"ProjectStream", uno::makeAny(xStream)}, + {"DataStream", uno::makeAny(xDataStream)} + }); +} + void OOXMLDocumentImpl::resolveActiveXStream(Stream & rStream) { // Resolving all ActiveX[n].xml files from ActiveX folder. @@ -947,6 +1017,11 @@ uno::Sequence<beans::PropertyValue > OOXMLDocumentImpl::getEmbeddingsList( ) return mxEmbeddingsList; } +uno::Sequence<beans::PropertyValue> OOXMLDocumentImpl::getVBA() +{ + return maVBA; +} + OOXMLDocument * OOXMLDocumentFactory::createDocument (const OOXMLStream::Pointer_t& pStream, diff --git a/writerfilter/source/ooxml/OOXMLDocumentImpl.hxx b/writerfilter/source/ooxml/OOXMLDocumentImpl.hxx index 43bdeb651d78..c4245cae7b02 100644 --- a/writerfilter/source/ooxml/OOXMLDocumentImpl.hxx +++ b/writerfilter/source/ooxml/OOXMLDocumentImpl.hxx @@ -53,6 +53,8 @@ class OOXMLDocumentImpl : public OOXMLDocument css::uno::Reference<css::io::XInputStream> mxActiveXBin; css::uno::Reference<css::io::XInputStream> mxEmbeddings; css::uno::Sequence < css::beans::PropertyValue > mxEmbeddingsList; + /// List of VBA-related streams. + css::uno::Sequence<css::beans::PropertyValue> maVBA; bool mbIsSubstream; bool mbSkipImages; /// How many paragraphs equal to 1 percent? @@ -91,6 +93,7 @@ protected: void resolveActiveXStream(Stream & rStream); void resolveGlossaryStream(Stream & rStream); void resolveEmbeddingsStream(const OOXMLStream::Pointer_t& pStream); + void preserveVBA(); public: OOXMLDocumentImpl(OOXMLStream::Pointer_t const & pStream, const css::uno::Reference<css::task::XStatusIndicator>& xStatusIndicator, bool bSkipImages, const css::uno::Sequence<css::beans::PropertyValue>& rDescriptor); virtual ~OOXMLDocumentImpl() override; @@ -136,6 +139,7 @@ public: virtual css::uno::Reference<css::xml::dom::XDocument> getGlossaryDocDom() override; virtual css::uno::Sequence<css::uno::Sequence< css::uno::Any> > getGlossaryDomList() override; virtual css::uno::Sequence<css::beans::PropertyValue > getEmbeddingsList() override; + virtual css::uno::Sequence<css::beans::PropertyValue> getVBA() override; void incrementProgress(); bool IsSkipImages() { return mbSkipImages; }; diff --git a/writerfilter/source/ooxml/OOXMLStreamImpl.hxx b/writerfilter/source/ooxml/OOXMLStreamImpl.hxx index aff8189b7396..cb0276d84649 100644 --- a/writerfilter/source/ooxml/OOXMLStreamImpl.hxx +++ b/writerfilter/source/ooxml/OOXMLStreamImpl.hxx @@ -80,6 +80,9 @@ public: // Giving access to mxDocumentStream. It is needed by resolving custom xml to get list of customxml's used in document. const css::uno::Reference<css::io::XStream>& accessDocumentStream() { return mxDocumentStream;} + + const css::uno::Reference<css::embed::XStorage>& getStorage() { return mxStorage; } + const OUString& getPath() { return msPath; } }; }} #endif // INCLUDED_WRITERFILTER_SOURCE_OOXML_OOXMLSTREAMIMPL_HXX _______________________________________________ Libreoffice-commits mailing list libreoffice-comm...@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/libreoffice-commits