diff --git a/sw/CppunitTest_sw_htmlexport2.mk b/sw/CppunitTest_sw_htmlexport2.mk
new file mode 100644
index 000000000000..4d2cb5884034
--- /dev/null
+++ b/sw/CppunitTest_sw_htmlexport2.mk
@@ -0,0 +1,80 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#*************************************************************************
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+#*************************************************************************
+
+$(eval $(call gb_CppunitTest_CppunitTest,sw_htmlexport2))
+
+$(eval $(call gb_CppunitTest_use_common_precompiled_header,sw_htmlexport2))
+
+$(eval $(call gb_CppunitTest_add_exception_objects,sw_htmlexport2, \
+ sw/qa/extras/htmlexport/htmlexport2 \
+))
+
+$(eval $(call gb_CppunitTest_use_libraries,sw_htmlexport2, \
+ comphelper \
+ cppu \
+ cppuhelper \
+ editeng \
+ i18nlangtag \
+ msfilter \
+ sal \
+ sfx \
+ subsequenttest \
+ sot \
+ sw \
+ swqahelper \
+ svl \
+ svt \
+ test \
+ tl \
+ unotest \
+ utl \
+ vcl \
+))
+
+$(eval $(call gb_CppunitTest_use_externals,sw_htmlexport2,\
+ boost_headers \
+ libxml2 \
+))
+
+$(eval $(call gb_CppunitTest_set_include,sw_htmlexport2,\
+ -I$(SRCDIR)/sw/inc \
+ -I$(SRCDIR)/sw/source/core/inc \
+ -I$(SRCDIR)/sw/source/uibase/inc \
+ -I$(SRCDIR)/sw/qa/inc \
+ $$(INCLUDE) \
+))
+
+$(eval $(call gb_CppunitTest_use_api,sw_htmlexport2,\
+ udkapi \
+ offapi \
+ oovbaapi \
+))
+
+$(eval $(call gb_CppunitTest_use_ure,sw_htmlexport2))
+$(eval $(call gb_CppunitTest_use_vcl,sw_htmlexport2))
+
+$(eval $(call gb_CppunitTest_use_custom_headers,sw_htmlexport2,\
+ officecfg/registry \
+))
+
+$(eval $(call gb_CppunitTest_use_rdb,sw_htmlexport2,services))
+
+$(eval $(call gb_CppunitTest_use_configuration,sw_htmlexport2))
+
+ifeq ($(OS),WNT)
+# Initializing DocumentSignatureManager will require gpgme-w32spawn.exe in workdir/LinkTarget/Executable
+# In fact, it is not even required to complete test successfully, but the dialog would stop execution
+$(eval $(call gb_CppunitTest_use_packages,sw_htmlexport2,\
+ $(call gb_Helper_optional,GPGMEPP,gpgmepp)\
+))
+endif
+
+# vim: set noet sw=4 ts=4:
diff --git a/sw/Module_sw.mk b/sw/Module_sw.mk
index 6b53fbbbde2b..6d4ed9f55967 100644
--- a/sw/Module_sw.mk
+++ b/sw/Module_sw.mk
@@ -73,6 +73,7 @@ $(eval $(call gb_Module_add_slowcheck_targets,sw,\
CppunitTest_sw_docbookexport \
CppunitTest_sw_fodfexport \
CppunitTest_sw_htmlexport \
+ CppunitTest_sw_htmlexport2 \
CppunitTest_sw_xhtmlexport \
CppunitTest_sw_htmlimport \
CppunitTest_sw_indexingexport \
diff --git a/sw/qa/extras/htmlexport/htmlexport.cxx b/sw/qa/extras/htmlexport/htmlexport.cxx
index 44eca01a2b13..a20b11ec41ef 100644
--- a/sw/qa/extras/htmlexport/htmlexport.cxx
+++ b/sw/qa/extras/htmlexport/htmlexport.cxx
@@ -7,275 +7,39 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
-#include
+#include "htmlmodeltestbase.hxx"
#include
-#include
#include
-#include
-#include
#include
#include
#include
#include
-#include
#include
#include
#include
-#include
#include
-#include
-#include
-
-#include
-#include
#include
-#include
#include
#include
-#include
-#include
-#include
-#include
#include
#include
-#include
-#include
#include
#include
-#include
#include
-#include
-#include
#include
#include
#include
#include
#include
-#include
#include
#include
-#include
-#include
namespace
{
-/// Test RTF parser that just extracts a single OLE2 object from a file.
-class TestReqIfRtfReader : public SvRTFParser
-{
-public:
- TestReqIfRtfReader(SvStream& rStream);
- void NextToken(int nToken) override;
- bool WriteObjectData(SvStream& rOLE);
- tools::Long GetObjw() const { return m_nObjw; }
- tools::Long GetObjh() const { return m_nObjh; }
- int getWmetafile() const { return m_nWmetafile; }
-
-private:
- bool m_bInObjData = false;
- OStringBuffer m_aHex;
- tools::Long m_nObjw = 0;
- tools::Long m_nObjh = 0;
- int m_nWmetafile = 0;
-};
-
-TestReqIfRtfReader::TestReqIfRtfReader(SvStream& rStream)
- : SvRTFParser(rStream)
-{
-}
-
-void TestReqIfRtfReader::NextToken(int nToken)
-{
- switch (nToken)
- {
- case '}':
- m_bInObjData = false;
- break;
- case RTF_TEXTTOKEN:
- if (m_bInObjData)
- m_aHex.append(OUStringToOString(aToken, RTL_TEXTENCODING_ASCII_US));
- break;
- case RTF_OBJDATA:
- m_bInObjData = true;
- break;
- case RTF_OBJW:
- m_nObjw = nTokenValue;
- break;
- case RTF_OBJH:
- m_nObjh = nTokenValue;
- break;
- case RTF_WMETAFILE:
- m_nWmetafile = nTokenValue;
- break;
- }
-}
-
-bool TestReqIfRtfReader::WriteObjectData(SvStream& rOLE)
-{
- OString aObjdata = m_aHex.makeStringAndClear();
-
- SvMemoryStream aStream;
- int b = 0;
- int count = 2;
-
- // Feed the destination text to a stream.
- for (int i = 0; i < aObjdata.getLength(); ++i)
- {
- char ch = aObjdata[i];
- if (ch != 0x0d && ch != 0x0a)
- {
- b = b << 4;
- sal_Int8 parsed = msfilter::rtfutil::AsHex(ch);
- if (parsed == -1)
- return false;
- b += parsed;
- count--;
- if (!count)
- {
- aStream.WriteChar(b);
- count = 2;
- b = 0;
- }
- }
- }
-
- aStream.Seek(0);
- rOLE.WriteStream(aStream);
- return true;
-}
-
-/// Parser for [MS-OLEDS] 2.2.5 EmbeddedObject, aka OLE1.
-struct OLE1Reader
-{
- sal_uInt32 m_nNativeDataSize;
- std::vector m_aNativeData;
- sal_uInt32 m_nPresentationDataSize;
-
- OLE1Reader(SvStream& rStream);
-};
-
-OLE1Reader::OLE1Reader(SvStream& rStream)
-{
- // Skip ObjectHeader, see [MS-OLEDS] 2.2.4.
- rStream.Seek(0);
- CPPUNIT_ASSERT(rStream.remainingSize());
- sal_uInt32 nData;
- rStream.ReadUInt32(nData); // OLEVersion
- rStream.ReadUInt32(nData); // FormatID
- rStream.ReadUInt32(nData); // ClassName
- rStream.SeekRel(nData);
- rStream.ReadUInt32(nData); // TopicName
- rStream.SeekRel(nData);
- rStream.ReadUInt32(nData); // ItemName
- rStream.SeekRel(nData);
-
- rStream.ReadUInt32(m_nNativeDataSize);
- m_aNativeData.resize(m_nNativeDataSize);
- rStream.ReadBytes(m_aNativeData.data(), m_aNativeData.size());
-
- rStream.ReadUInt32(nData); // OLEVersion for presentation data
- CPPUNIT_ASSERT(rStream.good());
- rStream.ReadUInt32(nData); // FormatID
- rStream.ReadUInt32(nData); // ClassName
- rStream.SeekRel(nData);
- rStream.ReadUInt32(nData); // Width
- rStream.ReadUInt32(nData); // Height
- rStream.ReadUInt32(nData); // PresentationDataSize
- m_nPresentationDataSize = nData;
-}
-
-/// Covers sw/source/filter/html/wrthtml.cxx and related fixes.
-class HtmlExportTest : public SwModelTestBase, public HtmlTestTools
-{
-public:
- HtmlExportTest()
- : SwModelTestBase(u"/sw/qa/extras/htmlexport/data/"_ustr, u"HTML (StarWriter)"_ustr)
- {
- }
-
- /// 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);
- }
-};
-
-/// HTML export of the sw doc model tests.
-class SwHtmlDomExportTest : public SwModelTestBase, public HtmlTestTools
-{
-public:
- SwHtmlDomExportTest()
- : SwModelTestBase(u"/sw/qa/extras/htmlexport/data/"_ustr)
- {
- }
-
- OUString GetObjectPath(const OUString& ext);
- /// Get the .ole path, assuming maTempFile is an XHTML export result.
- OUString GetOlePath() { return GetObjectPath(u".ole"_ustr); }
- OUString GetPngPath() { return GetObjectPath(u".png"_ustr); }
- /// Parse the ole1 data out of an RTF fragment URL.
- void ParseOle1FromRtfUrl(const OUString& rRtfUrl, SvMemoryStream& rOle1);
- /// Export using the C++ HTML export filter, with xhtmlns=reqif-xhtml.
- void ExportToReqif();
- /// Import using the C++ HTML import filter, with xhtmlns=reqif-xhtml.
- void ImportFromReqif(const OUString& rUrl);
- /// Export using the C++ HTML export filter
- void ExportToHTML();
-};
-
-OUString SwHtmlDomExportTest::GetObjectPath(const OUString& ext)
-{
- assert(ext.startsWith("."));
- xmlDocUniquePtr pDoc = WrapReqifFromTempFile();
- OUString aOlePath = getXPath(
- pDoc, "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:p/reqif-xhtml:object", "data");
- CPPUNIT_ASSERT(aOlePath.endsWith(ext));
- INetURLObject aUrl(maTempFile.GetURL());
- aUrl.setBase(aOlePath.subView(0, aOlePath.getLength() - ext.getLength()));
- aUrl.setExtension(ext.subView(1));
- return aUrl.GetMainURL(INetURLObject::DecodeMechanism::NONE);
-}
-
-void SwHtmlDomExportTest::ParseOle1FromRtfUrl(const OUString& rRtfUrl, SvMemoryStream& rOle1)
-{
- SvMemoryStream aRtf;
- HtmlExportTest::wrapRtfFragment(rRtfUrl, aRtf);
- tools::SvRef xReader(new TestReqIfRtfReader(aRtf));
- CPPUNIT_ASSERT(xReader->CallParser() != SvParserState::Error);
- CPPUNIT_ASSERT(xReader->WriteObjectData(rOle1));
- CPPUNIT_ASSERT(rOle1.Tell());
-}
-
-void SwHtmlDomExportTest::ExportToReqif()
-{
- setFilterOptions(u"xhtmlns=reqif-xhtml"_ustr);
- save(u"HTML (StarWriter)"_ustr);
-}
-
-void SwHtmlDomExportTest::ExportToHTML()
-{
- uno::Sequence aStoreProperties = {
- comphelper::makePropertyValue(u"FilterName"_ustr, u"HTML (StarWriter)"_ustr),
- };
- saveWithParams(aStoreProperties);
-}
-
-void SwHtmlDomExportTest::ImportFromReqif(const OUString& rUrl)
-{
- uno::Sequence aLoadProperties = {
- comphelper::makePropertyValue(u"FilterName"_ustr, u"HTML (StarWriter)"_ustr),
- comphelper::makePropertyValue(u"FilterOptions"_ustr, u"xhtmlns=reqif-xhtml"_ustr),
- };
- loadWithParams(rUrl, aLoadProperties);
-}
-
CPPUNIT_TEST_FIXTURE(HtmlExportTest, testFdo81276)
{
auto verify = [this]() {
@@ -1817,1581 +1581,6 @@ CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, testReqifEmbedJPGShapeDirectly)
assertXPath(pXmlDoc, "//reqif-xhtml:p/reqif-xhtml:object/reqif-xhtml:object", "type",
u"image/png");
}
-
-CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, testReqifEmbedPNGShapeAsOLE)
-{
- // Given a document with an image shape:
- createSwDoc();
- uno::Reference xFactory(mxComponent, uno::UNO_QUERY);
- uno::Reference xShape(
- xFactory->createInstance(u"com.sun.star.drawing.GraphicObjectShape"_ustr), uno::UNO_QUERY);
- xShape->setSize(awt::Size(10000, 10000));
- uno::Reference xShapeProps(xShape, uno::UNO_QUERY);
- xShapeProps->setPropertyValue(u"GraphicURL"_ustr, uno::Any(createFileURL(u"ole2.png")));
- uno::Reference xDrawPageSupplier(mxComponent, uno::UNO_QUERY);
- xDrawPageSupplier->getDrawPage()->add(xShape);
-
- // When exporting to XHTML:
- uno::Sequence aStoreProperties = {
- comphelper::makePropertyValue(u"FilterName"_ustr, u"HTML (StarWriter)"_ustr),
- comphelper::makePropertyValue(u"FilterOptions"_ustr, u"xhtmlns=reqif-xhtml"_ustr),
- comphelper::makePropertyValue(u"ExportImagesAsOLE"_ustr, true),
- };
- saveWithParams(aStoreProperties);
-
- // Then make sure the PNG is embedded with an RTF wrapper:
- xmlDocUniquePtr pXmlDoc = WrapReqifFromTempFile();
- // Without the accompanying fix in place, this test would have failed with:
- // - Expected: text/rtf
- // - Actual : image/png
- // i.e. the OLE wrapper around the PNG was missing.
- assertXPath(pXmlDoc, "//reqif-xhtml:p/reqif-xhtml:object", "type", u"text/rtf");
-}
-
-CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, testReqifEmbedShapeAsPNG)
-{
- // FIXME: the DPI check should be removed when either (1) the test is fixed to work with
- // non-default DPI; or (2) unit tests on Windows are made to use svp VCL plugin.
- if (!IsDefaultDPI())
- return;
- // Given a document with a shape:
- createSwDoc();
- uno::Reference xFactory(mxComponent, uno::UNO_QUERY);
- uno::Reference xShape(
- xFactory->createInstance(u"com.sun.star.drawing.RectangleShape"_ustr), uno::UNO_QUERY);
- xShape->setSize(awt::Size(10000, 10000));
- uno::Reference xDrawPageSupplier(mxComponent, uno::UNO_QUERY);
- xDrawPageSupplier->getDrawPage()->add(xShape);
-
- // When exporting to XHTML:
- ExportToReqif();
-
- // Then make sure the shape is embedded as a PNG:
- xmlDocUniquePtr pXmlDoc = WrapReqifFromTempFile();
- // Without the accompanying fix in place, this test would have failed with:
- // - Expected: image/png
- // - Actual : image/x-vclgraphic
- // i.e. the result was invalid ReqIF.
- assertXPath(pXmlDoc, "//reqif-xhtml:p/reqif-xhtml:object", "type", u"image/png");
-
- // Then check the pixel size of the shape:
- Size aPixelSize(Application::GetDefaultDevice()->LogicToPixel(Size(10000, 10000),
- MapMode(MapUnit::Map100thMM)));
- // Without the accompanying fix in place, this test would have failed with:
- // - no attribute 'width' exist
- // i.e. shapes had no width.
- assertXPath(pXmlDoc, "//reqif-xhtml:p/reqif-xhtml:object", "width",
- OUString::number(aPixelSize.getWidth()));
-}
-
-CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, testShapeAsImageHtml)
-{
- // Given a document with a shape:
- createSwDoc();
- uno::Reference xFactory(mxComponent, uno::UNO_QUERY);
- uno::Reference xShape(
- xFactory->createInstance(u"com.sun.star.drawing.RectangleShape"_ustr), uno::UNO_QUERY);
- xShape->setSize(awt::Size(5080, 2540));
- uno::Reference xDrawPageSupplier(mxComponent, uno::UNO_QUERY);
- xDrawPageSupplier->getDrawPage()->add(xShape);
-
- // When exporting to plain HTML:
- saveAndReload(u"HTML (StarWriter)"_ustr);
-
- // Without the accompanying fix in place, this test would have failed with:
- // - Expected:
- // - Actual : />
- // i.e. the output was not well-formed.
- CPPUNIT_ASSERT_EQUAL(u" "_ustr, getParagraph(1)->getString());
-}
-
-CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, testJson)
-{
- // Given a document with a shape:
- createSwDoc();
- uno::Reference xFactory(mxComponent, uno::UNO_QUERY);
- uno::Reference xShape(
- xFactory->createInstance(u"com.sun.star.drawing.RectangleShape"_ustr), uno::UNO_QUERY);
- xShape->setSize(awt::Size(2540, 2540));
- uno::Reference xDrawPageSupplier(mxComponent, uno::UNO_QUERY);
- xDrawPageSupplier->getDrawPage()->add(xShape);
-
- // When exporting to HTML, and specifying options as JSON:
- setFilterOptions(u"{\"XhtmlNs\":{\"type\":\"string\", \"value\":\"reqif-xhtml\"},"
- "\"ShapeDPI\":{\"type\":\"long\",\"value\":\"192\"}}"_ustr);
- save(u"HTML (StarWriter)"_ustr);
-
- // Then make sure those options are not ignored:
- // Without the accompanying fix in place, this test would have failed, as GetPngPath() expects
- // XML output, but xhtmlns=reqif-xhtml was ignored.
- OUString aPngUrl = GetPngPath();
- SvFileStream aFileStream(aPngUrl, StreamMode::READ);
- GraphicDescriptor aDescriptor(aFileStream, nullptr);
- aDescriptor.Detect(/*bExtendedInfo=*/true);
- // Make sure that the increased DPI is taken into account:
- tools::Long nExpected = 192;
- CPPUNIT_ASSERT_EQUAL(nExpected, aDescriptor.GetSizePixel().getWidth());
-}
-
-CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, testReqifEmbedShapeAsPNGCustomDPI)
-{
- // FIXME: the DPI check should be removed when either (1) the test is fixed to work with
- // non-default DPI; or (2) unit tests on Windows are made to use svp VCL plugin.
- if (!IsDefaultDPI())
- return;
- // Given a document with a shape:
- createSwDoc();
- uno::Reference xFactory(mxComponent, uno::UNO_QUERY);
- uno::Reference xShape(
- xFactory->createInstance(u"com.sun.star.drawing.RectangleShape"_ustr), uno::UNO_QUERY);
- xShape->setSize(awt::Size(5080, 2540));
- uno::Reference xDrawPageSupplier(mxComponent, uno::UNO_QUERY);
- xDrawPageSupplier->getDrawPage()->add(xShape);
- sal_Int32 nDPI = 600;
-
- // When exporting to XHTML:
- uno::Sequence aStoreProperties = {
- comphelper::makePropertyValue(u"FilterName"_ustr, u"HTML (StarWriter)"_ustr),
- comphelper::makePropertyValue(u"FilterOptions"_ustr, u"xhtmlns=reqif-xhtml"_ustr),
- comphelper::makePropertyValue(u"ShapeDPI"_ustr, nDPI),
- };
- saveWithParams(aStoreProperties);
-
- // Then make sure the shape is embedded as a PNG:
- xmlDocUniquePtr pXmlDoc = WrapReqifFromTempFile();
- assertXPath(pXmlDoc, "//reqif-xhtml:p/reqif-xhtml:object", "type", u"image/png");
-
- // Then check the pixel size of the shape:
- Size aPixelSize(Application::GetDefaultDevice()->LogicToPixel(Size(5080, 2540),
- MapMode(MapUnit::Map100thMM)));
- tools::Long nPNGWidth = 1200;
- OUString aPngUrl = GetPngPath();
- SvFileStream aFileStream(aPngUrl, StreamMode::READ);
- GraphicDescriptor aDescriptor(aFileStream, nullptr);
- aDescriptor.Detect(/*bExtendedInfo=*/true);
- // Without the accompanying fix in place, this test would have failed with:
- // - Expected: 1200
- // - Actual : 1000
- // i.e. first setting a double DPI didn't result in larger pixel width of the PNG, then it was
- // limited to 1000 pixels (because the pixel limit was 500k).
- CPPUNIT_ASSERT_EQUAL(nPNGWidth, aDescriptor.GetSizePixel().getWidth());
-
- // Then make sure the shape's logic size (in CSS pixels) don't change:
- assertXPath(pXmlDoc, "//reqif-xhtml:p/reqif-xhtml:object", "width",
- OUString::number(aPixelSize.getWidth()));
-}
-
-CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, testReqifOleBmpTransparent)
-{
- // Given a document with a transparent image:
- createSwDoc();
- uno::Sequence aArgs = {
- comphelper::makePropertyValue(u"FileName"_ustr, createFileURL(u"transparent.png")),
- };
- dispatchCommand(mxComponent, u".uno:InsertGraphic"_ustr, aArgs);
-
- // When exporting to reqif with ExportImagesAsOLE=true:
- uno::Sequence aStoreProperties = {
- comphelper::makePropertyValue(u"FilterName"_ustr, u"HTML (StarWriter)"_ustr),
- comphelper::makePropertyValue(u"FilterOptions"_ustr, u"xhtmlns=reqif-xhtml"_ustr),
- comphelper::makePropertyValue(u"ExportImagesAsOLE"_ustr, true),
- };
- saveWithParams(aStoreProperties);
-
- // Then make sure the transparent pixel turns into white:
- SvMemoryStream aOle1;
- ParseOle1FromRtfUrl(GetOlePath(), aOle1);
- OLE1Reader aOle1Reader(aOle1);
- SvMemoryStream aBitmapStream(aOle1Reader.m_aNativeData.data(), aOle1Reader.m_aNativeData.size(),
- StreamMode::READ);
- Bitmap aBitmap;
- ReadDIB(aBitmap, aBitmapStream, /*bFileHeader=*/true);
- Size aBitmapSize = aBitmap.GetSizePixel();
- BitmapEx aBitmapEx(aBitmap);
- Color nActualColor
- = aBitmapEx.GetPixelColor(aBitmapSize.getWidth() - 1, aBitmapSize.getHeight() - 1);
- // Without the accompanying fix in place, this test would have failed with:
- // - Expected: Color: R:255 G:255 B:255 A:0
- // - Actual : Color: R:0 G:0 B:0 A:0
- // i.e. the bitmap without an alpha channel was black, not white.
- CPPUNIT_ASSERT_EQUAL(COL_WHITE, nActualColor);
-}
-
-CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, testListsHeading)
-{
- // Given a document with lh, lh, li, li, lh and lh nodes:
- createSwDoc();
- SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell();
- pWrtShell->Insert(u"list 1, header 1"_ustr);
- pWrtShell->SplitNode();
- pWrtShell->Insert(u"list 1, header 2"_ustr);
- pWrtShell->SplitNode();
- pWrtShell->Insert(u"list 2, item 1"_ustr);
- pWrtShell->SplitNode();
- pWrtShell->Insert(u"list 2, item 2"_ustr);
- pWrtShell->SplitNode();
- pWrtShell->Insert(u"list 3, header 1"_ustr);
- pWrtShell->SplitNode();
- pWrtShell->Insert(u"list 3, header 2"_ustr);
- SwDoc* pDoc = pWrtShell->GetDoc();
- pWrtShell->Up(false, 5);
- {
- sal_uInt16 nPos = pDoc->MakeNumRule(pDoc->GetUniqueNumRuleName());
- SwNumRule* pNumRule = pDoc->GetNumRuleTable()[nPos];
- {
- SwNode& rNode = pWrtShell->GetCursor()->GetPoint()->GetNode();
- SwTextNode& rTextNode = *rNode.GetTextNode();
- rTextNode.SetAttr(SwNumRuleItem(pNumRule->GetName()));
- rTextNode.SetCountedInList(false);
- }
- pWrtShell->Down(false, 1);
- {
- SwNode& rNode = pWrtShell->GetCursor()->GetPoint()->GetNode();
- SwTextNode& rTextNode = *rNode.GetTextNode();
- rTextNode.SetAttr(SwNumRuleItem(pNumRule->GetName()));
- rTextNode.SetCountedInList(false);
- }
- }
- pWrtShell->Down(false, 1);
- {
- sal_uInt16 nPos = pDoc->MakeNumRule(pDoc->GetUniqueNumRuleName());
- SwNumRule* pNumRule = pDoc->GetNumRuleTable()[nPos];
- {
- SwNode& rNode = pWrtShell->GetCursor()->GetPoint()->GetNode();
- SwTextNode& rTextNode = *rNode.GetTextNode();
- rTextNode.SetAttr(SwNumRuleItem(pNumRule->GetName()));
- }
- pWrtShell->Down(false, 1);
- {
- SwNode& rNode = pWrtShell->GetCursor()->GetPoint()->GetNode();
- SwTextNode& rTextNode = *rNode.GetTextNode();
- rTextNode.SetAttr(SwNumRuleItem(pNumRule->GetName()));
- }
- }
- pWrtShell->Down(false, 1);
- {
- sal_uInt16 nPos = pDoc->MakeNumRule(pDoc->GetUniqueNumRuleName());
- SwNumRule* pNumRule = pDoc->GetNumRuleTable()[nPos];
- {
- SwNode& rNode = pWrtShell->GetCursor()->GetPoint()->GetNode();
- SwTextNode& rTextNode = *rNode.GetTextNode();
- rTextNode.SetAttr(SwNumRuleItem(pNumRule->GetName()));
- rTextNode.SetCountedInList(false);
- }
- pWrtShell->Down(false, 1);
- {
- SwNode& rNode = pWrtShell->GetCursor()->GetPoint()->GetNode();
- SwTextNode& rTextNode = *rNode.GetTextNode();
- rTextNode.SetAttr(SwNumRuleItem(pNumRule->GetName()));
- rTextNode.SetCountedInList(false);
- }
- }
-
- // When exporting to ReqIF:
- ExportToReqif();
-
- // Then make sure the output is valid xhtml:
- xmlDocUniquePtr pXmlDoc = WrapReqifFromTempFile();
-
- OUString aContent
- = getXPathContent(pXmlDoc, "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:ol/"
- "reqif-xhtml:li[@style='display: block']/reqif-xhtml:p");
- CPPUNIT_ASSERT_EQUAL(u"list 1, header 1"_ustr, aContent.trim());
-}
-
-CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, testOleEmfPreviewToHtml)
-{
- // Given a document containing an embedded object, with EMF preview:
- createSwDoc("ole2.odt");
-
- // When exporting to HTML:
- ExportToHTML();
-
- // Then make sure the tag has matching file extension and data:
- htmlDocUniquePtr pDoc = parseHtml(maTempFile);
- OUString aPath = getXPath(pDoc, "/html/body/p/img", "src");
- // Without the accompanying fix in place, this test would have failed, as aPath was
- // ole_html_3978e5f373402b43.JPG, with EMF data.
- CPPUNIT_ASSERT(aPath.endsWith("gif"));
-}
-
-CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, testNestedBullets)
-{
- // Given a documented with nested lists:
- createSwDoc();
- SwDoc* pDoc = getSwDoc();
- SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell();
- pWrtShell->Insert(u"first"_ustr);
- sal_uInt16 nPos = pDoc->MakeNumRule(pDoc->GetUniqueNumRuleName());
- SwNumRule* pNumRule = pDoc->GetNumRuleTable()[nPos];
- {
- SwNode& rNode = pWrtShell->GetCursor()->GetPoint()->GetNode();
- SwTextNode& rTextNode = *rNode.GetTextNode();
- rTextNode.SetAttr(SwNumRuleItem(pNumRule->GetName()));
- rTextNode.SetAttrListLevel(0);
- }
- pWrtShell->SplitNode();
- pWrtShell->Insert(u"second"_ustr);
- {
- SwNode& rNode = pWrtShell->GetCursor()->GetPoint()->GetNode();
- SwTextNode& rTextNode = *rNode.GetTextNode();
- rTextNode.SetAttr(SwNumRuleItem(pNumRule->GetName()));
- rTextNode.SetAttrListLevel(1);
- }
-
- // When exporting to xhtml:
- ExportToReqif();
-
- // Then make sure that there is a between the outer and the inner :
- xmlDocUniquePtr pXmlDoc = WrapReqifFromTempFile();
- // Without the accompanying fix in place, this test would have failed with:
- // - XPath '//reqif-xhtml:ol/reqif-xhtml:li/reqif-xhtml:ol/reqif-xhtml:li/reqif-xhtml:p' not found
- // i.e. the - inside the outer
was missing.
- assertXPathContent(
- pXmlDoc, "//reqif-xhtml:ol/reqif-xhtml:li/reqif-xhtml:ol/reqif-xhtml:li/reqif-xhtml:p",
- u"second");
-}
-
-CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, testTrailingLineBreak)
-{
- // Given a document with a trailing line-break:
- createSwDoc();
- SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell();
- pWrtShell->Insert(u"test\n"_ustr);
-
- // When exporting to reqif-xhtml:
- ExportToReqif();
-
- // Then make sure that we still have a single line-break:
- xmlDocUniquePtr pXmlDoc = WrapReqifFromTempFile();
- // Without the accompanying fix in place, this test would have failed with:
- // - Expected: 1
- // - Actual : 2
- // - XPath '//reqif-xhtml:br' number of nodes is incorrect
- assertXPath(pXmlDoc, "//reqif-xhtml:br", 1);
-
- // Then test the import side:
- // Given an empty document:
- // When importing a
from reqif-xhtml:
- ImportFromReqif(maTempFile.GetURL());
-
- // Then make sure that line-break is not lost:
- pWrtShell = getSwDocShell()->GetWrtShell();
- OUString aActual = pWrtShell->GetCursor()->GetPointNode().GetTextNode()->GetText();
- // Without the accompanying fix in place, this test would have failed, as the trailing
- // line-break was lost.
- CPPUNIT_ASSERT_EQUAL(u"test\n"_ustr, aActual);
-}
-
-CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, testLeadingTab)
-{
- // Given a document with leading tabs:
- createSwDoc();
- SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell();
- pWrtShell->Insert(u"\t first"_ustr);
- pWrtShell->SplitNode();
- pWrtShell->Insert(u"\t\t second"_ustr);
- pWrtShell->SplitNode();
- pWrtShell->Insert(u"thi \t rd"_ustr);
-
- // When exporting to HTML, using LeadingTabWidth=2:
- uno::Sequence aStoreProperties = {
- comphelper::makePropertyValue(u"FilterName"_ustr, u"HTML (StarWriter)"_ustr),
- comphelper::makePropertyValue(u"FilterOptions"_ustr, u"xhtmlns=reqif-xhtml"_ustr),
- comphelper::makePropertyValue(u"LeadingTabWidth"_ustr, static_cast(2)),
- };
- saveWithParams(aStoreProperties);
-
- // Then make sure that leading tabs are replaced with 2 nbsps:
- xmlDocUniquePtr pXmlDoc = WrapReqifFromTempFile();
- // Without the accompanying fix in place, this test would have failed with:
- // - Expected: first
- // - Actual : first
- // i.e. the leading tab was not replaced by 2 nbsps.
- assertXPathContent(pXmlDoc, "//reqif-xhtml:p[1]", u"\xa0\xa0 first");
- // Test a leading tab that is not at the start of the paragraph:
- assertXPathContent(pXmlDoc, "//reqif-xhtml:p[2]", u"\xa0\xa0\xa0\xa0 second");
- // Test a tab which is not leading:
- assertXPathContent(pXmlDoc, "//reqif-xhtml:p[3]", u"thi \t rd");
-}
-
-CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, testLeadingTabHTML)
-{
- // Given a document with leading tabs:
- createSwDoc();
- SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell();
- pWrtShell->Insert(u"\t test"_ustr);
-
- // When exporting to plain HTML, using LeadingTabWidth=2:
- uno::Sequence aStoreProperties = {
- comphelper::makePropertyValue(u"FilterName"_ustr, u"HTML (StarWriter)"_ustr),
- comphelper::makePropertyValue(u"LeadingTabWidth"_ustr, static_cast(2)),
- };
- saveWithParams(aStoreProperties);
-
- // Then make sure that leading tabs are replaced with 2 nbsps:
- htmlDocUniquePtr pHtmlDoc = parseHtml(maTempFile);
- CPPUNIT_ASSERT(pHtmlDoc);
- // Without the accompanying fix in place, this test would have failed with:
- // - Expected: test
- // - Actual : test
- // i.e. the leading tab was not replaced by 2 nbsps.
- assertXPathContent(pHtmlDoc, "/html/body/p", SAL_NEWLINE_STRING u"\xa0\xa0 test");
-}
-
-CPPUNIT_TEST_FIXTURE(HtmlExportTest, testClearingBreak)
-{
- auto verify = [this]() {
- uno::Reference xParagraph(getParagraph(1), uno::UNO_QUERY);
- uno::Reference xPortions = xParagraph->createEnumeration();
- uno::Reference xPortion;
- OUString aPortionType;
- while (true)
- {
- // Ignore leading comments.
- xPortion.set(xPortions->nextElement(), uno::UNO_QUERY);
- xPortion->getPropertyValue(u"TextPortionType"_ustr) >>= aPortionType;
- if (aPortionType != "Annotation")
- {
- break;
- }
- }
- // Skip "foo".
- // Without the accompanying fix in place, this test would have failed with:
- // An uncaught exception of type com.sun.star.container.NoSuchElementException
- // i.e. the first para was just comments + text portion, the clearing break was lost.
- xPortion.set(xPortions->nextElement(), uno::UNO_QUERY);
- xPortion->getPropertyValue(u"TextPortionType"_ustr) >>= aPortionType;
- CPPUNIT_ASSERT_EQUAL(u"LineBreak"_ustr, aPortionType);
- uno::Reference xLineBreak;
- xPortion->getPropertyValue(u"LineBreak"_ustr) >>= xLineBreak;
- sal_Int16 eClear{};
- uno::Reference xLineBreakProps(xLineBreak, uno::UNO_QUERY);
- xLineBreakProps->getPropertyValue(u"Clear"_ustr) >>= eClear;
- CPPUNIT_ASSERT_EQUAL(static_cast(SwLineBreakClear::ALL), eClear);
- };
-
- // Given a document with an at-para anchored image + a clearing break:
- // When loading that file:
- createSwWebDoc("clearing-break.html");
- // Then make sure that the clear property of the break is not ignored:
- verify();
- saveAndReload(mpFilter);
- // Make sure that the clear property of the break is not ignored during export:
- verify();
-}
-
-CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, testTableBackground)
-{
- // Given a document with two tables: first stable has a background, second table has a
- // background in its first row:
- createSwDoc();
- SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell();
- SwInsertTableOptions aInsertTableOptions(SwInsertTableFlags::DefaultBorder,
- /*nRowsToRepeat=*/0);
- pWrtShell->InsertTable(aInsertTableOptions, /*nRows=*/1, /*nCols=*/1);
- pWrtShell->MoveTable(GotoPrevTable, fnTableStart);
- SvxBrushItem aBrush(COL_LIGHTRED, RES_BACKGROUND);
- pWrtShell->SetTabBackground(aBrush);
- pWrtShell->Down(/*bSelect=*/false);
- pWrtShell->SplitNode();
- pWrtShell->InsertTable(aInsertTableOptions, /*nRows=*/2, /*nCols=*/1);
- pWrtShell->MoveTable(GotoPrevTable, fnTableStart);
- aBrush.SetColor(COL_LIGHTGREEN);
- pWrtShell->SetRowBackground(aBrush);
- pWrtShell->Down(/*bSelect=*/false);
- // Second row has an explicit transparent background.
- aBrush.SetColor(COL_TRANSPARENT);
- pWrtShell->SetRowBackground(aBrush);
-
- // When exporting to reqif-xhtml:
- ExportToReqif();
-
- // Then make sure that CSS markup is used, not HTML one:
- xmlDocUniquePtr pXmlDoc = WrapReqifFromTempFile();
- // Without the accompanying fix in place, this test would have failed with:
- // - XPath '//reqif-xhtml:table[1]' no attribute 'style' exist
- // i.e. HTML markup was used for the table background color.
- assertXPath(pXmlDoc, "//reqif-xhtml:table[1]", "style", u"background: #ff0000");
- assertXPathNoAttribute(pXmlDoc, "//reqif-xhtml:table[1]", "bgcolor");
- assertXPath(pXmlDoc, "//reqif-xhtml:table[2]/reqif-xhtml:tr[1]", "style",
- u"background: #00ff00");
- assertXPathNoAttribute(pXmlDoc, "//reqif-xhtml:table[2]/reqif-xhtml:tr[1]", "bgcolor");
- // Second row has no explicit style, the default is not written.
- assertXPathNoAttribute(pXmlDoc, "//reqif-xhtml:table[2]/reqif-xhtml:tr[2]", "style");
-}
-
-CPPUNIT_TEST_FIXTURE(HtmlExportTest, testImageKeepRatio)
-{
- // Given a document with an image: width is relative, height is "keep ratio":
- createSwDoc();
- uno::Reference xFactory(mxComponent, uno::UNO_QUERY);
- uno::Reference xTextGraphic(
- xFactory->createInstance(u"com.sun.star.text.TextGraphicObject"_ustr), uno::UNO_QUERY);
- xTextGraphic->setPropertyValue(u"AnchorType"_ustr,
- uno::Any(text::TextContentAnchorType_AS_CHARACTER));
- xTextGraphic->setPropertyValue(u"RelativeWidth"_ustr, uno::Any(static_cast(42)));
- xTextGraphic->setPropertyValue(u"IsSyncHeightToWidth"_ustr, uno::Any(true));
- uno::Reference xTextDocument(mxComponent, uno::UNO_QUERY);
- uno::Reference xBodyText = xTextDocument->getText();
- uno::Reference xCursor(xBodyText->createTextCursor());
- uno::Reference xTextContent(xTextGraphic, uno::UNO_QUERY);
- xBodyText->insertTextContent(xCursor, xTextContent, false);
-
- // When exporting to HTML:
- save(mpFilter);
-
- // Then make sure that the width is not a fixed size, that would break on resizing the browser
- // window:
- htmlDocUniquePtr pDoc = parseHtml(maTempFile);
- // Without the accompanying fix in place, this test would have failed with:
- // - Expected: auto
- // - Actual : 2
- // i.e. a static (CSS pixel) height was written.
- assertXPath(pDoc, "/html/body/p/img", "height", u"auto");
-}
-
-CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, testSectionDir)
-{
- // Given a document with a section:
- createSwDoc();
- SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell();
- pWrtShell->Insert(u"test"_ustr);
- pWrtShell->SelAll();
- SwSectionData aSectionData(SectionType::Content, u"mysect"_ustr);
- pWrtShell->InsertSection(aSectionData);
-
- // When exporting to (reqif-)xhtml:
- ExportToReqif();
-
- // Then make sure CSS is used to export the text direction of the section:
- xmlDocUniquePtr pXmlDoc = WrapReqifFromTempFile();
- // Without the accompanying fix in place, this test would have failed with:
- // - XPath '//reqif-xhtml:div[@id='mysect']' no attribute 'style' exist
- // i.e. the dir="ltr" HTML attribute was used instead.
- assertXPath(pXmlDoc, "//reqif-xhtml:div[@id='mysect']", "style", u"dir: ltr");
-}
-
-CPPUNIT_TEST_FIXTURE(HtmlExportTest, testTdf114769)
-{
- // Create document from scratch since relative urls to filesystem can be replaced
- // by absolute during save/load
- createSwDoc();
- SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell();
- pWrtShell->Insert(u"Hyperlink1"_ustr);
- pWrtShell->SplitNode();
- pWrtShell->Insert(u"Hyperlink2"_ustr);
- pWrtShell->SplitNode();
- pWrtShell->Insert(u"Hyperlink3"_ustr);
- pWrtShell->SplitNode();
- pWrtShell->Insert(u"Hyperlink4"_ustr);
- pWrtShell->SplitNode();
- pWrtShell->Insert(u"Hyperlink5"_ustr);
- pWrtShell->SplitNode();
-
- // Normal external URL
- uno::Reference xRun(getRun(getParagraph(1), 1), uno::UNO_QUERY);
- xRun->setPropertyValue(u"HyperLinkURL"_ustr, uno::Any(u"http://libreoffice.org/"_ustr));
-
- // Bookmark reference
- xRun.set(getRun(getParagraph(2), 1), uno::UNO_QUERY);
- xRun->setPropertyValue(u"HyperLinkURL"_ustr, uno::Any(u"#some_bookmark"_ustr));
-
- // Filesystem absolute link
- xRun.set(getRun(getParagraph(3), 1), uno::UNO_QUERY);
- xRun->setPropertyValue(u"HyperLinkURL"_ustr, uno::Any(u"C:\\test.txt"_ustr));
-
- // Filesystem relative link
- xRun.set(getRun(getParagraph(4), 1), uno::UNO_QUERY);
- xRun->setPropertyValue(u"HyperLinkURL"_ustr, uno::Any(u"..\\..\\test.odt"_ustr));
-
- // Filesystem relative link
- xRun.set(getRun(getParagraph(5), 1), uno::UNO_QUERY);
- xRun->setPropertyValue(u"HyperLinkURL"_ustr, uno::Any(u".\\another.odt"_ustr));
-
- // Export
- save(mpFilter);
-
- htmlDocUniquePtr pHtmlDoc = parseHtml(maTempFile);
- CPPUNIT_ASSERT(pHtmlDoc);
-
- assertXPath(pHtmlDoc, "/html/body/p[1]/a", "href", u"http://libreoffice.org/");
- assertXPath(pHtmlDoc, "/html/body/p[2]/a", "href", u"#some_bookmark");
- assertXPath(pHtmlDoc, "/html/body/p[3]/a", "href", u"C:\\test.txt");
- assertXPath(pHtmlDoc, "/html/body/p[4]/a", "href", u"..\\..\\test.odt");
- assertXPath(pHtmlDoc, "/html/body/p[5]/a", "href", u".\\another.odt");
-}
-
-CPPUNIT_TEST_FIXTURE(HtmlExportTest, testTdf153923)
-{
- createSwDoc("TableWithIndent.fodt");
- save(mpFilter);
-
- // Parse it as XML (strict!)
- xmlDocUniquePtr pDoc = parseXml(maTempFile);
- // Without the fix in place, this would fail
- CPPUNIT_ASSERT(pDoc);
-
- assertXPath(pDoc, "/html/body//dl", 3);
- // The 'dd' tag was not closed
- assertXPath(pDoc, "/html/body//dd", 3);
-}
-
-CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, testTdf153923_ReqIF)
-{
- createSwDoc("TableWithIndent.fodt");
- ExportToReqif();
-
- xmlDocUniquePtr pDoc = WrapReqifFromTempFile();
-
- assertXPath(pDoc, "//reqif-xhtml:table");
- // There should be no 'dd' or 'dl' tags, used as a hack for table indentation
- assertXPath(pDoc, "//reqif-xhtml:dl", 0);
- assertXPath(pDoc, "//reqif-xhtml:dd", 0);
-}
-
-CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, testReqIfTransparentTifImg)
-{
- // reqIf export must keep the TIF encoding of the image
- createSwDoc("reqif-transparent-tif-img.odt");
- ExportToReqif();
-
- xmlDocUniquePtr pXmlDoc = WrapReqifFromTempFile();
- assertXPath(pXmlDoc, "//reqif-xhtml:p/reqif-xhtml:object[1]", "type", u"image/tiff");
- OUString imageName = getXPath(pXmlDoc, "//reqif-xhtml:p/reqif-xhtml:object[1]", "data");
- // Without the accompanying fix in place, this test would have failed,
- // ending with .gif, because XOutFlags::UseGifIfSensible flag combined
- // with the transparent image would result in GIF export
- CPPUNIT_ASSERT(imageName.endsWith(".tif"));
-
- INetURLObject aURL(maTempFile.GetURL());
- aURL.setName(imageName);
- GraphicDescriptor aDescriptor(aURL);
- aDescriptor.Detect();
- CPPUNIT_ASSERT_EQUAL(GraphicFileFormat::TIF, aDescriptor.GetFileFormat());
-}
-
-CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, testTdf155387)
-{
- createSwDoc("sub_li_and_ctd.fodt");
- ExportToReqif();
-
- // Without the fix in place, this would fail
- xmlDocUniquePtr pDoc = WrapReqifFromTempFile();
-
- // Single top-level list
- assertXPath(pDoc, "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:ul");
- // Single top-level item
- assertXPath(pDoc, "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:ul/reqif-xhtml:li");
- // 4 top-level paragraphs in the item
- assertXPath(pDoc,
- "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:ul/reqif-xhtml:li/reqif-xhtml:p", 4);
- // 2 sublists in the item
- assertXPath(
- pDoc, "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:ul/reqif-xhtml:li/reqif-xhtml:ul", 2);
- // 2 items in the first sublist
- assertXPath(pDoc,
- "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:ul/reqif-xhtml:li/reqif-xhtml:ul[1]/"
- "reqif-xhtml:li",
- 2);
- // Check the last (most nested) subitem's text
- assertXPathContent(
- pDoc,
- "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:ul/reqif-xhtml:li/reqif-xhtml:ul[2]/"
- "reqif-xhtml:li/reqif-xhtml:ul/reqif-xhtml:li/reqif-xhtml:p",
- u"l3");
-}
-
-CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, testTdf155496)
-{
- createSwDoc("listItemSubheader.fodt");
- ExportToReqif();
-
- // Without the fix in place, this would fail
- xmlDocUniquePtr pDoc = WrapReqifFromTempFile();
-
- // Two top-level lists
- assertXPath(pDoc, "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:ul", 2);
- // Single top-level item
- assertXPath(pDoc, "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:ul[1]/reqif-xhtml:li");
- // One top-level paragraph in the item
- assertXPath(pDoc,
- "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:ul[1]/reqif-xhtml:li/reqif-xhtml:p");
- // One sublist in the item
- assertXPath(
- pDoc, "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:ul[1]/reqif-xhtml:li/reqif-xhtml:ul");
- // One item in the sublist
- assertXPath(pDoc,
- "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:ul[1]/reqif-xhtml:li/reqif-xhtml:ul/"
- "reqif-xhtml:li");
- // Check its text
- OUString aContent = getXPathContent(
- pDoc, "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:ul[1]/reqif-xhtml:li/reqif-xhtml:ul/"
- "reqif-xhtml:li/reqif-xhtml:p");
- CPPUNIT_ASSERT_EQUAL(u"list 1 item 1\n\t\tsub-header"_ustr, aContent.trim());
-}
-
-CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, testReqIF_RightAlignedTable)
-{
- createSwDoc("tableRight.fodt");
- ExportToReqif();
-
- xmlDocUniquePtr pDoc = WrapReqifFromTempFile();
-
- // No 'align' attribute must be present in 'div'
- assertXPathNoAttribute(pDoc, "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:div", "align");
-}
-
-CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, testReqIF_ListsWithNumFormat)
-{
- createSwDoc("listsWithNumFormat.fodt");
- ExportToReqif();
-
- xmlDocUniquePtr pDoc = WrapReqifFromTempFile();
-
- // No 'type' attribute must be present in 'ol'
- assertXPathNoAttribute(pDoc, "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:ol[1]", "type");
- assertXPathNoAttribute(pDoc, "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:ol[2]", "type");
- assertXPathNoAttribute(pDoc, "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:ol[3]", "type");
- assertXPathNoAttribute(pDoc, "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:ol[4]", "type");
-}
-
-CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, testTdf155871)
-{
- createSwDoc("tdf155871.fodt");
- ExportToReqif();
-
- // Without the fix in place, this would fail
- xmlDocUniquePtr pDoc = WrapReqifFromTempFile();
-}
-
-CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, testReqIF_ListsNoStartAttribute)
-{
- createSwDoc("twoListsWithSameStyle.fodt");
- ExportToReqif();
-
- xmlDocUniquePtr pDoc = WrapReqifFromTempFile();
-
- // No 'start' attribute must be present in 'ol'
- assertXPath(pDoc, "//reqif-xhtml:ol[@start]", 0);
-}
-
-CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, testReqIF_FrameTextAsObjectAltText)
-{
- createSwDoc("frameWithText.fodt");
- ExportToReqif();
-
- xmlDocUniquePtr pDoc = WrapReqifFromTempFile();
-
- // Without the fix, this would fail with
- // - Expected: Some text in frame &
- // - Actual : Frame1
- // i.e., frame name was used as the object element content, not frame text
- assertXPathContent(pDoc,
- "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:p[2]/reqif-xhtml:object",
- u"Some text in frame & ");
-}
-
-CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, testSingleOleExport)
-{
- // Given a document containing an embedded OLE object:
- createSwDoc("ole2.odt");
-
- // Create a selection for that object:
- auto xDrawPageSupplier(mxComponent.queryThrow());
- auto xDrawPage(xDrawPageSupplier->getDrawPage());
- auto xModel(mxComponent.queryThrow());
- auto xController(xModel->getCurrentController().queryThrow());
- xController->select(xDrawPage->getByIndex(0));
-
- // Store only the selection
- css::uno::Sequence aStoreProperties = {
- comphelper::makePropertyValue(u"FilterName"_ustr, u"HTML (StarWriter)"_ustr),
- comphelper::makePropertyValue(u"FilterOptions"_ustr, u"xhtmlns=reqif-xhtml"_ustr),
- comphelper::makePropertyValue(u"RTFOLEMimeType"_ustr, u"text/rtf"_ustr),
- comphelper::makePropertyValue(u"SelectionOnly"_ustr, true),
- };
- saveWithParams(aStoreProperties);
-
- xmlDocUniquePtr pXmlDoc = WrapReqifFromTempFile();
-
- // The root element must be reqif-xhtml:object
- assertXPath(pXmlDoc, "/reqif-xhtml:html/reqif-xhtml:object", "type", u"text/rtf");
- // It has no children
- assertXPathChildren(pXmlDoc, "/reqif-xhtml:html/reqif-xhtml:object", 0);
- // And the content is empty
- assertXPathContent(pXmlDoc, "/reqif-xhtml:html/reqif-xhtml:object", u"");
-
- OUString aRtfData = getXPath(pXmlDoc, "/reqif-xhtml:html/reqif-xhtml:object", "data");
- INetURLObject aUrl(maTempFile.GetURL());
- aUrl.setName(aRtfData);
- SvMemoryStream aRtf;
- HtmlExportTest::wrapRtfFragment(aUrl.GetMainURL(INetURLObject::DecodeMechanism::NONE), aRtf);
- tools::SvRef xReader(new TestReqIfRtfReader(aRtf));
- // The RTF OLE exports correctly
- CPPUNIT_ASSERT(xReader->CallParser() != SvParserState::Error);
- CPPUNIT_ASSERT_EQUAL(tools::Long(9358), xReader->GetObjw());
- CPPUNIT_ASSERT_EQUAL(tools::Long(450), xReader->GetObjh());
-}
-
-CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, testReqIF_Tdf156602)
-{
- createSwDoc("NestingInA1.fodt");
- ExportToReqif();
-
- xmlDocUniquePtr pDoc = WrapReqifFromTempFile();
-
- // The outer table must be kept in the document where the outer table is the first element,
- // and its A1 starts with a nested table
-
- // Only two sub-elements must be inside the div: an outer table and a trailing paragraph
- assertXPathChildren(pDoc, "/reqif-xhtml:html/reqif-xhtml:div", 2);
- // The outer table must have exactly two rows
- assertXPath(pDoc, "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:table/reqif-xhtml:tr", 2);
- // First outer table cell must have two sub-elements: an inner table and a trailing paragraph
- assertXPathChildren(
- pDoc,
- "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:table/reqif-xhtml:tr[1]/reqif-xhtml:td[1]",
- 2);
- // The inner table must have exactly two rows
- assertXPath(
- pDoc,
- "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:table/reqif-xhtml:tr[1]/reqif-xhtml:td[1]/"
- "reqif-xhtml:table/reqif-xhtml:tr",
- 2);
- // Check all the elements' content
- assertXPathContent(
- pDoc,
- "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:table/reqif-xhtml:tr[1]/reqif-xhtml:td[1]/"
- "reqif-xhtml:table/reqif-xhtml:tr[1]/reqif-xhtml:td[1]/reqif-xhtml:p",
- u"Inner.A1");
- assertXPathContent(
- pDoc,
- "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:table/reqif-xhtml:tr[1]/reqif-xhtml:td[1]/"
- "reqif-xhtml:table/reqif-xhtml:tr[1]/reqif-xhtml:td[2]/reqif-xhtml:p",
- u"Inner.B1");
- assertXPathContent(
- pDoc,
- "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:table/reqif-xhtml:tr[1]/reqif-xhtml:td[1]/"
- "reqif-xhtml:table/reqif-xhtml:tr[2]/reqif-xhtml:td[1]/reqif-xhtml:p",
- u"Inner.A2");
- assertXPathContent(
- pDoc,
- "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:table/reqif-xhtml:tr[1]/reqif-xhtml:td[1]/"
- "reqif-xhtml:table/reqif-xhtml:tr[2]/reqif-xhtml:td[2]/reqif-xhtml:p",
- u"Inner.B2");
- assertXPathContent(
- pDoc,
- "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:table/reqif-xhtml:tr[1]/reqif-xhtml:td[1]/"
- "reqif-xhtml:p",
- u"Outer.A1");
- assertXPathContent(
- pDoc,
- "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:table/reqif-xhtml:tr[1]/reqif-xhtml:td[2]/"
- "reqif-xhtml:p",
- u"Outer.B1");
- assertXPathContent(
- pDoc,
- "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:table/reqif-xhtml:tr[2]/reqif-xhtml:td[1]/"
- "reqif-xhtml:p",
- u"Outer.A2");
- assertXPathContent(
- pDoc,
- "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:table/reqif-xhtml:tr[2]/reqif-xhtml:td[2]/"
- "reqif-xhtml:p",
- u"Outer.B2");
- assertXPathContent(pDoc, "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:p", u"Following text");
-}
-
-CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, testTdf156647_CellPaddingRoundtrip)
-{
- // Given a document with a table with cell padding:
- createSwDoc("table_cell_padding.fodt");
- {
- auto xTable = getParagraphOrTable(1);
- auto aTableBorder = getProperty(xTable, u"TableBorder2"_ustr);
- CPPUNIT_ASSERT_EQUAL(sal_Int16(1270), aTableBorder.Distance);
- CPPUNIT_ASSERT(aTableBorder.IsDistanceValid);
- }
- // When exporting to reqif-xhtml:
- ExportToReqif();
- // Make sure that we export it:
- xmlDocUniquePtr pXmlDoc = WrapReqifFromTempFile();
- assertXPath(pXmlDoc, "//reqif-xhtml:table", "cellpadding", u"48"); // px
- // Now import it
- ImportFromReqif(maTempFile.GetURL());
- // Then make sure that padding is not lost:
- {
- auto xTable = getParagraphOrTable(1);
- auto aTableBorder = getProperty(xTable, u"TableBorder2"_ustr);
- // Without the accompanying fix in place, this test would have failed:
- // - Expected: 1270
- // - Actual : 97
- // as the padding was lost, and the default 55 twip padding was used.
- CPPUNIT_ASSERT_EQUAL(sal_Int16(1270), aTableBorder.Distance);
- CPPUNIT_ASSERT(aTableBorder.IsDistanceValid);
- }
-}
-
-CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, testTdf157643_WideHBorder)
-{
- // Given a document with a table with a wide border between its two rows:
- createSwDoc("table_with_wide_horizontal_border.fodt");
- // When exporting to reqif-xhtml:
- ExportToReqif();
- // Make sure that there's no extra tr's:
- xmlDocUniquePtr pXmlDoc = WrapReqifFromTempFile();
- assertXPath(pXmlDoc, "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:table/reqif-xhtml:tr", 2);
-}
-
-CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, testHTML_PreserveSpaces)
-{
- // Given a document with leading, trailing, and repeating intermediate spaces:
- createSwDoc();
- SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell();
- static constexpr OUString paraText = u"\t test \t more text \t"_ustr;
- pWrtShell->Insert(paraText);
-
- // When exporting to plain HTML, using PreserveSpaces:
- saveWithParams({
- comphelper::makePropertyValue(u"FilterName"_ustr, u"HTML (StarWriter)"_ustr),
- comphelper::makePropertyValue(u"PreserveSpaces"_ustr, true),
- });
-
- // Then make sure that "white-space: pre-wrap" is written into the paragraph's style:
- htmlDocUniquePtr pHtmlDoc = parseHtml(maTempFile);
- CPPUNIT_ASSERT(pHtmlDoc);
- const OUString style = getXPath(pHtmlDoc, "/html/body/p", "style");
- CPPUNIT_ASSERT(style.indexOf("white-space: pre-wrap") >= 0);
- // Also check that the paragraph text is correct, without modifications in whitespace
- assertXPathContent(pHtmlDoc, "/html/body/p", paraText);
-
- // Test import
-
- setImportFilterName(u"HTML (StarWriter)"_ustr);
- loadFromURL(maTempFile.GetURL());
- CPPUNIT_ASSERT_EQUAL(paraText, getParagraph(1)->getString());
-}
-
-CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, testReqIF_PreserveSpaces)
-{
- // Given a document with leading, trailing, and repeating intermediate spaces:
- createSwDoc();
- SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell();
- static constexpr OUString paraText = u"\t test \t more text \t"_ustr;
- pWrtShell->Insert(paraText);
-
- // When exporting to ReqIF, using PreserveSpaces:
- saveWithParams({
- comphelper::makePropertyValue(u"FilterName"_ustr, u"HTML (StarWriter)"_ustr),
- comphelper::makePropertyValue(u"FilterOptions"_ustr, u"xhtmlns=reqif-xhtml"_ustr),
- comphelper::makePropertyValue(u"PreserveSpaces"_ustr, true),
- });
-
- // Then make sure that xml:space="preserve" attribute exists in the paragraph element:
- xmlDocUniquePtr pXmlDoc = WrapReqifFromTempFile();
- assertXPath(pXmlDoc, "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:p", "space", u"preserve");
- // Also check that the paragraph text is correct, without modifications in whitespace
- assertXPathContent(pXmlDoc, "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:p", paraText);
-
- // Test import
-
- setImportFilterOptions(u"xhtmlns=reqif-xhtml"_ustr);
- setImportFilterName(u"HTML (StarWriter)"_ustr);
- loadFromURL(maTempFile.GetURL());
- CPPUNIT_ASSERT_EQUAL(paraText, getParagraph(1)->getString());
-}
-
-CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, testHTML_NoPreserveSpaces)
-{
- // Test cases where "PreserveSpaces" should not introduce respective markup
-
- const auto assertXPath_NoWhiteSpaceInStyle
- = [this](const xmlDocUniquePtr& pDoc, const char* pXPath) {
- xmlXPathObjectPtr pXmlObj = getXPathNode(pDoc, pXPath);
- xmlNodeSetPtr pXmlNodes = pXmlObj->nodesetval;
- CPPUNIT_ASSERT_EQUAL_MESSAGE(pXPath, 1, xmlXPathNodeSetGetLength(pXmlNodes));
- CPPUNIT_ASSERT(pXmlNodes);
- xmlNodePtr pXmlNode = pXmlNodes->nodeTab[0];
- if (xmlChar* prop = xmlGetProp(pXmlNode, BAD_CAST("style")))
- {
- OUString style = OUString::fromUtf8(reinterpret_cast(prop));
- CPPUNIT_ASSERT_MESSAGE(pXPath, style.indexOf("white-space:") < 0);
- }
- xmlXPathFreeObject(pXmlObj);
- };
- const auto assertXPath_HasWhiteSpaceInStyle
- = [this](const xmlDocUniquePtr& pDoc, const char* pXPath) {
- const OUString style = getXPath(pDoc, pXPath, "style");
- CPPUNIT_ASSERT_MESSAGE(pXPath, style.indexOf("white-space: pre-wrap") >= 0);
- };
-
- createSwDoc("test_no_space_preserve.fodt");
-
- // Export to plain HTML, using PreserveSpaces:
- saveWithParams({
- comphelper::makePropertyValue(u"FilterName"_ustr, u"HTML (StarWriter)"_ustr),
- comphelper::makePropertyValue(u"PreserveSpaces"_ustr, true),
- });
-
- htmlDocUniquePtr pHtmlDoc = parseHtml(maTempFile);
- CPPUNIT_ASSERT(pHtmlDoc);
-
- // No whitespace preservation, where no leading / trailing / double whitespace
- assertXPath_NoWhiteSpaceInStyle(pHtmlDoc, "/html/body/p[1]");
- // Whitespace preserved for a leading space
- assertXPath_HasWhiteSpaceInStyle(pHtmlDoc, "/html/body/p[2]");
- // Whitespace preserved for a trailing space
- assertXPath_HasWhiteSpaceInStyle(pHtmlDoc, "/html/body/p[3]");
- // Whitespace preserved for a double space
- assertXPath_HasWhiteSpaceInStyle(pHtmlDoc, "/html/body/p[4]");
- // No whitespace preservation for leading / trailing breaks
- assertXPath_NoWhiteSpaceInStyle(pHtmlDoc, "/html/body/p[5]");
- // Whitespace preserved for a leading break + space
- assertXPath_HasWhiteSpaceInStyle(pHtmlDoc, "/html/body/p[6]");
- // Whitespace preserved for a trailing space + break
- assertXPath_HasWhiteSpaceInStyle(pHtmlDoc, "/html/body/p[7]");
- // No whitespace preservation for a middle break
- assertXPath_NoWhiteSpaceInStyle(pHtmlDoc, "/html/body/p[8]");
- // Whitespace preserved for a middle space + break
- assertXPath_HasWhiteSpaceInStyle(pHtmlDoc, "/html/body/p[9]");
- // Whitespace preserved for a middle break + space
- assertXPath_HasWhiteSpaceInStyle(pHtmlDoc, "/html/body/p[10]");
- // No whitespace preservation for a trailing space and SVG
- assertXPath_NoWhiteSpaceInStyle(pHtmlDoc, "/html/body/p[11]");
-
- // Test import
-
- setImportFilterName(u"HTML (StarWriter)"_ustr);
- loadFromURL(maTempFile.GetURL());
-
- CPPUNIT_ASSERT_EQUAL(u"No special spaces"_ustr, getParagraph(1)->getString());
- CPPUNIT_ASSERT_EQUAL(u" Leading space"_ustr, getParagraph(2)->getString());
- CPPUNIT_ASSERT_EQUAL(u"Trailing space "_ustr, getParagraph(3)->getString());
- CPPUNIT_ASSERT_EQUAL(u"Double space"_ustr, getParagraph(4)->getString());
- // Trailing break is removed in SwHTMLParser::AppendTextNode, and replaced with para spacing
- CPPUNIT_ASSERT_EQUAL(u"\nLeading/trailing breaks"_ustr, getParagraph(5)->getString());
- CPPUNIT_ASSERT_EQUAL(u"\n Leading break + space"_ustr, getParagraph(6)->getString());
- // Trailing break is removed in SwHTMLParser::AppendTextNode, and replaced with para spacing
- CPPUNIT_ASSERT_EQUAL(u"Trailing space + break "_ustr, getParagraph(7)->getString());
- CPPUNIT_ASSERT_EQUAL(u"Middle\nbreak"_ustr, getParagraph(8)->getString());
- CPPUNIT_ASSERT_EQUAL(u"Middle space \n+ break"_ustr, getParagraph(9)->getString());
- CPPUNIT_ASSERT_EQUAL(u"Middle break\n + space"_ustr, getParagraph(10)->getString());
- // The SVG is replaced by a space in SwXParagraph::getString()
- CPPUNIT_ASSERT_EQUAL(u"Trailing space and SVG "_ustr, getParagraph(11)->getString());
-}
-
-CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, testReqIF_NoPreserveSpaces)
-{
- // Test cases where "PreserveSpaces" should not introduce respective markup
-
- createSwDoc("test_no_space_preserve.fodt");
-
- // Export to ReqIF, using PreserveSpaces:
- saveWithParams({
- comphelper::makePropertyValue(u"FilterName"_ustr, u"HTML (StarWriter)"_ustr),
- comphelper::makePropertyValue(u"FilterOptions"_ustr, u"xhtmlns=reqif-xhtml"_ustr),
- comphelper::makePropertyValue(u"PreserveSpaces"_ustr, true),
- });
-
- xmlDocUniquePtr pXmlDoc = WrapReqifFromTempFile();
-
- // No whitespace preservation, where no leading / trailing / double whitespace
- assertXPathNoAttribute(pXmlDoc, "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:p[1]", "space");
- // Whitespace preserved for a leading space
- assertXPath(pXmlDoc, "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:p[2]", "space",
- u"preserve");
- // Whitespace preserved for a trailing space
- assertXPath(pXmlDoc, "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:p[3]", "space",
- u"preserve");
- // Whitespace preserved for a double space
- assertXPath(pXmlDoc, "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:p[4]", "space",
- u"preserve");
- // No whitespace preservation for leading / trailing breaks
- assertXPathNoAttribute(pXmlDoc, "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:p[5]", "space");
- // Whitespace preserved for a leading break + space
- assertXPath(pXmlDoc, "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:p[6]", "space",
- u"preserve");
- // No whitespace preservation for a trailing space + break
- assertXPathNoAttribute(pXmlDoc, "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:p[7]", "space");
- // No whitespace preservation for a middle break
- assertXPathNoAttribute(pXmlDoc, "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:p[8]", "space");
- // No whitespace preservation for a middle space + break
- assertXPathNoAttribute(pXmlDoc, "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:p[9]", "space");
- // Whitespace preserved for a middle break + space
- assertXPath(pXmlDoc, "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:p[10]", "space",
- u"preserve");
- // No whitespace preservation for a trailing space and SVG
- assertXPathNoAttribute(pXmlDoc, "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:p[11]", "space");
-
- // Test import
-
- setImportFilterOptions(u"xhtmlns=reqif-xhtml"_ustr);
- setImportFilterName(u"HTML (StarWriter)"_ustr);
- loadFromURL(maTempFile.GetURL());
-
- CPPUNIT_ASSERT_EQUAL(u"No special spaces"_ustr, getParagraph(1)->getString());
- CPPUNIT_ASSERT_EQUAL(u" Leading space"_ustr, getParagraph(2)->getString());
- CPPUNIT_ASSERT_EQUAL(u"Trailing space "_ustr, getParagraph(3)->getString());
- CPPUNIT_ASSERT_EQUAL(u"Double space"_ustr, getParagraph(4)->getString());
- CPPUNIT_ASSERT_EQUAL(u"\nLeading/trailing breaks\n"_ustr, getParagraph(5)->getString());
- CPPUNIT_ASSERT_EQUAL(u"\n Leading break + space"_ustr, getParagraph(6)->getString());
- CPPUNIT_ASSERT_EQUAL(u"Trailing space + break \n"_ustr, getParagraph(7)->getString());
- CPPUNIT_ASSERT_EQUAL(u"Middle\nbreak"_ustr, getParagraph(8)->getString());
- CPPUNIT_ASSERT_EQUAL(u"Middle space \n+ break"_ustr, getParagraph(9)->getString());
- CPPUNIT_ASSERT_EQUAL(u"Middle break\n + space"_ustr, getParagraph(10)->getString());
- // The SVG is replaced by a space in SwXParagraph::getString()
- CPPUNIT_ASSERT_EQUAL(u"Trailing space and SVG "_ustr, getParagraph(11)->getString());
-}
-
-CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, testReqIF_ExportFormulasAsPDF)
-{
- // Given a document with a formula:
- createSwDoc("embedded_formula.fodt");
-
- // When exporting to reqif with ExportFormulasAsPDF=true:
- uno::Sequence aStoreProperties = {
- comphelper::makePropertyValue(u"FilterName"_ustr, u"HTML (StarWriter)"_ustr),
- comphelper::makePropertyValue(u"FilterOptions"_ustr, u"xhtmlns=reqif-xhtml"_ustr),
- comphelper::makePropertyValue(u"ExportFormulasAsPDF"_ustr, true),
- };
- saveWithParams(aStoreProperties);
-
- // Make sure that the formula is exported as PDF:
- xmlDocUniquePtr pXmlDoc = WrapReqifFromTempFile();
- assertXPath(pXmlDoc, "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:p[2]/reqif-xhtml:object",
- "type", u"application/pdf");
-
- css::uno::Sequence descr{
- comphelper::makePropertyValue(u"URL"_ustr, GetObjectPath(u".pdf"_ustr)),
- };
-
- uno::Reference xFactory(
- comphelper::getProcessComponentContext()->getServiceManager(), uno::UNO_QUERY_THROW);
- uno::Reference xTypeDetection(
- xFactory->createInstance(u"com.sun.star.document.TypeDetection"_ustr),
- uno::UNO_QUERY_THROW);
-
- CPPUNIT_ASSERT_EQUAL(u"pdf_Portable_Document_Format"_ustr,
- xTypeDetection->queryTypeByDescriptor(descr, true));
-}
-
-CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, testReqIF_NoBrClearForImageWrap)
-{
- // Given a document with a paragraph-anchored image with "none" wrap:
- createSwDoc("image_anchored_to_paragraph_no_wrap.fodt");
- // When exporting to reqif:
- ExportToReqif();
- // Make sure that there's no 'br' elements in the 'object' (used to represent the wrapping
- // in HTML export, using 'clear' attribute):
- xmlDocUniquePtr pXmlDoc = WrapReqifFromTempFile();
- assertXPath(pXmlDoc, "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:p/reqif-xhtml:object");
- assertXPath(pXmlDoc,
- "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:p/reqif-xhtml:object/reqif-xhtml:br",
- 0);
-}
-
-CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, testReqIF_Tdf160017_spanClosingOrder)
-{
- // Given a document with a paragraph having explicit font color and character border properties:
- createSwDoc("char_border_and_font_color.fodt");
- // When exporting to reqif:
- ExportToReqif();
- // Without the fix, this would fail, because there was an extra closing
- WrapReqifFromTempFile();
-}
-
-CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, testHTML_Tdf160017_spanClosingOrder)
-{
- // Given a document with a paragraph having explicit font color and character border properties:
- createSwDoc("char_border_and_font_color.fodt");
- // When exporting to HTML:
- ExportToHTML();
- // Parse it as XML (strict!)
- // Without the fix, this would fail, because span and font elements closed in wrong order
- CPPUNIT_ASSERT(parseXml(maTempFile));
-}
-
-CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, testHTML_Tdf160390)
-{
- // This document must not hang infinitely on HTML export
- createSwDoc("tdf160390.fodt");
- ExportToHTML();
-}
-
-CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, testHTML_160867)
-{
- // Given a document with an image with hyperlink, and text with hyperlink, both in a frame:
- createSwDoc("tdf160867_image_with_link.fodt");
- // When exporting to HTML:
- ExportToHTML();
- // Parse it as XML (strict!)
- xmlDocUniquePtr pDoc = parseXml(maTempFile);
- CPPUNIT_ASSERT(pDoc);
- assertXPath(pDoc, "/html/body/p", 2);
-
- // Test export of image and text hyperlinks in the image map.
- // Without the fix, the test would fail with
- // - Expected: 1
- // - Actual : 0
- // - In <>, XPath '/html/body/p[2]/map' number of nodes is incorrect
- const OUString mapName = getXPath(pDoc, "/html/body/p[2]/map", "name");
- assertXPath(pDoc, "/html/body/p[2]/map/area[1]", "shape", u"rect");
- CPPUNIT_ASSERT(getXPath(pDoc, "/html/body/p[2]/map/area[1]", "href").endsWith("foo/bar"));
- assertXPath(pDoc, "/html/body/p[2]/map/area[2]", "shape", u"rect");
- CPPUNIT_ASSERT(getXPath(pDoc, "/html/body/p[2]/map/area[2]", "href").endsWith("baz"));
- assertXPath(pDoc, "/html/body/p[2]/img", "usemap", Concat2View("#" + mapName));
-}
-
-CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, testReqIF_160867)
-{
- // Given a document with an image with hyperlink, and text with hyperlink, both in a frame:
- createSwDoc("tdf160867_image_with_link.fodt");
- // When exporting to reqif:
- ExportToReqif();
- // For now, we don't (yet) output the whole map in ReqIF case.
- // Make sure that the first hyperlink from the objects in the frame is output as an element
- // around the whole image of the frame.
- xmlDocUniquePtr pXmlDoc = WrapReqifFromTempFile();
- assertXPath(pXmlDoc, "//reqif-xhtml:p[2]/reqif-xhtml:a/reqif-xhtml:object");
- CPPUNIT_ASSERT(
- getXPath(pXmlDoc, "//reqif-xhtml:p[2]/reqif-xhtml:a", "href").endsWith("foo/bar"));
-}
-
-CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, testHTML_161979)
-{
- // Given a document with two embedded metafiles:
- createSwDoc("tdf161979_metafile.fodt");
- ExportToHTML();
- xmlDocUniquePtr pDoc = parseXml(maTempFile);
- CPPUNIT_ASSERT(pDoc);
- // First image: it has no EMF+ actions, and didn't use canvas rendering before the fix;
- // yet, it didn't export correctly.
- OUString imgName = getXPath(pDoc, "/html/body/p[2]/img", "src");
- CPPUNIT_ASSERT(imgName.endsWith(".gif"));
- INetURLObject aUrl(maTempFile.GetURL());
- aUrl.setName(imgName);
- Graphic graphic;
- CPPUNIT_ASSERT_EQUAL(ERRCODE_NONE, GraphicFilter().ImportGraphic(graphic, aUrl));
-
- // Check that only ~4% of pixels are not transparent (before the fix, it was completely black)
- BitmapEx bitmap = graphic.GetBitmapEx();
- Size size = bitmap.GetSizePixel();
- int numNonTransparent = 0;
- for (tools::Long y = 0; y < size.Height(); ++y)
- for (tools::Long x = 0; x < size.Width(); ++x)
- if (bitmap.GetPixelColor(x, y) != COL_TRANSPARENT)
- ++numNonTransparent;
- CPPUNIT_ASSERT_DOUBLES_EQUAL(0.04, numNonTransparent / double(size.Height() * size.Width()),
- 0.01);
-
- // Second image: it consists of EMF+ records (no EMF fallback). It used canvas rendering
- // before the fix; it also didn't export correctly.
- imgName = getXPath(pDoc, "/html/body/p[4]/img", "src");
- CPPUNIT_ASSERT(imgName.endsWith(".gif"));
- aUrl.SetURL(maTempFile.GetURL());
- aUrl.setName(imgName);
- graphic.Clear();
- CPPUNIT_ASSERT_EQUAL(ERRCODE_NONE, GraphicFilter().ImportGraphic(graphic, aUrl));
-
- // Check that some pixels are transparent (before the fix, it was completely black)
- bitmap = graphic.GetBitmapEx();
- size = bitmap.GetSizePixel();
- numNonTransparent = 0;
- for (tools::Long y = 0; y < size.Height(); ++y)
- for (tools::Long x = 0; x < size.Width(); ++x)
- if (bitmap.GetPixelColor(x, y) != COL_TRANSPARENT)
- ++numNonTransparent;
- CPPUNIT_ASSERT(numNonTransparent > 0);
- CPPUNIT_ASSERT(numNonTransparent < size.Height() * size.Width());
-}
-
-CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, testReqIF_exportAbsoluteURLs_ownRelative)
-{
- auto pBatch(comphelper::ConfigurationChanges::create());
- Resetter resetter([
- bInternetPreviousValue = officecfg::Office::Common::Save::URL::Internet::get(),
- bFileSystemPreviousValue = officecfg::Office::Common::Save::URL::FileSystem::get(), pBatch
- ]() {
- officecfg::Office::Common::Save::URL::Internet::set(bInternetPreviousValue, pBatch);
- officecfg::Office::Common::Save::URL::FileSystem::set(bFileSystemPreviousValue, pBatch);
- return pBatch->commit();
- });
- // Set saving absolute URLs
- officecfg::Office::Common::Save::URL::Internet::set(false, pBatch);
- officecfg::Office::Common::Save::URL::FileSystem::set(false, pBatch);
- pBatch->commit();
-
- createSwDoc("URLs.odt");
- // Export to ReqIF, using absolute URLs
- saveWithParams({
- comphelper::makePropertyValue(u"FilterName"_ustr, u"HTML (StarWriter)"_ustr),
- comphelper::makePropertyValue(u"FilterOptions"_ustr, u"xhtmlns=reqif-xhtml"_ustr),
- comphelper::makePropertyValue(u"ExportImagesAsOLE"_ustr, true),
- comphelper::makePropertyValue(u"RelativeOwnObjectURL"_ustr, true),
- });
- xmlDocUniquePtr pXmlDoc = WrapReqifFromTempFile();
-
- // HTTP URL: must be absolute
- assertXPath(pXmlDoc, "//reqif-xhtml:p[1]/reqif-xhtml:a", "href", u"http://www.example.org/");
- // file URL: must be absolute
- assertXPath(pXmlDoc, "//reqif-xhtml:p[2]/reqif-xhtml:a", "href",
- createFileURL(u"NonExistingPath/NonExistingFile.html"));
- // form URL: must be absolute
- assertXPath(pXmlDoc, "//reqif-xhtml:form", "action", u"https://www.example.org/submit");
- // linked image exported as object: generated, must be relative
- OUString url = getXPath(pXmlDoc, "//reqif-xhtml:p[3]/reqif-xhtml:object", "data");
- CPPUNIT_ASSERT(!url.startsWith("file:"));
- CPPUNIT_ASSERT(url.endsWith(".ole"));
- // its original image URL: must be absolute
- assertXPath(pXmlDoc, "//reqif-xhtml:p[3]/reqif-xhtml:object/reqif-xhtml:object", "data",
- createFileURL(u"external.png"));
- // embedded image exported as object: generated, must be relative
- url = getXPath(pXmlDoc, "//reqif-xhtml:p[4]/reqif-xhtml:object", "data");
- CPPUNIT_ASSERT(!url.startsWith("file:"));
- CPPUNIT_ASSERT(url.endsWith(".ole"));
- // its image URL: generated, must be relative
- url = getXPath(pXmlDoc, "//reqif-xhtml:p[4]/reqif-xhtml:object/reqif-xhtml:object", "data");
- CPPUNIT_ASSERT(!url.startsWith("file:"));
- CPPUNIT_ASSERT(url.endsWith(".png"));
- // unordered list with image bullet - it gets embedded as base64 data
- OUString style = getXPath(pXmlDoc, "//reqif-xhtml:ul", "style");
- CPPUNIT_ASSERT(style.indexOf("list-style-image: url(data:image/png;base64,") != -1);
- // an as-char frame, exported as a whole to an object, must be relative
- url = getXPath(pXmlDoc, "//reqif-xhtml:p[5]/reqif-xhtml:object", "data");
- CPPUNIT_ASSERT(!url.startsWith("file:"));
- CPPUNIT_ASSERT(url.endsWith(".ole"));
- // its file hyperlink must be absolute
- assertXPath(pXmlDoc, "//reqif-xhtml:p[5]/reqif-xhtml:object/reqif-xhtml:a", "href",
- createFileURL(u"foo/bar"));
- // its image URL: generated, must be relative
- url = getXPath(
- pXmlDoc, "//reqif-xhtml:p[5]/reqif-xhtml:object/reqif-xhtml:a/reqif-xhtml:object", "data");
- CPPUNIT_ASSERT(!url.startsWith("file:"));
- CPPUNIT_ASSERT(url.endsWith(".png"));
-}
-
-CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, testReqIF_exportRelativeURLs)
-{
- CPPUNIT_ASSERT(officecfg::Office::Common::Save::URL::Internet::get());
- CPPUNIT_ASSERT(officecfg::Office::Common::Save::URL::FileSystem::get());
-
- createSwDoc("URLs.odt");
- // Export to ReqIF, using relative URLs (the default)
- saveWithParams({
- comphelper::makePropertyValue(u"FilterName"_ustr, u"HTML (StarWriter)"_ustr),
- comphelper::makePropertyValue(u"FilterOptions"_ustr, u"xhtmlns=reqif-xhtml"_ustr),
- comphelper::makePropertyValue(u"ExportImagesAsOLE"_ustr, true),
- });
- xmlDocUniquePtr pXmlDoc = WrapReqifFromTempFile();
-
- // HTTP URL: must be absolute
- assertXPath(pXmlDoc, "//reqif-xhtml:p[1]/reqif-xhtml:a", "href", u"http://www.example.org/");
- // file URL: must be relative
- OUString url = getXPath(pXmlDoc, "//reqif-xhtml:p[2]/reqif-xhtml:a", "href");
- CPPUNIT_ASSERT(!url.startsWith("file:"));
- CPPUNIT_ASSERT(url.endsWith("NonExistingPath/NonExistingFile.html"));
- // form URL: must be absolute
- assertXPath(pXmlDoc, "//reqif-xhtml:form", "action", u"https://www.example.org/submit");
- // linked image exported as object: generated, must be relative
- url = getXPath(pXmlDoc, "//reqif-xhtml:p[3]/reqif-xhtml:object", "data");
- CPPUNIT_ASSERT(!url.startsWith("file:"));
- CPPUNIT_ASSERT(url.endsWith(".ole"));
- // its original image URL: must be relative
- url = getXPath(pXmlDoc, "//reqif-xhtml:p[3]/reqif-xhtml:object/reqif-xhtml:object", "data");
- CPPUNIT_ASSERT(!url.startsWith("file:"));
- CPPUNIT_ASSERT(url.endsWith("external.png"));
- // embedded image exported as object: generated, must be relative
- url = getXPath(pXmlDoc, "//reqif-xhtml:p[4]/reqif-xhtml:object", "data");
- CPPUNIT_ASSERT(!url.startsWith("file:"));
- CPPUNIT_ASSERT(url.endsWith(".ole"));
- // its image URL: generated, must be relative
- url = getXPath(pXmlDoc, "//reqif-xhtml:p[4]/reqif-xhtml:object/reqif-xhtml:object", "data");
- CPPUNIT_ASSERT(!url.startsWith("file:"));
- CPPUNIT_ASSERT(url.endsWith(".png"));
- // unordered list with image bullet - it gets embedded as base64 data
- OUString style = getXPath(pXmlDoc, "//reqif-xhtml:ul", "style");
- CPPUNIT_ASSERT(style.indexOf("list-style-image: url(data:image/png;base64,") != -1);
- // an as-char frame, exported as a whole to an object, must be relative
- url = getXPath(pXmlDoc, "//reqif-xhtml:p[5]/reqif-xhtml:object", "data");
- CPPUNIT_ASSERT(!url.startsWith("file:"));
- CPPUNIT_ASSERT(url.endsWith(".ole"));
- // its file hyperlink must be relative
- url = getXPath(pXmlDoc, "//reqif-xhtml:p[5]/reqif-xhtml:object/reqif-xhtml:a", "href");
- CPPUNIT_ASSERT(!url.startsWith("file:"));
- CPPUNIT_ASSERT(url.endsWith("foo/bar"));
- // its image URL: generated, must be relative
- url = getXPath(
- pXmlDoc, "//reqif-xhtml:p[5]/reqif-xhtml:object/reqif-xhtml:a/reqif-xhtml:object", "data");
- CPPUNIT_ASSERT(!url.startsWith("file:"));
- CPPUNIT_ASSERT(url.endsWith(".png"));
-}
-
-CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, testHTML_exportAbsoluteURLs_ownRelative)
-{
- auto pBatch(comphelper::ConfigurationChanges::create());
- Resetter resetter([
- bInternetPreviousValue = officecfg::Office::Common::Save::URL::Internet::get(),
- bFileSystemPreviousValue = officecfg::Office::Common::Save::URL::FileSystem::get(), pBatch
- ]() {
- officecfg::Office::Common::Save::URL::Internet::set(bInternetPreviousValue, pBatch);
- officecfg::Office::Common::Save::URL::FileSystem::set(bFileSystemPreviousValue, pBatch);
- return pBatch->commit();
- });
- // Set saving absolute URLs
- officecfg::Office::Common::Save::URL::Internet::set(false, pBatch);
- officecfg::Office::Common::Save::URL::FileSystem::set(false, pBatch);
- pBatch->commit();
-
- createSwDoc("URLs.odt");
- // Export to HTML, using absolute URLs
- saveWithParams({
- comphelper::makePropertyValue(u"FilterName"_ustr, u"HTML (StarWriter)"_ustr),
- comphelper::makePropertyValue(u"RelativeOwnObjectURL"_ustr, true),
- });
- htmlDocUniquePtr pHtmlDoc = parseHtml(maTempFile);
-
- // HTTP URL: must be absolute
- assertXPath(pHtmlDoc, "//p[1]/a", "href", u"http://www.example.org/");
- // file URL: must be absolute
- assertXPath(pHtmlDoc, "//p[2]/a", "href",
- createFileURL(u"NonExistingPath/NonExistingFile.html"));
- // form URL: must be absolute
- assertXPath(pHtmlDoc, "//form", "action", u"https://www.example.org/submit");
- // linked image: must be absolute
- assertXPath(pHtmlDoc, "//p[3]/img", "src", createFileURL(u"external.png"));
- // embedded image: generated, must be relative
- OUString url = getXPath(pHtmlDoc, "//p[4]/img", "src");
- CPPUNIT_ASSERT(!url.startsWith("file:"));
- CPPUNIT_ASSERT(url.endsWith(".png"));
- // unordered list with image bullet - it gets embedded as base64 data
- OUString style = getXPath(pHtmlDoc, "//ul", "style");
- CPPUNIT_ASSERT(style.indexOf("list-style-image: url(data:image/png;base64,") != -1);
- // image-in-frame file hyperlink must be absolute; FIXME: HTMLOutFuncs::Out_ImageMap
- // assertXPath(pHtmlDoc, "//p[5]/map/area", "href", createFileURL(u"foo/bar"));
- // its image URL: generated, must be relative
- url = getXPath(pHtmlDoc, "//p[5]/img", "src");
- CPPUNIT_ASSERT(!url.startsWith("file:"));
- CPPUNIT_ASSERT(url.endsWith(".gif"));
-}
-
-CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, testHTML_exportRelativeURLs)
-{
- CPPUNIT_ASSERT(officecfg::Office::Common::Save::URL::Internet::get());
- CPPUNIT_ASSERT(officecfg::Office::Common::Save::URL::FileSystem::get());
-
- createSwDoc("URLs.odt");
- // Export to HTML, using relative URLs (the default)
- ExportToHTML();
- htmlDocUniquePtr pHtmlDoc = parseHtml(maTempFile);
-
- // HTTP URL: must be absolute
- assertXPath(pHtmlDoc, "//p[1]/a", "href", u"http://www.example.org/");
- // file URL: must be relative
- OUString url = getXPath(pHtmlDoc, "//p[2]/a", "href");
- CPPUNIT_ASSERT(!url.startsWith("file:"));
- CPPUNIT_ASSERT(url.endsWith("NonExistingPath/NonExistingFile.html"));
- // form URL: must be absolute
- assertXPath(pHtmlDoc, "//form", "action", u"https://www.example.org/submit");
- // linked image: must be relative
- url = getXPath(pHtmlDoc, "//p[3]/img", "src");
- CPPUNIT_ASSERT(!url.startsWith("file:"));
- CPPUNIT_ASSERT(url.endsWith("external.png"));
- // embedded image: generated, must be relative
- url = getXPath(pHtmlDoc, "//p[4]/img", "src");
- CPPUNIT_ASSERT(!url.startsWith("file:"));
- CPPUNIT_ASSERT(url.endsWith(".png"));
- // unordered list with image bullet - it gets embedded as base64 data
- OUString style = getXPath(pHtmlDoc, "//ul", "style");
- CPPUNIT_ASSERT(style.indexOf("list-style-image: url(data:image/png;base64,") != -1);
- // image-in-frame file hyperlink must be relative
- url = getXPath(pHtmlDoc, "//p[5]/map/area", "href");
- CPPUNIT_ASSERT(!url.startsWith("file:"));
- CPPUNIT_ASSERT(url.endsWith("foo/bar"));
- // its image URL: generated, must be relative
- url = getXPath(pHtmlDoc, "//p[5]/img", "src");
- CPPUNIT_ASSERT(!url.startsWith("file:"));
- CPPUNIT_ASSERT(url.endsWith(".gif"));
-}
-
-CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, testReqIF_162282)
-{
- // Given a document with an embedded metafile:
- createSwDoc("tdf162282.odt");
- ExportToReqif();
- xmlDocUniquePtr pDoc = WrapReqifFromTempFile();
-
- // Check that the exported EMF is exactly the same as in the ODF package
- assertXPath(pDoc, "//reqif-xhtml:p/reqif-xhtml:object", "type", u"image/x-emf");
- OUString imgName = getXPath(pDoc, "//reqif-xhtml:p/reqif-xhtml:object", "data");
- CPPUNIT_ASSERT(imgName.endsWith(".emf"));
- INetURLObject aUrl(maTempFile.GetURL());
- aUrl.setName(imgName);
- SvFileStream aEmfStream(aUrl.GetMainURL(INetURLObject::DecodeMechanism::NONE),
- StreamMode::READ);
-
- // without the fix, this would fail with
- // - Expected: 220
- // - Actual : 111260
- CPPUNIT_ASSERT_EQUAL(sal_uInt64(220), aEmfStream.TellEnd());
-
- css::uno::Sequence emfData(220);
- aEmfStream.ReadBytes(emfData.getArray(), emfData.getLength());
-
- const css::uno::Sequence correctData{
- 0x01, 0x00, 0x00, 0x00, 0x6C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0xF4, 0x01, 0x00, 0x00, 0xF4, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x88, 0x13, 0x00, 0x00, 0x88, 0x13, 0x00, 0x00, 0x20, 0x45, 0x4D, 0x46, 0x00,
- 0x00, 0x01, 0x00, 0xDC, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x07, 0x00,
- 0x00, 0x38, 0x04, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x6C, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF4, 0x01, 0x00, 0x00, 0xF4,
- 0x01, 0x00, 0x00, 0x39, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
- 0x2D, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0x96, 0x00, 0x00,
- 0x00, 0xFA, 0x00, 0x00, 0x00, 0x2C, 0x01, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00,
- 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x2F, 0x00, 0x00, 0x00, 0x28,
- 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x2C, 0x01, 0x00, 0x00,
- 0x2C, 0x01, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x2C, 0x01, 0x00,
- 0x00, 0xC8, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00,
- };
- CPPUNIT_ASSERT_EQUAL(correctData, emfData);
-}
-
-CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, testHTML_162426)
-{
- // Given a document with an image with style:wrap="none":
- createSwDoc("tdf162426_image_with_wrap_none.fodt");
- // Before the fix, an assertion failed in HtmlWriter::attribute when exporting to HTML :
- ExportToHTML();
-
- xmlDocUniquePtr pDoc = parseXml(maTempFile);
- CPPUNIT_ASSERT(pDoc);
-
- // Before the fix, the 'border' attribute was written after the 'img' tag was already closed,
- // so without the assertion, this would fail with
- // - In <>, XPath '/html/body/p/img' no attribute 'border' exist
- assertXPath(pDoc, "/html/body/p/img", "border", u"0");
-}
-
-CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, testHTML_163873)
-{
- // Given a document with an image with style:wrap="none":
- createSwDoc("tdf131728.docx");
- // Before the fix, an assertion failed in HtmlWriter::attribute when exporting to HTML :
- ExportToHTML();
-
- xmlDocUniquePtr pDoc = parseXml(maTempFile);
- CPPUNIT_ASSERT(pDoc);
-
- // Before the fix, inline headings weren't inline
- assertXPath(pDoc, "/html/body/p[5]/span/h2", "style", u"display:inline;");
- assertXPath(pDoc, "/html/body/p[6]/span/h2", "style", u"display:inline;");
- assertXPath(pDoc, "/html/body/p[7]/span/h2", "style", u"display:inline;");
- assertXPath(pDoc, "/html/body/p[11]/span/h2", "style", u"display:inline;");
- assertXPath(pDoc, "/html/body/p[14]/span/h2", "style", u"display:inline;");
-}
-
} // end of anonymous namespace
CPPUNIT_PLUGIN_IMPLEMENT();
diff --git a/sw/qa/extras/htmlexport/htmlexport2.cxx b/sw/qa/extras/htmlexport/htmlexport2.cxx
new file mode 100644
index 000000000000..99e9d5347b15
--- /dev/null
+++ b/sw/qa/extras/htmlexport/htmlexport2.cxx
@@ -0,0 +1,1612 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include "htmlmodeltestbase.hxx"
+
+#include
+
+#include
+#include
+#include
+
+#include
+
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+namespace
+{
+CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, testReqifEmbedPNGShapeAsOLE)
+{
+ // Given a document with an image shape:
+ createSwDoc();
+ uno::Reference xFactory(mxComponent, uno::UNO_QUERY);
+ uno::Reference xShape(
+ xFactory->createInstance(u"com.sun.star.drawing.GraphicObjectShape"_ustr), uno::UNO_QUERY);
+ xShape->setSize(awt::Size(10000, 10000));
+ uno::Reference xShapeProps(xShape, uno::UNO_QUERY);
+ xShapeProps->setPropertyValue(u"GraphicURL"_ustr, uno::Any(createFileURL(u"ole2.png")));
+ uno::Reference xDrawPageSupplier(mxComponent, uno::UNO_QUERY);
+ xDrawPageSupplier->getDrawPage()->add(xShape);
+
+ // When exporting to XHTML:
+ uno::Sequence aStoreProperties = {
+ comphelper::makePropertyValue(u"FilterName"_ustr, u"HTML (StarWriter)"_ustr),
+ comphelper::makePropertyValue(u"FilterOptions"_ustr, u"xhtmlns=reqif-xhtml"_ustr),
+ comphelper::makePropertyValue(u"ExportImagesAsOLE"_ustr, true),
+ };
+ saveWithParams(aStoreProperties);
+
+ // Then make sure the PNG is embedded with an RTF wrapper:
+ xmlDocUniquePtr pXmlDoc = WrapReqifFromTempFile();
+ // Without the accompanying fix in place, this test would have failed with:
+ // - Expected: text/rtf
+ // - Actual : image/png
+ // i.e. the OLE wrapper around the PNG was missing.
+ assertXPath(pXmlDoc, "//reqif-xhtml:p/reqif-xhtml:object", "type", u"text/rtf");
+}
+
+CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, testReqifEmbedShapeAsPNG)
+{
+ // FIXME: the DPI check should be removed when either (1) the test is fixed to work with
+ // non-default DPI; or (2) unit tests on Windows are made to use svp VCL plugin.
+ if (!IsDefaultDPI())
+ return;
+ // Given a document with a shape:
+ createSwDoc();
+ uno::Reference xFactory(mxComponent, uno::UNO_QUERY);
+ uno::Reference xShape(
+ xFactory->createInstance(u"com.sun.star.drawing.RectangleShape"_ustr), uno::UNO_QUERY);
+ xShape->setSize(awt::Size(10000, 10000));
+ uno::Reference xDrawPageSupplier(mxComponent, uno::UNO_QUERY);
+ xDrawPageSupplier->getDrawPage()->add(xShape);
+
+ // When exporting to XHTML:
+ ExportToReqif();
+
+ // Then make sure the shape is embedded as a PNG:
+ xmlDocUniquePtr pXmlDoc = WrapReqifFromTempFile();
+ // Without the accompanying fix in place, this test would have failed with:
+ // - Expected: image/png
+ // - Actual : image/x-vclgraphic
+ // i.e. the result was invalid ReqIF.
+ assertXPath(pXmlDoc, "//reqif-xhtml:p/reqif-xhtml:object", "type", u"image/png");
+
+ // Then check the pixel size of the shape:
+ Size aPixelSize(Application::GetDefaultDevice()->LogicToPixel(Size(10000, 10000),
+ MapMode(MapUnit::Map100thMM)));
+ // Without the accompanying fix in place, this test would have failed with:
+ // - no attribute 'width' exist
+ // i.e. shapes had no width.
+ assertXPath(pXmlDoc, "//reqif-xhtml:p/reqif-xhtml:object", "width",
+ OUString::number(aPixelSize.getWidth()));
+}
+
+CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, testShapeAsImageHtml)
+{
+ // Given a document with a shape:
+ createSwDoc();
+ uno::Reference xFactory(mxComponent, uno::UNO_QUERY);
+ uno::Reference xShape(
+ xFactory->createInstance(u"com.sun.star.drawing.RectangleShape"_ustr), uno::UNO_QUERY);
+ xShape->setSize(awt::Size(5080, 2540));
+ uno::Reference xDrawPageSupplier(mxComponent, uno::UNO_QUERY);
+ xDrawPageSupplier->getDrawPage()->add(xShape);
+
+ // When exporting to plain HTML:
+ saveAndReload(u"HTML (StarWriter)"_ustr);
+
+ // Without the accompanying fix in place, this test would have failed with:
+ // - Expected:
+ // - Actual : />
+ // i.e. the output was not well-formed.
+ CPPUNIT_ASSERT_EQUAL(u" "_ustr, getParagraph(1)->getString());
+}
+
+CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, testJson)
+{
+ // Given a document with a shape:
+ createSwDoc();
+ uno::Reference xFactory(mxComponent, uno::UNO_QUERY);
+ uno::Reference xShape(
+ xFactory->createInstance(u"com.sun.star.drawing.RectangleShape"_ustr), uno::UNO_QUERY);
+ xShape->setSize(awt::Size(2540, 2540));
+ uno::Reference xDrawPageSupplier(mxComponent, uno::UNO_QUERY);
+ xDrawPageSupplier->getDrawPage()->add(xShape);
+
+ // When exporting to HTML, and specifying options as JSON:
+ setFilterOptions(u"{\"XhtmlNs\":{\"type\":\"string\", \"value\":\"reqif-xhtml\"},"
+ "\"ShapeDPI\":{\"type\":\"long\",\"value\":\"192\"}}"_ustr);
+ save(u"HTML (StarWriter)"_ustr);
+
+ // Then make sure those options are not ignored:
+ // Without the accompanying fix in place, this test would have failed, as GetPngPath() expects
+ // XML output, but xhtmlns=reqif-xhtml was ignored.
+ OUString aPngUrl = GetPngPath();
+ SvFileStream aFileStream(aPngUrl, StreamMode::READ);
+ GraphicDescriptor aDescriptor(aFileStream, nullptr);
+ aDescriptor.Detect(/*bExtendedInfo=*/true);
+ // Make sure that the increased DPI is taken into account:
+ tools::Long nExpected = 192;
+ CPPUNIT_ASSERT_EQUAL(nExpected, aDescriptor.GetSizePixel().getWidth());
+}
+
+CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, testReqifEmbedShapeAsPNGCustomDPI)
+{
+ // FIXME: the DPI check should be removed when either (1) the test is fixed to work with
+ // non-default DPI; or (2) unit tests on Windows are made to use svp VCL plugin.
+ if (!IsDefaultDPI())
+ return;
+ // Given a document with a shape:
+ createSwDoc();
+ uno::Reference xFactory(mxComponent, uno::UNO_QUERY);
+ uno::Reference xShape(
+ xFactory->createInstance(u"com.sun.star.drawing.RectangleShape"_ustr), uno::UNO_QUERY);
+ xShape->setSize(awt::Size(5080, 2540));
+ uno::Reference xDrawPageSupplier(mxComponent, uno::UNO_QUERY);
+ xDrawPageSupplier->getDrawPage()->add(xShape);
+ sal_Int32 nDPI = 600;
+
+ // When exporting to XHTML:
+ uno::Sequence aStoreProperties = {
+ comphelper::makePropertyValue(u"FilterName"_ustr, u"HTML (StarWriter)"_ustr),
+ comphelper::makePropertyValue(u"FilterOptions"_ustr, u"xhtmlns=reqif-xhtml"_ustr),
+ comphelper::makePropertyValue(u"ShapeDPI"_ustr, nDPI),
+ };
+ saveWithParams(aStoreProperties);
+
+ // Then make sure the shape is embedded as a PNG:
+ xmlDocUniquePtr pXmlDoc = WrapReqifFromTempFile();
+ assertXPath(pXmlDoc, "//reqif-xhtml:p/reqif-xhtml:object", "type", u"image/png");
+
+ // Then check the pixel size of the shape:
+ Size aPixelSize(Application::GetDefaultDevice()->LogicToPixel(Size(5080, 2540),
+ MapMode(MapUnit::Map100thMM)));
+ tools::Long nPNGWidth = 1200;
+ OUString aPngUrl = GetPngPath();
+ SvFileStream aFileStream(aPngUrl, StreamMode::READ);
+ GraphicDescriptor aDescriptor(aFileStream, nullptr);
+ aDescriptor.Detect(/*bExtendedInfo=*/true);
+ // Without the accompanying fix in place, this test would have failed with:
+ // - Expected: 1200
+ // - Actual : 1000
+ // i.e. first setting a double DPI didn't result in larger pixel width of the PNG, then it was
+ // limited to 1000 pixels (because the pixel limit was 500k).
+ CPPUNIT_ASSERT_EQUAL(nPNGWidth, aDescriptor.GetSizePixel().getWidth());
+
+ // Then make sure the shape's logic size (in CSS pixels) don't change:
+ assertXPath(pXmlDoc, "//reqif-xhtml:p/reqif-xhtml:object", "width",
+ OUString::number(aPixelSize.getWidth()));
+}
+
+CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, testReqifOleBmpTransparent)
+{
+ // Given a document with a transparent image:
+ createSwDoc();
+ uno::Sequence aArgs = {
+ comphelper::makePropertyValue(u"FileName"_ustr, createFileURL(u"transparent.png")),
+ };
+ dispatchCommand(mxComponent, u".uno:InsertGraphic"_ustr, aArgs);
+
+ // When exporting to reqif with ExportImagesAsOLE=true:
+ uno::Sequence aStoreProperties = {
+ comphelper::makePropertyValue(u"FilterName"_ustr, u"HTML (StarWriter)"_ustr),
+ comphelper::makePropertyValue(u"FilterOptions"_ustr, u"xhtmlns=reqif-xhtml"_ustr),
+ comphelper::makePropertyValue(u"ExportImagesAsOLE"_ustr, true),
+ };
+ saveWithParams(aStoreProperties);
+
+ // Then make sure the transparent pixel turns into white:
+ SvMemoryStream aOle1;
+ ParseOle1FromRtfUrl(GetOlePath(), aOle1);
+ OLE1Reader aOle1Reader(aOle1);
+ SvMemoryStream aBitmapStream(aOle1Reader.m_aNativeData.data(), aOle1Reader.m_aNativeData.size(),
+ StreamMode::READ);
+ Bitmap aBitmap;
+ ReadDIB(aBitmap, aBitmapStream, /*bFileHeader=*/true);
+ Size aBitmapSize = aBitmap.GetSizePixel();
+ BitmapEx aBitmapEx(aBitmap);
+ Color nActualColor
+ = aBitmapEx.GetPixelColor(aBitmapSize.getWidth() - 1, aBitmapSize.getHeight() - 1);
+ // Without the accompanying fix in place, this test would have failed with:
+ // - Expected: Color: R:255 G:255 B:255 A:0
+ // - Actual : Color: R:0 G:0 B:0 A:0
+ // i.e. the bitmap without an alpha channel was black, not white.
+ CPPUNIT_ASSERT_EQUAL(COL_WHITE, nActualColor);
+}
+
+CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, testListsHeading)
+{
+ // Given a document with lh, lh, li, li, lh and lh nodes:
+ createSwDoc();
+ SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell();
+ pWrtShell->Insert(u"list 1, header 1"_ustr);
+ pWrtShell->SplitNode();
+ pWrtShell->Insert(u"list 1, header 2"_ustr);
+ pWrtShell->SplitNode();
+ pWrtShell->Insert(u"list 2, item 1"_ustr);
+ pWrtShell->SplitNode();
+ pWrtShell->Insert(u"list 2, item 2"_ustr);
+ pWrtShell->SplitNode();
+ pWrtShell->Insert(u"list 3, header 1"_ustr);
+ pWrtShell->SplitNode();
+ pWrtShell->Insert(u"list 3, header 2"_ustr);
+ SwDoc* pDoc = pWrtShell->GetDoc();
+ pWrtShell->Up(false, 5);
+ {
+ sal_uInt16 nPos = pDoc->MakeNumRule(pDoc->GetUniqueNumRuleName());
+ SwNumRule* pNumRule = pDoc->GetNumRuleTable()[nPos];
+ {
+ SwNode& rNode = pWrtShell->GetCursor()->GetPoint()->GetNode();
+ SwTextNode& rTextNode = *rNode.GetTextNode();
+ rTextNode.SetAttr(SwNumRuleItem(pNumRule->GetName()));
+ rTextNode.SetCountedInList(false);
+ }
+ pWrtShell->Down(false, 1);
+ {
+ SwNode& rNode = pWrtShell->GetCursor()->GetPoint()->GetNode();
+ SwTextNode& rTextNode = *rNode.GetTextNode();
+ rTextNode.SetAttr(SwNumRuleItem(pNumRule->GetName()));
+ rTextNode.SetCountedInList(false);
+ }
+ }
+ pWrtShell->Down(false, 1);
+ {
+ sal_uInt16 nPos = pDoc->MakeNumRule(pDoc->GetUniqueNumRuleName());
+ SwNumRule* pNumRule = pDoc->GetNumRuleTable()[nPos];
+ {
+ SwNode& rNode = pWrtShell->GetCursor()->GetPoint()->GetNode();
+ SwTextNode& rTextNode = *rNode.GetTextNode();
+ rTextNode.SetAttr(SwNumRuleItem(pNumRule->GetName()));
+ }
+ pWrtShell->Down(false, 1);
+ {
+ SwNode& rNode = pWrtShell->GetCursor()->GetPoint()->GetNode();
+ SwTextNode& rTextNode = *rNode.GetTextNode();
+ rTextNode.SetAttr(SwNumRuleItem(pNumRule->GetName()));
+ }
+ }
+ pWrtShell->Down(false, 1);
+ {
+ sal_uInt16 nPos = pDoc->MakeNumRule(pDoc->GetUniqueNumRuleName());
+ SwNumRule* pNumRule = pDoc->GetNumRuleTable()[nPos];
+ {
+ SwNode& rNode = pWrtShell->GetCursor()->GetPoint()->GetNode();
+ SwTextNode& rTextNode = *rNode.GetTextNode();
+ rTextNode.SetAttr(SwNumRuleItem(pNumRule->GetName()));
+ rTextNode.SetCountedInList(false);
+ }
+ pWrtShell->Down(false, 1);
+ {
+ SwNode& rNode = pWrtShell->GetCursor()->GetPoint()->GetNode();
+ SwTextNode& rTextNode = *rNode.GetTextNode();
+ rTextNode.SetAttr(SwNumRuleItem(pNumRule->GetName()));
+ rTextNode.SetCountedInList(false);
+ }
+ }
+
+ // When exporting to ReqIF:
+ ExportToReqif();
+
+ // Then make sure the output is valid xhtml:
+ xmlDocUniquePtr pXmlDoc = WrapReqifFromTempFile();
+
+ OUString aContent
+ = getXPathContent(pXmlDoc, "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:ol/"
+ "reqif-xhtml:li[@style='display: block']/reqif-xhtml:p");
+ CPPUNIT_ASSERT_EQUAL(u"list 1, header 1"_ustr, aContent.trim());
+}
+
+CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, testOleEmfPreviewToHtml)
+{
+ // Given a document containing an embedded object, with EMF preview:
+ createSwDoc("ole2.odt");
+
+ // When exporting to HTML:
+ ExportToHTML();
+
+ // Then make sure the tag has matching file extension and data:
+ htmlDocUniquePtr pDoc = parseHtml(maTempFile);
+ OUString aPath = getXPath(pDoc, "/html/body/p/img", "src");
+ // Without the accompanying fix in place, this test would have failed, as aPath was
+ // ole_html_3978e5f373402b43.JPG, with EMF data.
+ CPPUNIT_ASSERT(aPath.endsWith("gif"));
+}
+
+CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, testNestedBullets)
+{
+ // Given a documented with nested lists:
+ createSwDoc();
+ SwDoc* pDoc = getSwDoc();
+ SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell();
+ pWrtShell->Insert(u"first"_ustr);
+ sal_uInt16 nPos = pDoc->MakeNumRule(pDoc->GetUniqueNumRuleName());
+ SwNumRule* pNumRule = pDoc->GetNumRuleTable()[nPos];
+ {
+ SwNode& rNode = pWrtShell->GetCursor()->GetPoint()->GetNode();
+ SwTextNode& rTextNode = *rNode.GetTextNode();
+ rTextNode.SetAttr(SwNumRuleItem(pNumRule->GetName()));
+ rTextNode.SetAttrListLevel(0);
+ }
+ pWrtShell->SplitNode();
+ pWrtShell->Insert(u"second"_ustr);
+ {
+ SwNode& rNode = pWrtShell->GetCursor()->GetPoint()->GetNode();
+ SwTextNode& rTextNode = *rNode.GetTextNode();
+ rTextNode.SetAttr(SwNumRuleItem(pNumRule->GetName()));
+ rTextNode.SetAttrListLevel(1);
+ }
+
+ // When exporting to xhtml:
+ ExportToReqif();
+
+ // Then make sure that there is a - between the outer and the inner
:
+ xmlDocUniquePtr pXmlDoc = WrapReqifFromTempFile();
+ // Without the accompanying fix in place, this test would have failed with:
+ // - XPath '//reqif-xhtml:ol/reqif-xhtml:li/reqif-xhtml:ol/reqif-xhtml:li/reqif-xhtml:p' not found
+ // i.e. the - inside the outer
was missing.
+ assertXPathContent(
+ pXmlDoc, "//reqif-xhtml:ol/reqif-xhtml:li/reqif-xhtml:ol/reqif-xhtml:li/reqif-xhtml:p",
+ u"second");
+}
+
+CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, testTrailingLineBreak)
+{
+ // Given a document with a trailing line-break:
+ createSwDoc();
+ SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell();
+ pWrtShell->Insert(u"test\n"_ustr);
+
+ // When exporting to reqif-xhtml:
+ ExportToReqif();
+
+ // Then make sure that we still have a single line-break:
+ xmlDocUniquePtr pXmlDoc = WrapReqifFromTempFile();
+ // Without the accompanying fix in place, this test would have failed with:
+ // - Expected: 1
+ // - Actual : 2
+ // - XPath '//reqif-xhtml:br' number of nodes is incorrect
+ assertXPath(pXmlDoc, "//reqif-xhtml:br", 1);
+
+ // Then test the import side:
+ // Given an empty document:
+ // When importing a
from reqif-xhtml:
+ ImportFromReqif(maTempFile.GetURL());
+
+ // Then make sure that line-break is not lost:
+ pWrtShell = getSwDocShell()->GetWrtShell();
+ OUString aActual = pWrtShell->GetCursor()->GetPointNode().GetTextNode()->GetText();
+ // Without the accompanying fix in place, this test would have failed, as the trailing
+ // line-break was lost.
+ CPPUNIT_ASSERT_EQUAL(u"test\n"_ustr, aActual);
+}
+
+CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, testLeadingTab)
+{
+ // Given a document with leading tabs:
+ createSwDoc();
+ SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell();
+ pWrtShell->Insert(u"\t first"_ustr);
+ pWrtShell->SplitNode();
+ pWrtShell->Insert(u"\t\t second"_ustr);
+ pWrtShell->SplitNode();
+ pWrtShell->Insert(u"thi \t rd"_ustr);
+
+ // When exporting to HTML, using LeadingTabWidth=2:
+ uno::Sequence aStoreProperties = {
+ comphelper::makePropertyValue(u"FilterName"_ustr, u"HTML (StarWriter)"_ustr),
+ comphelper::makePropertyValue(u"FilterOptions"_ustr, u"xhtmlns=reqif-xhtml"_ustr),
+ comphelper::makePropertyValue(u"LeadingTabWidth"_ustr, static_cast(2)),
+ };
+ saveWithParams(aStoreProperties);
+
+ // Then make sure that leading tabs are replaced with 2 nbsps:
+ xmlDocUniquePtr pXmlDoc = WrapReqifFromTempFile();
+ // Without the accompanying fix in place, this test would have failed with:
+ // - Expected: first
+ // - Actual : first
+ // i.e. the leading tab was not replaced by 2 nbsps.
+ assertXPathContent(pXmlDoc, "//reqif-xhtml:p[1]", u"\xa0\xa0 first");
+ // Test a leading tab that is not at the start of the paragraph:
+ assertXPathContent(pXmlDoc, "//reqif-xhtml:p[2]", u"\xa0\xa0\xa0\xa0 second");
+ // Test a tab which is not leading:
+ assertXPathContent(pXmlDoc, "//reqif-xhtml:p[3]", u"thi \t rd");
+}
+
+CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, testLeadingTabHTML)
+{
+ // Given a document with leading tabs:
+ createSwDoc();
+ SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell();
+ pWrtShell->Insert(u"\t test"_ustr);
+
+ // When exporting to plain HTML, using LeadingTabWidth=2:
+ uno::Sequence aStoreProperties = {
+ comphelper::makePropertyValue(u"FilterName"_ustr, u"HTML (StarWriter)"_ustr),
+ comphelper::makePropertyValue(u"LeadingTabWidth"_ustr, static_cast(2)),
+ };
+ saveWithParams(aStoreProperties);
+
+ // Then make sure that leading tabs are replaced with 2 nbsps:
+ htmlDocUniquePtr pHtmlDoc = parseHtml(maTempFile);
+ CPPUNIT_ASSERT(pHtmlDoc);
+ // Without the accompanying fix in place, this test would have failed with:
+ // - Expected: test
+ // - Actual : test
+ // i.e. the leading tab was not replaced by 2 nbsps.
+ assertXPathContent(pHtmlDoc, "/html/body/p", SAL_NEWLINE_STRING u"\xa0\xa0 test");
+}
+
+CPPUNIT_TEST_FIXTURE(HtmlExportTest, testClearingBreak)
+{
+ auto verify = [this]() {
+ uno::Reference xParagraph(getParagraph(1), uno::UNO_QUERY);
+ uno::Reference xPortions = xParagraph->createEnumeration();
+ uno::Reference xPortion;
+ OUString aPortionType;
+ while (true)
+ {
+ // Ignore leading comments.
+ xPortion.set(xPortions->nextElement(), uno::UNO_QUERY);
+ xPortion->getPropertyValue(u"TextPortionType"_ustr) >>= aPortionType;
+ if (aPortionType != "Annotation")
+ {
+ break;
+ }
+ }
+ // Skip "foo".
+ // Without the accompanying fix in place, this test would have failed with:
+ // An uncaught exception of type com.sun.star.container.NoSuchElementException
+ // i.e. the first para was just comments + text portion, the clearing break was lost.
+ xPortion.set(xPortions->nextElement(), uno::UNO_QUERY);
+ xPortion->getPropertyValue(u"TextPortionType"_ustr) >>= aPortionType;
+ CPPUNIT_ASSERT_EQUAL(u"LineBreak"_ustr, aPortionType);
+ uno::Reference xLineBreak;
+ xPortion->getPropertyValue(u"LineBreak"_ustr) >>= xLineBreak;
+ sal_Int16 eClear{};
+ uno::Reference xLineBreakProps(xLineBreak, uno::UNO_QUERY);
+ xLineBreakProps->getPropertyValue(u"Clear"_ustr) >>= eClear;
+ CPPUNIT_ASSERT_EQUAL(static_cast(SwLineBreakClear::ALL), eClear);
+ };
+
+ // Given a document with an at-para anchored image + a clearing break:
+ // When loading that file:
+ createSwWebDoc("clearing-break.html");
+ // Then make sure that the clear property of the break is not ignored:
+ verify();
+ saveAndReload(mpFilter);
+ // Make sure that the clear property of the break is not ignored during export:
+ verify();
+}
+
+CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, testTableBackground)
+{
+ // Given a document with two tables: first stable has a background, second table has a
+ // background in its first row:
+ createSwDoc();
+ SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell();
+ SwInsertTableOptions aInsertTableOptions(SwInsertTableFlags::DefaultBorder,
+ /*nRowsToRepeat=*/0);
+ pWrtShell->InsertTable(aInsertTableOptions, /*nRows=*/1, /*nCols=*/1);
+ pWrtShell->MoveTable(GotoPrevTable, fnTableStart);
+ SvxBrushItem aBrush(COL_LIGHTRED, RES_BACKGROUND);
+ pWrtShell->SetTabBackground(aBrush);
+ pWrtShell->Down(/*bSelect=*/false);
+ pWrtShell->SplitNode();
+ pWrtShell->InsertTable(aInsertTableOptions, /*nRows=*/2, /*nCols=*/1);
+ pWrtShell->MoveTable(GotoPrevTable, fnTableStart);
+ aBrush.SetColor(COL_LIGHTGREEN);
+ pWrtShell->SetRowBackground(aBrush);
+ pWrtShell->Down(/*bSelect=*/false);
+ // Second row has an explicit transparent background.
+ aBrush.SetColor(COL_TRANSPARENT);
+ pWrtShell->SetRowBackground(aBrush);
+
+ // When exporting to reqif-xhtml:
+ ExportToReqif();
+
+ // Then make sure that CSS markup is used, not HTML one:
+ xmlDocUniquePtr pXmlDoc = WrapReqifFromTempFile();
+ // Without the accompanying fix in place, this test would have failed with:
+ // - XPath '//reqif-xhtml:table[1]' no attribute 'style' exist
+ // i.e. HTML markup was used for the table background color.
+ assertXPath(pXmlDoc, "//reqif-xhtml:table[1]", "style", u"background: #ff0000");
+ assertXPathNoAttribute(pXmlDoc, "//reqif-xhtml:table[1]", "bgcolor");
+ assertXPath(pXmlDoc, "//reqif-xhtml:table[2]/reqif-xhtml:tr[1]", "style",
+ u"background: #00ff00");
+ assertXPathNoAttribute(pXmlDoc, "//reqif-xhtml:table[2]/reqif-xhtml:tr[1]", "bgcolor");
+ // Second row has no explicit style, the default is not written.
+ assertXPathNoAttribute(pXmlDoc, "//reqif-xhtml:table[2]/reqif-xhtml:tr[2]", "style");
+}
+
+CPPUNIT_TEST_FIXTURE(HtmlExportTest, testImageKeepRatio)
+{
+ // Given a document with an image: width is relative, height is "keep ratio":
+ createSwDoc();
+ uno::Reference xFactory(mxComponent, uno::UNO_QUERY);
+ uno::Reference xTextGraphic(
+ xFactory->createInstance(u"com.sun.star.text.TextGraphicObject"_ustr), uno::UNO_QUERY);
+ xTextGraphic->setPropertyValue(u"AnchorType"_ustr,
+ uno::Any(text::TextContentAnchorType_AS_CHARACTER));
+ xTextGraphic->setPropertyValue(u"RelativeWidth"_ustr, uno::Any(static_cast(42)));
+ xTextGraphic->setPropertyValue(u"IsSyncHeightToWidth"_ustr, uno::Any(true));
+ uno::Reference xTextDocument(mxComponent, uno::UNO_QUERY);
+ uno::Reference xBodyText = xTextDocument->getText();
+ uno::Reference xCursor(xBodyText->createTextCursor());
+ uno::Reference xTextContent(xTextGraphic, uno::UNO_QUERY);
+ xBodyText->insertTextContent(xCursor, xTextContent, false);
+
+ // When exporting to HTML:
+ save(mpFilter);
+
+ // Then make sure that the width is not a fixed size, that would break on resizing the browser
+ // window:
+ htmlDocUniquePtr pDoc = parseHtml(maTempFile);
+ // Without the accompanying fix in place, this test would have failed with:
+ // - Expected: auto
+ // - Actual : 2
+ // i.e. a static (CSS pixel) height was written.
+ assertXPath(pDoc, "/html/body/p/img", "height", u"auto");
+}
+
+CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, testSectionDir)
+{
+ // Given a document with a section:
+ createSwDoc();
+ SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell();
+ pWrtShell->Insert(u"test"_ustr);
+ pWrtShell->SelAll();
+ SwSectionData aSectionData(SectionType::Content, u"mysect"_ustr);
+ pWrtShell->InsertSection(aSectionData);
+
+ // When exporting to (reqif-)xhtml:
+ ExportToReqif();
+
+ // Then make sure CSS is used to export the text direction of the section:
+ xmlDocUniquePtr pXmlDoc = WrapReqifFromTempFile();
+ // Without the accompanying fix in place, this test would have failed with:
+ // - XPath '//reqif-xhtml:div[@id='mysect']' no attribute 'style' exist
+ // i.e. the dir="ltr" HTML attribute was used instead.
+ assertXPath(pXmlDoc, "//reqif-xhtml:div[@id='mysect']", "style", u"dir: ltr");
+}
+
+CPPUNIT_TEST_FIXTURE(HtmlExportTest, testTdf114769)
+{
+ // Create document from scratch since relative urls to filesystem can be replaced
+ // by absolute during save/load
+ createSwDoc();
+ SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell();
+ pWrtShell->Insert(u"Hyperlink1"_ustr);
+ pWrtShell->SplitNode();
+ pWrtShell->Insert(u"Hyperlink2"_ustr);
+ pWrtShell->SplitNode();
+ pWrtShell->Insert(u"Hyperlink3"_ustr);
+ pWrtShell->SplitNode();
+ pWrtShell->Insert(u"Hyperlink4"_ustr);
+ pWrtShell->SplitNode();
+ pWrtShell->Insert(u"Hyperlink5"_ustr);
+ pWrtShell->SplitNode();
+
+ // Normal external URL
+ uno::Reference xRun(getRun(getParagraph(1), 1), uno::UNO_QUERY);
+ xRun->setPropertyValue(u"HyperLinkURL"_ustr, uno::Any(u"http://libreoffice.org/"_ustr));
+
+ // Bookmark reference
+ xRun.set(getRun(getParagraph(2), 1), uno::UNO_QUERY);
+ xRun->setPropertyValue(u"HyperLinkURL"_ustr, uno::Any(u"#some_bookmark"_ustr));
+
+ // Filesystem absolute link
+ xRun.set(getRun(getParagraph(3), 1), uno::UNO_QUERY);
+ xRun->setPropertyValue(u"HyperLinkURL"_ustr, uno::Any(u"C:\\test.txt"_ustr));
+
+ // Filesystem relative link
+ xRun.set(getRun(getParagraph(4), 1), uno::UNO_QUERY);
+ xRun->setPropertyValue(u"HyperLinkURL"_ustr, uno::Any(u"..\\..\\test.odt"_ustr));
+
+ // Filesystem relative link
+ xRun.set(getRun(getParagraph(5), 1), uno::UNO_QUERY);
+ xRun->setPropertyValue(u"HyperLinkURL"_ustr, uno::Any(u".\\another.odt"_ustr));
+
+ // Export
+ save(mpFilter);
+
+ htmlDocUniquePtr pHtmlDoc = parseHtml(maTempFile);
+ CPPUNIT_ASSERT(pHtmlDoc);
+
+ assertXPath(pHtmlDoc, "/html/body/p[1]/a", "href", u"http://libreoffice.org/");
+ assertXPath(pHtmlDoc, "/html/body/p[2]/a", "href", u"#some_bookmark");
+ assertXPath(pHtmlDoc, "/html/body/p[3]/a", "href", u"C:\\test.txt");
+ assertXPath(pHtmlDoc, "/html/body/p[4]/a", "href", u"..\\..\\test.odt");
+ assertXPath(pHtmlDoc, "/html/body/p[5]/a", "href", u".\\another.odt");
+}
+
+CPPUNIT_TEST_FIXTURE(HtmlExportTest, testTdf153923)
+{
+ createSwDoc("TableWithIndent.fodt");
+ save(mpFilter);
+
+ // Parse it as XML (strict!)
+ xmlDocUniquePtr pDoc = parseXml(maTempFile);
+ // Without the fix in place, this would fail
+ CPPUNIT_ASSERT(pDoc);
+
+ assertXPath(pDoc, "/html/body//dl", 3);
+ // The 'dd' tag was not closed
+ assertXPath(pDoc, "/html/body//dd", 3);
+}
+
+CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, testTdf153923_ReqIF)
+{
+ createSwDoc("TableWithIndent.fodt");
+ ExportToReqif();
+
+ xmlDocUniquePtr pDoc = WrapReqifFromTempFile();
+
+ assertXPath(pDoc, "//reqif-xhtml:table");
+ // There should be no 'dd' or 'dl' tags, used as a hack for table indentation
+ assertXPath(pDoc, "//reqif-xhtml:dl", 0);
+ assertXPath(pDoc, "//reqif-xhtml:dd", 0);
+}
+
+CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, testReqIfTransparentTifImg)
+{
+ // reqIf export must keep the TIF encoding of the image
+ createSwDoc("reqif-transparent-tif-img.odt");
+ ExportToReqif();
+
+ xmlDocUniquePtr pXmlDoc = WrapReqifFromTempFile();
+ assertXPath(pXmlDoc, "//reqif-xhtml:p/reqif-xhtml:object[1]", "type", u"image/tiff");
+ OUString imageName = getXPath(pXmlDoc, "//reqif-xhtml:p/reqif-xhtml:object[1]", "data");
+ // Without the accompanying fix in place, this test would have failed,
+ // ending with .gif, because XOutFlags::UseGifIfSensible flag combined
+ // with the transparent image would result in GIF export
+ CPPUNIT_ASSERT(imageName.endsWith(".tif"));
+
+ INetURLObject aURL(maTempFile.GetURL());
+ aURL.setName(imageName);
+ GraphicDescriptor aDescriptor(aURL);
+ aDescriptor.Detect();
+ CPPUNIT_ASSERT_EQUAL(GraphicFileFormat::TIF, aDescriptor.GetFileFormat());
+}
+
+CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, testTdf155387)
+{
+ createSwDoc("sub_li_and_ctd.fodt");
+ ExportToReqif();
+
+ // Without the fix in place, this would fail
+ xmlDocUniquePtr pDoc = WrapReqifFromTempFile();
+
+ // Single top-level list
+ assertXPath(pDoc, "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:ul");
+ // Single top-level item
+ assertXPath(pDoc, "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:ul/reqif-xhtml:li");
+ // 4 top-level paragraphs in the item
+ assertXPath(pDoc,
+ "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:ul/reqif-xhtml:li/reqif-xhtml:p", 4);
+ // 2 sublists in the item
+ assertXPath(
+ pDoc, "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:ul/reqif-xhtml:li/reqif-xhtml:ul", 2);
+ // 2 items in the first sublist
+ assertXPath(pDoc,
+ "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:ul/reqif-xhtml:li/reqif-xhtml:ul[1]/"
+ "reqif-xhtml:li",
+ 2);
+ // Check the last (most nested) subitem's text
+ assertXPathContent(
+ pDoc,
+ "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:ul/reqif-xhtml:li/reqif-xhtml:ul[2]/"
+ "reqif-xhtml:li/reqif-xhtml:ul/reqif-xhtml:li/reqif-xhtml:p",
+ u"l3");
+}
+
+CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, testTdf155496)
+{
+ createSwDoc("listItemSubheader.fodt");
+ ExportToReqif();
+
+ // Without the fix in place, this would fail
+ xmlDocUniquePtr pDoc = WrapReqifFromTempFile();
+
+ // Two top-level lists
+ assertXPath(pDoc, "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:ul", 2);
+ // Single top-level item
+ assertXPath(pDoc, "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:ul[1]/reqif-xhtml:li");
+ // One top-level paragraph in the item
+ assertXPath(pDoc,
+ "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:ul[1]/reqif-xhtml:li/reqif-xhtml:p");
+ // One sublist in the item
+ assertXPath(
+ pDoc, "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:ul[1]/reqif-xhtml:li/reqif-xhtml:ul");
+ // One item in the sublist
+ assertXPath(pDoc,
+ "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:ul[1]/reqif-xhtml:li/reqif-xhtml:ul/"
+ "reqif-xhtml:li");
+ // Check its text
+ OUString aContent = getXPathContent(
+ pDoc, "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:ul[1]/reqif-xhtml:li/reqif-xhtml:ul/"
+ "reqif-xhtml:li/reqif-xhtml:p");
+ CPPUNIT_ASSERT_EQUAL(u"list 1 item 1\n\t\tsub-header"_ustr, aContent.trim());
+}
+
+CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, testReqIF_RightAlignedTable)
+{
+ createSwDoc("tableRight.fodt");
+ ExportToReqif();
+
+ xmlDocUniquePtr pDoc = WrapReqifFromTempFile();
+
+ // No 'align' attribute must be present in 'div'
+ assertXPathNoAttribute(pDoc, "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:div", "align");
+}
+
+CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, testReqIF_ListsWithNumFormat)
+{
+ createSwDoc("listsWithNumFormat.fodt");
+ ExportToReqif();
+
+ xmlDocUniquePtr pDoc = WrapReqifFromTempFile();
+
+ // No 'type' attribute must be present in 'ol'
+ assertXPathNoAttribute(pDoc, "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:ol[1]", "type");
+ assertXPathNoAttribute(pDoc, "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:ol[2]", "type");
+ assertXPathNoAttribute(pDoc, "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:ol[3]", "type");
+ assertXPathNoAttribute(pDoc, "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:ol[4]", "type");
+}
+
+CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, testTdf155871)
+{
+ createSwDoc("tdf155871.fodt");
+ ExportToReqif();
+
+ // Without the fix in place, this would fail
+ xmlDocUniquePtr pDoc = WrapReqifFromTempFile();
+}
+
+CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, testReqIF_ListsNoStartAttribute)
+{
+ createSwDoc("twoListsWithSameStyle.fodt");
+ ExportToReqif();
+
+ xmlDocUniquePtr pDoc = WrapReqifFromTempFile();
+
+ // No 'start' attribute must be present in 'ol'
+ assertXPath(pDoc, "//reqif-xhtml:ol[@start]", 0);
+}
+
+CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, testReqIF_FrameTextAsObjectAltText)
+{
+ createSwDoc("frameWithText.fodt");
+ ExportToReqif();
+
+ xmlDocUniquePtr pDoc = WrapReqifFromTempFile();
+
+ // Without the fix, this would fail with
+ // - Expected: Some text in frame &
+ // - Actual : Frame1
+ // i.e., frame name was used as the object element content, not frame text
+ assertXPathContent(pDoc,
+ "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:p[2]/reqif-xhtml:object",
+ u"Some text in frame & ");
+}
+
+CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, testSingleOleExport)
+{
+ // Given a document containing an embedded OLE object:
+ createSwDoc("ole2.odt");
+
+ // Create a selection for that object:
+ auto xDrawPageSupplier(mxComponent.queryThrow());
+ auto xDrawPage(xDrawPageSupplier->getDrawPage());
+ auto xModel(mxComponent.queryThrow());
+ auto xController(xModel->getCurrentController().queryThrow());
+ xController->select(xDrawPage->getByIndex(0));
+
+ // Store only the selection
+ css::uno::Sequence aStoreProperties = {
+ comphelper::makePropertyValue(u"FilterName"_ustr, u"HTML (StarWriter)"_ustr),
+ comphelper::makePropertyValue(u"FilterOptions"_ustr, u"xhtmlns=reqif-xhtml"_ustr),
+ comphelper::makePropertyValue(u"RTFOLEMimeType"_ustr, u"text/rtf"_ustr),
+ comphelper::makePropertyValue(u"SelectionOnly"_ustr, true),
+ };
+ saveWithParams(aStoreProperties);
+
+ xmlDocUniquePtr pXmlDoc = WrapReqifFromTempFile();
+
+ // The root element must be reqif-xhtml:object
+ assertXPath(pXmlDoc, "/reqif-xhtml:html/reqif-xhtml:object", "type", u"text/rtf");
+ // It has no children
+ assertXPathChildren(pXmlDoc, "/reqif-xhtml:html/reqif-xhtml:object", 0);
+ // And the content is empty
+ assertXPathContent(pXmlDoc, "/reqif-xhtml:html/reqif-xhtml:object", u"");
+
+ OUString aRtfData = getXPath(pXmlDoc, "/reqif-xhtml:html/reqif-xhtml:object", "data");
+ INetURLObject aUrl(maTempFile.GetURL());
+ aUrl.setName(aRtfData);
+ SvMemoryStream aRtf;
+ HtmlExportTest::wrapRtfFragment(aUrl.GetMainURL(INetURLObject::DecodeMechanism::NONE), aRtf);
+ tools::SvRef xReader(new TestReqIfRtfReader(aRtf));
+ // The RTF OLE exports correctly
+ CPPUNIT_ASSERT(xReader->CallParser() != SvParserState::Error);
+ CPPUNIT_ASSERT_EQUAL(tools::Long(9358), xReader->GetObjw());
+ CPPUNIT_ASSERT_EQUAL(tools::Long(450), xReader->GetObjh());
+}
+
+CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, testReqIF_Tdf156602)
+{
+ createSwDoc("NestingInA1.fodt");
+ ExportToReqif();
+
+ xmlDocUniquePtr pDoc = WrapReqifFromTempFile();
+
+ // The outer table must be kept in the document where the outer table is the first element,
+ // and its A1 starts with a nested table
+
+ // Only two sub-elements must be inside the div: an outer table and a trailing paragraph
+ assertXPathChildren(pDoc, "/reqif-xhtml:html/reqif-xhtml:div", 2);
+ // The outer table must have exactly two rows
+ assertXPath(pDoc, "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:table/reqif-xhtml:tr", 2);
+ // First outer table cell must have two sub-elements: an inner table and a trailing paragraph
+ assertXPathChildren(
+ pDoc,
+ "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:table/reqif-xhtml:tr[1]/reqif-xhtml:td[1]",
+ 2);
+ // The inner table must have exactly two rows
+ assertXPath(
+ pDoc,
+ "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:table/reqif-xhtml:tr[1]/reqif-xhtml:td[1]/"
+ "reqif-xhtml:table/reqif-xhtml:tr",
+ 2);
+ // Check all the elements' content
+ assertXPathContent(
+ pDoc,
+ "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:table/reqif-xhtml:tr[1]/reqif-xhtml:td[1]/"
+ "reqif-xhtml:table/reqif-xhtml:tr[1]/reqif-xhtml:td[1]/reqif-xhtml:p",
+ u"Inner.A1");
+ assertXPathContent(
+ pDoc,
+ "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:table/reqif-xhtml:tr[1]/reqif-xhtml:td[1]/"
+ "reqif-xhtml:table/reqif-xhtml:tr[1]/reqif-xhtml:td[2]/reqif-xhtml:p",
+ u"Inner.B1");
+ assertXPathContent(
+ pDoc,
+ "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:table/reqif-xhtml:tr[1]/reqif-xhtml:td[1]/"
+ "reqif-xhtml:table/reqif-xhtml:tr[2]/reqif-xhtml:td[1]/reqif-xhtml:p",
+ u"Inner.A2");
+ assertXPathContent(
+ pDoc,
+ "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:table/reqif-xhtml:tr[1]/reqif-xhtml:td[1]/"
+ "reqif-xhtml:table/reqif-xhtml:tr[2]/reqif-xhtml:td[2]/reqif-xhtml:p",
+ u"Inner.B2");
+ assertXPathContent(
+ pDoc,
+ "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:table/reqif-xhtml:tr[1]/reqif-xhtml:td[1]/"
+ "reqif-xhtml:p",
+ u"Outer.A1");
+ assertXPathContent(
+ pDoc,
+ "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:table/reqif-xhtml:tr[1]/reqif-xhtml:td[2]/"
+ "reqif-xhtml:p",
+ u"Outer.B1");
+ assertXPathContent(
+ pDoc,
+ "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:table/reqif-xhtml:tr[2]/reqif-xhtml:td[1]/"
+ "reqif-xhtml:p",
+ u"Outer.A2");
+ assertXPathContent(
+ pDoc,
+ "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:table/reqif-xhtml:tr[2]/reqif-xhtml:td[2]/"
+ "reqif-xhtml:p",
+ u"Outer.B2");
+ assertXPathContent(pDoc, "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:p", u"Following text");
+}
+
+CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, testTdf156647_CellPaddingRoundtrip)
+{
+ // Given a document with a table with cell padding:
+ createSwDoc("table_cell_padding.fodt");
+ {
+ auto xTable = getParagraphOrTable(1);
+ auto aTableBorder = getProperty(xTable, u"TableBorder2"_ustr);
+ CPPUNIT_ASSERT_EQUAL(sal_Int16(1270), aTableBorder.Distance);
+ CPPUNIT_ASSERT(aTableBorder.IsDistanceValid);
+ }
+ // When exporting to reqif-xhtml:
+ ExportToReqif();
+ // Make sure that we export it:
+ xmlDocUniquePtr pXmlDoc = WrapReqifFromTempFile();
+ assertXPath(pXmlDoc, "//reqif-xhtml:table", "cellpadding", u"48"); // px
+ // Now import it
+ ImportFromReqif(maTempFile.GetURL());
+ // Then make sure that padding is not lost:
+ {
+ auto xTable = getParagraphOrTable(1);
+ auto aTableBorder = getProperty(xTable, u"TableBorder2"_ustr);
+ // Without the accompanying fix in place, this test would have failed:
+ // - Expected: 1270
+ // - Actual : 97
+ // as the padding was lost, and the default 55 twip padding was used.
+ CPPUNIT_ASSERT_EQUAL(sal_Int16(1270), aTableBorder.Distance);
+ CPPUNIT_ASSERT(aTableBorder.IsDistanceValid);
+ }
+}
+
+CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, testTdf157643_WideHBorder)
+{
+ // Given a document with a table with a wide border between its two rows:
+ createSwDoc("table_with_wide_horizontal_border.fodt");
+ // When exporting to reqif-xhtml:
+ ExportToReqif();
+ // Make sure that there's no extra tr's:
+ xmlDocUniquePtr pXmlDoc = WrapReqifFromTempFile();
+ assertXPath(pXmlDoc, "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:table/reqif-xhtml:tr", 2);
+}
+
+CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, testHTML_PreserveSpaces)
+{
+ // Given a document with leading, trailing, and repeating intermediate spaces:
+ createSwDoc();
+ SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell();
+ static constexpr OUString paraText = u"\t test \t more text \t"_ustr;
+ pWrtShell->Insert(paraText);
+
+ // When exporting to plain HTML, using PreserveSpaces:
+ saveWithParams({
+ comphelper::makePropertyValue(u"FilterName"_ustr, u"HTML (StarWriter)"_ustr),
+ comphelper::makePropertyValue(u"PreserveSpaces"_ustr, true),
+ });
+
+ // Then make sure that "white-space: pre-wrap" is written into the paragraph's style:
+ htmlDocUniquePtr pHtmlDoc = parseHtml(maTempFile);
+ CPPUNIT_ASSERT(pHtmlDoc);
+ const OUString style = getXPath(pHtmlDoc, "/html/body/p", "style");
+ CPPUNIT_ASSERT(style.indexOf("white-space: pre-wrap") >= 0);
+ // Also check that the paragraph text is correct, without modifications in whitespace
+ assertXPathContent(pHtmlDoc, "/html/body/p", paraText);
+
+ // Test import
+
+ setImportFilterName(u"HTML (StarWriter)"_ustr);
+ loadFromURL(maTempFile.GetURL());
+ CPPUNIT_ASSERT_EQUAL(paraText, getParagraph(1)->getString());
+}
+
+CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, testReqIF_PreserveSpaces)
+{
+ // Given a document with leading, trailing, and repeating intermediate spaces:
+ createSwDoc();
+ SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell();
+ static constexpr OUString paraText = u"\t test \t more text \t"_ustr;
+ pWrtShell->Insert(paraText);
+
+ // When exporting to ReqIF, using PreserveSpaces:
+ saveWithParams({
+ comphelper::makePropertyValue(u"FilterName"_ustr, u"HTML (StarWriter)"_ustr),
+ comphelper::makePropertyValue(u"FilterOptions"_ustr, u"xhtmlns=reqif-xhtml"_ustr),
+ comphelper::makePropertyValue(u"PreserveSpaces"_ustr, true),
+ });
+
+ // Then make sure that xml:space="preserve" attribute exists in the paragraph element:
+ xmlDocUniquePtr pXmlDoc = WrapReqifFromTempFile();
+ assertXPath(pXmlDoc, "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:p", "space", u"preserve");
+ // Also check that the paragraph text is correct, without modifications in whitespace
+ assertXPathContent(pXmlDoc, "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:p", paraText);
+
+ // Test import
+
+ setImportFilterOptions(u"xhtmlns=reqif-xhtml"_ustr);
+ setImportFilterName(u"HTML (StarWriter)"_ustr);
+ loadFromURL(maTempFile.GetURL());
+ CPPUNIT_ASSERT_EQUAL(paraText, getParagraph(1)->getString());
+}
+
+CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, testHTML_NoPreserveSpaces)
+{
+ // Test cases where "PreserveSpaces" should not introduce respective markup
+
+ const auto assertXPath_NoWhiteSpaceInStyle
+ = [this](const xmlDocUniquePtr& pDoc, const char* pXPath) {
+ xmlXPathObjectPtr pXmlObj = getXPathNode(pDoc, pXPath);
+ xmlNodeSetPtr pXmlNodes = pXmlObj->nodesetval;
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(pXPath, 1, xmlXPathNodeSetGetLength(pXmlNodes));
+ CPPUNIT_ASSERT(pXmlNodes);
+ xmlNodePtr pXmlNode = pXmlNodes->nodeTab[0];
+ if (xmlChar* prop = xmlGetProp(pXmlNode, BAD_CAST("style")))
+ {
+ OUString style = OUString::fromUtf8(reinterpret_cast(prop));
+ CPPUNIT_ASSERT_MESSAGE(pXPath, style.indexOf("white-space:") < 0);
+ }
+ xmlXPathFreeObject(pXmlObj);
+ };
+ const auto assertXPath_HasWhiteSpaceInStyle
+ = [this](const xmlDocUniquePtr& pDoc, const char* pXPath) {
+ const OUString style = getXPath(pDoc, pXPath, "style");
+ CPPUNIT_ASSERT_MESSAGE(pXPath, style.indexOf("white-space: pre-wrap") >= 0);
+ };
+
+ createSwDoc("test_no_space_preserve.fodt");
+
+ // Export to plain HTML, using PreserveSpaces:
+ saveWithParams({
+ comphelper::makePropertyValue(u"FilterName"_ustr, u"HTML (StarWriter)"_ustr),
+ comphelper::makePropertyValue(u"PreserveSpaces"_ustr, true),
+ });
+
+ htmlDocUniquePtr pHtmlDoc = parseHtml(maTempFile);
+ CPPUNIT_ASSERT(pHtmlDoc);
+
+ // No whitespace preservation, where no leading / trailing / double whitespace
+ assertXPath_NoWhiteSpaceInStyle(pHtmlDoc, "/html/body/p[1]");
+ // Whitespace preserved for a leading space
+ assertXPath_HasWhiteSpaceInStyle(pHtmlDoc, "/html/body/p[2]");
+ // Whitespace preserved for a trailing space
+ assertXPath_HasWhiteSpaceInStyle(pHtmlDoc, "/html/body/p[3]");
+ // Whitespace preserved for a double space
+ assertXPath_HasWhiteSpaceInStyle(pHtmlDoc, "/html/body/p[4]");
+ // No whitespace preservation for leading / trailing breaks
+ assertXPath_NoWhiteSpaceInStyle(pHtmlDoc, "/html/body/p[5]");
+ // Whitespace preserved for a leading break + space
+ assertXPath_HasWhiteSpaceInStyle(pHtmlDoc, "/html/body/p[6]");
+ // Whitespace preserved for a trailing space + break
+ assertXPath_HasWhiteSpaceInStyle(pHtmlDoc, "/html/body/p[7]");
+ // No whitespace preservation for a middle break
+ assertXPath_NoWhiteSpaceInStyle(pHtmlDoc, "/html/body/p[8]");
+ // Whitespace preserved for a middle space + break
+ assertXPath_HasWhiteSpaceInStyle(pHtmlDoc, "/html/body/p[9]");
+ // Whitespace preserved for a middle break + space
+ assertXPath_HasWhiteSpaceInStyle(pHtmlDoc, "/html/body/p[10]");
+ // No whitespace preservation for a trailing space and SVG
+ assertXPath_NoWhiteSpaceInStyle(pHtmlDoc, "/html/body/p[11]");
+
+ // Test import
+
+ setImportFilterName(u"HTML (StarWriter)"_ustr);
+ loadFromURL(maTempFile.GetURL());
+
+ CPPUNIT_ASSERT_EQUAL(u"No special spaces"_ustr, getParagraph(1)->getString());
+ CPPUNIT_ASSERT_EQUAL(u" Leading space"_ustr, getParagraph(2)->getString());
+ CPPUNIT_ASSERT_EQUAL(u"Trailing space "_ustr, getParagraph(3)->getString());
+ CPPUNIT_ASSERT_EQUAL(u"Double space"_ustr, getParagraph(4)->getString());
+ // Trailing break is removed in SwHTMLParser::AppendTextNode, and replaced with para spacing
+ CPPUNIT_ASSERT_EQUAL(u"\nLeading/trailing breaks"_ustr, getParagraph(5)->getString());
+ CPPUNIT_ASSERT_EQUAL(u"\n Leading break + space"_ustr, getParagraph(6)->getString());
+ // Trailing break is removed in SwHTMLParser::AppendTextNode, and replaced with para spacing
+ CPPUNIT_ASSERT_EQUAL(u"Trailing space + break "_ustr, getParagraph(7)->getString());
+ CPPUNIT_ASSERT_EQUAL(u"Middle\nbreak"_ustr, getParagraph(8)->getString());
+ CPPUNIT_ASSERT_EQUAL(u"Middle space \n+ break"_ustr, getParagraph(9)->getString());
+ CPPUNIT_ASSERT_EQUAL(u"Middle break\n + space"_ustr, getParagraph(10)->getString());
+ // The SVG is replaced by a space in SwXParagraph::getString()
+ CPPUNIT_ASSERT_EQUAL(u"Trailing space and SVG "_ustr, getParagraph(11)->getString());
+}
+
+CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, testReqIF_NoPreserveSpaces)
+{
+ // Test cases where "PreserveSpaces" should not introduce respective markup
+
+ createSwDoc("test_no_space_preserve.fodt");
+
+ // Export to ReqIF, using PreserveSpaces:
+ saveWithParams({
+ comphelper::makePropertyValue(u"FilterName"_ustr, u"HTML (StarWriter)"_ustr),
+ comphelper::makePropertyValue(u"FilterOptions"_ustr, u"xhtmlns=reqif-xhtml"_ustr),
+ comphelper::makePropertyValue(u"PreserveSpaces"_ustr, true),
+ });
+
+ xmlDocUniquePtr pXmlDoc = WrapReqifFromTempFile();
+
+ // No whitespace preservation, where no leading / trailing / double whitespace
+ assertXPathNoAttribute(pXmlDoc, "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:p[1]", "space");
+ // Whitespace preserved for a leading space
+ assertXPath(pXmlDoc, "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:p[2]", "space",
+ u"preserve");
+ // Whitespace preserved for a trailing space
+ assertXPath(pXmlDoc, "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:p[3]", "space",
+ u"preserve");
+ // Whitespace preserved for a double space
+ assertXPath(pXmlDoc, "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:p[4]", "space",
+ u"preserve");
+ // No whitespace preservation for leading / trailing breaks
+ assertXPathNoAttribute(pXmlDoc, "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:p[5]", "space");
+ // Whitespace preserved for a leading break + space
+ assertXPath(pXmlDoc, "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:p[6]", "space",
+ u"preserve");
+ // No whitespace preservation for a trailing space + break
+ assertXPathNoAttribute(pXmlDoc, "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:p[7]", "space");
+ // No whitespace preservation for a middle break
+ assertXPathNoAttribute(pXmlDoc, "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:p[8]", "space");
+ // No whitespace preservation for a middle space + break
+ assertXPathNoAttribute(pXmlDoc, "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:p[9]", "space");
+ // Whitespace preserved for a middle break + space
+ assertXPath(pXmlDoc, "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:p[10]", "space",
+ u"preserve");
+ // No whitespace preservation for a trailing space and SVG
+ assertXPathNoAttribute(pXmlDoc, "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:p[11]", "space");
+
+ // Test import
+
+ setImportFilterOptions(u"xhtmlns=reqif-xhtml"_ustr);
+ setImportFilterName(u"HTML (StarWriter)"_ustr);
+ loadFromURL(maTempFile.GetURL());
+
+ CPPUNIT_ASSERT_EQUAL(u"No special spaces"_ustr, getParagraph(1)->getString());
+ CPPUNIT_ASSERT_EQUAL(u" Leading space"_ustr, getParagraph(2)->getString());
+ CPPUNIT_ASSERT_EQUAL(u"Trailing space "_ustr, getParagraph(3)->getString());
+ CPPUNIT_ASSERT_EQUAL(u"Double space"_ustr, getParagraph(4)->getString());
+ CPPUNIT_ASSERT_EQUAL(u"\nLeading/trailing breaks\n"_ustr, getParagraph(5)->getString());
+ CPPUNIT_ASSERT_EQUAL(u"\n Leading break + space"_ustr, getParagraph(6)->getString());
+ CPPUNIT_ASSERT_EQUAL(u"Trailing space + break \n"_ustr, getParagraph(7)->getString());
+ CPPUNIT_ASSERT_EQUAL(u"Middle\nbreak"_ustr, getParagraph(8)->getString());
+ CPPUNIT_ASSERT_EQUAL(u"Middle space \n+ break"_ustr, getParagraph(9)->getString());
+ CPPUNIT_ASSERT_EQUAL(u"Middle break\n + space"_ustr, getParagraph(10)->getString());
+ // The SVG is replaced by a space in SwXParagraph::getString()
+ CPPUNIT_ASSERT_EQUAL(u"Trailing space and SVG "_ustr, getParagraph(11)->getString());
+}
+
+CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, testReqIF_ExportFormulasAsPDF)
+{
+ // Given a document with a formula:
+ createSwDoc("embedded_formula.fodt");
+
+ // When exporting to reqif with ExportFormulasAsPDF=true:
+ uno::Sequence aStoreProperties = {
+ comphelper::makePropertyValue(u"FilterName"_ustr, u"HTML (StarWriter)"_ustr),
+ comphelper::makePropertyValue(u"FilterOptions"_ustr, u"xhtmlns=reqif-xhtml"_ustr),
+ comphelper::makePropertyValue(u"ExportFormulasAsPDF"_ustr, true),
+ };
+ saveWithParams(aStoreProperties);
+
+ // Make sure that the formula is exported as PDF:
+ xmlDocUniquePtr pXmlDoc = WrapReqifFromTempFile();
+ assertXPath(pXmlDoc, "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:p[2]/reqif-xhtml:object",
+ "type", u"application/pdf");
+
+ css::uno::Sequence descr{
+ comphelper::makePropertyValue(u"URL"_ustr, GetObjectPath(u".pdf"_ustr)),
+ };
+
+ uno::Reference xFactory(
+ comphelper::getProcessComponentContext()->getServiceManager(), uno::UNO_QUERY_THROW);
+ uno::Reference xTypeDetection(
+ xFactory->createInstance(u"com.sun.star.document.TypeDetection"_ustr),
+ uno::UNO_QUERY_THROW);
+
+ CPPUNIT_ASSERT_EQUAL(u"pdf_Portable_Document_Format"_ustr,
+ xTypeDetection->queryTypeByDescriptor(descr, true));
+}
+
+CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, testReqIF_NoBrClearForImageWrap)
+{
+ // Given a document with a paragraph-anchored image with "none" wrap:
+ createSwDoc("image_anchored_to_paragraph_no_wrap.fodt");
+ // When exporting to reqif:
+ ExportToReqif();
+ // Make sure that there's no 'br' elements in the 'object' (used to represent the wrapping
+ // in HTML export, using 'clear' attribute):
+ xmlDocUniquePtr pXmlDoc = WrapReqifFromTempFile();
+ assertXPath(pXmlDoc, "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:p/reqif-xhtml:object");
+ assertXPath(pXmlDoc,
+ "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:p/reqif-xhtml:object/reqif-xhtml:br",
+ 0);
+}
+
+CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, testReqIF_Tdf160017_spanClosingOrder)
+{
+ // Given a document with a paragraph having explicit font color and character border properties:
+ createSwDoc("char_border_and_font_color.fodt");
+ // When exporting to reqif:
+ ExportToReqif();
+ // Without the fix, this would fail, because there was an extra closing
+ WrapReqifFromTempFile();
+}
+
+CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, testHTML_Tdf160017_spanClosingOrder)
+{
+ // Given a document with a paragraph having explicit font color and character border properties:
+ createSwDoc("char_border_and_font_color.fodt");
+ // When exporting to HTML:
+ ExportToHTML();
+ // Parse it as XML (strict!)
+ // Without the fix, this would fail, because span and font elements closed in wrong order
+ CPPUNIT_ASSERT(parseXml(maTempFile));
+}
+
+CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, testHTML_Tdf160390)
+{
+ // This document must not hang infinitely on HTML export
+ createSwDoc("tdf160390.fodt");
+ ExportToHTML();
+}
+
+CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, testHTML_160867)
+{
+ // Given a document with an image with hyperlink, and text with hyperlink, both in a frame:
+ createSwDoc("tdf160867_image_with_link.fodt");
+ // When exporting to HTML:
+ ExportToHTML();
+ // Parse it as XML (strict!)
+ xmlDocUniquePtr pDoc = parseXml(maTempFile);
+ CPPUNIT_ASSERT(pDoc);
+ assertXPath(pDoc, "/html/body/p", 2);
+
+ // Test export of image and text hyperlinks in the image map.
+ // Without the fix, the test would fail with
+ // - Expected: 1
+ // - Actual : 0
+ // - In <>, XPath '/html/body/p[2]/map' number of nodes is incorrect
+ const OUString mapName = getXPath(pDoc, "/html/body/p[2]/map", "name");
+ assertXPath(pDoc, "/html/body/p[2]/map/area[1]", "shape", u"rect");
+ CPPUNIT_ASSERT(getXPath(pDoc, "/html/body/p[2]/map/area[1]", "href").endsWith("foo/bar"));
+ assertXPath(pDoc, "/html/body/p[2]/map/area[2]", "shape", u"rect");
+ CPPUNIT_ASSERT(getXPath(pDoc, "/html/body/p[2]/map/area[2]", "href").endsWith("baz"));
+ assertXPath(pDoc, "/html/body/p[2]/img", "usemap", Concat2View("#" + mapName));
+}
+
+CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, testReqIF_160867)
+{
+ // Given a document with an image with hyperlink, and text with hyperlink, both in a frame:
+ createSwDoc("tdf160867_image_with_link.fodt");
+ // When exporting to reqif:
+ ExportToReqif();
+ // For now, we don't (yet) output the whole map in ReqIF case.
+ // Make sure that the first hyperlink from the objects in the frame is output as an element
+ // around the whole image of the frame.
+ xmlDocUniquePtr pXmlDoc = WrapReqifFromTempFile();
+ assertXPath(pXmlDoc, "//reqif-xhtml:p[2]/reqif-xhtml:a/reqif-xhtml:object");
+ CPPUNIT_ASSERT(
+ getXPath(pXmlDoc, "//reqif-xhtml:p[2]/reqif-xhtml:a", "href").endsWith("foo/bar"));
+}
+
+CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, testHTML_161979)
+{
+ // Given a document with two embedded metafiles:
+ createSwDoc("tdf161979_metafile.fodt");
+ ExportToHTML();
+ xmlDocUniquePtr pDoc = parseXml(maTempFile);
+ CPPUNIT_ASSERT(pDoc);
+ // First image: it has no EMF+ actions, and didn't use canvas rendering before the fix;
+ // yet, it didn't export correctly.
+ OUString imgName = getXPath(pDoc, "/html/body/p[2]/img", "src");
+ CPPUNIT_ASSERT(imgName.endsWith(".gif"));
+ INetURLObject aUrl(maTempFile.GetURL());
+ aUrl.setName(imgName);
+ Graphic graphic;
+ CPPUNIT_ASSERT_EQUAL(ERRCODE_NONE, GraphicFilter().ImportGraphic(graphic, aUrl));
+
+ // Check that only ~4% of pixels are not transparent (before the fix, it was completely black)
+ BitmapEx bitmap = graphic.GetBitmapEx();
+ Size size = bitmap.GetSizePixel();
+ int numNonTransparent = 0;
+ for (tools::Long y = 0; y < size.Height(); ++y)
+ for (tools::Long x = 0; x < size.Width(); ++x)
+ if (bitmap.GetPixelColor(x, y) != COL_TRANSPARENT)
+ ++numNonTransparent;
+ CPPUNIT_ASSERT_DOUBLES_EQUAL(0.04, numNonTransparent / double(size.Height() * size.Width()),
+ 0.01);
+
+ // Second image: it consists of EMF+ records (no EMF fallback). It used canvas rendering
+ // before the fix; it also didn't export correctly.
+ imgName = getXPath(pDoc, "/html/body/p[4]/img", "src");
+ CPPUNIT_ASSERT(imgName.endsWith(".gif"));
+ aUrl.SetURL(maTempFile.GetURL());
+ aUrl.setName(imgName);
+ graphic.Clear();
+ CPPUNIT_ASSERT_EQUAL(ERRCODE_NONE, GraphicFilter().ImportGraphic(graphic, aUrl));
+
+ // Check that some pixels are transparent (before the fix, it was completely black)
+ bitmap = graphic.GetBitmapEx();
+ size = bitmap.GetSizePixel();
+ numNonTransparent = 0;
+ for (tools::Long y = 0; y < size.Height(); ++y)
+ for (tools::Long x = 0; x < size.Width(); ++x)
+ if (bitmap.GetPixelColor(x, y) != COL_TRANSPARENT)
+ ++numNonTransparent;
+ CPPUNIT_ASSERT(numNonTransparent > 0);
+ CPPUNIT_ASSERT(numNonTransparent < size.Height() * size.Width());
+}
+
+CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, testReqIF_exportAbsoluteURLs_ownRelative)
+{
+ auto pBatch(comphelper::ConfigurationChanges::create());
+ Resetter resetter([
+ bInternetPreviousValue = officecfg::Office::Common::Save::URL::Internet::get(),
+ bFileSystemPreviousValue = officecfg::Office::Common::Save::URL::FileSystem::get(), pBatch
+ ]() {
+ officecfg::Office::Common::Save::URL::Internet::set(bInternetPreviousValue, pBatch);
+ officecfg::Office::Common::Save::URL::FileSystem::set(bFileSystemPreviousValue, pBatch);
+ return pBatch->commit();
+ });
+ // Set saving absolute URLs
+ officecfg::Office::Common::Save::URL::Internet::set(false, pBatch);
+ officecfg::Office::Common::Save::URL::FileSystem::set(false, pBatch);
+ pBatch->commit();
+
+ createSwDoc("URLs.odt");
+ // Export to ReqIF, using absolute URLs
+ saveWithParams({
+ comphelper::makePropertyValue(u"FilterName"_ustr, u"HTML (StarWriter)"_ustr),
+ comphelper::makePropertyValue(u"FilterOptions"_ustr, u"xhtmlns=reqif-xhtml"_ustr),
+ comphelper::makePropertyValue(u"ExportImagesAsOLE"_ustr, true),
+ comphelper::makePropertyValue(u"RelativeOwnObjectURL"_ustr, true),
+ });
+ xmlDocUniquePtr pXmlDoc = WrapReqifFromTempFile();
+
+ // HTTP URL: must be absolute
+ assertXPath(pXmlDoc, "//reqif-xhtml:p[1]/reqif-xhtml:a", "href", u"http://www.example.org/");
+ // file URL: must be absolute
+ assertXPath(pXmlDoc, "//reqif-xhtml:p[2]/reqif-xhtml:a", "href",
+ createFileURL(u"NonExistingPath/NonExistingFile.html"));
+ // form URL: must be absolute
+ assertXPath(pXmlDoc, "//reqif-xhtml:form", "action", u"https://www.example.org/submit");
+ // linked image exported as object: generated, must be relative
+ OUString url = getXPath(pXmlDoc, "//reqif-xhtml:p[3]/reqif-xhtml:object", "data");
+ CPPUNIT_ASSERT(!url.startsWith("file:"));
+ CPPUNIT_ASSERT(url.endsWith(".ole"));
+ // its original image URL: must be absolute
+ assertXPath(pXmlDoc, "//reqif-xhtml:p[3]/reqif-xhtml:object/reqif-xhtml:object", "data",
+ createFileURL(u"external.png"));
+ // embedded image exported as object: generated, must be relative
+ url = getXPath(pXmlDoc, "//reqif-xhtml:p[4]/reqif-xhtml:object", "data");
+ CPPUNIT_ASSERT(!url.startsWith("file:"));
+ CPPUNIT_ASSERT(url.endsWith(".ole"));
+ // its image URL: generated, must be relative
+ url = getXPath(pXmlDoc, "//reqif-xhtml:p[4]/reqif-xhtml:object/reqif-xhtml:object", "data");
+ CPPUNIT_ASSERT(!url.startsWith("file:"));
+ CPPUNIT_ASSERT(url.endsWith(".png"));
+ // unordered list with image bullet - it gets embedded as base64 data
+ OUString style = getXPath(pXmlDoc, "//reqif-xhtml:ul", "style");
+ CPPUNIT_ASSERT(style.indexOf("list-style-image: url(data:image/png;base64,") != -1);
+ // an as-char frame, exported as a whole to an object, must be relative
+ url = getXPath(pXmlDoc, "//reqif-xhtml:p[5]/reqif-xhtml:object", "data");
+ CPPUNIT_ASSERT(!url.startsWith("file:"));
+ CPPUNIT_ASSERT(url.endsWith(".ole"));
+ // its file hyperlink must be absolute
+ assertXPath(pXmlDoc, "//reqif-xhtml:p[5]/reqif-xhtml:object/reqif-xhtml:a", "href",
+ createFileURL(u"foo/bar"));
+ // its image URL: generated, must be relative
+ url = getXPath(
+ pXmlDoc, "//reqif-xhtml:p[5]/reqif-xhtml:object/reqif-xhtml:a/reqif-xhtml:object", "data");
+ CPPUNIT_ASSERT(!url.startsWith("file:"));
+ CPPUNIT_ASSERT(url.endsWith(".png"));
+}
+
+CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, testReqIF_exportRelativeURLs)
+{
+ CPPUNIT_ASSERT(officecfg::Office::Common::Save::URL::Internet::get());
+ CPPUNIT_ASSERT(officecfg::Office::Common::Save::URL::FileSystem::get());
+
+ createSwDoc("URLs.odt");
+ // Export to ReqIF, using relative URLs (the default)
+ saveWithParams({
+ comphelper::makePropertyValue(u"FilterName"_ustr, u"HTML (StarWriter)"_ustr),
+ comphelper::makePropertyValue(u"FilterOptions"_ustr, u"xhtmlns=reqif-xhtml"_ustr),
+ comphelper::makePropertyValue(u"ExportImagesAsOLE"_ustr, true),
+ });
+ xmlDocUniquePtr pXmlDoc = WrapReqifFromTempFile();
+
+ // HTTP URL: must be absolute
+ assertXPath(pXmlDoc, "//reqif-xhtml:p[1]/reqif-xhtml:a", "href", u"http://www.example.org/");
+ // file URL: must be relative
+ OUString url = getXPath(pXmlDoc, "//reqif-xhtml:p[2]/reqif-xhtml:a", "href");
+ CPPUNIT_ASSERT(!url.startsWith("file:"));
+ CPPUNIT_ASSERT(url.endsWith("NonExistingPath/NonExistingFile.html"));
+ // form URL: must be absolute
+ assertXPath(pXmlDoc, "//reqif-xhtml:form", "action", u"https://www.example.org/submit");
+ // linked image exported as object: generated, must be relative
+ url = getXPath(pXmlDoc, "//reqif-xhtml:p[3]/reqif-xhtml:object", "data");
+ CPPUNIT_ASSERT(!url.startsWith("file:"));
+ CPPUNIT_ASSERT(url.endsWith(".ole"));
+ // its original image URL: must be relative
+ url = getXPath(pXmlDoc, "//reqif-xhtml:p[3]/reqif-xhtml:object/reqif-xhtml:object", "data");
+ CPPUNIT_ASSERT(!url.startsWith("file:"));
+ CPPUNIT_ASSERT(url.endsWith("external.png"));
+ // embedded image exported as object: generated, must be relative
+ url = getXPath(pXmlDoc, "//reqif-xhtml:p[4]/reqif-xhtml:object", "data");
+ CPPUNIT_ASSERT(!url.startsWith("file:"));
+ CPPUNIT_ASSERT(url.endsWith(".ole"));
+ // its image URL: generated, must be relative
+ url = getXPath(pXmlDoc, "//reqif-xhtml:p[4]/reqif-xhtml:object/reqif-xhtml:object", "data");
+ CPPUNIT_ASSERT(!url.startsWith("file:"));
+ CPPUNIT_ASSERT(url.endsWith(".png"));
+ // unordered list with image bullet - it gets embedded as base64 data
+ OUString style = getXPath(pXmlDoc, "//reqif-xhtml:ul", "style");
+ CPPUNIT_ASSERT(style.indexOf("list-style-image: url(data:image/png;base64,") != -1);
+ // an as-char frame, exported as a whole to an object, must be relative
+ url = getXPath(pXmlDoc, "//reqif-xhtml:p[5]/reqif-xhtml:object", "data");
+ CPPUNIT_ASSERT(!url.startsWith("file:"));
+ CPPUNIT_ASSERT(url.endsWith(".ole"));
+ // its file hyperlink must be relative
+ url = getXPath(pXmlDoc, "//reqif-xhtml:p[5]/reqif-xhtml:object/reqif-xhtml:a", "href");
+ CPPUNIT_ASSERT(!url.startsWith("file:"));
+ CPPUNIT_ASSERT(url.endsWith("foo/bar"));
+ // its image URL: generated, must be relative
+ url = getXPath(
+ pXmlDoc, "//reqif-xhtml:p[5]/reqif-xhtml:object/reqif-xhtml:a/reqif-xhtml:object", "data");
+ CPPUNIT_ASSERT(!url.startsWith("file:"));
+ CPPUNIT_ASSERT(url.endsWith(".png"));
+}
+
+CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, testHTML_exportAbsoluteURLs_ownRelative)
+{
+ auto pBatch(comphelper::ConfigurationChanges::create());
+ Resetter resetter([
+ bInternetPreviousValue = officecfg::Office::Common::Save::URL::Internet::get(),
+ bFileSystemPreviousValue = officecfg::Office::Common::Save::URL::FileSystem::get(), pBatch
+ ]() {
+ officecfg::Office::Common::Save::URL::Internet::set(bInternetPreviousValue, pBatch);
+ officecfg::Office::Common::Save::URL::FileSystem::set(bFileSystemPreviousValue, pBatch);
+ return pBatch->commit();
+ });
+ // Set saving absolute URLs
+ officecfg::Office::Common::Save::URL::Internet::set(false, pBatch);
+ officecfg::Office::Common::Save::URL::FileSystem::set(false, pBatch);
+ pBatch->commit();
+
+ createSwDoc("URLs.odt");
+ // Export to HTML, using absolute URLs
+ saveWithParams({
+ comphelper::makePropertyValue(u"FilterName"_ustr, u"HTML (StarWriter)"_ustr),
+ comphelper::makePropertyValue(u"RelativeOwnObjectURL"_ustr, true),
+ });
+ htmlDocUniquePtr pHtmlDoc = parseHtml(maTempFile);
+
+ // HTTP URL: must be absolute
+ assertXPath(pHtmlDoc, "//p[1]/a", "href", u"http://www.example.org/");
+ // file URL: must be absolute
+ assertXPath(pHtmlDoc, "//p[2]/a", "href",
+ createFileURL(u"NonExistingPath/NonExistingFile.html"));
+ // form URL: must be absolute
+ assertXPath(pHtmlDoc, "//form", "action", u"https://www.example.org/submit");
+ // linked image: must be absolute
+ assertXPath(pHtmlDoc, "//p[3]/img", "src", createFileURL(u"external.png"));
+ // embedded image: generated, must be relative
+ OUString url = getXPath(pHtmlDoc, "//p[4]/img", "src");
+ CPPUNIT_ASSERT(!url.startsWith("file:"));
+ CPPUNIT_ASSERT(url.endsWith(".png"));
+ // unordered list with image bullet - it gets embedded as base64 data
+ OUString style = getXPath(pHtmlDoc, "//ul", "style");
+ CPPUNIT_ASSERT(style.indexOf("list-style-image: url(data:image/png;base64,") != -1);
+ // image-in-frame file hyperlink must be absolute; FIXME: HTMLOutFuncs::Out_ImageMap
+ // assertXPath(pHtmlDoc, "//p[5]/map/area", "href", createFileURL(u"foo/bar"));
+ // its image URL: generated, must be relative
+ url = getXPath(pHtmlDoc, "//p[5]/img", "src");
+ CPPUNIT_ASSERT(!url.startsWith("file:"));
+ CPPUNIT_ASSERT(url.endsWith(".gif"));
+}
+
+CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, testHTML_exportRelativeURLs)
+{
+ CPPUNIT_ASSERT(officecfg::Office::Common::Save::URL::Internet::get());
+ CPPUNIT_ASSERT(officecfg::Office::Common::Save::URL::FileSystem::get());
+
+ createSwDoc("URLs.odt");
+ // Export to HTML, using relative URLs (the default)
+ ExportToHTML();
+ htmlDocUniquePtr pHtmlDoc = parseHtml(maTempFile);
+
+ // HTTP URL: must be absolute
+ assertXPath(pHtmlDoc, "//p[1]/a", "href", u"http://www.example.org/");
+ // file URL: must be relative
+ OUString url = getXPath(pHtmlDoc, "//p[2]/a", "href");
+ CPPUNIT_ASSERT(!url.startsWith("file:"));
+ CPPUNIT_ASSERT(url.endsWith("NonExistingPath/NonExistingFile.html"));
+ // form URL: must be absolute
+ assertXPath(pHtmlDoc, "//form", "action", u"https://www.example.org/submit");
+ // linked image: must be relative
+ url = getXPath(pHtmlDoc, "//p[3]/img", "src");
+ CPPUNIT_ASSERT(!url.startsWith("file:"));
+ CPPUNIT_ASSERT(url.endsWith("external.png"));
+ // embedded image: generated, must be relative
+ url = getXPath(pHtmlDoc, "//p[4]/img", "src");
+ CPPUNIT_ASSERT(!url.startsWith("file:"));
+ CPPUNIT_ASSERT(url.endsWith(".png"));
+ // unordered list with image bullet - it gets embedded as base64 data
+ OUString style = getXPath(pHtmlDoc, "//ul", "style");
+ CPPUNIT_ASSERT(style.indexOf("list-style-image: url(data:image/png;base64,") != -1);
+ // image-in-frame file hyperlink must be relative
+ url = getXPath(pHtmlDoc, "//p[5]/map/area", "href");
+ CPPUNIT_ASSERT(!url.startsWith("file:"));
+ CPPUNIT_ASSERT(url.endsWith("foo/bar"));
+ // its image URL: generated, must be relative
+ url = getXPath(pHtmlDoc, "//p[5]/img", "src");
+ CPPUNIT_ASSERT(!url.startsWith("file:"));
+ CPPUNIT_ASSERT(url.endsWith(".gif"));
+}
+
+CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, testReqIF_162282)
+{
+ // Given a document with an embedded metafile:
+ createSwDoc("tdf162282.odt");
+ ExportToReqif();
+ xmlDocUniquePtr pDoc = WrapReqifFromTempFile();
+
+ // Check that the exported EMF is exactly the same as in the ODF package
+ assertXPath(pDoc, "//reqif-xhtml:p/reqif-xhtml:object", "type", u"image/x-emf");
+ OUString imgName = getXPath(pDoc, "//reqif-xhtml:p/reqif-xhtml:object", "data");
+ CPPUNIT_ASSERT(imgName.endsWith(".emf"));
+ INetURLObject aUrl(maTempFile.GetURL());
+ aUrl.setName(imgName);
+ SvFileStream aEmfStream(aUrl.GetMainURL(INetURLObject::DecodeMechanism::NONE),
+ StreamMode::READ);
+
+ // without the fix, this would fail with
+ // - Expected: 220
+ // - Actual : 111260
+ CPPUNIT_ASSERT_EQUAL(sal_uInt64(220), aEmfStream.TellEnd());
+
+ css::uno::Sequence emfData(220);
+ aEmfStream.ReadBytes(emfData.getArray(), emfData.getLength());
+
+ const css::uno::Sequence correctData{
+ 0x01, 0x00, 0x00, 0x00, 0x6C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0xF4, 0x01, 0x00, 0x00, 0xF4, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x88, 0x13, 0x00, 0x00, 0x88, 0x13, 0x00, 0x00, 0x20, 0x45, 0x4D, 0x46, 0x00,
+ 0x00, 0x01, 0x00, 0xDC, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x07, 0x00,
+ 0x00, 0x38, 0x04, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x6C, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF4, 0x01, 0x00, 0x00, 0xF4,
+ 0x01, 0x00, 0x00, 0x39, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x2D, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0x96, 0x00, 0x00,
+ 0x00, 0xFA, 0x00, 0x00, 0x00, 0x2C, 0x01, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00,
+ 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x2F, 0x00, 0x00, 0x00, 0x28,
+ 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x2C, 0x01, 0x00, 0x00,
+ 0x2C, 0x01, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x2C, 0x01, 0x00,
+ 0x00, 0xC8, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00,
+ };
+ CPPUNIT_ASSERT_EQUAL(correctData, emfData);
+}
+
+CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, testHTML_162426)
+{
+ // Given a document with an image with style:wrap="none":
+ createSwDoc("tdf162426_image_with_wrap_none.fodt");
+ // Before the fix, an assertion failed in HtmlWriter::attribute when exporting to HTML :
+ ExportToHTML();
+
+ xmlDocUniquePtr pDoc = parseXml(maTempFile);
+ CPPUNIT_ASSERT(pDoc);
+
+ // Before the fix, the 'border' attribute was written after the 'img' tag was already closed,
+ // so without the assertion, this would fail with
+ // - In <>, XPath '/html/body/p/img' no attribute 'border' exist
+ assertXPath(pDoc, "/html/body/p/img", "border", u"0");
+}
+
+CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, testHTML_163873)
+{
+ // Given a document with an image with style:wrap="none":
+ createSwDoc("tdf131728.docx");
+ // Before the fix, an assertion failed in HtmlWriter::attribute when exporting to HTML :
+ ExportToHTML();
+
+ xmlDocUniquePtr pDoc = parseXml(maTempFile);
+ CPPUNIT_ASSERT(pDoc);
+
+ // Before the fix, inline headings weren't inline
+ assertXPath(pDoc, "/html/body/p[5]/span/h2", "style", u"display:inline;");
+ assertXPath(pDoc, "/html/body/p[6]/span/h2", "style", u"display:inline;");
+ assertXPath(pDoc, "/html/body/p[7]/span/h2", "style", u"display:inline;");
+ assertXPath(pDoc, "/html/body/p[11]/span/h2", "style", u"display:inline;");
+ assertXPath(pDoc, "/html/body/p[14]/span/h2", "style", u"display:inline;");
+}
+
+} // end of anonymous namespace
+CPPUNIT_PLUGIN_IMPLEMENT();
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/qa/extras/htmlexport/htmlmodeltestbase.hxx b/sw/qa/extras/htmlexport/htmlmodeltestbase.hxx
new file mode 100644
index 000000000000..c3fc357118d8
--- /dev/null
+++ b/sw/qa/extras/htmlexport/htmlmodeltestbase.hxx
@@ -0,0 +1,232 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+
+/// Test RTF parser that just extracts a single OLE2 object from a file.
+class TestReqIfRtfReader : public SvRTFParser
+{
+public:
+ TestReqIfRtfReader(SvStream& rStream);
+ void NextToken(int nToken) override;
+ bool WriteObjectData(SvStream& rOLE);
+ tools::Long GetObjw() const { return m_nObjw; }
+ tools::Long GetObjh() const { return m_nObjh; }
+ int getWmetafile() const { return m_nWmetafile; }
+
+private:
+ bool m_bInObjData = false;
+ OStringBuffer m_aHex;
+ tools::Long m_nObjw = 0;
+ tools::Long m_nObjh = 0;
+ int m_nWmetafile = 0;
+};
+
+TestReqIfRtfReader::TestReqIfRtfReader(SvStream& rStream)
+ : SvRTFParser(rStream)
+{
+}
+
+void TestReqIfRtfReader::NextToken(int nToken)
+{
+ switch (nToken)
+ {
+ case '}':
+ m_bInObjData = false;
+ break;
+ case RTF_TEXTTOKEN:
+ if (m_bInObjData)
+ m_aHex.append(OUStringToOString(aToken, RTL_TEXTENCODING_ASCII_US));
+ break;
+ case RTF_OBJDATA:
+ m_bInObjData = true;
+ break;
+ case RTF_OBJW:
+ m_nObjw = nTokenValue;
+ break;
+ case RTF_OBJH:
+ m_nObjh = nTokenValue;
+ break;
+ case RTF_WMETAFILE:
+ m_nWmetafile = nTokenValue;
+ break;
+ }
+}
+
+bool TestReqIfRtfReader::WriteObjectData(SvStream& rOLE)
+{
+ OString aObjdata = m_aHex.makeStringAndClear();
+
+ SvMemoryStream aStream;
+ int b = 0;
+ int count = 2;
+
+ // Feed the destination text to a stream.
+ for (int i = 0; i < aObjdata.getLength(); ++i)
+ {
+ char ch = aObjdata[i];
+ if (ch != 0x0d && ch != 0x0a)
+ {
+ b = b << 4;
+ sal_Int8 parsed = msfilter::rtfutil::AsHex(ch);
+ if (parsed == -1)
+ return false;
+ b += parsed;
+ count--;
+ if (!count)
+ {
+ aStream.WriteChar(b);
+ count = 2;
+ b = 0;
+ }
+ }
+ }
+
+ aStream.Seek(0);
+ rOLE.WriteStream(aStream);
+ return true;
+}
+
+/// Parser for [MS-OLEDS] 2.2.5 EmbeddedObject, aka OLE1.
+struct OLE1Reader
+{
+ sal_uInt32 m_nNativeDataSize;
+ std::vector m_aNativeData;
+ sal_uInt32 m_nPresentationDataSize;
+
+ OLE1Reader(SvStream& rStream);
+};
+
+OLE1Reader::OLE1Reader(SvStream& rStream)
+{
+ // Skip ObjectHeader, see [MS-OLEDS] 2.2.4.
+ rStream.Seek(0);
+ CPPUNIT_ASSERT(rStream.remainingSize());
+ sal_uInt32 nData;
+ rStream.ReadUInt32(nData); // OLEVersion
+ rStream.ReadUInt32(nData); // FormatID
+ rStream.ReadUInt32(nData); // ClassName
+ rStream.SeekRel(nData);
+ rStream.ReadUInt32(nData); // TopicName
+ rStream.SeekRel(nData);
+ rStream.ReadUInt32(nData); // ItemName
+ rStream.SeekRel(nData);
+
+ rStream.ReadUInt32(m_nNativeDataSize);
+ m_aNativeData.resize(m_nNativeDataSize);
+ rStream.ReadBytes(m_aNativeData.data(), m_aNativeData.size());
+
+ rStream.ReadUInt32(nData); // OLEVersion for presentation data
+ CPPUNIT_ASSERT(rStream.good());
+ rStream.ReadUInt32(nData); // FormatID
+ rStream.ReadUInt32(nData); // ClassName
+ rStream.SeekRel(nData);
+ rStream.ReadUInt32(nData); // Width
+ rStream.ReadUInt32(nData); // Height
+ rStream.ReadUInt32(nData); // PresentationDataSize
+ m_nPresentationDataSize = nData;
+}
+
+/// Covers sw/source/filter/html/wrthtml.cxx and related fixes.
+class HtmlExportTest : public SwModelTestBase, public HtmlTestTools
+{
+public:
+ HtmlExportTest()
+ : SwModelTestBase(u"/sw/qa/extras/htmlexport/data/"_ustr, u"HTML (StarWriter)"_ustr)
+ {
+ }
+
+ /// 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);
+ }
+};
+
+/// HTML export of the sw doc model tests.
+class SwHtmlDomExportTest : public SwModelTestBase, public HtmlTestTools
+{
+public:
+ SwHtmlDomExportTest()
+ : SwModelTestBase(u"/sw/qa/extras/htmlexport/data/"_ustr)
+ {
+ }
+
+ OUString GetObjectPath(const OUString& ext);
+ /// Get the .ole path, assuming maTempFile is an XHTML export result.
+ OUString GetOlePath() { return GetObjectPath(u".ole"_ustr); }
+ OUString GetPngPath() { return GetObjectPath(u".png"_ustr); }
+ /// Parse the ole1 data out of an RTF fragment URL.
+ void ParseOle1FromRtfUrl(const OUString& rRtfUrl, SvMemoryStream& rOle1);
+ /// Export using the C++ HTML export filter, with xhtmlns=reqif-xhtml.
+ void ExportToReqif();
+ /// Import using the C++ HTML import filter, with xhtmlns=reqif-xhtml.
+ void ImportFromReqif(const OUString& rUrl);
+ /// Export using the C++ HTML export filter
+ void ExportToHTML();
+};
+
+OUString SwHtmlDomExportTest::GetObjectPath(const OUString& ext)
+{
+ assert(ext.startsWith("."));
+ xmlDocUniquePtr pDoc = WrapReqifFromTempFile();
+ OUString aOlePath = getXPath(
+ pDoc, "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:p/reqif-xhtml:object", "data");
+ CPPUNIT_ASSERT(aOlePath.endsWith(ext));
+ INetURLObject aUrl(maTempFile.GetURL());
+ aUrl.setBase(aOlePath.subView(0, aOlePath.getLength() - ext.getLength()));
+ aUrl.setExtension(ext.subView(1));
+ return aUrl.GetMainURL(INetURLObject::DecodeMechanism::NONE);
+}
+
+void SwHtmlDomExportTest::ParseOle1FromRtfUrl(const OUString& rRtfUrl, SvMemoryStream& rOle1)
+{
+ SvMemoryStream aRtf;
+ HtmlExportTest::wrapRtfFragment(rRtfUrl, aRtf);
+ tools::SvRef xReader(new TestReqIfRtfReader(aRtf));
+ CPPUNIT_ASSERT(xReader->CallParser() != SvParserState::Error);
+ CPPUNIT_ASSERT(xReader->WriteObjectData(rOle1));
+ CPPUNIT_ASSERT(rOle1.Tell());
+}
+
+void SwHtmlDomExportTest::ExportToReqif()
+{
+ setFilterOptions(u"xhtmlns=reqif-xhtml"_ustr);
+ save(u"HTML (StarWriter)"_ustr);
+}
+
+void SwHtmlDomExportTest::ExportToHTML()
+{
+ uno::Sequence aStoreProperties = {
+ comphelper::makePropertyValue(u"FilterName"_ustr, u"HTML (StarWriter)"_ustr),
+ };
+ saveWithParams(aStoreProperties);
+}
+
+void SwHtmlDomExportTest::ImportFromReqif(const OUString& rUrl)
+{
+ uno::Sequence aLoadProperties = {
+ comphelper::makePropertyValue(u"FilterName"_ustr, u"HTML (StarWriter)"_ustr),
+ comphelper::makePropertyValue(u"FilterOptions"_ustr, u"xhtmlns=reqif-xhtml"_ustr),
+ };
+ loadWithParams(rUrl, aLoadProperties);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */