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 <test/bootstrapfixture.hxx>
11 #include <unotest/macros_test.hxx>
12 #include <test/xmltesttools.hxx>
14 #include <com/sun/star/frame/Desktop.hpp>
15 #include <com/sun/star/frame/XStorable.hpp>
16 #include <com/sun/star/io/XOutputStream.hpp>
17 #include <unotools/streamwrap.hxx>
18 #include <unotools/mediadescriptor.hxx>
19 #include <tools/stream.hxx>
21 using namespace ::com::sun::star
;
23 char const DATA_DIRECTORY
[] = "/filter/qa/unit/data/";
26 class SvgFilterTest
: public test::BootstrapFixture
, public unotest::MacrosTest
, public XmlTestTools
29 uno::Reference
<lang::XComponent
> mxComponent
;
32 void setUp() override
;
33 void tearDown() override
;
34 void registerNamespaces(xmlXPathContextPtr
& pXmlXpathCtx
) override
;
35 uno::Reference
<lang::XComponent
>& getComponent() { return mxComponent
; }
36 void load(const OUString
& rURL
);
39 void SvgFilterTest::setUp()
41 test::BootstrapFixture::setUp();
43 mxDesktop
.set(frame::Desktop::create(mxComponentContext
));
46 void SvgFilterTest::tearDown()
49 mxComponent
->dispose();
51 test::BootstrapFixture::tearDown();
54 void SvgFilterTest::load(const OUString
& rFileName
)
56 OUString aURL
= m_directories
.getURLFromSrc(DATA_DIRECTORY
) + rFileName
;
57 mxComponent
= loadFromDesktop(aURL
);
60 void SvgFilterTest::registerNamespaces(xmlXPathContextPtr
& pXmlXpathCtx
)
62 xmlXPathRegisterNs(pXmlXpathCtx
, BAD_CAST("svg"), BAD_CAST("http://www.w3.org/2000/svg"));
65 CPPUNIT_TEST_FIXTURE(SvgFilterTest
, testPreserveJpg
)
68 // Load a document with a jpeg image in it.
69 load("preserve-jpg.odt");
72 dispatchCommand(getComponent(), ".uno:JumpToNextFrame", {});
74 // Export the selection to SVG.
75 uno::Reference
<frame::XStorable
> xStorable(getComponent(), uno::UNO_QUERY_THROW
);
76 SvMemoryStream aStream
;
77 uno::Reference
<io::XOutputStream
> xOut
= new utl::OOutputStreamWrapper(aStream
);
78 utl::MediaDescriptor aMediaDescriptor
;
79 aMediaDescriptor
["FilterName"] <<= OUString("writer_svg_Export");
80 aMediaDescriptor
["SelectionOnly"] <<= true;
81 aMediaDescriptor
["OutputStream"] <<= xOut
;
82 xStorable
->storeToURL("private:stream", aMediaDescriptor
.getAsConstPropertyValueList());
83 aStream
.Seek(STREAM_SEEK_TO_BEGIN
);
85 // Make sure that the original JPG data is reused and we don't perform a PNG re-compress.
86 xmlDocUniquePtr pXmlDoc
= parseXmlStream(&aStream
);
87 OUString aAttributeValue
= getXPath(pXmlDoc
, "//svg:image", "href");
89 // Without the accompanying fix in place, this test would have failed with:
90 // - Expression: aAttributeValue.startsWith("data:image/jpeg")
91 // i.e. the SVG export result re-compressed the image as PNG, even if the original and the
92 // transformed image is the same, so there is no need for that.
93 CPPUNIT_ASSERT(aAttributeValue
.startsWith("data:image/jpeg"));
97 CPPUNIT_TEST_FIXTURE(SvgFilterTest
, testSemiTransparentLine
)
99 // Load a document with a semi-transparent line shape.
100 load("semi-transparent-line.odg");
103 uno::Reference
<frame::XStorable
> xStorable(getComponent(), uno::UNO_QUERY_THROW
);
104 SvMemoryStream aStream
;
105 uno::Reference
<io::XOutputStream
> xOut
= new utl::OOutputStreamWrapper(aStream
);
106 utl::MediaDescriptor aMediaDescriptor
;
107 aMediaDescriptor
["FilterName"] <<= OUString("draw_svg_Export");
108 aMediaDescriptor
["OutputStream"] <<= xOut
;
109 xStorable
->storeToURL("private:stream", aMediaDescriptor
.getAsConstPropertyValueList());
110 aStream
.Seek(STREAM_SEEK_TO_BEGIN
);
112 // Get the style of the group around the actual <path> element.
113 xmlDocUniquePtr pXmlDoc
= parseXmlStream(&aStream
);
114 OUString aStyle
= getXPath(
115 pXmlDoc
, "//svg:g[@class='com.sun.star.drawing.LineShape']/svg:g/svg:g", "style");
116 OUString
aPrefix("opacity: ");
117 // Without the accompanying fix in place, this test would have failed, as the style was
118 // "mask:url(#mask1)", not "opacity: <value>".
119 CPPUNIT_ASSERT(aStyle
.startsWith(aPrefix
));
120 int nPercent
= std::round(aStyle
.copy(aPrefix
.getLength()).toDouble() * 100);
121 // Make sure that the line is still 30% opaque, rather than completely invisible.
122 CPPUNIT_ASSERT_EQUAL(30, nPercent
);
125 CPPUNIT_TEST_FIXTURE(SvgFilterTest
, testSemiTransparentText
)
127 // Two shapes, one with transparent text and the other one with
128 // opaque text. We expect both to be exported to the SVG with the
129 // correct transparency factor applied for the first shape.
131 // Load draw document with transparent text in one box
132 load("TransparentText.odg");
135 uno::Reference
<frame::XStorable
> xStorable(getComponent(), uno::UNO_QUERY_THROW
);
137 SvMemoryStream aStream
;
138 uno::Reference
<io::XOutputStream
> xOut
= new utl::OOutputStreamWrapper(aStream
);
139 utl::MediaDescriptor aMediaDescriptor
;
140 aMediaDescriptor
["FilterName"] <<= OUString("draw_svg_Export");
141 aMediaDescriptor
["OutputStream"] <<= xOut
;
142 xStorable
->storeToURL("private:stream", aMediaDescriptor
.getAsConstPropertyValueList());
143 aStream
.Seek(STREAM_SEEK_TO_BEGIN
);
145 xmlDocUniquePtr pXmlDoc
= parseXmlStream(&aStream
);
147 // We expect 2 groups of class "TextShape" that
148 // have some svg:text node inside.
149 // Without the accompanying fix in place, this test would have failed with:
152 // i.e. the 2nd shape lots its text.
154 assertXPath(pXmlDoc
, "//svg:g[@class='TextShape']//svg:text", 2);
156 // First shape has semi-transparent text.
157 assertXPath(pXmlDoc
, "//svg:text[1]/svg:tspan/svg:tspan/svg:tspan[@fill-opacity='0.8']");
159 // Second shape has normal text.
160 assertXPath(pXmlDoc
, "//svg:text[2]/svg:tspan/svg:tspan/svg:tspan[@fill-opacity]", 0);
163 CPPUNIT_TEST_FIXTURE(SvgFilterTest
, testShapeNographic
)
165 // Load a document containing a 3D shape.
166 load("shape-nographic.odp");
169 uno::Reference
<frame::XStorable
> xStorable(getComponent(), uno::UNO_QUERY_THROW
);
170 SvMemoryStream aStream
;
171 uno::Reference
<io::XOutputStream
> xOut
= new utl::OOutputStreamWrapper(aStream
);
172 utl::MediaDescriptor aMediaDescriptor
;
173 aMediaDescriptor
["FilterName"] <<= OUString("impress_svg_Export");
174 aMediaDescriptor
["OutputStream"] <<= xOut
;
176 // Without the accompanying fix in place, this test would have failed with:
177 // An uncaught exception of type com.sun.star.io.IOException
178 // - SfxBaseModel::impl_store <private:stream> failed: 0xc10(Error Area:Io Class:Write Code:16)
179 xStorable
->storeToURL("private:stream", aMediaDescriptor
.getAsConstPropertyValueList());
182 CPPUNIT_TEST_FIXTURE(SvgFilterTest
, testCustomBullet
)
184 // Given a presentation with a custom bullet:
185 load(u
"custom-bullet.fodp");
187 // When exporting that to SVG:
188 uno::Reference
<frame::XStorable
> xStorable(getComponent(), uno::UNO_QUERY_THROW
);
189 SvMemoryStream aStream
;
190 uno::Reference
<io::XOutputStream
> xOut
= new utl::OOutputStreamWrapper(aStream
);
191 utl::MediaDescriptor aMediaDescriptor
;
192 aMediaDescriptor
["FilterName"] <<= OUString("impress_svg_Export");
193 aMediaDescriptor
["OutputStream"] <<= xOut
;
194 xStorable
->storeToURL("private:stream", aMediaDescriptor
.getAsConstPropertyValueList());
196 // Then make sure the bullet glyph is not lost:
197 aStream
.Seek(STREAM_SEEK_TO_BEGIN
);
198 xmlDocUniquePtr pXmlDoc
= parseXmlStream(&aStream
);
199 // Without the accompanying fix in place, this test would have failed with:
202 // - XPath '//svg:g[@class='BulletChars']//svg:path' number of nodes is incorrect
203 // i.e. the custom bullet used '<use transform="scale(285,285)"
204 // xlink:href="#bullet-char-template-45"/>', but nobody produced a bullet-char-template-45,
205 // instead we need the path of the glyph inline.
206 CPPUNIT_ASSERT(!getXPath(pXmlDoc
, "//svg:g[@class='BulletChars']//svg:path", "d").isEmpty());
209 CPPUNIT_TEST_FIXTURE(SvgFilterTest
, attributeRedefinedTest
)
211 // Load document containing empty paragraphs with ids.
212 load("attributeRedefinedTest.odp");
215 uno::Reference
<frame::XStorable
> xStorable(getComponent(), uno::UNO_QUERY_THROW
);
216 SvMemoryStream aStream
;
217 uno::Reference
<io::XOutputStream
> xOut
= new utl::OOutputStreamWrapper(aStream
);
218 utl::MediaDescriptor aMediaDescriptor
;
219 aMediaDescriptor
["FilterName"] <<= OUString("impress_svg_Export");
220 aMediaDescriptor
["OutputStream"] <<= xOut
;
221 xStorable
->storeToURL("private:stream", aMediaDescriptor
.getAsConstPropertyValueList());
222 aStream
.Seek(STREAM_SEEK_TO_BEGIN
);
224 xmlDocUniquePtr pXmlDoc
= parseXmlStream(&aStream
);
226 // We expect four paragraph
227 // 2 empty paragraphs with ids
228 // 2 paragraphs with text
229 // Without the accompanying fix the test would have failed with
232 // i.e. 2 of the empty paragraph do not get generated even if there
233 // is id imported for the paragraphs
234 // If we don't create the empty paragraphs the id attribute attribute gets redefined like this:
235 // <tspan id="id14" id="id15" id="id17" class="TextParagraph" font-family="Bahnschrift Light" font-size="1129px" font-weight="400">
237 OString xPath
= "//svg:g[@class='TextShape']//svg:text[@class='SVGTextShape']//"
238 "svg:tspan[@class='TextParagraph']";
239 assertXPath(pXmlDoc
, xPath
, 4);
241 //assert that each tspan element with TextParagraph class has id and the tspan element of
242 //each empty paragraph doesnot contain tspan element with class TextPosition
243 assertXPath(pXmlDoc
, xPath
+ "[1]", "id", "id4");
244 assertXPath(pXmlDoc
, xPath
+ "[2]", "id", "id5");
245 assertXPath(pXmlDoc
, xPath
+ "[2]//svg:tspan[@class='TextPosition']", 0);
246 assertXPath(pXmlDoc
, xPath
+ "[3]", "id", "id6");
247 assertXPath(pXmlDoc
, xPath
+ "[3]//svg:tspan[@class='TextPosition']", 0);
248 assertXPath(pXmlDoc
, xPath
+ "[4]", "id", "id7");
251 CPPUNIT_PLUGIN_IMPLEMENT();
253 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */