[Libreoffice-commits] core.git: Branch 'libreoffice-5-4' - sw/qa sw/source writerfilter/inc writerfilter/source

Miklos Vajna vmiklos at collabora.co.uk
Wed Jun 7 02:49:42 UTC 2017


 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 35c3dfddf9933809525540d02484c2754510f6d9
Author: Miklos Vajna <vmiklos at 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 <ci at libreoffice.org>
    Reviewed-by: Miklos Vajna <vmiklos at collabora.co.uk>
    (cherry picked from commit 8a59b30bb1af55f7afd8b98e4b60234f98d84c76)
    Reviewed-on: https://gerrit.libreoffice.org/38458
    Reviewed-by: Christian Lohmaier <lohmaier+LibreOffice at googlemail.com>

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 f96280aa893f..66711fbc21e3 100644
--- a/sw/qa/extras/ooxmlexport/ooxmlexport9.cxx
+++ b/sw/qa/extras/ooxmlexport/ooxmlexport9.cxx
@@ -92,6 +92,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 4b7eff5bb94e..25d30ef02d1e 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


More information about the Libreoffice-commits mailing list