[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