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

Miklos Vajna vmiklos at collabora.co.uk
Tue Mar 27 08:22:39 UTC 2018


 sw/CppunitTest_sw_htmlexport.mk           |   40 ---------
 sw/qa/extras/htmlexport/htmlexport.cxx    |   22 +++++
 sw/qa/extras/htmlimport/htmlimport.cxx    |   17 ---
 sw/source/filter/html/htmlplug.cxx        |   45 +++++++---
 sw/source/filter/html/htmlreqifreader.cxx |  130 +++++++++++++++++++++++++++++-
 sw/source/filter/html/htmlreqifreader.hxx |    3 
 6 files changed, 189 insertions(+), 68 deletions(-)

New commits:
commit 04630f26d06c4d3ec22b2a8b97e6a5e69cc70d5e
Author: Miklos Vajna <vmiklos at collabora.co.uk>
Date:   Mon Mar 26 16:25:06 2018 +0200

    sw XHTML export: support OLE2-in-RTF objects
    
    Need to repeat what OLE1 calls the class name in the OLE1 and RTF
    wrappers, so parse our own OLE2 data during export.
    
    Change-Id: Ic14fa648d1f78c29579bd9ba49ce6f491d4541b5
    Reviewed-on: https://gerrit.libreoffice.org/51906
    Reviewed-by: Miklos Vajna <vmiklos at collabora.co.uk>
    Tested-by: Jenkins <ci at libreoffice.org>

diff --git a/sw/CppunitTest_sw_htmlexport.mk b/sw/CppunitTest_sw_htmlexport.mk
index 850c105efd13..4dabbe61606f 100644
--- a/sw/CppunitTest_sw_htmlexport.mk
+++ b/sw/CppunitTest_sw_htmlexport.mk
@@ -48,45 +48,7 @@ $(eval $(call gb_CppunitTest_use_sdk_api,sw_htmlexport))
 $(eval $(call gb_CppunitTest_use_ure,sw_htmlexport))
 $(eval $(call gb_CppunitTest_use_vcl,sw_htmlexport))
 
-$(eval $(call gb_CppunitTest_use_components,sw_htmlexport,\
-	basic/util/sb \
-	canvas/source/factory/canvasfactory \
-    comphelper/util/comphelp \
-    configmgr/source/configmgr \
-    dbaccess/util/dba \
-    embeddedobj/util/embobj \
-    emfio/emfio \
-    filter/source/config/cache/filterconfig1 \
-    filter/source/storagefilterdetect/storagefd \
-    filter/source/textfilterdetect/textfd \
-	forms/util/frm \
-    framework/util/fwk \
-    i18npool/util/i18npool \
-    linguistic/source/lng \
-    oox/util/oox \
-    package/source/xstor/xstor \
-	sc/util/sc \
-	sc/util/scfilt \
-    package/util/package2 \
-    sax/source/expatwrap/expwrap \
-    sw/util/sw \
-    sw/util/swd \
-    sw/util/msword \
-    sfx2/util/sfx \
-    starmath/util/sm \
-    svgio/svgio \
-    svl/source/fsstor/fsstorage \
-    svtools/util/svt \
-    toolkit/util/tk \
-    ucb/source/core/ucb1 \
-    ucb/source/ucp/file/ucpfile1 \
-    unotools/util/utl \
-    unoxml/source/service/unoxml \
-    uui/util/uui \
-    vcl/vcl.common \
-    writerfilter/util/writerfilter \
-    xmloff/util/xo \
-))
+$(eval $(call gb_CppunitTest_use_rdb,sw_htmlexport,services))
 
 $(eval $(call gb_CppunitTest_use_configuration,sw_htmlexport))
 
diff --git a/sw/qa/extras/htmlimport/data/ole2.ole b/sw/qa/extras/htmlexport/data/ole2.ole
similarity index 100%
rename from sw/qa/extras/htmlimport/data/ole2.ole
rename to sw/qa/extras/htmlexport/data/ole2.ole
diff --git a/sw/qa/extras/htmlimport/data/ole2.png b/sw/qa/extras/htmlexport/data/ole2.png
similarity index 100%
rename from sw/qa/extras/htmlimport/data/ole2.png
rename to sw/qa/extras/htmlexport/data/ole2.png
diff --git a/sw/qa/extras/htmlimport/data/reqif-ole2.xhtml b/sw/qa/extras/htmlexport/data/reqif-ole2.xhtml
similarity index 100%
rename from sw/qa/extras/htmlimport/data/reqif-ole2.xhtml
rename to sw/qa/extras/htmlexport/data/reqif-ole2.xhtml
diff --git a/sw/qa/extras/htmlexport/htmlexport.cxx b/sw/qa/extras/htmlexport/htmlexport.cxx
index 44d4c11d5728..7f10e4befa90 100644
--- a/sw/qa/extras/htmlexport/htmlexport.cxx
+++ b/sw/qa/extras/htmlexport/htmlexport.cxx
@@ -14,6 +14,8 @@
 #include <com/sun/star/drawing/FillStyle.hpp>
 #include <com/sun/star/document/XEmbeddedObjectSupplier2.hpp>
 #include <com/sun/star/embed/ElementModes.hpp>
+#include <com/sun/star/io/XActiveDataStreamer.hpp>
+#include <com/sun/star/io/XSeekable.hpp>
 #include <rtl/byteseq.hxx>
 
 #include <swmodule.hxx>
@@ -476,6 +478,26 @@ DECLARE_HTMLEXPORT_TEST(testReqIfTable, "reqif-table.xhtml")
     assertXPathNoAttribute(pDoc, "/html/body/div/table/tr/th", "bgcolor");
 }
 
+DECLARE_HTMLEXPORT_ROUNDTRIP_TEST(testReqIfOle2, "reqif-ole2.xhtml")
+{
+    uno::Reference<text::XTextEmbeddedObjectsSupplier> xSupplier(mxComponent, uno::UNO_QUERY);
+    uno::Reference<container::XIndexAccess> xObjects(xSupplier->getEmbeddedObjects(),
+                                                     uno::UNO_QUERY);
+    uno::Reference<document::XEmbeddedObjectSupplier2> xObject(xObjects->getByIndex(0),
+                                                               uno::UNO_QUERY);
+    uno::Reference<io::XActiveDataStreamer> xEmbeddedObject(xObject->getExtendedControlOverEmbeddedObject(), uno::UNO_QUERY);
+    // This failed, the "RTF fragment" native data was loaded as-is, we had no
+    // filter to handle it, so nothing happened on double-click.
+    CPPUNIT_ASSERT(xEmbeddedObject.is());
+    uno::Reference<io::XSeekable> xStream(xEmbeddedObject->getStream(), uno::UNO_QUERY);
+    // This was 80913, the RTF hexdump -> OLE1 binary -> OLE2 conversion was
+    // missing.
+    CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int64>(38912), xStream->getLength());
+    // Finally the export also failed as it tried to open the stream from the
+    // document storage, but the embedded object already opened it, so an
+    // exception of type com.sun.star.io.IOException was thrown.
+}
+
 CPPUNIT_PLUGIN_IMPLEMENT();
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/qa/extras/htmlimport/htmlimport.cxx b/sw/qa/extras/htmlimport/htmlimport.cxx
index da9270701ce6..5a45e92d3698 100644
--- a/sw/qa/extras/htmlimport/htmlimport.cxx
+++ b/sw/qa/extras/htmlimport/htmlimport.cxx
@@ -297,23 +297,6 @@ DECLARE_HTMLIMPORT_TEST(testReqIfBr, "reqif-br.xhtml")
     CPPUNIT_ASSERT(getParagraph(1)->getString().startsWith("aaa\nbbb"));
 }
 
