diff options
author | Miklos Vajna <vmiklos@collabora.co.uk> | 2018-03-26 16:25:06 +0200 |
---|---|---|
committer | Miklos Vajna <vmiklos@collabora.co.uk> | 2018-03-27 10:22:11 +0200 |
commit | 04630f26d06c4d3ec22b2a8b97e6a5e69cc70d5e (patch) | |
tree | fa348ff4dc796b32db0cac6c95f1b63e899952af | |
parent | c91f8e55fa9a335a4cc10f70e95e8a60a658c13b (diff) |
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@collabora.co.uk>
Tested-by: Jenkins <ci@libreoffice.org>
-rw-r--r-- | sw/CppunitTest_sw_htmlexport.mk | 40 | ||||
-rw-r--r-- | sw/qa/extras/htmlexport/data/ole2.ole (renamed from sw/qa/extras/htmlimport/data/ole2.ole) | 0 | ||||
-rw-r--r-- | sw/qa/extras/htmlexport/data/ole2.png (renamed from sw/qa/extras/htmlimport/data/ole2.png) | bin | 766 -> 766 bytes | |||
-rw-r--r-- | sw/qa/extras/htmlexport/data/reqif-ole2.xhtml (renamed from sw/qa/extras/htmlimport/data/reqif-ole2.xhtml) | 0 | ||||
-rw-r--r-- | sw/qa/extras/htmlexport/htmlexport.cxx | 22 | ||||
-rw-r--r-- | sw/qa/extras/htmlimport/htmlimport.cxx | 17 | ||||
-rw-r--r-- | sw/source/filter/html/htmlplug.cxx | 45 | ||||
-rw-r--r-- | sw/source/filter/html/htmlreqifreader.cxx | 130 | ||||
-rw-r--r-- | sw/source/filter/html/htmlreqifreader.hxx | 3 |
9 files changed, 189 insertions, 68 deletions
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 index 96407e88fa3d..96407e88fa3d 100644 --- a/sw/qa/extras/htmlimport/data/ole2.ole +++ b/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 Binary files differindex fdad35484e7c..fdad35484e7c 100644 --- a/sw/qa/extras/htmlimport/data/ole2.png +++ b/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 index 716ecd1bda63..716ecd1bda63 100644 --- a/sw/qa/extras/htmlimport/data/reqif-ole2.xhtml +++ b/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 |