summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMiklos Vajna <vmiklos@collabora.com>2020-09-02 17:44:19 +0200
committerMiklos Vajna <vmiklos@collabora.com>2020-09-03 17:07:51 +0200
commitaf09c54be6fd03fd362715bc69d025a4561e228f (patch)
tree2292732cf75a491ee6f69b5e363718c0bc1d895f
parente0ca5569833664fa32aa6519032fbcaa0b16b14d (diff)
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. (cherry picked from commit 0d027abbc5609b096d2a954e77aa7354a55928ab) Conflicts: sw/source/filter/html/htmlreqifreader.cxx Change-Id: I158db1c87044a3895d0c64a6e5a5384686627d96
-rw-r--r--sw/qa/extras/htmlexport/data/no-ole2-pres-data.odtbin0 -> 25483 bytes
-rw-r--r--sw/qa/extras/htmlexport/htmlexport.cxx74
-rw-r--r--sw/source/filter/html/htmlreqifreader.cxx65
3 files changed, 108 insertions, 31 deletions
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
--- /dev/null
+++ b/sw/qa/extras/htmlexport/data/no-ole2-pres-data.odt
Binary files differ
diff --git a/sw/qa/extras/htmlexport/htmlexport.cxx b/sw/qa/extras/htmlexport/htmlexport.cxx
index 8119e509b8b0..158399287ce0 100644
--- a/sw/qa/extras/htmlexport/htmlexport.cxx
+++ b/sw/qa/extras/htmlexport/htmlexport.cxx
@@ -66,6 +66,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
{
@@ -1019,12 +1029,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;
@@ -1159,6 +1165,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);
+ xmlDocPtr 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 30e6bc223e17..69a538cb4d88 100644
--- a/sw/source/filter/html/htmlreqifreader.cxx
+++ b/sw/source/filter/html/htmlreqifreader.cxx
@@ -233,10 +233,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));
@@ -283,33 +288,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);
+ 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;
}