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 <vmik...@collabora.com>
AuthorDate: Wed Sep 2 17:44:19 2020 +0200
Commit:     Miklos Vajna <vmik...@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 <vmik...@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;
 }
_______________________________________________
Libreoffice-commits mailing list
libreoffice-comm...@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/libreoffice-commits

Reply via email to