diff options
Diffstat (limited to 'filter/qa/unit')
20 files changed, 734 insertions, 0 deletions
diff --git a/filter/qa/unit/data/TransparentText.odg b/filter/qa/unit/data/TransparentText.odg Binary files differnew file mode 100644 index 000000000..d3027d17d --- /dev/null +++ b/filter/qa/unit/data/TransparentText.odg diff --git a/filter/qa/unit/data/attributeRedefinedTest.odp b/filter/qa/unit/data/attributeRedefinedTest.odp Binary files differnew file mode 100644 index 000000000..dfb814bfb --- /dev/null +++ b/filter/qa/unit/data/attributeRedefinedTest.odp diff --git a/filter/qa/unit/data/calc.ots b/filter/qa/unit/data/calc.ots Binary files differnew file mode 100644 index 000000000..d16d2307f --- /dev/null +++ b/filter/qa/unit/data/calc.ots diff --git a/filter/qa/unit/data/custom-bullet.fodp b/filter/qa/unit/data/custom-bullet.fodp new file mode 100644 index 000000000..4139260f9 --- /dev/null +++ b/filter/qa/unit/data/custom-bullet.fodp @@ -0,0 +1,36 @@ +<?xml version="1.0" encoding="UTF-8"?> +<office:document xmlns:presentation="urn:oasis:names:tc:opendocument:xmlns:presentation:1.0" xmlns:svg="urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0" xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0" xmlns:style="urn:oasis:names:tc:opendocument:xmlns:style:1.0" xmlns:draw="urn:oasis:names:tc:opendocument:xmlns:drawing:1.0" xmlns:fo="urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0" xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" office:version="1.3" office:mimetype="application/vnd.oasis.opendocument.presentation"> + <office:automatic-styles> + <style:page-layout style:name="PM1"> + <style:page-layout-properties fo:margin-top="0cm" fo:margin-bottom="0cm" fo:margin-left="0cm" fo:margin-right="0cm" fo:page-width="28cm" fo:page-height="15.75cm" style:print-orientation="landscape"/> + </style:page-layout> + <style:style style:name="gr1" style:family="graphic" style:parent-style-name="standard"> + <style:graphic-properties draw:stroke="none" svg:stroke-color="#000000" draw:fill="none" draw:fill-color="#ffffff" draw:auto-grow-height="true" draw:auto-grow-width="false" fo:max-height="0cm" fo:min-height="0cm"/> + </style:style> + <text:list-style style:name="L1"> + <text:list-level-style-bullet text:level="1" text:bullet-char="-"> + <style:list-level-properties text:min-label-width="0.6cm"/> + <style:text-properties fo:font-family="OpenSymbol" style:font-style-name="Regular" style:font-charset="x-symbol" style:use-window-font-color="true" fo:font-size="45%"/> + </text:list-level-style-bullet> + </text:list-style> + </office:automatic-styles> + <office:master-styles> + <style:master-page style:name="Default" style:page-layout-name="PM1"> + </style:master-page> + </office:master-styles> + <office:body> + <office:presentation> + <draw:page draw:name="page1" draw:master-page-name="Default"> + <draw:frame draw:style-name="gr1" draw:text-style-name="P8" draw:layer="layout" svg:width="9.525cm" svg:height="0.962cm" svg:x="3.175cm" svg:y="2.54cm"> + <draw:text-box> + <text:list text:style-name="L1"> + <text:list-item> + <text:p>hello</text:p> + </text:list-item> + </text:list> + </draw:text-box> + </draw:frame> + </draw:page> + </office:presentation> + </office:body> +</office:document> diff --git a/filter/qa/unit/data/empty.doc b/filter/qa/unit/data/empty.doc new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/filter/qa/unit/data/empty.doc diff --git a/filter/qa/unit/data/empty.odp b/filter/qa/unit/data/empty.odp new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/filter/qa/unit/data/empty.odp diff --git a/filter/qa/unit/data/empty.ods b/filter/qa/unit/data/empty.ods new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/filter/qa/unit/data/empty.ods diff --git a/filter/qa/unit/data/empty.odt b/filter/qa/unit/data/empty.odt new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/filter/qa/unit/data/empty.odt diff --git a/filter/qa/unit/data/empty.pptx b/filter/qa/unit/data/empty.pptx new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/filter/qa/unit/data/empty.pptx diff --git a/filter/qa/unit/data/filter-dialogs-test.txt b/filter/qa/unit/data/filter-dialogs-test.txt new file mode 100644 index 000000000..22792ad28 --- /dev/null +++ b/filter/qa/unit/data/filter-dialogs-test.txt @@ -0,0 +1,49 @@ +# -*- 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/. +# + +# This file contains all dialogs that the unit tests in the module +# will work on if it is in script mode. It will read one-by-one, +# try to open it and create a screenshot that will be saved in +# workdir/screenshots using the pattern of the ui-file name. +# +# Syntax: +# - empty lines are allowed +# - lines starting with '#' are treated as comment +# - all other lines should contain a *.ui filename in the same +# notation as in the dialog constructors (see code) + +# +# The 'known' dialogs which have a hard-coded representation +# in registerKnownDialogsByID/createDialogByID +# + +# No known dialogs in filter for now + +# +# Dialogs without a hard-coded representation. These will +# be visualized using a fallback based on weld::Builder +# + +# currently deactivated, leads to problems and the test to not work +# This is typically a hint that these should be hard-coded in the +# test case since they need some document and model data to work + +filter/ui/pdfoptionsdialog.ui +filter/ui/xsltfilterdialog.ui +filter/ui/pdfgeneralpage.ui +filter/ui/pdfviewpage.ui +filter/ui/pdfuserinterfacepage.ui +filter/ui/pdfsecuritypage.ui +filter/ui/pdflinkspage.ui +filter/ui/pdfsignpage.ui +filter/ui/xmlfiltertabpagegeneral.ui +filter/ui/xmlfiltertabpagetransformation.ui +filter/ui/testxmlfilter.ui +filter/ui/warnpdfdialog.ui +filter/ui/xmlfiltersettings.ui diff --git a/filter/qa/unit/data/impress.otp b/filter/qa/unit/data/impress.otp Binary files differnew file mode 100644 index 000000000..199a5f9d4 --- /dev/null +++ b/filter/qa/unit/data/impress.otp diff --git a/filter/qa/unit/data/preserve-jpg.odt b/filter/qa/unit/data/preserve-jpg.odt Binary files differnew file mode 100644 index 000000000..83768bd47 --- /dev/null +++ b/filter/qa/unit/data/preserve-jpg.odt diff --git a/filter/qa/unit/data/semi-transparent-fill.odg b/filter/qa/unit/data/semi-transparent-fill.odg Binary files differnew file mode 100644 index 000000000..713f48991 --- /dev/null +++ b/filter/qa/unit/data/semi-transparent-fill.odg diff --git a/filter/qa/unit/data/semi-transparent-line.odg b/filter/qa/unit/data/semi-transparent-line.odg Binary files differnew file mode 100644 index 000000000..2d28a694c --- /dev/null +++ b/filter/qa/unit/data/semi-transparent-line.odg diff --git a/filter/qa/unit/data/shape-nographic.odp b/filter/qa/unit/data/shape-nographic.odp Binary files differnew file mode 100644 index 000000000..43186d614 --- /dev/null +++ b/filter/qa/unit/data/shape-nographic.odp diff --git a/filter/qa/unit/data/tdf114428.xhtml b/filter/qa/unit/data/tdf114428.xhtml new file mode 100644 index 000000000..f08f0fa4a --- /dev/null +++ b/filter/qa/unit/data/tdf114428.xhtml @@ -0,0 +1,9 @@ +<?xml version="1.0" encoding="utf-8"?> +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" +"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <title>Title of document</title> + </head> + <body>hello world</body> +</html> diff --git a/filter/qa/unit/data/writer.ott b/filter/qa/unit/data/writer.ott Binary files differnew file mode 100644 index 000000000..1ded03150 --- /dev/null +++ b/filter/qa/unit/data/writer.ott diff --git a/filter/qa/unit/filter-dialogs-test.cxx b/filter/qa/unit/filter-dialogs-test.cxx new file mode 100644 index 000000000..946a1961d --- /dev/null +++ b/filter/qa/unit/filter-dialogs-test.cxx @@ -0,0 +1,61 @@ +/* -*- 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 <sal/config.h> +#include <test/screenshot_test.hxx> +#include <vcl/abstdlg.hxx> + +using namespace ::com::sun::star; + +/// Test opening a dialog in filter +class FilterDialogsTest : public ScreenshotTest +{ +private: + /// helper method to populate KnownDialogs, called in setUp(). Needs to be + /// written and has to add entries to KnownDialogs + virtual void registerKnownDialogsByID(mapType& rKnownDialogs) override; + + /// dialog creation for known dialogs by ID. Has to be implemented for + /// each registered known dialog + virtual VclPtr<VclAbstractDialog> createDialogByID(sal_uInt32 nID) override; + +public: + FilterDialogsTest(); + + // try to open a dialog + void openAnyDialog(); + + CPPUNIT_TEST_SUITE(FilterDialogsTest); + CPPUNIT_TEST(openAnyDialog); + CPPUNIT_TEST_SUITE_END(); +}; + +FilterDialogsTest::FilterDialogsTest() {} + +void FilterDialogsTest::registerKnownDialogsByID(mapType& /*rKnownDialogs*/) +{ + // fill map of known dialogs +} + +VclPtr<VclAbstractDialog> FilterDialogsTest::createDialogByID(sal_uInt32 /*nID*/) +{ + return nullptr; +} + +void FilterDialogsTest::openAnyDialog() +{ + /// process input file containing the UXMLDescriptions of the dialogs to dump + processDialogBatchFile(u"filter/qa/unit/data/filter-dialogs-test.txt"); +} + +CPPUNIT_TEST_SUITE_REGISTRATION(FilterDialogsTest); + +CPPUNIT_PLUGIN_IMPLEMENT(); + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/filter/qa/unit/svg.cxx b/filter/qa/unit/svg.cxx new file mode 100644 index 000000000..22695acd6 --- /dev/null +++ b/filter/qa/unit/svg.cxx @@ -0,0 +1,380 @@ +/* -*- 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 <sal/config.h> + +#include <string_view> + +#include <test/bootstrapfixture.hxx> +#include <unotest/macros_test.hxx> +#include <test/xmltesttools.hxx> + +#include <com/sun/star/frame/Desktop.hpp> +#include <com/sun/star/frame/XStorable.hpp> +#include <com/sun/star/io/XOutputStream.hpp> +#include <com/sun/star/drawing/XShape.hpp> +#include <com/sun/star/drawing/XDrawPagesSupplier.hpp> +#include <com/sun/star/text/XTextRange.hpp> +#include <com/sun/star/text/ControlCharacter.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> + +#include <unotools/streamwrap.hxx> +#include <unotools/mediadescriptor.hxx> +#include <tools/stream.hxx> + +using namespace ::com::sun::star; + +constexpr OUStringLiteral DATA_DIRECTORY = u"/filter/qa/unit/data/"; + +/// SVG filter tests. +class SvgFilterTest : public test::BootstrapFixture, public unotest::MacrosTest, public XmlTestTools +{ +private: + uno::Reference<lang::XComponent> mxComponent; + +public: + void setUp() override; + void tearDown() override; + void registerNamespaces(xmlXPathContextPtr& pXmlXpathCtx) override; + uno::Reference<lang::XComponent>& getComponent() { return mxComponent; } + void load(std::u16string_view rURL); +}; + +void SvgFilterTest::setUp() +{ + test::BootstrapFixture::setUp(); + + mxDesktop.set(frame::Desktop::create(mxComponentContext)); +} + +void SvgFilterTest::tearDown() +{ + if (mxComponent.is()) + mxComponent->dispose(); + + test::BootstrapFixture::tearDown(); +} + +void SvgFilterTest::load(std::u16string_view rFileName) +{ + OUString aURL = m_directories.getURLFromSrc(DATA_DIRECTORY) + rFileName; + mxComponent = loadFromDesktop(aURL); +} + +void SvgFilterTest::registerNamespaces(xmlXPathContextPtr& pXmlXpathCtx) +{ + xmlXPathRegisterNs(pXmlXpathCtx, BAD_CAST("svg"), BAD_CAST("http://www.w3.org/2000/svg")); +} + +CPPUNIT_TEST_FIXTURE(SvgFilterTest, testPreserveJpg) +{ +#if !defined(MACOSX) + // Load a document with a jpeg image in it. + load(u"preserve-jpg.odt"); + + // Select the image. + dispatchCommand(getComponent(), ".uno:JumpToNextFrame", {}); + + // Export the selection to SVG. + uno::Reference<frame::XStorable> xStorable(getComponent(), uno::UNO_QUERY_THROW); + SvMemoryStream aStream; + uno::Reference<io::XOutputStream> xOut = new utl::OOutputStreamWrapper(aStream); + utl::MediaDescriptor aMediaDescriptor; + aMediaDescriptor["FilterName"] <<= OUString("writer_svg_Export"); + aMediaDescriptor["SelectionOnly"] <<= true; + aMediaDescriptor["OutputStream"] <<= xOut; + xStorable->storeToURL("private:stream", aMediaDescriptor.getAsConstPropertyValueList()); + aStream.Seek(STREAM_SEEK_TO_BEGIN); + + // Make sure that the original JPG data is reused and we don't perform a PNG re-compress. + xmlDocUniquePtr pXmlDoc = parseXmlStream(&aStream); + OUString aAttributeValue = getXPath(pXmlDoc, "//svg:image", "href"); + + // Without the accompanying fix in place, this test would have failed with: + // - Expression: aAttributeValue.startsWith("data:image/jpeg") + // i.e. the SVG export result re-compressed the image as PNG, even if the original and the + // transformed image is the same, so there is no need for that. + CPPUNIT_ASSERT(aAttributeValue.startsWith("data:image/jpeg")); +#endif +} + +CPPUNIT_TEST_FIXTURE(SvgFilterTest, testSemiTransparentLine) +{ + // Load a document with a semi-transparent line shape. + load(u"semi-transparent-line.odg"); + + // Export it to SVG. + uno::Reference<frame::XStorable> xStorable(getComponent(), uno::UNO_QUERY_THROW); + SvMemoryStream aStream; + uno::Reference<io::XOutputStream> xOut = new utl::OOutputStreamWrapper(aStream); + utl::MediaDescriptor aMediaDescriptor; + aMediaDescriptor["FilterName"] <<= OUString("draw_svg_Export"); + aMediaDescriptor["OutputStream"] <<= xOut; + xStorable->storeToURL("private:stream", aMediaDescriptor.getAsConstPropertyValueList()); + aStream.Seek(STREAM_SEEK_TO_BEGIN); + + // Get the style of the group around the actual <path> element. + xmlDocUniquePtr pXmlDoc = parseXmlStream(&aStream); + OUString aStyle = getXPath( + pXmlDoc, "//svg:g[@class='com.sun.star.drawing.LineShape']/svg:g/svg:g", "style"); + // Without the accompanying fix in place, this test would have failed, as the style was + // "mask:url(#mask1)", not "opacity: <value>". + CPPUNIT_ASSERT(aStyle.startsWith("opacity: ", &aStyle)); + int nPercent = std::round(aStyle.toDouble() * 100); + // Make sure that the line is still 30% opaque, rather than completely invisible. + CPPUNIT_ASSERT_EQUAL(30, nPercent); +} + +CPPUNIT_TEST_FIXTURE(SvgFilterTest, testSemiTransparentFillWithTransparentLine) +{ + // Load a document with a shape with semi-transparent fill and line + load(u"semi-transparent-fill.odg"); + + // Export it to SVG. + uno::Reference<frame::XStorable> xStorable(getComponent(), uno::UNO_QUERY_THROW); + SvMemoryStream aStream; + uno::Reference<io::XOutputStream> xOut = new utl::OOutputStreamWrapper(aStream); + utl::MediaDescriptor aMediaDescriptor; + aMediaDescriptor["FilterName"] <<= OUString("draw_svg_Export"); + aMediaDescriptor["OutputStream"] <<= xOut; + xStorable->storeToURL("private:stream", aMediaDescriptor.getAsConstPropertyValueList()); + aStream.Seek(STREAM_SEEK_TO_BEGIN); + + // Get the style of the group around the actual <path> element. + xmlDocUniquePtr pXmlDoc = parseXmlStream(&aStream); + OUString aStyle = getXPath( + pXmlDoc, "//svg:g[@class='com.sun.star.drawing.EllipseShape']/svg:g/svg:g", "style"); + CPPUNIT_ASSERT(aStyle.startsWith("opacity: ", &aStyle)); + int nPercent = std::round(aStyle.toDouble() * 100); + // Make sure that the line is still 50% opaque + CPPUNIT_ASSERT_EQUAL(50, nPercent); + + // Get the stroke of the fill of the EllipseShape (it must be "none") + OUString aStroke = getXPath( + pXmlDoc, "//svg:g[@class='com.sun.star.drawing.EllipseShape']/svg:g/svg:path", "stroke"); + // Without the accompanying fix in place, this test would have failed, as the stroke was + // "rgb(255,255,255)", not "none". + CPPUNIT_ASSERT_EQUAL(OUString("none"), aStroke); +} + +CPPUNIT_TEST_FIXTURE(SvgFilterTest, testSemiTransparentText) +{ + // Two shapes, one with transparent text and the other one with + // opaque text. We expect both to be exported to the SVG with the + // correct transparency factor applied for the first shape. + + // Load draw document with transparent text in one box + load(u"TransparentText.odg"); + + // Export to SVG. + uno::Reference<frame::XStorable> xStorable(getComponent(), uno::UNO_QUERY_THROW); + + SvMemoryStream aStream; + uno::Reference<io::XOutputStream> xOut = new utl::OOutputStreamWrapper(aStream); + utl::MediaDescriptor aMediaDescriptor; + aMediaDescriptor["FilterName"] <<= OUString("draw_svg_Export"); + aMediaDescriptor["OutputStream"] <<= xOut; + xStorable->storeToURL("private:stream", aMediaDescriptor.getAsConstPropertyValueList()); + aStream.Seek(STREAM_SEEK_TO_BEGIN); + + xmlDocUniquePtr pXmlDoc = parseXmlStream(&aStream); + + // We expect 2 groups of class "TextShape" that + // have some svg:text node inside. + // Without the accompanying fix in place, this test would have failed with: + // - Expected: 2 + // - Actual : 1 + // i.e. the 2nd shape lots its text. + + assertXPath(pXmlDoc, "//svg:g[@class='TextShape']//svg:text", 2); + + // First shape has semi-transparent text. + assertXPath(pXmlDoc, "//svg:text[1]/svg:tspan/svg:tspan/svg:tspan[@fill-opacity='0.8']"); + + // Second shape has normal text. + assertXPath(pXmlDoc, "//svg:text[2]/svg:tspan/svg:tspan/svg:tspan[@fill-opacity]", 0); +} + +CPPUNIT_TEST_FIXTURE(SvgFilterTest, testSemiTransparentMultiParaText) +{ + // Given a shape with semi-transparent, multi-paragraph text: + getComponent() + = loadFromDesktop("private:factory/simpress", "com.sun.star.drawing.DrawingDocument"); + uno::Reference<lang::XMultiServiceFactory> xFactory(getComponent(), uno::UNO_QUERY); + uno::Reference<drawing::XShape> xShape( + xFactory->createInstance("com.sun.star.drawing.TextShape"), uno::UNO_QUERY); + uno::Reference<drawing::XDrawPagesSupplier> xDrawPagesSupplier(getComponent(), uno::UNO_QUERY); + uno::Reference<drawing::XShapes> xDrawPage(xDrawPagesSupplier->getDrawPages()->getByIndex(0), + uno::UNO_QUERY); + xDrawPage->add(xShape); + xShape->setSize(awt::Size(10000, 10000)); + uno::Reference<text::XSimpleText> xShapeText(xShape, uno::UNO_QUERY); + uno::Reference<text::XTextCursor> xCursor = xShapeText->createTextCursor(); + xShapeText->insertString(xCursor, "foo", /*bAbsorb=*/false); + xShapeText->insertControlCharacter(xCursor, text::ControlCharacter::APPEND_PARAGRAPH, + /*bAbsorb=*/false); + xShapeText->insertString(xCursor, "bar", /*bAbsorb=*/false); + uno::Reference<beans::XPropertySet> xShapeProps(xShape, uno::UNO_QUERY); + xShapeProps->setPropertyValue("CharColor", uno::Any(static_cast<sal_Int32>(0xff0000))); + xShapeProps->setPropertyValue("CharTransparence", uno::Any(static_cast<sal_Int16>(20))); + + // When exporting to SVG: + uno::Reference<frame::XStorable> xStorable(getComponent(), uno::UNO_QUERY_THROW); + SvMemoryStream aStream; + uno::Reference<io::XOutputStream> xOut = new utl::OOutputStreamWrapper(aStream); + utl::MediaDescriptor aMediaDescriptor; + aMediaDescriptor["FilterName"] <<= OUString("draw_svg_Export"); + aMediaDescriptor["OutputStream"] <<= xOut; + xStorable->storeToURL("private:stream", aMediaDescriptor.getAsConstPropertyValueList()); + aStream.Seek(STREAM_SEEK_TO_BEGIN); + + // Then make sure that the two semi-tranparent paragraphs have the same X position: + xmlDocUniquePtr pXmlDoc = parseXmlStream(&aStream); + assertXPath(pXmlDoc, "(//svg:g[@class='TextShape']//svg:tspan[@class='TextPosition'])[1]", "x", + "250"); + assertXPath(pXmlDoc, + "(//svg:g[@class='TextShape']//svg:tspan[@class='TextPosition'])[1]/svg:tspan", + "fill-opacity", "0.8"); + // Without the accompanying fix in place, this test would have failed with: + // - Expected: 250 + // - Actual : 8819 + // i.e. the X position of the second paragraph was wrong. + assertXPath(pXmlDoc, "(//svg:g[@class='TextShape']//svg:tspan[@class='TextPosition'])[2]", "x", + "250"); + assertXPath(pXmlDoc, + "(//svg:g[@class='TextShape']//svg:tspan[@class='TextPosition'])[2]/svg:tspan", + "fill-opacity", "0.8"); +} + +CPPUNIT_TEST_FIXTURE(SvgFilterTest, testShapeNographic) +{ + // Load a document containing a 3D shape. + load(u"shape-nographic.odp"); + + // Export to SVG. + uno::Reference<frame::XStorable> xStorable(getComponent(), uno::UNO_QUERY_THROW); + SvMemoryStream aStream; + uno::Reference<io::XOutputStream> xOut = new utl::OOutputStreamWrapper(aStream); + utl::MediaDescriptor aMediaDescriptor; + aMediaDescriptor["FilterName"] <<= OUString("impress_svg_Export"); + aMediaDescriptor["OutputStream"] <<= xOut; + + // Without the accompanying fix in place, this test would have failed with: + // An uncaught exception of type com.sun.star.io.IOException + // - SfxBaseModel::impl_store <private:stream> failed: 0xc10(Error Area:Io Class:Write Code:16) + xStorable->storeToURL("private:stream", aMediaDescriptor.getAsConstPropertyValueList()); +} + +CPPUNIT_TEST_FIXTURE(SvgFilterTest, testCustomBullet) +{ + // Given a presentation with a custom bullet: + load(u"custom-bullet.fodp"); + + // When exporting that to SVG: + uno::Reference<frame::XStorable> xStorable(getComponent(), uno::UNO_QUERY_THROW); + SvMemoryStream aStream; + uno::Reference<io::XOutputStream> xOut = new utl::OOutputStreamWrapper(aStream); + utl::MediaDescriptor aMediaDescriptor; + aMediaDescriptor["FilterName"] <<= OUString("impress_svg_Export"); + aMediaDescriptor["OutputStream"] <<= xOut; + xStorable->storeToURL("private:stream", aMediaDescriptor.getAsConstPropertyValueList()); + + // Then make sure the bullet glyph is not lost: + aStream.Seek(STREAM_SEEK_TO_BEGIN); + xmlDocUniquePtr pXmlDoc = parseXmlStream(&aStream); + // Without the accompanying fix in place, this test would have failed with: + // - Expected: 1 + // - Actual : 0 + // - XPath '//svg:g[@class='BulletChars']//svg:path' number of nodes is incorrect + // i.e. the custom bullet used '<use transform="scale(285,285)" + // xlink:href="#bullet-char-template-45"/>', but nobody produced a bullet-char-template-45, + // instead we need the path of the glyph inline. + CPPUNIT_ASSERT(!getXPath(pXmlDoc, "//svg:g[@class='BulletChars']//svg:path", "d").isEmpty()); +} + +CPPUNIT_TEST_FIXTURE(SvgFilterTest, attributeRedefinedTest) +{ + // Load document containing empty paragraphs with ids. + load(u"attributeRedefinedTest.odp"); + + // Export to SVG. + uno::Reference<frame::XStorable> xStorable(getComponent(), uno::UNO_QUERY_THROW); + SvMemoryStream aStream; + uno::Reference<io::XOutputStream> xOut = new utl::OOutputStreamWrapper(aStream); + utl::MediaDescriptor aMediaDescriptor; + aMediaDescriptor["FilterName"] <<= OUString("impress_svg_Export"); + aMediaDescriptor["OutputStream"] <<= xOut; + xStorable->storeToURL("private:stream", aMediaDescriptor.getAsConstPropertyValueList()); + aStream.Seek(STREAM_SEEK_TO_BEGIN); + + xmlDocUniquePtr pXmlDoc = parseXmlStream(&aStream); + + // We expect four paragraph + // 2 empty paragraphs with ids + // 2 paragraphs with text + // Without the accompanying fix the test would have failed with + // Expected : 4 + // Actual : 2 + // i.e. 2 of the empty paragraph do not get generated even if there + // is id imported for the paragraphs + // If we don't create the empty paragraphs the id attribute gets redefined like this: + // <tspan id="id14" id="id15" id="id17" class="TextParagraph" font-family="Bahnschrift Light" font-size="1129px" font-weight="400"> + + OString xPath = "//svg:g[@class='TextShape']//svg:text[@class='SVGTextShape']//" + "svg:tspan[@class='TextParagraph']"; + assertXPath(pXmlDoc, xPath, 4); + + //assert that each tspan element with TextParagraph class has id and the tspan element of + //each empty paragraph does not contain tspan element with class TextPosition + assertXPath(pXmlDoc, xPath + "[1]", "id", "id4"); + assertXPath(pXmlDoc, xPath + "[2]", "id", "id5"); + assertXPath(pXmlDoc, xPath + "[2]//svg:tspan[@class='TextPosition']", 0); + assertXPath(pXmlDoc, xPath + "[3]", "id", "id6"); + assertXPath(pXmlDoc, xPath + "[3]//svg:tspan[@class='TextPosition']", 0); + assertXPath(pXmlDoc, xPath + "[4]", "id", "id7"); +} + +CPPUNIT_TEST_FIXTURE(SvgFilterTest, testTab) +{ + // Given a shape with "A\tB" text: + getComponent() = loadFromDesktop("private:factory/simpress", + "com.sun.star.presentation.PresentationDocument"); + uno::Reference<lang::XMultiServiceFactory> xFactory(getComponent(), uno::UNO_QUERY); + uno::Reference<drawing::XShape> xShape( + xFactory->createInstance("com.sun.star.drawing.TextShape"), uno::UNO_QUERY); + uno::Reference<drawing::XDrawPagesSupplier> xDrawPagesSupplier(getComponent(), uno::UNO_QUERY); + uno::Reference<drawing::XShapes> xDrawPage(xDrawPagesSupplier->getDrawPages()->getByIndex(0), + uno::UNO_QUERY); + xDrawPage->add(xShape); + xShape->setSize(awt::Size(10000, 10000)); + uno::Reference<text::XTextRange> xShapeText(xShape, uno::UNO_QUERY); + xShapeText->setString("A\tB"); + + // When exporting that document to SVG: + uno::Reference<frame::XStorable> xStorable(getComponent(), uno::UNO_QUERY_THROW); + SvMemoryStream aStream; + uno::Reference<io::XOutputStream> xOut = new utl::OOutputStreamWrapper(aStream); + utl::MediaDescriptor aMediaDescriptor; + aMediaDescriptor["FilterName"] <<= OUString("impress_svg_Export"); + aMediaDescriptor["OutputStream"] <<= xOut; + xStorable->storeToURL("private:stream", aMediaDescriptor.getAsConstPropertyValueList()); + + // Then make sure the the tab is not lost: + aStream.Seek(STREAM_SEEK_TO_BEGIN); + xmlDocUniquePtr pXmlDoc = parseXmlStream(&aStream); + // Without the accompanying fix in place, this test would have failed with: + // - Expected: 2 + // - Actual : 1 + // i.e. the 2nd text portion was not positioned, which looked as if the tab is lost. + assertXPath(pXmlDoc, "//svg:g[@class='TextShape']//svg:tspan[@class='TextPosition']", 2); +} + +CPPUNIT_PLUGIN_IMPLEMENT(); + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/filter/qa/unit/textfilterdetect.cxx b/filter/qa/unit/textfilterdetect.cxx new file mode 100644 index 000000000..3c8daf2f2 --- /dev/null +++ b/filter/qa/unit/textfilterdetect.cxx @@ -0,0 +1,199 @@ +/* -*- 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 <test/bootstrapfixture.hxx> +#include <unotest/macros_test.hxx> + +#include <com/sun/star/document/XExtendedFilterDetection.hpp> +#include <com/sun/star/frame/Desktop.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/drawing/XDrawPagesSupplier.hpp> +#include <com/sun/star/sheet/XCellRangesAccess.hpp> +#include <com/sun/star/sheet/XSpreadsheetDocument.hpp> +#include <com/sun/star/text/XTextDocument.hpp> + +#include <comphelper/propertyvalue.hxx> +#include <sfx2/docfac.hxx> +#include <unotools/mediadescriptor.hxx> +#include <unotools/streamwrap.hxx> +#include <tools/stream.hxx> + +namespace com::sun::star::io +{ +class XInputStream; +} + +using namespace com::sun::star; + +namespace +{ +/// Test class for PlainTextFilterDetect. +class TextFilterDetectTest : public test::BootstrapFixture, public unotest::MacrosTest +{ +public: + void setUp() override; +}; + +void TextFilterDetectTest::setUp() +{ + test::BootstrapFixture::setUp(); + + mxDesktop.set(frame::Desktop::create(mxComponentContext)); +} + +constexpr OUStringLiteral DATA_DIRECTORY = u"/filter/qa/unit/data/"; + +CPPUNIT_TEST_FIXTURE(TextFilterDetectTest, testTdf114428) +{ + uno::Reference<document::XExtendedFilterDetection> xDetect( + getMultiServiceFactory()->createInstance("com.sun.star.comp.filters.PlainTextFilterDetect"), + uno::UNO_QUERY); + OUString aURL = m_directories.getURLFromSrc(DATA_DIRECTORY) + "tdf114428.xhtml"; + SvFileStream aStream(aURL, StreamMode::READ); + uno::Reference<io::XInputStream> xStream(new utl::OStreamWrapper(aStream)); + uno::Sequence<beans::PropertyValue> aDescriptor + = { comphelper::makePropertyValue("DocumentService", + OUString("com.sun.star.text.TextDocument")), + comphelper::makePropertyValue("InputStream", xStream), + comphelper::makePropertyValue("TypeName", OUString("generic_HTML")) }; + xDetect->detect(aDescriptor); + utl::MediaDescriptor aMediaDesc(aDescriptor); + OUString aFilterName = aMediaDesc.getUnpackedValueOrDefault("FilterName", OUString()); + // This was empty, XML declaration caused HTML detect to not handle XHTML. + CPPUNIT_ASSERT_EQUAL(OUString("HTML (StarWriter)"), aFilterName); +} + +CPPUNIT_TEST_FIXTURE(TextFilterDetectTest, testEmptyFile) +{ + const OUString sDataDirectory = m_directories.getURLFromSrc(DATA_DIRECTORY); + auto supportsService = [](const uno::Reference<lang::XComponent>& x, const OUString& s) { + return uno::Reference<lang::XServiceInfo>(x, uno::UNO_QUERY_THROW)->supportsService(s); + }; + + // Given an empty file, with a pptx extension + // When loading the file + auto xComponent = loadFromDesktop(sDataDirectory + "empty.pptx"); + + // Then make sure it is opened in Impress. + // Without the accompanying fix in place, this test would have failed, as it was opened in + // Writer instead. + CPPUNIT_ASSERT(supportsService(xComponent, "com.sun.star.presentation.PresentationDocument")); + xComponent->dispose(); + + // Now also test ODT + xComponent = loadFromDesktop(sDataDirectory + "empty.odt"); + // Make sure it opens in Writer. + CPPUNIT_ASSERT(supportsService(xComponent, "com.sun.star.text.TextDocument")); + xComponent->dispose(); + + // ... and ODS + xComponent = loadFromDesktop(sDataDirectory + "empty.ods"); + // Make sure it opens in Calc. + CPPUNIT_ASSERT(supportsService(xComponent, "com.sun.star.sheet.SpreadsheetDocument")); + xComponent->dispose(); + + // ... and ODP + xComponent = loadFromDesktop(sDataDirectory + "empty.odp"); + // Without the accompanying fix in place, this test would have failed, as it was opened in + // Writer instead. + CPPUNIT_ASSERT(supportsService(xComponent, "com.sun.star.presentation.PresentationDocument")); + xComponent->dispose(); + + // ... and DOC + // Without the accompanying fix in place, this test would have failed, the import filter aborted + // loading. + xComponent = loadFromDesktop(sDataDirectory + "empty.doc"); + CPPUNIT_ASSERT(supportsService(xComponent, "com.sun.star.text.TextDocument")); + { + uno::Reference<frame::XModel> xModel(xComponent, uno::UNO_QUERY); + uno::Sequence<beans::PropertyValue> aArgs = xModel->getArgs(); + comphelper::SequenceAsHashMap aMap(aArgs); + OUString aFilterName; + aMap["FilterName"] >>= aFilterName; + // Without the accompanying fix in place, this test would have failed with: + // - Expected: MS Word 97 + // - Actual : MS WinWord 6.0 + // i.e. opening worked, but saving back failed instead of producing a WW8 binary file. + CPPUNIT_ASSERT_EQUAL(OUString("MS Word 97"), aFilterName); + } + xComponent->dispose(); + + // Now test with default templates set + + SfxObjectFactory::SetStandardTemplate("com.sun.star.presentation.PresentationDocument", + sDataDirectory + "impress.otp"); + SfxObjectFactory::SetStandardTemplate("com.sun.star.text.TextDocument", + sDataDirectory + "writer.ott"); + SfxObjectFactory::SetStandardTemplate("com.sun.star.sheet.SpreadsheetDocument", + sDataDirectory + "calc.ots"); + + xComponent = loadFromDesktop(sDataDirectory + "empty.pptx"); + { + uno::Reference<drawing::XDrawPagesSupplier> xDoc(xComponent, uno::UNO_QUERY_THROW); + uno::Reference<drawing::XDrawPages> xPages(xDoc->getDrawPages(), uno::UNO_SET_THROW); + uno::Reference<drawing::XDrawPage> xPage(xPages->getByIndex(0), uno::UNO_QUERY_THROW); + uno::Reference<text::XTextRange> xBox(xPage->getByIndex(0), uno::UNO_QUERY_THROW); + + // Make sure the template's text was loaded + CPPUNIT_ASSERT_EQUAL(OUString("Title of Impress template"), xBox->getString()); + } + xComponent->dispose(); + + xComponent = loadFromDesktop(sDataDirectory + "empty.odt"); + { + uno::Reference<text::XTextDocument> xDoc(xComponent, uno::UNO_QUERY_THROW); + uno::Reference<container::XEnumerationAccess> xEA(xDoc->getText(), uno::UNO_QUERY_THROW); + uno::Reference<container::XEnumeration> xEnum(xEA->createEnumeration(), uno::UNO_SET_THROW); + uno::Reference<text::XTextRange> xParagraph(xEnum->nextElement(), uno::UNO_QUERY_THROW); + + // Make sure the template's text was loaded + CPPUNIT_ASSERT_EQUAL(OUString(u"Writer template’s first line"), xParagraph->getString()); + } + xComponent->dispose(); + + xComponent = loadFromDesktop(sDataDirectory + "empty.ods"); + { + uno::Reference<sheet::XSpreadsheetDocument> xDoc(xComponent, uno::UNO_QUERY_THROW); + uno::Reference<sheet::XCellRangesAccess> xRA(xDoc->getSheets(), uno::UNO_QUERY_THROW); + uno::Reference<text::XTextRange> xC(xRA->getCellByPosition(0, 0, 0), uno::UNO_QUERY_THROW); + + // Make sure the template's text was loaded + CPPUNIT_ASSERT_EQUAL(OUString(u"Calc template’s first cell"), xC->getString()); + } + xComponent->dispose(); + + xComponent = loadFromDesktop(sDataDirectory + "empty.odp"); + { + uno::Reference<drawing::XDrawPagesSupplier> xDoc(xComponent, uno::UNO_QUERY_THROW); + uno::Reference<drawing::XDrawPages> xPages(xDoc->getDrawPages(), uno::UNO_SET_THROW); + uno::Reference<drawing::XDrawPage> xPage(xPages->getByIndex(0), uno::UNO_QUERY_THROW); + uno::Reference<text::XTextRange> xBox(xPage->getByIndex(0), uno::UNO_QUERY_THROW); + + // Make sure the template's text was loaded + CPPUNIT_ASSERT_EQUAL(OUString("Title of Impress template"), xBox->getString()); + } + xComponent->dispose(); + + xComponent = loadFromDesktop(sDataDirectory + "empty.doc"); + { + uno::Reference<text::XTextDocument> xDoc(xComponent, uno::UNO_QUERY_THROW); + uno::Reference<container::XEnumerationAccess> xEA(xDoc->getText(), uno::UNO_QUERY_THROW); + uno::Reference<container::XEnumeration> xEnum(xEA->createEnumeration(), uno::UNO_SET_THROW); + uno::Reference<text::XTextRange> xParagraph(xEnum->nextElement(), uno::UNO_QUERY_THROW); + + // Make sure the template's text was loaded + CPPUNIT_ASSERT_EQUAL(OUString(u"Writer template’s first line"), xParagraph->getString()); + } + xComponent->dispose(); +} +} + +CPPUNIT_PLUGIN_IMPLEMENT(); + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |