Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/LibreOffice/sw/qa/extras/htmlexport/   (Office von Apache Version 25.8.3.2©)  Datei vom 5.10.2025 mit Größe 72 kB image not shown  

Quelle  htmlexport2.cxx   Sprache: 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 <config_poppler.h>
#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", u"\n\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, UIName(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 & ");
}

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)
{
#if ENABLE_PDFIMPORT
    // 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));
#endif
}

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)
{
--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=90 H=94 G=91

¤ Dauer der Verarbeitung: 0.21 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.