sw/CppunitTest_sw_htmlexport2.mk | 80 + sw/Module_sw.mk | 1 sw/qa/extras/htmlexport/htmlexport.cxx | 1813 -------------------------- sw/qa/extras/htmlexport/htmlexport2.cxx | 1612 +++++++++++++++++++++++ sw/qa/extras/htmlexport/htmlmodeltestbase.hxx | 232 +++ 5 files changed, 1926 insertions(+), 1812 deletions(-)
New commits: commit e8a7423baabd53c8e9f9dcdac687cdbd2aa44ec0 Author: Xisco Fauli <[email protected]> AuthorDate: Wed Nov 20 14:50:12 2024 +0100 Commit: Xisco Fauli <[email protected]> CommitDate: Wed Nov 20 17:00:44 2024 +0100 CppunitTest_sw_htmlexport: split in two It already had 114 tests Change-Id: I16bddb42364c542fb2b5e115672096984b29e975 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/176847 Tested-by: Jenkins Reviewed-by: Xisco Fauli <[email protected]> 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 <swmodeltestbase.hxx> +#include "htmlmodeltestbase.hxx" #include <memory> -#include <string_view> #include <com/sun/star/document/XEmbeddedObjectSupplier2.hpp> -#include <com/sun/star/document/XTypeDetection.hpp> -#include <com/sun/star/embed/ElementModes.hpp> #include <com/sun/star/io/XActiveDataStreamer.hpp> #include <com/sun/star/io/XSeekable.hpp> #include <com/sun/star/style/ParagraphAdjust.hpp> #include <com/sun/star/awt/FontUnderline.hpp> -#include <com/sun/star/table/TableBorder2.hpp> #include <com/sun/star/text/XTextTablesSupplier.hpp> #include <com/sun/star/text/XTextTable.hpp> #include <com/sun/star/text/XTextEmbeddedObjectsSupplier.hpp> -#include <com/sun/star/document/XStorageBasedDocument.hpp> #include <com/sun/star/packages/zip/ZipFileAccess.hpp> -#include <com/sun/star/view/XSelectionSupplier.hpp> -#include <officecfg/Office/Common.hxx> - -#include <test/htmltesttools.hxx> -#include <tools/urlobj.hxx> #include <svtools/rtfkeywd.hxx> -#include <comphelper/propertyvalue.hxx> #include <comphelper/propertysequence.hxx> #include <comphelper/scopeguard.hxx> -#include <svtools/parrtf.hxx> -#include <rtl/strbuf.hxx> -#include <svtools/rtftoken.h> -#include <filter/msfilter/rtfutil.hxx> #include <sot/storage.hxx> #include <vcl/svapp.hxx> -#include <unotools/mediadescriptor.hxx> -#include <svtools/htmlcfg.hxx> #include <unotools/ucbstreamhelper.hxx> #include <comphelper/processfactory.hxx> -#include <vcl/graphicfilter.hxx> #include <vcl/dibtools.hxx> -#include <o3tl/string_view.hxx> -#include <editeng/brushitem.hxx> #include <swmodule.hxx> #include <swdll.hxx> #include <usrpref.hxx> #include <wrtsh.hxx> #include <ndtxt.hxx> -#include <paratr.hxx> #include <docsh.hxx> #include <unotxdoc.hxx> -#include <formatlinebreak.hxx> -#include <itabenum.hxx> 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<char> 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<TestReqIfRtfReader> 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<beans::PropertyValue> aStoreProperties = { - comphelper::makePropertyValue(u"FilterName"_ustr, u"HTML (StarWriter)"_ustr), - }; - saveWithParams(aStoreProperties); -} - -void SwHtmlDomExportTest::ImportFromReqif(const OUString& rUrl) -{ - uno::Sequence<beans::PropertyValue> 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<css::lang::XMultiServiceFactory> xFactory(mxComponent, uno::UNO_QUERY); - uno::Reference<drawing::XShape> xShape( - xFactory->createInstance(u"com.sun.star.drawing.GraphicObjectShape"_ustr), uno::UNO_QUERY); - xShape->setSize(awt::Size(10000, 10000)); - uno::Reference<beans::XPropertySet> xShapeProps(xShape, uno::UNO_QUERY); - xShapeProps->setPropertyValue(u"GraphicURL"_ustr, uno::Any(createFileURL(u"ole2.png"))); - uno::Reference<drawing::XDrawPageSupplier> xDrawPageSupplier(mxComponent, uno::UNO_QUERY); - xDrawPageSupplier->getDrawPage()->add(xShape); - - // When exporting to XHTML: - uno::Sequence<beans::PropertyValue> 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<css::lang::XMultiServiceFactory> xFactory(mxComponent, uno::UNO_QUERY); - uno::Reference<drawing::XShape> xShape( - xFactory->createInstance(u"com.sun.star.drawing.RectangleShape"_ustr), uno::UNO_QUERY); - xShape->setSize(awt::Size(10000, 10000)); - uno::Reference<drawing::XDrawPageSupplier> 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<css::lang::XMultiServiceFactory> xFactory(mxComponent, uno::UNO_QUERY); - uno::Reference<drawing::XShape> xShape( - xFactory->createInstance(u"com.sun.star.drawing.RectangleShape"_ustr), uno::UNO_QUERY); - xShape->setSize(awt::Size(5080, 2540)); - uno::Reference<drawing::XDrawPageSupplier> 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<css::lang::XMultiServiceFactory> xFactory(mxComponent, uno::UNO_QUERY); - uno::Reference<drawing::XShape> xShape( - xFactory->createInstance(u"com.sun.star.drawing.RectangleShape"_ustr), uno::UNO_QUERY); - xShape->setSize(awt::Size(2540, 2540)); - uno::Reference<drawing::XDrawPageSupplier> 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<css::lang::XMultiServiceFactory> xFactory(mxComponent, uno::UNO_QUERY); - uno::Reference<drawing::XShape> xShape( - xFactory->createInstance(u"com.sun.star.drawing.RectangleShape"_ustr), uno::UNO_QUERY); - xShape->setSize(awt::Size(5080, 2540)); - uno::Reference<drawing::XDrawPageSupplier> xDrawPageSupplier(mxComponent, uno::UNO_QUERY); - xDrawPageSupplier->getDrawPage()->add(xShape); - sal_Int32 nDPI = 600; - - // When exporting to XHTML: - uno::Sequence<beans::PropertyValue> 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<beans::PropertyValue> 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<beans::PropertyValue> 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 <img> 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 <li> between the outer and the inner <ol>: - 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 <li> inside the outer <ol> 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 "_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 <br> 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 "_ustr, aActual); -} - -CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, testLeadingTab) -{ - // Given a document with leading tabs: - createSwDoc(); - SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell(); - pWrtShell->Insert(u" first"_ustr); - pWrtShell->SplitNode(); - pWrtShell->Insert(u" second"_ustr); - pWrtShell->SplitNode(); - pWrtShell->Insert(u"thi rd"_ustr); - - // When exporting to HTML, using LeadingTabWidth=2: - uno::Sequence<beans::PropertyValue> 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<sal_Int32>(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: <nbsp><nbsp><space>first - // - Actual : <tab><space>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 rd"); -} - -CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, testLeadingTabHTML) -{ - // Given a document with leading tabs: - createSwDoc(); - SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell(); - pWrtShell->Insert(u" test"_ustr); - - // When exporting to plain HTML, using LeadingTabWidth=2: - uno::Sequence<beans::PropertyValue> aStoreProperties = { - comphelper::makePropertyValue(u"FilterName"_ustr, u"HTML (StarWriter)"_ustr), - comphelper::makePropertyValue(u"LeadingTabWidth"_ustr, static_cast<sal_Int32>(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: <newline><nbsp><nbsp><space>test - // - Actual : <newline><tab><space>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<container::XEnumerationAccess> xParagraph(getParagraph(1), uno::UNO_QUERY); - uno::Reference<container::XEnumeration> xPortions = xParagraph->createEnumeration(); - uno::Reference<beans::XPropertySet> 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<text::XTextContent> xLineBreak; - xPortion->getPropertyValue(u"LineBreak"_ustr) >>= xLineBreak; - sal_Int16 eClear{}; - uno::Reference<beans::XPropertySet> xLineBreakProps(xLineBreak, uno::UNO_QUERY); - xLineBreakProps->getPropertyValue(u"Clear"_ustr) >>= eClear; - CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int16>(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<lang::XMultiServiceFactory> xFactory(mxComponent, uno::UNO_QUERY); - uno::Reference<beans::XPropertySet> 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<sal_Int16>(42))); - xTextGraphic->setPropertyValue(u"IsSyncHeightToWidth"_ustr, uno::Any(true)); - uno::Reference<text::XTextDocument> xTextDocument(mxComponent, uno::UNO_QUERY); - uno::Reference<text::XText> xBodyText = xTextDocument->getText(); - uno::Reference<text::XTextCursor> xCursor(xBodyText->createTextCursor()); - uno::Reference<text::XTextContent> 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<beans::XPropertySet> 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 sub-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 & <foo> - // - 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 & <foo>"); -} - -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<css::drawing::XDrawPageSupplier>()); - auto xDrawPage(xDrawPageSupplier->getDrawPage()); - auto xModel(mxComponent.queryThrow<css::frame::XModel>()); - auto xController(xModel->getCurrentController().queryThrow<css::view::XSelectionSupplier>()); - xController->select(xDrawPage->getByIndex(0)); - - // Store only the selection - css::uno::Sequence<css::beans::PropertyValue> 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<TestReqIfRtfReader> 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<css::table::TableBorder2>(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<css::table::TableBorder2>(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" test more text "_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" test more text "_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<const char*>(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" Leading/trailing breaks"_ustr, getParagraph(5)->getString()); - CPPUNIT_ASSERT_EQUAL(u" 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 break"_ustr, getParagraph(8)->getString()); - CPPUNIT_ASSERT_EQUAL(u"Middle space + break"_ustr, getParagraph(9)->getString()); - CPPUNIT_ASSERT_EQUAL(u"Middle break + 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" Leading/trailing breaks "_ustr, getParagraph(5)->getString()); - CPPUNIT_ASSERT_EQUAL(u" Leading break + space"_ustr, getParagraph(6)->getString()); - CPPUNIT_ASSERT_EQUAL(u"Trailing space + break "_ustr, getParagraph(7)->getString()); - CPPUNIT_ASSERT_EQUAL(u"Middle break"_ustr, getParagraph(8)->getString()); - CPPUNIT_ASSERT_EQUAL(u"Middle space + break"_ustr, getParagraph(9)->getString()); - CPPUNIT_ASSERT_EQUAL(u"Middle break + 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<beans::PropertyValue> 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<css::beans::PropertyValue> descr{ - comphelper::makePropertyValue(u"URL"_ustr, GetObjectPath(u".pdf"_ustr)), - }; - - uno::Reference<lang::XMultiServiceFactory> xFactory( - comphelper::getProcessComponentContext()->getServiceManager(), uno::UNO_QUERY_THROW); - uno::Reference<document::XTypeDetection> 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 </reqif-xhtml:span> - 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 <a> 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<sal_uInt8> emfData(220); - aEmfStream.ReadBytes(emfData.getArray(), emfData.getLength()); - - const css::uno::Sequence<sal_uInt8> 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 <memory> + +#include <com/sun/star/document/XTypeDetection.hpp> +#include <com/sun/star/table/TableBorder2.hpp> +#include <com/sun/star/view/XSelectionSupplier.hpp> + +#include <officecfg/Office/Common.hxx> + +#include <vcl/svapp.hxx> +#include <comphelper/processfactory.hxx> +#include <vcl/graphicfilter.hxx> +#include <vcl/dibtools.hxx> +#include <editeng/brushitem.hxx> + +#include <wrtsh.hxx> +#include <ndtxt.hxx> +#include <docsh.hxx> +#include <unotxdoc.hxx> +#include <formatlinebreak.hxx> +#include <itabenum.hxx> + +namespace +{ +CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, testReqifEmbedPNGShapeAsOLE) +{ + // Given a document with an image shape: + createSwDoc(); + uno::Reference<css::lang::XMultiServiceFactory> xFactory(mxComponent, uno::UNO_QUERY); + uno::Reference<drawing::XShape> xShape( + xFactory->createInstance(u"com.sun.star.drawing.GraphicObjectShape"_ustr), uno::UNO_QUERY); + xShape->setSize(awt::Size(10000, 10000)); + uno::Reference<beans::XPropertySet> xShapeProps(xShape, uno::UNO_QUERY); + xShapeProps->setPropertyValue(u"GraphicURL"_ustr, uno::Any(createFileURL(u"ole2.png"))); + uno::Reference<drawing::XDrawPageSupplier> xDrawPageSupplier(mxComponent, uno::UNO_QUERY); + xDrawPageSupplier->getDrawPage()->add(xShape); + + // When exporting to XHTML: + uno::Sequence<beans::PropertyValue> 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<css::lang::XMultiServiceFactory> xFactory(mxComponent, uno::UNO_QUERY); + uno::Reference<drawing::XShape> xShape( + xFactory->createInstance(u"com.sun.star.drawing.RectangleShape"_ustr), uno::UNO_QUERY); + xShape->setSize(awt::Size(10000, 10000)); + uno::Reference<drawing::XDrawPageSupplier> 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<css::lang::XMultiServiceFactory> xFactory(mxComponent, uno::UNO_QUERY); + uno::Reference<drawing::XShape> xShape( + xFactory->createInstance(u"com.sun.star.drawing.RectangleShape"_ustr), uno::UNO_QUERY); + xShape->setSize(awt::Size(5080, 2540)); + uno::Reference<drawing::XDrawPageSupplier> 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<css::lang::XMultiServiceFactory> xFactory(mxComponent, uno::UNO_QUERY); + uno::Reference<drawing::XShape> xShape( + xFactory->createInstance(u"com.sun.star.drawing.RectangleShape"_ustr), uno::UNO_QUERY); + xShape->setSize(awt::Size(2540, 2540)); + uno::Reference<drawing::XDrawPageSupplier> 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<css::lang::XMultiServiceFactory> xFactory(mxComponent, uno::UNO_QUERY); + uno::Reference<drawing::XShape> xShape( + xFactory->createInstance(u"com.sun.star.drawing.RectangleShape"_ustr), uno::UNO_QUERY); + xShape->setSize(awt::Size(5080, 2540)); + uno::Reference<drawing::XDrawPageSupplier> xDrawPageSupplier(mxComponent, uno::UNO_QUERY); + xDrawPageSupplier->getDrawPage()->add(xShape); + sal_Int32 nDPI = 600; + + // When exporting to XHTML: + uno::Sequence<beans::PropertyValue> 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<beans::PropertyValue> 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<beans::PropertyValue> 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()); -e ... etc. - the rest is truncated
