[Libreoffice-commits] core.git: sw/qa sw/source

Miklos Vajna (via logerrit) logerrit at kemper.freedesktop.org
Wed Sep 2 18:10:58 UTC 2020


 sw/qa/extras/htmlexport/data/no-ole2-pres-data.odt |binary
 sw/qa/extras/htmlexport/htmlexport.cxx             |   74 +++++++++++++++++++--
 sw/source/filter/html/htmlreqifreader.cxx          |   65 +++++++++++-------
 3 files changed, 108 insertions(+), 31 deletions(-)

New commits:
commit 0d027abbc5609b096d2a954e77aa7354a55928ab
Author:     Miklos Vajna <vmiklos at collabora.com>
AuthorDate: Wed Sep 2 17:44:19 2020 +0200
Commit:     Miklos Vajna <vmiklos at collabora.com>
CommitDate: Wed Sep 2 20:10:10 2020 +0200

    sw reqif-xhtml export, embedded objects: take OLE1 pres data from rtf if needed
    
    Next to the native data of an embedded object, the presentation data /
    replacement is included at several layers:
    
    - the OLE2 container may have it
    - the OLE1 container may have it
    - the RTF container may have it
    - the PNG file next to the RTF container may have it
    
    Given that various consumers pick one of the above, we try to provide
    presentation data in all layers.
    
    We already had code to generate the OLE1 presentation data from the OLE2
    container, but we gave up for OLE1 in case the OLE2 container didn't
    have it. This means that in case the RTF container is wrapped in a
    proper RTF file, Word refuses the edit the embedded object.
    
    Fix the problem by taking the presentation data from RTF for OLE1
    purposes, in case it's missing from the OLE2 container.
    
    Change-Id: I158db1c87044a3895d0c64a6e5a5384686627d96
    (cherry picked from commit 5c37f5713a5b9e14fcc378d91e5ed8d40edc40a4)
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/101946
    Reviewed-by: Miklos Vajna <vmiklos at collabora.com>
    Tested-by: Jenkins

diff --git a/sw/qa/extras/htmlexport/data/no-ole2-pres-data.odt b/sw/qa/extras/htmlexport/data/no-ole2-pres-data.odt
new file mode 100644
index 000000000000..cd65a1755746
Binary files /dev/null and b/sw/qa/extras/htmlexport/data/no-ole2-pres-data.odt differ
diff --git a/sw/qa/extras/htmlexport/htmlexport.cxx b/sw/qa/extras/htmlexport/htmlexport.cxx
index d4d59103fffe..a7abb6190b8f 100644
--- a/sw/qa/extras/htmlexport/htmlexport.cxx
+++ b/sw/qa/extras/htmlexport/htmlexport.cxx
@@ -74,6 +74,16 @@ public:
         rStream.Seek(0);
     }
 
+    /// Wraps an RTF fragment into a complete RTF file, so an RTF parser can handle it.
+    static void wrapRtfFragment(const OUString& rURL, SvMemoryStream& rStream)
+    {
+        SvFileStream aRtfStream(rURL, StreamMode::READ);
+        rStream.WriteOString("{\\rtf1");
+        rStream.WriteStream(aRtfStream);
+        rStream.WriteOString("}");
+        rStream.Seek(0);
+    }
+
 private:
     bool mustCalcLayoutOf(const char* filename) override
     {
@@ -1012,12 +1022,8 @@ CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, testReqifOle1PDF)
     OUString aRtfUrl = aUrl.GetMainURL(INetURLObject::DecodeMechanism::NONE);
 
     // Parse the ole1 data out of that.
-    SvFileStream aRtfStream(aRtfUrl, StreamMode::READ);
     SvMemoryStream aRtf;
-    aRtf.WriteOString("{\\rtf1");
-    aRtf.WriteStream(aRtfStream);
-    aRtf.WriteOString("}");
-    aRtf.Seek(0);
+    HtmlExportTest::wrapRtfFragment(aRtfUrl, aRtf);
     tools::SvRef<TestReqIfRtfReader> xReader(new TestReqIfRtfReader(aRtf));
     CPPUNIT_ASSERT(xReader->CallParser() != SvParserState::Error);
     SvMemoryStream aOle1;
@@ -1152,6 +1158,64 @@ CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, testUnderlineNone)
     assertXPathNoAttribute(pXmlDoc, "//reqif-xhtml:div/reqif-xhtml:p", "style");
 }
 
+CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, testReqifOle1PresDataNoOle2)
+{
+    // Save to reqif-xhtml.
+    OUString aURL = m_directories.getURLFromSrc(DATA_DIRECTORY) + "no-ole2-pres-data.odt";
+    mxComponent = loadFromDesktop(aURL, "com.sun.star.text.TextDocument", {});
+    uno::Reference<frame::XStorable> xStorable(mxComponent, uno::UNO_QUERY);
+    uno::Sequence<beans::PropertyValue> aStoreProperties = {
+        comphelper::makePropertyValue("FilterName", OUString("HTML (StarWriter)")),
+        comphelper::makePropertyValue("FilterOptions", OUString("xhtmlns=reqif-xhtml")),
+    };
+    xStorable->storeToURL(maTempFile.GetURL(), aStoreProperties);
+
+    // Get the .ole path.
+    SvMemoryStream aStream;
+    HtmlExportTest::wrapFragment(maTempFile, aStream);
+    xmlDocUniquePtr pDoc = parseXmlStream(&aStream);
+    CPPUNIT_ASSERT(pDoc);
+    OUString aOlePath = getXPath(
+        pDoc, "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:p/reqif-xhtml:object", "data");
+    OUString aOleSuffix(".ole");
+    CPPUNIT_ASSERT(aOlePath.endsWith(aOleSuffix));
+    INetURLObject aUrl(maTempFile.GetURL());
+    aUrl.setBase(aOlePath.copy(0, aOlePath.getLength() - aOleSuffix.getLength()));
+    aUrl.setExtension("ole");
+    OUString aRtfUrl = aUrl.GetMainURL(INetURLObject::DecodeMechanism::NONE);
+
+    // Parse the ole1 data out of the RTF fragment.
+    SvMemoryStream aRtf;
+    HtmlExportTest::wrapRtfFragment(aRtfUrl, aRtf);
+    tools::SvRef<TestReqIfRtfReader> xReader(new TestReqIfRtfReader(aRtf));
+    CPPUNIT_ASSERT(xReader->CallParser() != SvParserState::Error);
+    SvMemoryStream aOle1;
+    CPPUNIT_ASSERT(xReader->WriteObjectData(aOle1));
+    CPPUNIT_ASSERT(aOle1.Tell());
+
+    // Check the content of the ole1 data.
+    // Skip ObjectHeader, see [MS-OLEDS] 2.2.4.
+    aOle1.Seek(0);
+    sal_uInt32 nData;
+    aOle1.ReadUInt32(nData); // OLEVersion
+    aOle1.ReadUInt32(nData); // FormatID
+    aOle1.ReadUInt32(nData); // ClassName
+    aOle1.SeekRel(nData);
+    aOle1.ReadUInt32(nData); // TopicName
+    aOle1.SeekRel(nData);
+    aOle1.ReadUInt32(nData); // ItemName
+    aOle1.SeekRel(nData);
+    aOle1.ReadUInt32(nData); // NativeDataSize
+    aOle1.SeekRel(nData);
+
+    aOle1.ReadUInt32(nData); // OLEVersion for presentation data
+
+    // Without the accompanying fix in place, this test would have failed as there was no
+    // presentation data after the native data in the OLE1 container. The result was not editable in
+    // Word.
+    CPPUNIT_ASSERT(aOle1.good());
+}
+
 CPPUNIT_PLUGIN_IMPLEMENT();
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/html/htmlreqifreader.cxx b/sw/source/filter/html/htmlreqifreader.cxx
index 87368da014bd..26119adfed46 100644
--- a/sw/source/filter/html/htmlreqifreader.cxx
+++ b/sw/source/filter/html/htmlreqifreader.cxx
@@ -229,10 +229,15 @@ OString InsertOLE1HeaderFromOle10NativeStream(tools::SvRef<SotStorage>& xStorage
     return aClassName;
 }
 
-/// Inserts an OLE1 header before an OLE2 storage.
+/**
+ * Writes an OLE1 header and data from rOle2 to rOle1.
+ *
+ * In case rOle2 has presentation data, then its size is written to nWidth/nHeight.  Otherwise
+ * nWidth/nHeight/pPresentationData/nPresentationData is used for the presentation data.
+ */
 OString InsertOLE1Header(SvStream& rOle2, SvStream& rOle1, sal_uInt32& nWidth, sal_uInt32& nHeight,
-                         SwOLENode& rOLENode, const sal_uInt8* /*pPresentationData*/,
-                         sal_uInt64 /*nPresentationData*/)
+                         SwOLENode& rOLENode, const sal_uInt8* pPresentationData,
+                         sal_uInt64 nPresentationData)
 {
     rOle2.Seek(0);
     tools::SvRef<SotStorage> xStorage(new SotStorage(rOle2));
@@ -278,33 +283,41 @@ OString InsertOLE1Header(SvStream& rOle2, SvStream& rOle1, sal_uInt32& nWidth, s
 
     // Write Presentation.
     SvMemoryStream aPresentationData;
+    // OLEVersion.
+    rOle1.WriteUInt32(0x00000501);
+    // FormatID: constant means the ClassName field is present.
+    rOle1.WriteUInt32(0x00000005);
+    // ClassName: null terminated pascal string.
+    OString aPresentationClassName("METAFILEPICT");
+    rOle1.WriteUInt32(aPresentationClassName.getLength() + 1);
+    rOle1.WriteOString(aPresentationClassName);
+    rOle1.WriteChar(0);
+    const sal_uInt8* pBytes = nullptr;
+    sal_uInt64 nBytes = 0;
     if (ParseOLE2Presentation(rOle2, nWidth, nHeight, aPresentationData))
     {
         // Take presentation data for OLE1 from OLE2.
-        // OLEVersion.
-        rOle1.WriteUInt32(0x00000501);
-        // FormatID: constant means the ClassName field is present.
-        rOle1.WriteUInt32(0x00000005);
-        // ClassName: null terminated pascal string.
-        OString aPresentationClassName("METAFILEPICT");
-        rOle1.WriteUInt32(aPresentationClassName.getLength() + 1);
-        rOle1.WriteOString(aPresentationClassName);
-        rOle1.WriteChar(0);
-        // Width.
-        rOle1.WriteUInt32(nWidth);
-        // Height.
-        rOle1.WriteUInt32(nHeight * -1);
-        // PresentationDataSize
-        sal_uInt32 nPresentationData = aPresentationData.Tell();
-        rOle1.WriteUInt32(8 + nPresentationData);
-        // Reserved1-4.
-        rOle1.WriteUInt16(0x0008);
-        rOle1.WriteUInt16(0x31b1);
-        rOle1.WriteUInt16(0x1dd9);
-        rOle1.WriteUInt16(0x0000);
-        aPresentationData.Seek(0);
-        rOle1.WriteStream(aPresentationData, nPresentationData);
+        pBytes = static_cast<const sal_uInt8*>(aPresentationData.GetData());
+        nBytes = aPresentationData.Tell();
+    }
+    else
+    {
+        // Take presentation data for OLE1 from RTF.
+        pBytes = pPresentationData;
+        nBytes = nPresentationData;
     }
+    // Width.
+    rOle1.WriteUInt32(nWidth);
+    // Height.
+    rOle1.WriteUInt32(nHeight * -1);
+    // PresentationDataSize
+    rOle1.WriteUInt32(8 + nPresentationData);
+    // Reserved1-4.
+    rOle1.WriteUInt16(0x0008);
+    rOle1.WriteUInt16(0x31b1);
+    rOle1.WriteUInt16(0x1dd9);
+    rOle1.WriteUInt16(0x0000);
+    rOle1.WriteBytes(pBytes, nBytes);
 
     return aClassName;
 }


More information about the Libreoffice-commits mailing list