-DECLARE_HTMLIMPORT_TEST(testReqIfOle2, "reqif-ole2.xhtml")
-{
-    uno::Reference<text::XTextEmbeddedObjectsSupplier> xSupplier(mxComponent, uno::UNO_QUERY);
-    uno::Reference<container::XIndexAccess> xObjects(xSupplier->getEmbeddedObjects(),
-                                                     uno::UNO_QUERY);
-    uno::Reference<document::XEmbeddedObjectSupplier2> xObject(xObjects->getByIndex(0),
-                                                               uno::UNO_QUERY);
-    uno::Reference<io::XActiveDataStreamer> xEmbeddedObject(xObject->getExtendedControlOverEmbeddedObject(), uno::UNO_QUERY);
-    // This failed, the "RTF fragment" native data was loaded as-is, we had no
-    // filter to handle it, so nothing happened on double-click.
-    CPPUNIT_ASSERT(xEmbeddedObject.is());
-    uno::Reference<io::XSeekable> xStream(xEmbeddedObject->getStream(), uno::UNO_QUERY);
-    // This was 80913, the RTF hexdump -> OLE1 binary -> OLE2 conversion was
-    // missing.
-    CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int64>(38912), xStream->getLength());
-}
-
 CPPUNIT_PLUGIN_IMPLEMENT();
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/html/htmlplug.cxx b/sw/source/filter/html/htmlplug.cxx
index 1f153c424ba7..296b68a3b901 100644
--- a/sw/source/filter/html/htmlplug.cxx
+++ b/sw/source/filter/html/htmlplug.cxx
@@ -61,12 +61,14 @@
 #include <com/sun/star/beans/XPropertySet.hpp>
 #include <com/sun/star/frame/XStorable.hpp>
 #include <com/sun/star/embed/ElementModes.hpp>
+#include <com/sun/star/io/XActiveDataStreamer.hpp>
 
 #include <comphelper/embeddedobjectcontainer.hxx>
 #include <comphelper/classids.hxx>
 #include <rtl/uri.hxx>
 #include <comphelper/storagehelper.hxx>
 #include <vcl/graphicfilter.hxx>
+#include <unotools/ucbstreamhelper.hxx>
 
 using namespace com::sun::star;
 
