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