remove assert looking for new compatibilityMode DOCX
[LibreOffice.git] / filter / qa / unit / svg.cxx
blob366102fd964c54c19e567246f2cc2d1f6145ee3e
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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/.
8 */
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;
29 /// SVG filter tests.
30 class SvgFilterTest : public UnoApiXmlTest
32 public:
33 SvgFilterTest();
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");
54 // Select the image.
55 dispatchCommand(mxComponent, u".uno:JumpToNextFrame"_ustr, {});
57 // Export the selection to SVG.
58 saveWithParams({
59 comphelper::makePropertyValue(u"FilterName"_ustr, u"writer_svg_Export"_ustr),
60 comphelper::makePropertyValue(u"SelectionOnly"_ustr, true),
61 });
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"));
72 #endif
75 CPPUNIT_TEST_FIXTURE(SvgFilterTest, testSemiTransparentLine)
77 // Load a document with a semi-transparent line shape.
78 loadFromFile(u"semi-transparent-line.odg");
80 // Export it to SVG.
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");
100 // Export it to SVG.
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");
129 // Export to SVG.
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:
136 // - Expected: 2
137 // - Actual : 1
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),
158 uno::UNO_QUERY);
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,
165 /*bAbsorb=*/false);
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",
177 u"250");
178 assertXPath(pXmlDoc,
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:
182 // - Expected: 250
183 // - Actual : 8819
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",
186 u"250");
187 assertXPath(pXmlDoc,
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");
197 // Export to SVG.
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:
222 // - Expected: 1
223 // - Actual : 0
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");
236 // Export to SVG.
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
244 // Expected : 4
245 // Actual : 2
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),
274 uno::UNO_QUERY);
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:
286 // - Expected: 2
287 // - Actual : 1
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");
297 // Export to SVG.
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:
306 // - Expected: 1
307 // - Actual : 0
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.
323 assertXPath(pXmlDoc,
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%.
328 assertXPath(pXmlDoc,
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.
333 assertXPath(pXmlDoc,
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");
343 // Export to SVG.
344 save(u"draw_svg_Export"_ustr);
346 xmlDocUniquePtr pXmlDoc = parseExportedFile();
348 OUString sTransform
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:
378 // - Expected: 1
379 // - Actual : 0
382 CPPUNIT_PLUGIN_IMPLEMENT();
384 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */