Kaydet (Commit) 04630f26 authored tarafından Miklos Vajna's avatar Miklos Vajna

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/51906Reviewed-by: 's avatarMiklos Vajna <vmiklos@collabora.co.uk>
Tested-by: 's avatarJenkins <ci@libreoffice.org>
üst c91f8e55
......@@ -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))
......
......@@ -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: */
......@@ -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: */
......@@ -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)
......
......@@ -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: */
......@@ -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
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment