1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
10 #include <sal/config.h>
12 #include <test/unoapixml_test.hxx>
14 #include <com/sun/star/frame/XStorable.hpp>
15 #include <com/sun/star/io/XOutputStream.hpp>
16 #include <com/sun/star/drawing/XShape.hpp>
17 #include <com/sun/star/drawing/XDrawPagesSupplier.hpp>
18 #include <com/sun/star/text/XTextRange.hpp>
19 #include <com/sun/star/text/ControlCharacter.hpp>
20 #include <com/sun/star/beans/XPropertySet.hpp>
22 #include <comphelper/propertyvalue.hxx>
23 #include <unotools/streamwrap.hxx>
24 #include <unotools/mediadescriptor.hxx>
25 #include <tools/stream.hxx>
27 using namespace ::com::sun::star
;
30 class SvgFilterTest
: public UnoApiXmlTest
34 void registerNamespaces(xmlXPathContextPtr
& pXmlXpathCtx
) override
;
37 SvgFilterTest::SvgFilterTest()
38 : UnoApiXmlTest(u
"/filter/qa/unit/data/"_ustr
)
42 void SvgFilterTest::registerNamespaces(xmlXPathContextPtr
& pXmlXpathCtx
)
44 xmlXPathRegisterNs(pXmlXpathCtx
, BAD_CAST("svg"), BAD_CAST("http://www.w3.org/2000/svg"));
47 CPPUNIT_TEST_FIXTURE(SvgFilterTest
, testPreserveJpg
)
49 // On Windows, SVGFilter::filterWriterOrCalc can't get current frame to obtain selection
50 #if !defined(MACOSX) && !defined(_WIN32)
51 // Load a document with a jpeg image in it.
52 loadFromFile(u
"preserve-jpg.odt");
55 dispatchCommand(mxComponent
, u
".uno:JumpToNextFrame"_ustr
, {});
57 // Export the selection to SVG.
59 comphelper::makePropertyValue(u
"FilterName"_ustr
, u
"writer_svg_Export"_ustr
),
60 comphelper::makePropertyValue(u
"SelectionOnly"_ustr
, true),
63 // Make sure that the original JPG data is reused and we don't perform a PNG re-compress.
64 xmlDocUniquePtr pXmlDoc
= parseExportedFile();
65 OUString aAttributeValue
= getXPath(pXmlDoc
, "//svg:image", "href");
67 // Without the accompanying fix in place, this test would have failed with:
68 // - Expression: aAttributeValue.startsWith("data:image/jpeg")
69 // i.e. the SVG export result re-compressed the image as PNG, even if the original and the
70 // transformed image is the same, so there is no need for that.
71 CPPUNIT_ASSERT(aAttributeValue
.startsWith("data:image/jpeg"));
75 CPPUNIT_TEST_FIXTURE(SvgFilterTest
, testSemiTransparentLine
)
77 // Load a document with a semi-transparent line shape.
78 loadFromFile(u
"semi-transparent-line.odg");
81 save(u
"draw_svg_Export"_ustr
);
83 // Get the style of the group around the actual <path> element.
84 xmlDocUniquePtr pXmlDoc
= parseExportedFile();
85 OUString aStyle
= getXPath(
86 pXmlDoc
, "//svg:g[@class='com.sun.star.drawing.LineShape']/svg:g/svg:g", "style");
87 // Without the accompanying fix in place, this test would have failed, as the style was
88 // "mask:url(#mask1)", not "opacity: <value>".
89 CPPUNIT_ASSERT(aStyle
.startsWith("opacity: ", &aStyle
));
90 int nPercent
= std::round(aStyle
.toDouble() * 100);
91 // Make sure that the line is still 30% opaque, rather than completely invisible.
92 CPPUNIT_ASSERT_EQUAL(30, nPercent
);
95 CPPUNIT_TEST_FIXTURE(SvgFilterTest
, testSemiTransparentFillWithTransparentLine
)
97 // Load a document with a shape with semi-transparent fill and line
98 loadFromFile(u
"semi-transparent-fill.odg");
101 save(u
"draw_svg_Export"_ustr
);
103 // Get the style of the group around the actual <path> element.
104 xmlDocUniquePtr pXmlDoc
= parseExportedFile();
105 OUString aStyle
= getXPath(
106 pXmlDoc
, "//svg:g[@class='com.sun.star.drawing.EllipseShape']/svg:g/svg:g", "style");
107 CPPUNIT_ASSERT(aStyle
.startsWith("opacity: ", &aStyle
));
108 int nPercent
= std::round(aStyle
.toDouble() * 100);
109 // Make sure that the line is still 50% opaque
110 CPPUNIT_ASSERT_EQUAL(50, nPercent
);
112 // Get the stroke of the fill of the EllipseShape (it must be "none")
113 OUString aStroke
= getXPath(
114 pXmlDoc
, "//svg:g[@class='com.sun.star.drawing.EllipseShape']/svg:g/svg:path", "stroke");
115 // Without the accompanying fix in place, this test would have failed, as the stroke was
116 // "rgb(255,255,255)", not "none".
117 CPPUNIT_ASSERT_EQUAL(u
"none"_ustr
, aStroke
);
120 CPPUNIT_TEST_FIXTURE(SvgFilterTest
, testSemiTransparentText
)
122 // Two shapes, one with transparent text and the other one with
123 // opaque text. We expect both to be exported to the SVG with the
124 // correct transparency factor applied for the first shape.
126 // Load draw document with transparent text in one box
127 loadFromFile(u
"TransparentText.odg");
130 save(u
"draw_svg_Export"_ustr
);
132 xmlDocUniquePtr pXmlDoc
= parseExportedFile();
133 // We expect 2 groups of class "TextShape" that
134 // have some svg:text node inside.
135 // Without the accompanying fix in place, this test would have failed with:
138 // i.e. the 2nd shape lots its text.
140 assertXPath(pXmlDoc
, "//svg:g[@class='TextShape']//svg:text", 2);
142 // First shape has semi-transparent text.
143 assertXPath(pXmlDoc
, "//svg:text[1]/svg:tspan/svg:tspan/svg:tspan[@fill-opacity='0.8']");
145 // Second shape has normal text.
146 assertXPath(pXmlDoc
, "//svg:text[2]/svg:tspan/svg:tspan/svg:tspan[@fill-opacity]", 0);
149 CPPUNIT_TEST_FIXTURE(SvgFilterTest
, testSemiTransparentMultiParaText
)
151 // Given a shape with semi-transparent, multi-paragraph text:
152 loadFromURL(u
"private:factory/simpress"_ustr
);
153 uno::Reference
<lang::XMultiServiceFactory
> xFactory(mxComponent
, uno::UNO_QUERY
);
154 uno::Reference
<drawing::XShape
> xShape(
155 xFactory
->createInstance(u
"com.sun.star.drawing.TextShape"_ustr
), uno::UNO_QUERY
);
156 uno::Reference
<drawing::XDrawPagesSupplier
> xDrawPagesSupplier(mxComponent
, uno::UNO_QUERY
);
157 uno::Reference
<drawing::XShapes
> xDrawPage(xDrawPagesSupplier
->getDrawPages()->getByIndex(0),
159 xDrawPage
->add(xShape
);
160 xShape
->setSize(awt::Size(10000, 10000));
161 uno::Reference
<text::XSimpleText
> xShapeText(xShape
, uno::UNO_QUERY
);
162 uno::Reference
<text::XTextCursor
> xCursor
= xShapeText
->createTextCursor();
163 xShapeText
->insertString(xCursor
, u
"foo"_ustr
, /*bAbsorb=*/false);
164 xShapeText
->insertControlCharacter(xCursor
, text::ControlCharacter::APPEND_PARAGRAPH
,
166 xShapeText
->insertString(xCursor
, u
"bar"_ustr
, /*bAbsorb=*/false);
167 uno::Reference
<beans::XPropertySet
> xShapeProps(xShape
, uno::UNO_QUERY
);
168 xShapeProps
->setPropertyValue(u
"CharColor"_ustr
, uno::Any(static_cast<sal_Int32
>(0xff0000)));
169 xShapeProps
->setPropertyValue(u
"CharTransparence"_ustr
, uno::Any(static_cast<sal_Int16
>(20)));
171 // When exporting to SVG:
172 save(u
"draw_svg_Export"_ustr
);
174 // Then make sure that the two semi-transparent paragraphs have the same X position:
175 xmlDocUniquePtr pXmlDoc
= parseExportedFile();
176 assertXPath(pXmlDoc
, "(//svg:g[@class='TextShape']//svg:tspan[@class='TextPosition'])[1]", "x",
179 "(//svg:g[@class='TextShape']//svg:tspan[@class='TextPosition'])[1]/svg:tspan",
180 "fill-opacity", u
"0.8");
181 // Without the accompanying fix in place, this test would have failed with:
184 // i.e. the X position of the second paragraph was wrong.
185 assertXPath(pXmlDoc
, "(//svg:g[@class='TextShape']//svg:tspan[@class='TextPosition'])[2]", "x",
188 "(//svg:g[@class='TextShape']//svg:tspan[@class='TextPosition'])[2]/svg:tspan",
189 "fill-opacity", u
"0.8");
192 CPPUNIT_TEST_FIXTURE(SvgFilterTest
, testShapeNographic
)
194 // Load a document containing a 3D shape.
195 loadFromFile(u
"shape-nographic.odp");
198 uno::Reference
<frame::XStorable
> xStorable(mxComponent
, uno::UNO_QUERY_THROW
);
199 SvMemoryStream aStream
;
200 uno::Reference
<io::XOutputStream
> xOut
= new utl::OOutputStreamWrapper(aStream
);
201 utl::MediaDescriptor aMediaDescriptor
;
202 aMediaDescriptor
[u
"FilterName"_ustr
] <<= u
"impress_svg_Export"_ustr
;
203 aMediaDescriptor
[u
"OutputStream"_ustr
] <<= xOut
;
205 // Without the accompanying fix in place, this test would have failed with:
206 // An uncaught exception of type com.sun.star.io.IOException
207 // - SfxBaseModel::impl_store <private:stream> failed: 0xc10(Error Area:Io Class:Write Code:16)
208 xStorable
->storeToURL(u
"private:stream"_ustr
, aMediaDescriptor
.getAsConstPropertyValueList());
211 CPPUNIT_TEST_FIXTURE(SvgFilterTest
, testCustomBullet
)
213 // Given a presentation with a custom bullet:
214 loadFromFile(u
"custom-bullet.fodp");
216 // When exporting that to SVG:
217 save(u
"impress_svg_Export"_ustr
);
219 // Then make sure the bullet glyph is not lost:
220 xmlDocUniquePtr pXmlDoc
= parseExportedFile();
221 // Without the accompanying fix in place, this test would have failed with:
224 // - XPath '//svg:g[@class='BulletChars']//svg:path' number of nodes is incorrect
225 // i.e. the custom bullet used '<use transform="scale(285,285)"
226 // xlink:href="#bullet-char-template-45"/>', but nobody produced a bullet-char-template-45,
227 // instead we need the path of the glyph inline.
228 CPPUNIT_ASSERT(!getXPath(pXmlDoc
, "//svg:g[@class='BulletChars']//svg:path", "d").isEmpty());
231 CPPUNIT_TEST_FIXTURE(SvgFilterTest
, attributeRedefinedTest
)
233 // Load document containing empty paragraphs with ids.
234 loadFromFile(u
"attributeRedefinedTest.odp");
237 save(u
"impress_svg_Export"_ustr
);
239 xmlDocUniquePtr pXmlDoc
= parseExportedFile();
240 // We expect four paragraph
241 // 2 empty paragraphs with ids
242 // 2 paragraphs with text
243 // Without the accompanying fix the test would have failed with
246 // i.e. 2 of the empty paragraph do not get generated even if there
247 // is id imported for the paragraphs
248 // If we don't create the empty paragraphs the id attribute gets redefined like this:
249 // <tspan id="id14" id="id15" id="id17" class="TextParagraph" font-family="Bahnschrift Light" font-size="1129px" font-weight="400">
251 OString xPath
= "//svg:g[@class='TextShape']//svg:text[@class='SVGTextShape']//"
252 "svg:tspan[@class='TextParagraph']"_ostr
;
253 assertXPath(pXmlDoc
, xPath
, 4);
255 //assert that each tspan element with TextParagraph class has id and the tspan element of
256 //each empty paragraph does not contain tspan element with class TextPosition
257 assertXPath(pXmlDoc
, xPath
+ "[1]", "id", u
"id4");
258 assertXPath(pXmlDoc
, xPath
+ "[2]", "id", u
"id5");
259 assertXPath(pXmlDoc
, xPath
+ "[2]//svg:tspan[@class='TextPosition']", 0);
260 assertXPath(pXmlDoc
, xPath
+ "[3]", "id", u
"id6");
261 assertXPath(pXmlDoc
, xPath
+ "[3]//svg:tspan[@class='TextPosition']", 0);
262 assertXPath(pXmlDoc
, xPath
+ "[4]", "id", u
"id7");
265 CPPUNIT_TEST_FIXTURE(SvgFilterTest
, testTab
)
267 // Given a shape with "A\tB" text:
268 loadFromURL(u
"private:factory/simpress"_ustr
);
269 uno::Reference
<lang::XMultiServiceFactory
> xFactory(mxComponent
, uno::UNO_QUERY
);
270 uno::Reference
<drawing::XShape
> xShape(
271 xFactory
->createInstance(u
"com.sun.star.drawing.TextShape"_ustr
), uno::UNO_QUERY
);
272 uno::Reference
<drawing::XDrawPagesSupplier
> xDrawPagesSupplier(mxComponent
, uno::UNO_QUERY
);
273 uno::Reference
<drawing::XShapes
> xDrawPage(xDrawPagesSupplier
->getDrawPages()->getByIndex(0),
275 xDrawPage
->add(xShape
);
276 xShape
->setSize(awt::Size(10000, 10000));
277 uno::Reference
<text::XTextRange
> xShapeText(xShape
, uno::UNO_QUERY
);
278 xShapeText
->setString(u
"A\tB"_ustr
);
280 // When exporting that document to SVG:
281 save(u
"impress_svg_Export"_ustr
);
283 // Then make sure the tab is not lost:
284 xmlDocUniquePtr pXmlDoc
= parseExportedFile();
285 // Without the accompanying fix in place, this test would have failed with:
288 // i.e. the 2nd text portion was not positioned, which looked as if the tab is lost.
289 assertXPath(pXmlDoc
, "//svg:g[@class='TextShape']//svg:tspan[@class='TextPosition']", 2);
292 CPPUNIT_TEST_FIXTURE(SvgFilterTest
, textInImage
)
294 // Load document containing empty paragraphs with ids.
295 loadFromFile(u
"text-in-image.odp");
298 save(u
"impress_svg_Export"_ustr
);
300 xmlDocUniquePtr pXmlDoc
= parseExportedFile();
302 // We expect the Graphic to have an image and a text
303 assertXPath(pXmlDoc
, "//svg:g[@class='Graphic']//svg:image", 1);
304 assertXPath(pXmlDoc
, "//svg:g[@class='Graphic']//svg:text", 1);
305 // Without the accompanying fix, this test would have failed with:
310 CPPUNIT_TEST_FIXTURE(SvgFilterTest
, testSemiTransparentTextBullet
)
312 // Given a document with 3 paragraphs, paragraph 2 is a bullet with 80% opacity.
313 loadFromFile(u
"semi-transparent-text-bullet.odg");
315 // When exporting to SVG:
316 save(u
"draw_svg_Export"_ustr
);
318 // Then make sure the result is correct:
319 xmlDocUniquePtr pXmlDoc
= parseExportedFile();
320 // We have 3 paragraphs.
321 assertXPath(pXmlDoc
, "//svg:g[@class='TextShape']//svg:text/svg:tspan", 3);
322 // Paragraph 1 has no spans with text opacity set.
324 "(//svg:g[@class='TextShape']//svg:text/svg:tspan)[1]/svg:tspan/"
325 "svg:tspan[@fill-opacity='0.8']",
327 // Paragraph 2 has a span with text opacity set to 80%.
329 "(//svg:g[@class='TextShape']//svg:text/svg:tspan)[2]/svg:tspan/"
330 "svg:tspan[@fill-opacity='0.8']",
332 // Paragraph 3 has no spans with text opacity set.
334 "(//svg:g[@class='TextShape']//svg:text/svg:tspan)[3]/svg:tspan/"
335 "svg:tspan[@fill-opacity='0.8']",
339 CPPUNIT_TEST_FIXTURE(SvgFilterTest
, testMapModeText
)
341 loadFromFile(u
"YAxis.odg");
344 save(u
"draw_svg_Export"_ustr
);
346 xmlDocUniquePtr pXmlDoc
= parseExportedFile();
349 = getXPath(pXmlDoc
, "(//svg:text[@class='SVGTextShape'])[last()]", "transform");
351 OUString aTextPositionX
352 = getXPath(pXmlDoc
, "(//svg:tspan[@class='TextPosition'])[last()]", "x");
353 OUString aTextPositionY
354 = getXPath(pXmlDoc
, "(//svg:tspan[@class='TextPosition'])[last()]", "y");
356 OUString
sExpectedTransform("rotate(-90 " + aTextPositionX
+ " " + aTextPositionY
+ ")");
358 // We expect the rotate point of the rotated text to match the start position of the text
359 CPPUNIT_ASSERT_EQUAL(sExpectedTransform
, sTransform
);
360 // Without the accompanying fix, this test would have failed with:
361 // - Expected: rotate(-90 3386 14754)
362 // - Actual : rotate(-90 -1651 14488)
363 // Because the (local) MapMode wasn't taken into account for the text position when peeking
364 // ahead to get the rotation.
367 CPPUNIT_TEST_FIXTURE(SvgFilterTest
, testTdf91315
)
369 // Load a presentation containing RTL text
370 loadFromFile(u
"tdf91315.fodp");
372 save(u
"impress_svg_Export"_ustr
);
374 xmlDocUniquePtr pXmlDoc
= parseExportedFile();
376 assertXPath(pXmlDoc
, "//svg:text[@direction='rtl']", 1);
377 // Without the accompanying fix, this test would have failed with:
382 CPPUNIT_PLUGIN_IMPLEMENT();
384 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */