1
0
Fork 0
libreoffice/sw/qa/extras/htmlexport/htmlexport2.cxx
Daniel Baumann 8e63e14cf6
Adding upstream version 4:25.2.3.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
2025-06-22 16:20:04 +02:00

1642 lines
73 KiB
C++

/* -*- 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());
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\n"_ustr);
// When exporting to reqif-xhtml:
ExportToReqif();
// Then make sure that we still have a single line-break:
xmlDocUniquePtr pXmlDoc = WrapReqifFromTempFile();
// Without the accompanying fix in place, this test would have failed with:
// - Expected: 1
// - Actual : 2
// - XPath '//reqif-xhtml:br' number of nodes is incorrect
assertXPath(pXmlDoc, "//reqif-xhtml:br", 1);
// Then test the import side:
// Given an empty document:
// When importing a <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\n"_ustr, aActual);
}
CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, testLeadingTab)
{
// Given a document with leading tabs:
createSwDoc();
SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell();
pWrtShell->Insert(u"\t first"_ustr);
pWrtShell->SplitNode();
pWrtShell->Insert(u"\t\t second"_ustr);
pWrtShell->SplitNode();
pWrtShell->Insert(u"thi \t rd"_ustr);
// When exporting to HTML, using LeadingTabWidth=2:
uno::Sequence<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 \t rd");
}
CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, testLeadingTabHTML)
{
// Given a document with leading tabs:
createSwDoc();
SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell();
pWrtShell->Insert(u"\t test"_ustr);
// When exporting to plain HTML, using LeadingTabWidth=2:
uno::Sequence<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\n\t\tsub-header"_ustr, aContent.trim());
}
CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, testReqIF_RightAlignedTable)
{
createSwDoc("tableRight.fodt");
ExportToReqif();
xmlDocUniquePtr pDoc = WrapReqifFromTempFile();
// No 'align' attribute must be present in 'div'
assertXPathNoAttribute(pDoc, "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:div", "align");
}
CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, testReqIF_ListsWithNumFormat)
{
createSwDoc("listsWithNumFormat.fodt");
ExportToReqif();
xmlDocUniquePtr pDoc = WrapReqifFromTempFile();
// No 'type' attribute must be present in 'ol'
assertXPathNoAttribute(pDoc, "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:ol[1]", "type");
assertXPathNoAttribute(pDoc, "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:ol[2]", "type");
assertXPathNoAttribute(pDoc, "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:ol[3]", "type");
assertXPathNoAttribute(pDoc, "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:ol[4]", "type");
}
CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, testTdf155871)
{
createSwDoc("tdf155871.fodt");
ExportToReqif();
// Without the fix in place, this would fail
xmlDocUniquePtr pDoc = WrapReqifFromTempFile();
}
CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, testReqIF_ListsNoStartAttribute)
{
createSwDoc("twoListsWithSameStyle.fodt");
ExportToReqif();
xmlDocUniquePtr pDoc = WrapReqifFromTempFile();
// No 'start' attribute must be present in 'ol'
assertXPath(pDoc, "//reqif-xhtml:ol[@start]", 0);
}
CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, testReqIF_FrameTextAsObjectAltText)
{
createSwDoc("frameWithText.fodt");
ExportToReqif();
xmlDocUniquePtr pDoc = WrapReqifFromTempFile();
// Without the fix, this would fail with
// - Expected: Some text in frame & <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"\t test \t more text \t"_ustr;
pWrtShell->Insert(paraText);
// When exporting to plain HTML, using PreserveSpaces:
saveWithParams({
comphelper::makePropertyValue(u"FilterName"_ustr, u"HTML (StarWriter)"_ustr),
comphelper::makePropertyValue(u"PreserveSpaces"_ustr, true),
});
// Then make sure that "white-space: pre-wrap" is written into the paragraph's style:
htmlDocUniquePtr pHtmlDoc = parseHtml(maTempFile);
CPPUNIT_ASSERT(pHtmlDoc);
const OUString style = getXPath(pHtmlDoc, "/html/body/p", "style");
CPPUNIT_ASSERT(style.indexOf("white-space: pre-wrap") >= 0);
// Also check that the paragraph text is correct, without modifications in whitespace
assertXPathContent(pHtmlDoc, "/html/body/p", paraText);
// Test import
setImportFilterName(u"HTML (StarWriter)"_ustr);
loadFromURL(maTempFile.GetURL());
CPPUNIT_ASSERT_EQUAL(paraText, getParagraph(1)->getString());
}
CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, testReqIF_PreserveSpaces)
{
// Given a document with leading, trailing, and repeating intermediate spaces:
createSwDoc();
SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell();
static constexpr OUString paraText = u"\t test \t more text \t"_ustr;
pWrtShell->Insert(paraText);
// When exporting to ReqIF, using PreserveSpaces:
saveWithParams({
comphelper::makePropertyValue(u"FilterName"_ustr, u"HTML (StarWriter)"_ustr),
comphelper::makePropertyValue(u"FilterOptions"_ustr, u"xhtmlns=reqif-xhtml"_ustr),
comphelper::makePropertyValue(u"PreserveSpaces"_ustr, true),
});
// Then make sure that xml:space="preserve" attribute exists in the paragraph element:
xmlDocUniquePtr pXmlDoc = WrapReqifFromTempFile();
assertXPath(pXmlDoc, "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:p", "space", u"preserve");
// Also check that the paragraph text is correct, without modifications in whitespace
assertXPathContent(pXmlDoc, "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:p", paraText);
// Test import
setImportFilterOptions(u"xhtmlns=reqif-xhtml"_ustr);
setImportFilterName(u"HTML (StarWriter)"_ustr);
loadFromURL(maTempFile.GetURL());
CPPUNIT_ASSERT_EQUAL(paraText, getParagraph(1)->getString());
}
CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, testHTML_NoPreserveSpaces)
{
// Test cases where "PreserveSpaces" should not introduce respective markup
const auto assertXPath_NoWhiteSpaceInStyle
= [this](const xmlDocUniquePtr& pDoc, const char* pXPath) {
xmlXPathObjectPtr pXmlObj = getXPathNode(pDoc, pXPath);
xmlNodeSetPtr pXmlNodes = pXmlObj->nodesetval;
CPPUNIT_ASSERT_EQUAL_MESSAGE(pXPath, 1, xmlXPathNodeSetGetLength(pXmlNodes));
CPPUNIT_ASSERT(pXmlNodes);
xmlNodePtr pXmlNode = pXmlNodes->nodeTab[0];
if (xmlChar* prop = xmlGetProp(pXmlNode, BAD_CAST("style")))
{
OUString style = OUString::fromUtf8(reinterpret_cast<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"\nLeading/trailing breaks"_ustr, getParagraph(5)->getString());
CPPUNIT_ASSERT_EQUAL(u"\n Leading break + space"_ustr, getParagraph(6)->getString());
// Trailing break is removed in SwHTMLParser::AppendTextNode, and replaced with para spacing
CPPUNIT_ASSERT_EQUAL(u"Trailing space + break "_ustr, getParagraph(7)->getString());
CPPUNIT_ASSERT_EQUAL(u"Middle\nbreak"_ustr, getParagraph(8)->getString());
CPPUNIT_ASSERT_EQUAL(u"Middle space \n+ break"_ustr, getParagraph(9)->getString());
CPPUNIT_ASSERT_EQUAL(u"Middle break\n + space"_ustr, getParagraph(10)->getString());
// The SVG is replaced by a space in SwXParagraph::getString()
CPPUNIT_ASSERT_EQUAL(u"Trailing space and SVG "_ustr, getParagraph(11)->getString());
}
CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, testReqIF_NoPreserveSpaces)
{
// Test cases where "PreserveSpaces" should not introduce respective markup
createSwDoc("test_no_space_preserve.fodt");
// Export to ReqIF, using PreserveSpaces:
saveWithParams({
comphelper::makePropertyValue(u"FilterName"_ustr, u"HTML (StarWriter)"_ustr),
comphelper::makePropertyValue(u"FilterOptions"_ustr, u"xhtmlns=reqif-xhtml"_ustr),
comphelper::makePropertyValue(u"PreserveSpaces"_ustr, true),
});
xmlDocUniquePtr pXmlDoc = WrapReqifFromTempFile();
// No whitespace preservation, where no leading / trailing / double whitespace
assertXPathNoAttribute(pXmlDoc, "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:p[1]", "space");
// Whitespace preserved for a leading space
assertXPath(pXmlDoc, "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:p[2]", "space",
u"preserve");
// Whitespace preserved for a trailing space
assertXPath(pXmlDoc, "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:p[3]", "space",
u"preserve");
// Whitespace preserved for a double space
assertXPath(pXmlDoc, "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:p[4]", "space",
u"preserve");
// No whitespace preservation for leading / trailing breaks
assertXPathNoAttribute(pXmlDoc, "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:p[5]", "space");
// Whitespace preserved for a leading break + space
assertXPath(pXmlDoc, "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:p[6]", "space",
u"preserve");
// No whitespace preservation for a trailing space + break
assertXPathNoAttribute(pXmlDoc, "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:p[7]", "space");
// No whitespace preservation for a middle break
assertXPathNoAttribute(pXmlDoc, "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:p[8]", "space");
// No whitespace preservation for a middle space + break
assertXPathNoAttribute(pXmlDoc, "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:p[9]", "space");
// Whitespace preserved for a middle break + space
assertXPath(pXmlDoc, "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:p[10]", "space",
u"preserve");
// No whitespace preservation for a trailing space and SVG
assertXPathNoAttribute(pXmlDoc, "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:p[11]", "space");
// Test import
setImportFilterOptions(u"xhtmlns=reqif-xhtml"_ustr);
setImportFilterName(u"HTML (StarWriter)"_ustr);
loadFromURL(maTempFile.GetURL());
CPPUNIT_ASSERT_EQUAL(u"No special spaces"_ustr, getParagraph(1)->getString());
CPPUNIT_ASSERT_EQUAL(u" Leading space"_ustr, getParagraph(2)->getString());
CPPUNIT_ASSERT_EQUAL(u"Trailing space "_ustr, getParagraph(3)->getString());
CPPUNIT_ASSERT_EQUAL(u"Double space"_ustr, getParagraph(4)->getString());
CPPUNIT_ASSERT_EQUAL(u"\nLeading/trailing breaks\n"_ustr, getParagraph(5)->getString());
CPPUNIT_ASSERT_EQUAL(u"\n Leading break + space"_ustr, getParagraph(6)->getString());
CPPUNIT_ASSERT_EQUAL(u"Trailing space + break \n"_ustr, getParagraph(7)->getString());
CPPUNIT_ASSERT_EQUAL(u"Middle\nbreak"_ustr, getParagraph(8)->getString());
CPPUNIT_ASSERT_EQUAL(u"Middle space \n+ break"_ustr, getParagraph(9)->getString());
CPPUNIT_ASSERT_EQUAL(u"Middle break\n + space"_ustr, getParagraph(10)->getString());
// The SVG is replaced by a space in SwXParagraph::getString()
CPPUNIT_ASSERT_EQUAL(u"Trailing space and SVG "_ustr, getParagraph(11)->getString());
}
CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, testReqIF_ExportFormulasAsPDF)
{
// Given a document with a formula:
createSwDoc("embedded_formula.fodt");
// When exporting to reqif with ExportFormulasAsPDF=true:
uno::Sequence<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"));
// There must be no 'target' attribute
assertXPathNoAttribute(pXmlDoc, "//reqif-xhtml:p[2]/reqif-xhtml:a", "target");
}
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;");
}
CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, testReqifNoTargetInA)
{
// Given a document with a link with target:
createSwDoc("link_with_target.fodt");
// When exporting to XHTML:
ExportToReqif();
// Check that 'a' element has no 'target' attribute
xmlDocUniquePtr pXmlDoc = WrapReqifFromTempFile();
// Without the accompanying fix in place, this test would have failed
assertXPathNoAttribute(pXmlDoc, "//reqif-xhtml:a", "target");
}
CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, testReqifNoObjectBorderExport)
{
// Given a document with an image with black border
createSwDoc("img_with_border.fodt");
// When exporting to XHTML:
ExportToReqif();
// Check that there's no 'font' element that used to be exported, containing the border color:
xmlDocUniquePtr pXmlDoc = WrapReqifFromTempFile();
// Without the accompanying fix in place, this test would have failed
assertXPath(pXmlDoc, "//reqif-xhtml:font", 0);
}
} // end of anonymous namespace
CPPUNIT_PLUGIN_IMPLEMENT();
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */