1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
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  *
9  * This file incorporates work covered by the following license notice:
10  *
11  *   Licensed to the Apache Software Foundation (ASF) under one or more
12  *   contributor license agreements. See the NOTICE file distributed
13  *   with this work for additional information regarding copyright
14  *   ownership. The ASF licenses this file to you under the Apache
15  *   License, Version 2.0 (the "License"); you may not use this file
16  *   except in compliance with the License. You may obtain a copy of
17  *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
18  */
19 
20 #include <config_features.h>
21 
22 #include "outputwrap.hxx"
23 #include <contentsink.hxx>
24 #include <pdfihelper.hxx>
25 #include <wrapper.hxx>
26 #include <pdfparse.hxx>
27 #include "../pdfiadaptor.hxx"
28 
29 #include <rtl/math.hxx>
30 #include <osl/file.hxx>
31 #include <comphelper/sequence.hxx>
32 
33 #include <cppunit/TestAssert.h>
34 #include <cppunit/extensions/HelperMacros.h>
35 #include <cppunit/plugin/TestPlugIn.h>
36 #include <test/bootstrapfixture.hxx>
37 
38 #include <com/sun/star/geometry/RealRectangle2D.hpp>
39 #include <com/sun/star/geometry/RealSize2D.hpp>
40 #include <com/sun/star/rendering/PathJoinType.hpp>
41 #include <com/sun/star/rendering/PathCapType.hpp>
42 #include <com/sun/star/rendering/BlendMode.hpp>
43 
44 #include <basegfx/utils/canvastools.hxx>
45 #include <basegfx/polygon/b2dpolypolygon.hxx>
46 #include <basegfx/polygon/b2dpolypolygontools.hxx>
47 #include <basegfx/polygon/b2dpolygonclipper.hxx>
48 
49 #include <unordered_map>
50 #include <vector>
51 
52 #include <rtl/ustring.hxx>
53 #include <rtl/ref.hxx>
54 
55 using namespace ::pdfparse;
56 using namespace ::pdfi;
57 using namespace ::com::sun::star;
58 
59 namespace
60 {
61 
62     class TestSink : public ContentSink
63     {
64     public:
TestSink()65         TestSink() :
66             m_nNextFontId( 1 ),
67             m_aIdToFont(),
68             m_aFontToId(),
69             m_aGCStack(1),
70             m_aPageSize(),
71             m_aHyperlinkBounds(),
72             m_aURI(),
73             m_aTextOut(),
74             m_nNumPages(0),
75             m_bPageEnded(false),
76             m_bRedCircleSeen(false),
77             m_bGreenStrokeSeen(false),
78             m_bDashedLineSeen(false),
79             m_bImageSeen(false)
80         {}
81 
check()82         void check()
83         {
84             CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE( "A4 page size (in 100th of points): Width", 79400, m_aPageSize.Width, 0.00000001);
85             CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE( "A4 page size (in 100th of points): Height", 59500, m_aPageSize.Height, 0.0000001 );
86             CPPUNIT_ASSERT_MESSAGE( "endPage() called", m_bPageEnded );
87             CPPUNIT_ASSERT_EQUAL_MESSAGE( "Num pages equal one", sal_Int32(1), m_nNumPages );
88             CPPUNIT_ASSERT_MESSAGE( "Correct hyperlink bounding box",
89                                     rtl::math::approxEqual(m_aHyperlinkBounds.X1,34.7 ) &&
90                                     rtl::math::approxEqual(m_aHyperlinkBounds.Y1,386.0) &&
91                                     rtl::math::approxEqual(m_aHyperlinkBounds.X2,166.7) &&
92                                     rtl::math::approxEqual(m_aHyperlinkBounds.Y2,406.2) );
93             CPPUNIT_ASSERT_EQUAL_MESSAGE( "Correct hyperlink URI", OUString("http://download.openoffice.org/"), m_aURI );
94 
95             const char* const sText = " \n \nThis is a testtext\nNew paragraph,\nnew line\n"
96                 "Hyperlink, this is\n?\nThis is more text\noutline mode\n?\nNew paragraph\n";
97             OString aTmp;
98             m_aTextOut.makeStringAndClear().convertToString( &aTmp,
99                                                              RTL_TEXTENCODING_ASCII_US,
100                                                              OUSTRING_TO_OSTRING_CVTFLAGS );
101             CPPUNIT_ASSERT_EQUAL_MESSAGE( "Imported text is \"This is a testtext New paragraph, new line"
102                                     " Hyperlink, this is * This is more text outline mode * New paragraph\"",
103                                     aTmp, OString(sText) );
104 
105             CPPUNIT_ASSERT_MESSAGE( "red circle seen in input", m_bRedCircleSeen );
106             CPPUNIT_ASSERT_MESSAGE( "green stroke seen in input", m_bGreenStrokeSeen );
107             CPPUNIT_ASSERT_MESSAGE( "dashed line seen in input", m_bDashedLineSeen );
108             CPPUNIT_ASSERT_MESSAGE( "image seen in input", m_bImageSeen );
109         }
110 
111     private:
getCurrentContext()112         GraphicsContext& getCurrentContext() { return m_aGCStack.back(); }
113 
114         // ContentSink interface implementation
setPageNum(sal_Int32 nNumPages)115         virtual void setPageNum( sal_Int32 nNumPages ) override
116         {
117             m_nNumPages = nNumPages;
118         }
119 
startPage(const geometry::RealSize2D & rSize)120         virtual void startPage( const geometry::RealSize2D& rSize ) override
121         {
122             m_aPageSize = rSize;
123         }
124 
endPage()125         virtual void endPage() override
126         {
127             m_bPageEnded = true;
128         }
129 
hyperLink(const geometry::RealRectangle2D & rBounds,const OUString & rURI)130         virtual void hyperLink( const geometry::RealRectangle2D& rBounds,
131                                 const OUString&             rURI ) override
132         {
133             m_aHyperlinkBounds = rBounds;
134             m_aURI = rURI;
135         }
136 
pushState()137         virtual void pushState() override
138         {
139             GraphicsContextStack::value_type const a(m_aGCStack.back());
140             m_aGCStack.push_back(a);
141         }
142 
popState()143         virtual void popState() override
144         {
145             m_aGCStack.pop_back();
146         }
147 
setTransformation(const geometry::AffineMatrix2D & rMatrix)148         virtual void setTransformation( const geometry::AffineMatrix2D& rMatrix ) override
149         {
150             basegfx::unotools::homMatrixFromAffineMatrix(
151                 getCurrentContext().Transformation,
152                 rMatrix );
153         }
154 
setLineDash(const uno::Sequence<double> & dashes,double start)155         virtual void setLineDash( const uno::Sequence<double>& dashes,
156                                   double                       start ) override
157         {
158             GraphicsContext& rContext( getCurrentContext() );
159             if( dashes.hasElements() )
160                 comphelper::sequenceToContainer(rContext.DashArray,dashes);
161             CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE( "line dashing start offset", 0.0, start, 0.000000001 );
162         }
163 
setFlatness(double nFlatness)164         virtual void setFlatness( double nFlatness ) override
165         {
166             getCurrentContext().Flatness = nFlatness;
167         }
168 
setLineJoin(sal_Int8 nJoin)169         virtual void setLineJoin(sal_Int8 nJoin) override
170         {
171             getCurrentContext().LineJoin = nJoin;
172         }
173 
setLineCap(sal_Int8 nCap)174         virtual void setLineCap(sal_Int8 nCap) override
175         {
176             getCurrentContext().LineCap = nCap;
177         }
178 
setMiterLimit(double nVal)179         virtual void setMiterLimit(double nVal) override
180         {
181             getCurrentContext().MiterLimit = nVal;
182         }
183 
setLineWidth(double nVal)184         virtual void setLineWidth(double nVal) override
185         {
186             getCurrentContext().LineWidth = nVal;
187         }
188 
setFillColor(const rendering::ARGBColor & rColor)189         virtual void setFillColor( const rendering::ARGBColor& rColor ) override
190         {
191             getCurrentContext().FillColor = rColor;
192         }
193 
setStrokeColor(const rendering::ARGBColor & rColor)194         virtual void setStrokeColor( const rendering::ARGBColor& rColor ) override
195         {
196             getCurrentContext().LineColor = rColor;
197         }
198 
setFont(const FontAttributes & rFont)199         virtual void setFont( const FontAttributes& rFont ) override
200         {
201             FontToIdMap::const_iterator it = m_aFontToId.find( rFont );
202             if( it != m_aFontToId.end() )
203                 getCurrentContext().FontId = it->second;
204             else
205             {
206                 m_aFontToId[ rFont ] = m_nNextFontId;
207                 m_aIdToFont[ m_nNextFontId ] = rFont;
208                 getCurrentContext().FontId = m_nNextFontId;
209                 m_nNextFontId++;
210             }
211         }
212 
strokePath(const uno::Reference<rendering::XPolyPolygon2D> & rPath)213         virtual void strokePath( const uno::Reference<rendering::XPolyPolygon2D>& rPath ) override
214         {
215             GraphicsContext& rContext( getCurrentContext() );
216             basegfx::B2DPolyPolygon aPath = basegfx::unotools::b2DPolyPolygonFromXPolyPolygon2D(rPath);
217             aPath.transform( rContext.Transformation );
218 
219             if( rContext.DashArray.empty() )
220             {
221                 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE( "Line color is green", 1.0, rContext.LineColor.Alpha, 0.00000001);
222                 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE( "Line color is green", 0.0, rContext.LineColor.Blue, 0.00000001);
223                 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE( "Line color is green", 1.0, rContext.LineColor.Green, 0.00000001);
224                 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE( "Line color is green", 0.0, rContext.LineColor.Red, 0.00000001);
225 
226                 CPPUNIT_ASSERT_MESSAGE( "Line width is 0",
227                                         rtl::math::approxEqual(rContext.LineWidth, 28.3) );
228 
229                 const char sExportString[] = "m53570 7650-35430 24100";
230                 CPPUNIT_ASSERT_EQUAL_MESSAGE( "Stroke is m535.7 518.5-354.3-241",
231                                         OUString(sExportString), basegfx::utils::exportToSvgD( aPath, true, true, false ) );
232 
233                 m_bGreenStrokeSeen = true;
234             }
235             else
236             {
237                 CPPUNIT_ASSERT_EQUAL_MESSAGE( "Dash array consists of four entries", std::vector<double>::size_type(4), rContext.DashArray.size());
238                 CPPUNIT_ASSERT_DOUBLES_EQUAL( 14.3764, rContext.DashArray[0], 1E-12 );
239                 CPPUNIT_ASSERT_DOUBLES_EQUAL( rContext.DashArray[0], rContext.DashArray[1], 1E-12 );
240                 CPPUNIT_ASSERT_DOUBLES_EQUAL( rContext.DashArray[1], rContext.DashArray[2], 1E-12 );
241                 CPPUNIT_ASSERT_DOUBLES_EQUAL( rContext.DashArray[2], rContext.DashArray[3], 1E-12 );
242 
243                 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE( "Line color is black", 1.0, rContext.LineColor.Alpha, 0.00000001);
244                 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE( "Line color is black", 0.0, rContext.LineColor.Blue, 0.00000001);
245                 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE( "Line color is black", 0.0, rContext.LineColor.Green, 0.00000001);
246                 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE( "Line color is black", 0.0, rContext.LineColor.Red, 0.00000001);
247 
248                 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE( "Line width is 0",
249                                         0, rContext.LineWidth, 0.0000001 );
250 
251                 const char sExportString[] = "m49890 5670.00000000001-35430 24090";
252                 CPPUNIT_ASSERT_EQUAL_MESSAGE( "Stroke is m49890 5670.00000000001-35430 24090",
253                                         OUString(sExportString), basegfx::utils::exportToSvgD( aPath, true, true, false ) );
254 
255                 m_bDashedLineSeen = true;
256             }
257             CPPUNIT_ASSERT_EQUAL_MESSAGE( "Blend mode is normal",
258                                     rendering::BlendMode::NORMAL, rContext.BlendMode );
259             CPPUNIT_ASSERT_EQUAL_MESSAGE( "Join type is round",
260                                     rendering::PathJoinType::ROUND, rContext.LineJoin );
261             CPPUNIT_ASSERT_EQUAL_MESSAGE( "Cap type is butt",
262                                     rendering::PathCapType::BUTT, rContext.LineCap );
263             CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE( "Line miter limit is 10",
264                                     10, rContext.MiterLimit, 0.0000001 );
265             CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE( "Flatness is 0",
266                                     1, rContext.Flatness, 0.00000001 );
267             CPPUNIT_ASSERT_EQUAL_MESSAGE( "Font id is 0",
268                                     sal_Int32(0), rContext.FontId );
269         }
270 
fillPath(const uno::Reference<rendering::XPolyPolygon2D> & rPath)271         virtual void fillPath( const uno::Reference<rendering::XPolyPolygon2D>& rPath ) override
272         {
273             GraphicsContext& rContext( getCurrentContext() );
274             basegfx::B2DPolyPolygon aPath = basegfx::unotools::b2DPolyPolygonFromXPolyPolygon2D(rPath);
275             aPath.transform( rContext.Transformation );
276 
277             CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE( "Line color is black", 1.0, rContext.LineColor.Alpha, 0.00000001);
278             CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE( "Line color is black", 0.0, rContext.LineColor.Blue, 0.00000001);
279             CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE( "Line color is black", 0.0, rContext.LineColor.Green, 0.00000001);
280             CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE( "Line color is black", 0.0, rContext.LineColor.Red, 0.00000001);
281 
282             CPPUNIT_ASSERT_EQUAL_MESSAGE( "Blend mode is normal",
283                                     rendering::BlendMode::NORMAL, rContext.BlendMode );
284             CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE( "Flatness is 10",
285                                     10, rContext.Flatness, 0.00000001 );
286             CPPUNIT_ASSERT_EQUAL_MESSAGE( "Font id is 0",
287                                     sal_Int32(0), rContext.FontId );
288         }
289 
eoFillPath(const uno::Reference<rendering::XPolyPolygon2D> & rPath)290         virtual void eoFillPath( const uno::Reference<rendering::XPolyPolygon2D>& rPath ) override
291         {
292             GraphicsContext& rContext( getCurrentContext() );
293             basegfx::B2DPolyPolygon aPath = basegfx::unotools::b2DPolyPolygonFromXPolyPolygon2D(rPath);
294             aPath.transform( rContext.Transformation );
295 
296             CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE( "Line color is black", 1.0, rContext.LineColor.Alpha, 0.00000001);
297             CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE( "Line color is black", 0.0, rContext.LineColor.Blue, 0.00000001);
298             CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE( "Line color is black", 0.0, rContext.LineColor.Green, 0.00000001);
299             CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE( "Line color is black", 0.0, rContext.LineColor.Red, 0.00000001);
300 
301             CPPUNIT_ASSERT_EQUAL_MESSAGE( "Blend mode is normal",
302                                     rendering::BlendMode::NORMAL, rContext.BlendMode );
303             CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE( "Flatness is 0",
304                                     1, rContext.Flatness, 0.00000001 );
305             CPPUNIT_ASSERT_EQUAL_MESSAGE( "Font id is 0",
306                                     sal_Int32(0), rContext.FontId );
307 
308             const char sExportString[] = "m12050 49610c-4310 0-7800-3490-7800-7800 0-4300 "
309                 "3490-7790 7800-7790 4300 0 7790 3490 7790 7790 0 4310-3490 7800-7790 7800z";
310             CPPUNIT_ASSERT_EQUAL_MESSAGE( "Stroke is a 4-bezier circle",
311                                     OUString(sExportString), basegfx::utils::exportToSvgD( aPath, true, true, false ) );
312 
313             m_bRedCircleSeen = true;
314         }
315 
intersectClip(const uno::Reference<rendering::XPolyPolygon2D> & rPath)316         virtual void intersectClip(const uno::Reference<rendering::XPolyPolygon2D>& rPath) override
317         {
318             basegfx::B2DPolyPolygon aNewClip = basegfx::unotools::b2DPolyPolygonFromXPolyPolygon2D(rPath);
319             basegfx::B2DPolyPolygon aCurClip = getCurrentContext().Clip;
320 
321             if( aCurClip.count() )  // #i92985# adapted API from (..., false, false) to (..., true, false)
322                 aNewClip = basegfx::utils::clipPolyPolygonOnPolyPolygon( aCurClip, aNewClip, true, false );
323 
324             getCurrentContext().Clip = aNewClip;
325         }
326 
intersectEoClip(const uno::Reference<rendering::XPolyPolygon2D> & rPath)327         virtual void intersectEoClip(const uno::Reference<rendering::XPolyPolygon2D>& rPath) override
328         {
329             basegfx::B2DPolyPolygon aNewClip = basegfx::unotools::b2DPolyPolygonFromXPolyPolygon2D(rPath);
330             basegfx::B2DPolyPolygon aCurClip = getCurrentContext().Clip;
331 
332             if( aCurClip.count() )  // #i92985# adapted API from (..., false, false) to (..., true, false)
333                 aNewClip = basegfx::utils::clipPolyPolygonOnPolyPolygon( aCurClip, aNewClip, true, false );
334 
335             getCurrentContext().Clip = aNewClip;
336         }
337 
drawGlyphs(const OUString & rGlyphs,const geometry::RealRectangle2D &,const geometry::Matrix2D &,double)338         virtual void drawGlyphs( const OUString&             rGlyphs,
339                                  const geometry::RealRectangle2D& /*rRect*/,
340                                  const geometry::Matrix2D&        /*rFontMatrix*/,
341                                  double /*fontSize*/) override
342         {
343             m_aTextOut.append(rGlyphs);
344         }
345 
endText()346         virtual void endText() override
347         {
348             m_aTextOut.append( "\n" );
349         }
350 
drawMask(const uno::Sequence<beans::PropertyValue> & xBitmap,bool)351         virtual void drawMask(const uno::Sequence<beans::PropertyValue>& xBitmap,
352                               bool                                       /*bInvert*/ ) override
353         {
354             CPPUNIT_ASSERT_EQUAL_MESSAGE( "drawMask received two properties",
355                                     sal_Int32(3), xBitmap.getLength() );
356             CPPUNIT_ASSERT_EQUAL_MESSAGE( "drawMask got URL param",
357                                     OUString("URL"), xBitmap[0].Name );
358             CPPUNIT_ASSERT_EQUAL_MESSAGE( "drawMask got InputStream param",
359                                     OUString("InputStream"), xBitmap[1].Name );
360         }
361 
drawImage(const uno::Sequence<beans::PropertyValue> & xBitmap)362         virtual void drawImage(const uno::Sequence<beans::PropertyValue>& xBitmap ) override
363         {
364             CPPUNIT_ASSERT_EQUAL_MESSAGE( "drawImage received two properties",
365                                     sal_Int32(3), xBitmap.getLength() );
366             CPPUNIT_ASSERT_EQUAL_MESSAGE( "drawImage got URL param",
367                                     OUString("URL"), xBitmap[0].Name );
368             CPPUNIT_ASSERT_EQUAL_MESSAGE( "drawImage got InputStream param",
369                                     OUString("InputStream"), xBitmap[1].Name );
370             m_bImageSeen = true;
371         }
372 
drawColorMaskedImage(const uno::Sequence<beans::PropertyValue> & xBitmap,const uno::Sequence<uno::Any> &)373         virtual void drawColorMaskedImage(const uno::Sequence<beans::PropertyValue>& xBitmap,
374                                           const uno::Sequence<uno::Any>&             /*xMaskColors*/ ) override
375         {
376             CPPUNIT_ASSERT_EQUAL_MESSAGE( "drawColorMaskedImage received two properties",
377                                     sal_Int32(3), xBitmap.getLength() );
378             CPPUNIT_ASSERT_EQUAL_MESSAGE( "drawColorMaskedImage got URL param",
379                                     OUString("URL"), xBitmap[0].Name );
380             CPPUNIT_ASSERT_EQUAL_MESSAGE( "drawColorMaskedImage got InputStream param",
381                                     OUString("InputStream"), xBitmap[1].Name );
382         }
383 
drawMaskedImage(const uno::Sequence<beans::PropertyValue> & xBitmap,const uno::Sequence<beans::PropertyValue> & xMask,bool)384         virtual void drawMaskedImage(const uno::Sequence<beans::PropertyValue>& xBitmap,
385                                      const uno::Sequence<beans::PropertyValue>& xMask,
386                                      bool                                       /*bInvertMask*/) override
387         {
388             CPPUNIT_ASSERT_EQUAL_MESSAGE( "drawMaskedImage received two properties #1",
389                                     sal_Int32(3), xBitmap.getLength() );
390             CPPUNIT_ASSERT_EQUAL_MESSAGE( "drawMaskedImage got URL param #1",
391                                     OUString("URL"), xBitmap[0].Name );
392             CPPUNIT_ASSERT_EQUAL_MESSAGE( "drawMaskedImage got InputStream param #1",
393                                     OUString("InputStream"), xBitmap[1].Name );
394 
395             CPPUNIT_ASSERT_EQUAL_MESSAGE( "drawMaskedImage received two properties #2",
396                                     sal_Int32(3), xMask.getLength() );
397             CPPUNIT_ASSERT_EQUAL_MESSAGE( "drawMaskedImage got URL param #2",
398                                     OUString("URL"), xMask[0].Name );
399             CPPUNIT_ASSERT_EQUAL_MESSAGE( "drawMaskedImage got InputStream param #2",
400                                     OUString("InputStream"), xMask[1].Name );
401         }
402 
drawAlphaMaskedImage(const uno::Sequence<beans::PropertyValue> & xBitmap,const uno::Sequence<beans::PropertyValue> & xMask)403         virtual void drawAlphaMaskedImage(const uno::Sequence<beans::PropertyValue>& xBitmap,
404                                           const uno::Sequence<beans::PropertyValue>& xMask) override
405         {
406             CPPUNIT_ASSERT_EQUAL_MESSAGE( "drawAlphaMaskedImage received two properties #1",
407                                     sal_Int32(3), xBitmap.getLength() );
408             CPPUNIT_ASSERT_EQUAL_MESSAGE( "drawAlphaMaskedImage got URL param #1",
409                                     OUString("URL"), xBitmap[0].Name );
410             CPPUNIT_ASSERT_EQUAL_MESSAGE( "drawAlphaMaskedImage got InputStream param #1",
411                                     OUString("InputStream"), xBitmap[1].Name );
412 
413             CPPUNIT_ASSERT_EQUAL_MESSAGE( "drawAlphaMaskedImage received two properties #2",
414                                     sal_Int32(3), xMask.getLength() );
415             CPPUNIT_ASSERT_EQUAL_MESSAGE( "drawAlphaMaskedImage got URL param #2",
416                                     OUString("URL"), xMask[0].Name );
417             CPPUNIT_ASSERT_EQUAL_MESSAGE( "drawAlphaMaskedImage got InputStream param #2",
418                                     OUString("InputStream"), xMask[1].Name );
419         }
420 
setTextRenderMode(sal_Int32)421         virtual void setTextRenderMode( sal_Int32 ) override
422         {
423         }
424 
425         typedef std::unordered_map<sal_Int32,FontAttributes> IdToFontMap;
426         typedef std::unordered_map<FontAttributes,sal_Int32,FontAttrHash> FontToIdMap;
427 
428         typedef std::vector<GraphicsContext> GraphicsContextStack;
429 
430         sal_Int32                 m_nNextFontId;
431         IdToFontMap               m_aIdToFont;
432         FontToIdMap               m_aFontToId;
433 
434         GraphicsContextStack      m_aGCStack;
435         geometry::RealSize2D      m_aPageSize;
436         geometry::RealRectangle2D m_aHyperlinkBounds;
437         OUString           m_aURI;
438         OUStringBuffer     m_aTextOut;
439         sal_Int32                 m_nNumPages;
440         bool                      m_bPageEnded;
441         bool                      m_bRedCircleSeen;
442         bool                      m_bGreenStrokeSeen;
443         bool                      m_bDashedLineSeen;
444         bool                      m_bImageSeen;
445     };
446 
447     class PDFITest : public test::BootstrapFixture
448     {
449     public:
testXPDFParser()450         void testXPDFParser()
451         {
452 #if HAVE_FEATURE_POPPLER
453             std::shared_ptr<TestSink> pSink( new TestSink() );
454             CPPUNIT_ASSERT(
455                 pdfi::xpdf_ImportFromFile(
456                     m_directories.getURLFromSrc("/sdext/source/pdfimport/test/testinput.pdf"),
457                     pSink,
458                     uno::Reference< task::XInteractionHandler >(),
459                     OUString(),
460                     getComponentContext(), "" ) );
461             pSink->check();
462 #endif
463         }
464 
testOdfDrawExport()465         void testOdfDrawExport()
466         {
467 #if HAVE_FEATURE_POPPLER
468             rtl::Reference<pdfi::PDFIRawAdaptor> xAdaptor( new pdfi::PDFIRawAdaptor(OUString(), getComponentContext()) );
469             xAdaptor->setTreeVisitorFactory( createDrawTreeVisitorFactory() );
470 
471             OUString tempFileURL;
472             CPPUNIT_ASSERT_EQUAL( osl::File::E_None, osl::File::createTempFile( nullptr, nullptr, &tempFileURL ) );
473             osl::File::remove( tempFileURL ); // FIXME the below apparently fails silently if the file already exists
474             CPPUNIT_ASSERT_MESSAGE("Exporting to ODF",
475                                    xAdaptor->odfConvert( m_directories.getURLFromSrc("/sdext/source/pdfimport/test/testinput.pdf"),
476                                                         new OutputWrap(tempFileURL),
477                                                         nullptr ));
478             osl::File::remove( tempFileURL );
479 #endif
480         }
481 
testOdfWriterExport()482         void testOdfWriterExport()
483         {
484 #if HAVE_FEATURE_POPPLER
485             rtl::Reference<pdfi::PDFIRawAdaptor> xAdaptor( new pdfi::PDFIRawAdaptor(OUString(), getComponentContext()) );
486             xAdaptor->setTreeVisitorFactory( createWriterTreeVisitorFactory() );
487 
488             OUString tempFileURL;
489             CPPUNIT_ASSERT_EQUAL( osl::File::E_None, osl::File::createTempFile( nullptr, nullptr, &tempFileURL ) );
490             osl::File::remove( tempFileURL ); // FIXME the below apparently fails silently if the file already exists
491             CPPUNIT_ASSERT_MESSAGE("Exporting to ODF",
492                                    xAdaptor->odfConvert( m_directories.getURLFromSrc("/sdext/source/pdfimport/test/testinput.pdf"),
493                                                         new OutputWrap(tempFileURL),
494                                                         nullptr ));
495             osl::File::remove( tempFileURL );
496 #endif
497         }
498 
testTdf96993()499         void testTdf96993()
500         {
501 #if HAVE_FEATURE_POPPLER
502             rtl::Reference<pdfi::PDFIRawAdaptor> xAdaptor(new pdfi::PDFIRawAdaptor(OUString(), getComponentContext()));
503             xAdaptor->setTreeVisitorFactory(createDrawTreeVisitorFactory());
504 
505             OString aOutput;
506             CPPUNIT_ASSERT_MESSAGE("Exporting to ODF",
507                 xAdaptor->odfConvert(m_directories.getURLFromSrc("/sdext/source/pdfimport/test/testTdf96993.pdf"),
508                 new OutputWrapString(aOutput),
509                 nullptr));
510             // This ensures that the imported image arrives properly flipped
511             CPPUNIT_ASSERT(aOutput.indexOf("draw:transform=\"matrix(18520.8333333333 0 0 26281.9444444444 0 0)\"") != -1);
512 #endif
513         }
514 
testTdf98421()515         void testTdf98421()
516         {
517 #if HAVE_FEATURE_POPPLER
518             rtl::Reference<pdfi::PDFIRawAdaptor> xAdaptor(new pdfi::PDFIRawAdaptor(OUString(), getComponentContext()));
519             xAdaptor->setTreeVisitorFactory(createWriterTreeVisitorFactory());
520 
521             OString aOutput;
522             CPPUNIT_ASSERT_MESSAGE("Exporting to ODF",
523                 xAdaptor->odfConvert(m_directories.getURLFromSrc("/sdext/source/pdfimport/test/testTdf96993.pdf"),
524                 new OutputWrapString(aOutput),
525                 nullptr));
526             // This ensures that the imported image arrives properly flipped
527             CPPUNIT_ASSERT(aOutput.indexOf("draw:transform=\"scale( 1.0 -1.0 ) translate( 0mm 0mm )\"") != -1);
528             CPPUNIT_ASSERT(aOutput.indexOf("svg:height=\"-262.82mm\"") != -1);
529 #endif
530         }
531 
testTdf105536()532         void testTdf105536()
533         {
534 #if HAVE_FEATURE_POPPLER
535             rtl::Reference<pdfi::PDFIRawAdaptor> xAdaptor(new pdfi::PDFIRawAdaptor(OUString(), getComponentContext()));
536             xAdaptor->setTreeVisitorFactory(createDrawTreeVisitorFactory());
537 
538             OString aOutput;
539             CPPUNIT_ASSERT_MESSAGE("Exporting to ODF",
540                 xAdaptor->odfConvert(m_directories.getURLFromSrc("/sdext/source/pdfimport/test/testTdf105536.pdf"),
541                 new OutputWrapString(aOutput),
542                 nullptr));
543             // This ensures that the imported image arrives properly flipped
544             CPPUNIT_ASSERT(aOutput.indexOf("draw:transform=\"matrix(-21488.4 0 0 -27978.1 21488.4 27978.1)\"") != -1);
545 #endif
546         }
547 
548         CPPUNIT_TEST_SUITE(PDFITest);
549         CPPUNIT_TEST(testXPDFParser);
550         CPPUNIT_TEST(testOdfWriterExport);
551         CPPUNIT_TEST(testOdfDrawExport);
552         CPPUNIT_TEST(testTdf96993);
553         CPPUNIT_TEST(testTdf98421);
554         CPPUNIT_TEST(testTdf105536);
555         CPPUNIT_TEST_SUITE_END();
556     };
557 
558 }
559 
560 CPPUNIT_TEST_SUITE_REGISTRATION(PDFITest);
561 
562 CPPUNIT_PLUGIN_IMPLEMENT();
563 
564 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
565