summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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;
}