@@ -1445,19 +1447,40 @@ Writer& OutHTML_FrameFormatOLENodeGrf( Writer& rWrt, const SwFrameFormat& rFrame
         aFileName = aURL.GetMainURL(INetURLObject::DecodeMechanism::NONE);
 
         // Write the data.
-        OUString aStreamName = pOLENd->GetOLEObj().GetCurrentPersistName();
-        uno::Reference<embed::XStorage> xStorage = pDocSh->GetStorage();
-        uno::Reference<io::XStream> xInStream
-            = xStorage->openStreamElement(aStreamName, embed::ElementModes::READ);
+        SwOLEObj& rOLEObj = pOLENd->GetOLEObj();
+        uno::Reference<embed::XEmbeddedObject> xEmbeddedObject = rOLEObj.GetOleRef();
+        OUString aFileType;
         SvFileStream aOutStream(aFileName, StreamMode::WRITE);
-        uno::Reference<io::XStream> xOutStream(new utl::OStreamWrapper(aOutStream));
-        comphelper::OStorageHelper::CopyInputToOutput(xInStream->getInputStream(),
-                                                      xOutStream->getOutputStream());
+        uno::Reference<io::XActiveDataStreamer> xStreamProvider;
+        if (xEmbeddedObject.is())
+            xStreamProvider.set(xEmbeddedObject, uno::UNO_QUERY);
+        if (xStreamProvider.is())
+        {
+            uno::Reference<io::XInputStream> xStream(xStreamProvider->getStream(), uno::UNO_QUERY);
+            if (xStream.is())
+            {
+                std::unique_ptr<SvStream> pStream(utl::UcbStreamHelper::CreateStream(xStream));
+                if (SwReqIfReader::WrapOleInRtf(*pStream, aOutStream))
+                {
+                    // OLE2 is always wrapped in RTF.
+                    aFileType = "text/rtf";
+                }
+            }
+        }
+        else
+        {
+            OUString aStreamName = rOLEObj.GetCurrentPersistName();
+            uno::Reference<embed::XStorage> xStorage = pDocSh->GetStorage();
+            uno::Reference<io::XStream> xInStream
+                = xStorage->openStreamElement(aStreamName, embed::ElementModes::READ);
+            uno::Reference<io::XStream> xOutStream(new utl::OStreamWrapper(aOutStream));
+            comphelper::OStorageHelper::CopyInputToOutput(xInStream->getInputStream(),
+                                                          xOutStream->getOutputStream());
+            uno::Reference<beans::XPropertySet> xOutStreamProps(xInStream, uno::UNO_QUERY);
+            if (xOutStreamProps.is())
+                xOutStreamProps->getPropertyValue("MediaType") >>= aFileType;
+        }
         aFileName = URIHelper::simpleNormalizedMakeRelative(rWrt.GetBaseURL(), aFileName);
-        uno::Reference<beans::XPropertySet> xOutStreamProps(xInStream, uno::UNO_QUERY);
-        OUString aFileType;
-        if (xOutStreamProps.is())
-            xOutStreamProps->getPropertyValue("MediaType") >>= aFileType;
 
         // Refer to this data.
         if (rHTMLWrt.m_bLFPossible)
diff --git a/sw/source/filter/html/htmlreqifreader.cxx b/sw/source/filter/html/htmlreqifreader.cxx
index eef33d93b293..2a4519119e31 100644
--- a/sw/source/filter/html/htmlreqifreader.cxx
+++ b/sw/source/filter/html/htmlreqifreader.cxx
@@ -9,12 +9,15 @@
 
 #include "htmlreqifreader.hxx"
 
+#include <comphelper/scopeguard.hxx>
+#include <filter/msfilter/rtfutil.hxx>
 #include <rtl/character.hxx>
 #include <rtl/strbuf.hxx>
+#include <sot/storage.hxx>
 #include <svtools/parrtf.hxx>
+#include <svtools/rtfkeywd.hxx>
 #include <svtools/rtftoken.h>
 #include <tools/stream.hxx>
-#include <filter/msfilter/rtfutil.hxx>
 
 namespace
 {
@@ -57,6 +60,100 @@ bool ReqIfRtfReader::WriteObjectData(SvStream& rOLE)
 {
     return msfilter::rtfutil::ExtractOLE2FromObjdata(m_aHex.makeStringAndClear(), rOLE);
 }
+
+/// Looks up what OLE1 calls the ClassName, see [MS-OLEDS] 2.3.8 CompObjStream.
+OString ExtractOLEClassName(const tools::SvRef<SotStorage>& xStorage)
+{
+    OString aRet;
+
+    SotStorageStream* pCompObj = xStorage->OpenSotStream("\1CompObj");
+    if (!pCompObj)
+        return aRet;
+
+    pCompObj->Seek(0);
+    pCompObj->SeekRel(28); // Header
+    if (!pCompObj->good())
+        return aRet;
+
+    sal_uInt32 nData;
+    pCompObj->ReadUInt32(nData); // AnsiUserType
+    pCompObj->SeekRel(nData);
+    if (!pCompObj->good())
+        return aRet;
+
+    pCompObj->ReadUInt32(nData); // AnsiClipboardFormat
+    pCompObj->SeekRel(nData);
+    if (!pCompObj->good())
+        return aRet;
+
+    pCompObj->ReadUInt32(nData); // Reserved1
+    return read_uInt8s_ToOString(*pCompObj, nData);
+}
+
+/// Inserts an OLE1 header before an OLE2 storage.
+OString InsertOLE1Header(SvStream& rOle2, SvStream& rOle1)
+{
+    rOle2.Seek(0);
+    tools::SvRef<SotStorage> xStorage(new SotStorage(rOle2));
+    if (xStorage->GetError() != ERRCODE_NONE)
+        return OString();
+
+    OString aClassName = ExtractOLEClassName(xStorage);
+
+    // Write ObjectHeader, see [MS-OLEDS] 2.2.4.
+    rOle1.Seek(0);
+    // OLEVersion.
+    rOle1.WriteUInt32(0);
+
+    // FormatID is EmbeddedObject.
+    rOle1.WriteUInt32(0x00000002);
+
+    // ClassName
+    rOle1.WriteUInt32(aClassName.getLength());
+    rOle1.WriteOString(aClassName);
+    // Null terminated pascal string.
+    rOle1.WriteChar(0);
+
+    // TopicName.
+    rOle1.WriteUInt32(0);
+
+    // ItemName.
+    rOle1.WriteUInt32(0);
+
+    // NativeDataSize
+    rOle2.Seek(STREAM_SEEK_TO_END);
+    rOle1.WriteUInt32(rOle2.Tell());
+
+    // Write the actual native data.
+    rOle2.Seek(0);
+    rOle1.WriteStream(rOle2);
+
+    return aClassName;
+}
+
+/// Writes rData on rSteram as a hexdump.
+void WriteHex(SvStream& rStream, SvMemoryStream& rData)
+{
+    rData.Seek(0);
+    sal_uInt64 nSize = rData.remainingSize();
+
+    sal_uInt32 nLimit = 64;
+    sal_uInt32 nBreak = 0;
+
+    for (sal_uInt64 i = 0; i < nSize; i++)
+    {
+        OString sNo = OString::number(static_cast<const sal_uInt8*>(rData.GetBuffer())[i], 16);
+        if (sNo.getLength() < 2)
+            rStream.WriteChar('0');
+        rStream.WriteCharPtr(sNo.getStr());
+
+        if (++nBreak == nLimit)
+        {
+            rStream.WriteCharPtr(SAL_NEWLINE_STRING);
+            nBreak = 0;
+        }
+    }
+}
 }
 
 namespace SwReqIfReader
@@ -79,6 +176,37 @@ bool ExtractOleFromRtf(SvStream& rRtf, SvStream& rOle)
     // Write the OLE2 data.
     return xReader->WriteObjectData(rOle);
 }
+
+bool WrapOleInRtf(SvStream& rOle2, SvStream& rRtf)
+{
+    sal_uInt64 nPos = rOle2.Tell();
+    comphelper::ScopeGuard g([&rOle2, nPos] { rOle2.Seek(nPos); });
+
+    // Write OLE1 header, then the RTF wrapper.
+    SvMemoryStream aOLE1;
+    OString aClassName = InsertOLE1Header(rOle2, aOLE1);
+
+    // Start object.
+    rRtf.WriteCharPtr("{" OOO_STRING_SVTOOLS_RTF_OBJECT);
+    rRtf.WriteCharPtr(OOO_STRING_SVTOOLS_RTF_OBJEMB);
+
+    // Start objclass.
+    rRtf.WriteCharPtr("{" OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_OBJCLASS " ");
+    rRtf.WriteOString(aClassName);
+    // End objclass.
+    rRtf.WriteCharPtr("}");
+
+    // Start objdata.
+    rRtf.WriteCharPtr(
+        "{" OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_OBJDATA SAL_NEWLINE_STRING);
+    WriteHex(rRtf, aOLE1);
+    // End objdata.
+    rRtf.WriteCharPtr("}");
+    // End object.
+    rRtf.WriteCharPtr("}");
+
+    return true;
+}
 }
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/html/htmlreqifreader.hxx b/sw/source/filter/html/htmlreqifreader.hxx
index 97d391a8efb0..ad8399edbf0e 100644
--- a/sw/source/filter/html/htmlreqifreader.hxx
+++ b/sw/source/filter/html/htmlreqifreader.hxx
@@ -15,6 +15,9 @@ namespace SwReqIfReader
 {
 /// Extracts an OLE2 container binary from an RTF fragment.
 bool ExtractOleFromRtf(SvStream& rRtf, SvStream& rOle);
+
+/// Wraps an OLE2 container binary in an RTF fragment.
+bool WrapOleInRtf(SvStream& rOle, SvStream& rRtf);
 }
 
 #endif // INCLUDED_SW_SOURCE_FILTER_HTML_HTMLREQIFREADER_HXX


More information about the Libreoffice-commits mailing list