diff options
-rw-r--r-- | sw/qa/extras/htmlexport/data/no-ole2-pres-data.odt | bin | 0 -> 25483 bytes | |||
-rw-r--r-- | sw/qa/extras/htmlexport/htmlexport.cxx | 74 | ||||
-rw-r--r-- | sw/source/filter/html/htmlreqifreader.cxx | 65 |
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 Binary files differnew file mode 100644 index 000000000000..cd65a1755746 --- /dev/null +++ b/sw/qa/extras/htmlexport/data/no-ole2-pres-data.odt 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; } |