1 /************************************************************************* 2 * 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * Copyright 2000, 2010 Oracle and/or its affiliates. 6 * 7 * OpenOffice.org - a multi-platform office productivity suite 8 * 9 * This file is part of OpenOffice.org. 10 * 11 * OpenOffice.org is free software: you can redistribute it and/or modify 12 * it under the terms of the GNU Lesser General Public License version 3 13 * only, as published by the Free Software Foundation. 14 * 15 * OpenOffice.org is distributed in the hope that it will be useful, 16 * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 * GNU Lesser General Public License version 3 for more details 19 * (a copy is included in the LICENSE file that accompanied this code). 20 * 21 * You should have received a copy of the GNU Lesser General Public License 22 * version 3 along with OpenOffice.org. If not, see 23 * <http://www.openoffice.org/license.html> 24 * for a copy of the LGPLv3 License. 25 * 26 ************************************************************************/ 27 28 // MARKER(update_precomp.py): autogen include statement, do not remove 29 #include "precompiled_sdext.hxx" 30 31 #include "contentsink.hxx" 32 #include "pdfparse.hxx" 33 #include "pdfihelper.hxx" 34 35 #include "osl/file.h" 36 #include "osl/thread.h" 37 #include "osl/process.h" 38 #include "osl/diagnose.h" 39 #include "rtl/ustring.hxx" 40 #include "rtl/ustrbuf.hxx" 41 #include "rtl/strbuf.hxx" 42 #include "rtl/byteseq.hxx" 43 44 #include "cppuhelper/exc_hlp.hxx" 45 #include "com/sun/star/io/XInputStream.hpp" 46 #include "com/sun/star/uno/XComponentContext.hpp" 47 #include "com/sun/star/awt/FontDescriptor.hpp" 48 #include "com/sun/star/deployment/XPackageInformationProvider.hpp" 49 #include "com/sun/star/beans/XMaterialHolder.hpp" 50 #include "com/sun/star/rendering/PathCapType.hpp" 51 #include "com/sun/star/rendering/PathJoinType.hpp" 52 #include "com/sun/star/rendering/XColorSpace.hpp" 53 #include "com/sun/star/rendering/XPolyPolygon2D.hpp" 54 #include "com/sun/star/rendering/XBitmap.hpp" 55 #include "com/sun/star/geometry/Matrix2D.hpp" 56 #include "com/sun/star/geometry/AffineMatrix2D.hpp" 57 #include "com/sun/star/geometry/RealRectangle2D.hpp" 58 #include "com/sun/star/task/XInteractionHandler.hpp" 59 60 #include "basegfx/point/b2dpoint.hxx" 61 #include "basegfx/polygon/b2dpolypolygon.hxx" 62 #include "basegfx/polygon/b2dpolygon.hxx" 63 #include "basegfx/tools/canvastools.hxx" 64 #include "basegfx/tools/unopolypolygon.hxx" 65 66 #include <boost/bind.hpp> 67 #include <boost/preprocessor/stringize.hpp> 68 #include <boost/scoped_ptr.hpp> 69 #include <boost/scoped_array.hpp> 70 71 #include <hash_map> 72 #include <string.h> 73 #ifdef WNT 74 #include <stdlib.h> 75 #include <ctype.h> 76 #endif 77 78 #include "rtl/bootstrap.h" 79 80 #include <string.h> // memcmp 81 82 #ifndef PDFI_IMPL_IDENTIFIER 83 # error define implementation name for pdfi extension, please! 84 #endif 85 86 using namespace com::sun::star; 87 88 namespace pdfi 89 { 90 91 namespace 92 { 93 94 // identifier of the strings coming from the out-of-process xpdf 95 // converter 96 enum parseKey { 97 CLIPPATH, 98 DRAWCHAR, 99 DRAWIMAGE, 100 DRAWLINK, 101 DRAWMASK, 102 DRAWMASKEDIMAGE, 103 DRAWSOFTMASKEDIMAGE, 104 ENDPAGE, 105 ENDTEXTOBJECT, 106 EOCLIPPATH, 107 EOFILLPATH, 108 FILLPATH, 109 HYPERLINK, 110 INTERSECTCLIP, 111 INTERSECTEOCLIP, 112 POPSTATE, 113 PUSHSTATE, 114 RESTORESTATE, 115 SAVESTATE, 116 SETBLENDMODE, 117 SETFILLCOLOR, 118 SETFONT, 119 SETLINECAP, 120 SETLINEDASH, 121 SETLINEJOIN, 122 SETLINEWIDTH, 123 SETMITERLIMIT, 124 SETPAGENUM, 125 SETSTROKECOLOR, 126 SETTEXTRENDERMODE, 127 SETTRANSFORMATION, 128 STARTPAGE, 129 STROKEPATH, 130 UPDATEBLENDMODE, 131 UPDATECTM, 132 UPDATEFILLCOLOR, 133 UPDATEFILLOPACITY, 134 UPDATEFLATNESS, 135 UPDATEFONT, 136 UPDATELINECAP, 137 UPDATELINEDASH, 138 UPDATELINEJOIN, 139 UPDATELINEWIDTH, 140 UPDATEMITERLIMIT, 141 UPDATESTROKECOLOR, 142 UPDATESTROKEOPACITY, 143 NONE 144 }; 145 146 #include "hash.cxx" 147 148 class Parser 149 { 150 typedef std::hash_map< sal_Int64, 151 FontAttributes > FontMapType; 152 153 const uno::Reference<uno::XComponentContext> m_xContext; 154 const ContentSinkSharedPtr m_pSink; 155 const oslFileHandle m_pErr; 156 ::rtl::OString m_aLine; 157 FontMapType m_aFontMap; 158 sal_Int32 m_nNextToken; 159 sal_Int32 m_nCharIndex; 160 161 const double minAreaThreshold; 162 const double minLineWidth; 163 164 ::rtl::OString readNextToken(); 165 void readInt32( sal_Int32& o_Value ); 166 sal_Int32 readInt32(); 167 void readInt64( sal_Int64& o_Value ); 168 void readDouble( double& o_Value ); 169 double readDouble(); 170 void readBinaryData( uno::Sequence<sal_Int8>& rBuf ); 171 172 uno::Reference<rendering::XPolyPolygon2D> readPath( double* ); 173 174 void readChar(); 175 void readLineCap(); 176 void readLineDash(); 177 void readLineJoin(); 178 void readTransformation(); 179 rendering::ARGBColor readColor(); 180 void parseFontFamilyName( FontAttributes& aResult ); 181 void readFont(); 182 uno::Sequence<beans::PropertyValue> readImageImpl(); 183 184 void readImage(); 185 void readMask(); 186 void readLink(); 187 void readMaskedImage(); 188 void readSoftMaskedImage(); 189 int parseFontCheckForString( const sal_Unicode* pCopy, const char* str, sal_Int32& nLen, 190 FontAttributes& aResult, bool bItalic, bool bBold); 191 int parseFontRemoveSuffix( const sal_Unicode* pCopy, const char* s, sal_Int32& nLen); 192 193 194 public: 195 Parser( const ContentSinkSharedPtr& rSink, 196 oslFileHandle pErr, 197 const uno::Reference<uno::XComponentContext>& xContext ) : 198 m_xContext(xContext), 199 m_pSink(rSink), 200 m_pErr(pErr), 201 m_aLine(), 202 m_aFontMap(101), 203 m_nNextToken(-1), 204 m_nCharIndex(-1), 205 minAreaThreshold( 300.0 ), 206 minLineWidth( 12 ) 207 {} 208 209 void parseLine( const ::rtl::OString& rLine ); 210 }; 211 212 213 namespace 214 { 215 216 /** Unescapes line-ending characters in input string. These 217 characters are encoded as pairs of characters: '\\' 'n', resp. 218 '\\' 'r'. This function converts them back to '\n', resp. '\r'. 219 */ 220 rtl::OString lcl_unescapeLineFeeds(const rtl::OString& i_rStr) 221 { 222 const size_t nOrigLen(sal::static_int_cast<size_t>(i_rStr.getLength())); 223 const sal_Char* const pOrig(i_rStr.getStr()); 224 sal_Char* const pBuffer(new sal_Char[nOrigLen + 1]); 225 226 const sal_Char* pRead(pOrig); 227 sal_Char* pWrite(pBuffer); 228 const sal_Char* pCur(pOrig); 229 while ((pCur = strchr(pCur, '\\')) != 0) 230 { 231 const sal_Char cNext(pCur[1]); 232 if (cNext == 'n' || cNext == 'r' || cNext == '\\') 233 { 234 const size_t nLen(pCur - pRead); 235 strncpy(pWrite, pRead, nLen); 236 pWrite += nLen; 237 *pWrite = cNext == 'n' ? '\n' : (cNext == 'r' ? '\r' : '\\'); 238 ++pWrite; 239 pCur = pRead = pCur + 2; 240 } 241 else 242 { 243 // Just continue on the next character. The current 244 // block will be copied the next time it goes through the 245 // 'if' branch. 246 ++pCur; 247 } 248 } 249 // maybe there are some data to copy yet 250 if (sal::static_int_cast<size_t>(pRead - pOrig) < nOrigLen) 251 { 252 const size_t nLen(nOrigLen - (pRead - pOrig)); 253 strncpy(pWrite, pRead, nLen); 254 pWrite += nLen; 255 } 256 *pWrite = '\0'; 257 258 rtl::OString aResult(pBuffer); 259 delete[] pBuffer; 260 return aResult; 261 } 262 263 } 264 265 266 ::rtl::OString Parser::readNextToken() 267 { 268 OSL_PRECOND(m_nCharIndex!=-1,"insufficient input"); 269 return m_aLine.getToken(m_nNextToken,' ',m_nCharIndex); 270 } 271 272 void Parser::readInt32( sal_Int32& o_Value ) 273 { 274 o_Value = readNextToken().toInt32(); 275 } 276 277 sal_Int32 Parser::readInt32() 278 { 279 return readNextToken().toInt32(); 280 } 281 282 void Parser::readInt64( sal_Int64& o_Value ) 283 { 284 o_Value = readNextToken().toInt64(); 285 } 286 287 void Parser::readDouble( double& o_Value ) 288 { 289 o_Value = readNextToken().toDouble(); 290 } 291 292 double Parser::readDouble() 293 { 294 return readNextToken().toDouble(); 295 } 296 297 void Parser::readBinaryData( uno::Sequence<sal_Int8>& rBuf ) 298 { 299 sal_Int32 nFileLen( rBuf.getLength() ); 300 sal_Int8* pBuf( rBuf.getArray() ); 301 sal_uInt64 nBytesRead(0); 302 oslFileError nRes=osl_File_E_None; 303 while( nFileLen && 304 osl_File_E_None == (nRes=osl_readFile( m_pErr, pBuf, nFileLen, &nBytesRead )) ) 305 { 306 pBuf += nBytesRead; 307 nFileLen -= sal::static_int_cast<sal_Int32>(nBytesRead); 308 } 309 310 OSL_PRECOND(nRes==osl_File_E_None, "inconsistent data"); 311 } 312 313 uno::Reference<rendering::XPolyPolygon2D> Parser::readPath( double* pArea = NULL ) 314 { 315 const rtl::OString aSubPathMarker( "subpath" ); 316 317 if( 0 != readNextToken().compareTo( aSubPathMarker ) ) 318 OSL_PRECOND(false, "broken path"); 319 320 basegfx::B2DPolyPolygon aResult; 321 while( m_nCharIndex != -1 ) 322 { 323 basegfx::B2DPolygon aSubPath; 324 325 sal_Int32 nClosedFlag; 326 readInt32( nClosedFlag ); 327 aSubPath.setClosed( nClosedFlag != 0 ); 328 329 sal_Int32 nContiguousControlPoints(0); 330 sal_Int32 nDummy=m_nCharIndex; 331 rtl::OString aCurrToken( m_aLine.getToken(m_nNextToken,' ',nDummy) ); 332 333 while( m_nCharIndex != -1 && 0 != aCurrToken.compareTo(aSubPathMarker) ) 334 { 335 sal_Int32 nCurveFlag; 336 double nX, nY; 337 readDouble( nX ); 338 readDouble( nY ); 339 readInt32( nCurveFlag ); 340 341 aSubPath.append(basegfx::B2DPoint(nX,nY)); 342 if( nCurveFlag ) 343 { 344 ++nContiguousControlPoints; 345 } 346 else if( nContiguousControlPoints ) 347 { 348 OSL_PRECOND(nContiguousControlPoints==2,"broken bezier path"); 349 350 // have two control points before us. the current one 351 // is a normal point - thus, convert previous points 352 // into bezier segment 353 const sal_uInt32 nPoints( aSubPath.count() ); 354 const basegfx::B2DPoint aCtrlA( aSubPath.getB2DPoint(nPoints-3) ); 355 const basegfx::B2DPoint aCtrlB( aSubPath.getB2DPoint(nPoints-2) ); 356 const basegfx::B2DPoint aEnd( aSubPath.getB2DPoint(nPoints-1) ); 357 aSubPath.remove(nPoints-3, 3); 358 aSubPath.appendBezierSegment(aCtrlA, aCtrlB, aEnd); 359 360 nContiguousControlPoints=0; 361 } 362 363 // one token look-ahead (new subpath or more points? 364 nDummy=m_nCharIndex; 365 aCurrToken = m_aLine.getToken(m_nNextToken,' ',nDummy); 366 } 367 368 aResult.append( aSubPath ); 369 if( m_nCharIndex != -1 ) 370 readNextToken(); 371 } 372 373 if( pArea ) 374 { 375 basegfx::B2DRange aRange( aResult.getB2DRange() ); 376 if( aRange.getWidth() <= minLineWidth || aRange.getHeight() <= minLineWidth) 377 *pArea = 0.0; 378 else 379 *pArea = aRange.getWidth() * aRange.getHeight(); 380 } 381 382 return static_cast<rendering::XLinePolyPolygon2D*>( 383 new basegfx::unotools::UnoPolyPolygon(aResult)); 384 } 385 386 void Parser::readChar() 387 { 388 geometry::Matrix2D aUnoMatrix; 389 geometry::RealRectangle2D aRect; 390 391 readDouble(aRect.X1); 392 readDouble(aRect.Y1); 393 readDouble(aRect.X2); 394 readDouble(aRect.Y2); 395 readDouble(aUnoMatrix.m00); 396 readDouble(aUnoMatrix.m01); 397 readDouble(aUnoMatrix.m10); 398 readDouble(aUnoMatrix.m11); 399 400 rtl::OString aChars = lcl_unescapeLineFeeds( m_aLine.copy( m_nCharIndex ) ); 401 402 // chars gobble up rest of line 403 m_nCharIndex = -1; 404 405 m_pSink->drawGlyphs( rtl::OStringToOUString( aChars, 406 RTL_TEXTENCODING_UTF8 ), 407 aRect, aUnoMatrix ); 408 } 409 410 void Parser::readLineCap() 411 { 412 sal_Int8 nCap(rendering::PathCapType::BUTT); 413 switch( readInt32() ) 414 { 415 default: 416 // FALLTHROUGH intended 417 case 0: nCap = rendering::PathCapType::BUTT; break; 418 case 1: nCap = rendering::PathCapType::ROUND; break; 419 case 2: nCap = rendering::PathCapType::SQUARE; break; 420 } 421 m_pSink->setLineCap(nCap); 422 } 423 424 void Parser::readLineDash() 425 { 426 if( m_nCharIndex == -1 ) 427 { 428 m_pSink->setLineDash( uno::Sequence<double>(), 0.0 ); 429 return; 430 } 431 432 const double nOffset(readDouble()); 433 const sal_Int32 nLen(readInt32()); 434 435 uno::Sequence<double> aDashArray(nLen); 436 double* pArray=aDashArray.getArray(); 437 for( sal_Int32 i=0; i<nLen; ++i ) 438 *pArray++ = readDouble(); 439 440 m_pSink->setLineDash( aDashArray, nOffset ); 441 } 442 443 void Parser::readLineJoin() 444 { 445 sal_Int8 nJoin(rendering::PathJoinType::MITER); 446 switch( readInt32() ) 447 { 448 default: 449 // FALLTHROUGH intended 450 case 0: nJoin = rendering::PathJoinType::MITER; break; 451 case 1: nJoin = rendering::PathJoinType::ROUND; break; 452 case 2: nJoin = rendering::PathJoinType::BEVEL; break; 453 } 454 m_pSink->setLineJoin(nJoin); 455 } 456 457 void Parser::readTransformation() 458 { 459 geometry::AffineMatrix2D aMat; 460 readDouble(aMat.m00); 461 readDouble(aMat.m10); 462 readDouble(aMat.m01); 463 readDouble(aMat.m11); 464 readDouble(aMat.m02); 465 readDouble(aMat.m12); 466 m_pSink->setTransformation( aMat ); 467 } 468 469 rendering::ARGBColor Parser::readColor() 470 { 471 rendering::ARGBColor aRes; 472 readDouble(aRes.Red); 473 readDouble(aRes.Green); 474 readDouble(aRes.Blue); 475 readDouble(aRes.Alpha); 476 return aRes; 477 } 478 479 int Parser::parseFontCheckForString( const sal_Unicode* pCopy, const char* s, sal_Int32& nLen, 480 FontAttributes& aResult, bool bItalic, bool bBold) 481 { 482 int l = strlen(s); 483 if (nLen < l) 484 return 0; 485 for (int i = 0; i < l; i++) 486 if (tolower(pCopy[i]) != s[i] 487 && toupper(pCopy[i]) != s[i]) 488 return 0; 489 aResult.isItalic = bItalic; 490 aResult.isBold = bBold; 491 nLen -= l; 492 pCopy += l; 493 return l; 494 } 495 496 int Parser::parseFontRemoveSuffix( const sal_Unicode* pCopy, const char* s, sal_Int32& nLen) 497 { 498 int l = strlen(s); 499 if (nLen < l) 500 return 0; 501 for (int i = 0; i < l; i++) 502 if ( pCopy[nLen - l + i] != s[i] ) 503 return 0; 504 nLen -= l; 505 return l; 506 } 507 508 void Parser::parseFontFamilyName( FontAttributes& aResult ) 509 { 510 rtl::OUStringBuffer aNewFamilyName( aResult.familyName.getLength() ); 511 512 const sal_Unicode* pCopy = aResult.familyName.getStr(); 513 sal_Int32 nLen = aResult.familyName.getLength(); 514 // parse out truetype subsets (e.g. BAAAAA+Thorndale) 515 if( nLen > 8 && pCopy[6] == sal_Unicode('+') ) 516 { 517 pCopy += 7; 518 nLen -= 7; 519 } 520 521 while( nLen ) 522 { 523 if (parseFontRemoveSuffix( pCopy, "PSMT", nLen)) {} 524 else if (parseFontRemoveSuffix( pCopy, "MT", nLen)) {} 525 526 if (parseFontCheckForString( pCopy, "Italic", nLen, aResult, true, false)) {} 527 else if (parseFontCheckForString( pCopy, "-Bold", nLen, aResult, false, true)) {} 528 else if (parseFontCheckForString( pCopy, "Bold", nLen, aResult, false, true)) {} 529 else if (parseFontCheckForString( pCopy, "-Roman", nLen, aResult, false, false)) {} 530 else if (parseFontCheckForString( pCopy, "-LightOblique", nLen, aResult, true, false)) {} 531 else if (parseFontCheckForString( pCopy, "-BoldOblique", nLen, aResult, true, true)) {} 532 else if (parseFontCheckForString( pCopy, "-Light", nLen, aResult, false, false)) {} 533 else if (parseFontCheckForString( pCopy, "-Reg", nLen, aResult, false, false)) {} 534 else 535 { 536 if( *pCopy != '-' ) 537 aNewFamilyName.append( *pCopy ); 538 pCopy++; 539 nLen--; 540 } 541 } 542 aResult.familyName = aNewFamilyName.makeStringAndClear(); 543 } 544 545 void Parser::readFont() 546 { 547 ::rtl::OString aFontName; 548 sal_Int64 nFontID; 549 sal_Int32 nIsEmbedded, nIsBold, nIsItalic, nIsUnderline, nFileLen; 550 double nSize; 551 552 readInt64(nFontID); 553 readInt32(nIsEmbedded); 554 readInt32(nIsBold); 555 readInt32(nIsItalic); 556 readInt32(nIsUnderline); 557 readDouble(nSize); 558 readInt32(nFileLen); 559 560 nSize = nSize < 0.0 ? -nSize : nSize; 561 aFontName = lcl_unescapeLineFeeds( m_aLine.copy( m_nCharIndex ) ); 562 563 // name gobbles up rest of line 564 m_nCharIndex = -1; 565 566 FontMapType::const_iterator pFont( m_aFontMap.find(nFontID) ); 567 if( pFont != m_aFontMap.end() ) 568 { 569 OSL_PRECOND(nFileLen==0,"font data for known font"); 570 FontAttributes aRes(pFont->second); 571 aRes.size = nSize; 572 m_pSink->setFont( aRes ); 573 574 return; 575 } 576 577 // yet unknown font - get info and add to map 578 FontAttributes aResult( rtl::OStringToOUString( aFontName, 579 RTL_TEXTENCODING_UTF8 ), 580 nIsBold != 0, 581 nIsItalic != 0, 582 nIsUnderline != 0, 583 false, 584 nSize ); 585 586 // extract textual attributes (bold, italic in the name, etc.) 587 parseFontFamilyName(aResult); 588 // need to read font file? 589 if( nFileLen ) 590 { 591 uno::Sequence<sal_Int8> aFontFile(nFileLen); 592 readBinaryData( aFontFile ); 593 594 awt::FontDescriptor aFD; 595 uno::Sequence< uno::Any > aArgs(1); 596 aArgs[0] <<= aFontFile; 597 598 try 599 { 600 uno::Reference< beans::XMaterialHolder > xMat( 601 m_xContext->getServiceManager()->createInstanceWithArgumentsAndContext( 602 rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.awt.FontIdentificator" ) ), 603 aArgs, 604 m_xContext ), 605 uno::UNO_QUERY ); 606 if( xMat.is() ) 607 { 608 uno::Any aRes( xMat->getMaterial() ); 609 if( aRes >>= aFD ) 610 { 611 aResult.familyName = aFD.Name; 612 parseFontFamilyName(aResult); 613 aResult.isBold = (aFD.Weight > 100.0); 614 aResult.isItalic = (aFD.Slant == awt::FontSlant_OBLIQUE || 615 aFD.Slant == awt::FontSlant_ITALIC ); 616 aResult.isUnderline = false; 617 aResult.size = 0; 618 } 619 } 620 } 621 catch( uno::Exception& ) 622 { 623 } 624 625 if( !aResult.familyName.getLength() ) 626 { 627 // last fallback 628 aResult.familyName = ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "Arial" ) ); 629 aResult.isUnderline = false; 630 } 631 632 } 633 m_aFontMap[nFontID] = aResult; 634 635 aResult.size = nSize; 636 m_pSink->setFont(aResult); 637 } 638 639 uno::Sequence<beans::PropertyValue> Parser::readImageImpl() 640 { 641 static const rtl::OString aJpegMarker( "JPEG" ); 642 static const rtl::OString aPbmMarker( "PBM" ); 643 static const rtl::OString aPpmMarker( "PPM" ); 644 static const rtl::OString aPngMarker( "PNG" ); 645 static const rtl::OUString aJpegFile( 646 RTL_CONSTASCII_USTRINGPARAM( "DUMMY.JPEG" )); 647 static const rtl::OUString aPbmFile( 648 RTL_CONSTASCII_USTRINGPARAM( "DUMMY.PBM" )); 649 static const rtl::OUString aPpmFile( 650 RTL_CONSTASCII_USTRINGPARAM( "DUMMY.PPM" )); 651 static const rtl::OUString aPngFile( 652 RTL_CONSTASCII_USTRINGPARAM( "DUMMY.PNG" )); 653 654 rtl::OString aToken = readNextToken(); 655 const sal_Int32 nImageSize( readInt32() ); 656 657 rtl::OUString aFileName; 658 if( aToken.compareTo( aPngMarker ) == 0 ) 659 aFileName = aPngFile; 660 else if( aToken.compareTo( aJpegMarker ) == 0 ) 661 aFileName = aJpegFile; 662 else if( aToken.compareTo( aPbmMarker ) == 0 ) 663 aFileName = aPbmFile; 664 else 665 { 666 OSL_PRECOND( aToken.compareTo( aPpmMarker ) == 0, 667 "Invalid bitmap format" ); 668 aFileName = aPpmFile; 669 } 670 671 uno::Sequence<sal_Int8> aDataSequence(nImageSize); 672 readBinaryData( aDataSequence ); 673 674 uno::Sequence< uno::Any > aStreamCreationArgs(1); 675 aStreamCreationArgs[0] <<= aDataSequence; 676 677 uno::Reference< uno::XComponentContext > xContext( m_xContext, uno::UNO_SET_THROW ); 678 uno::Reference< lang::XMultiComponentFactory > xFactory( xContext->getServiceManager(), uno::UNO_SET_THROW ); 679 uno::Reference< io::XInputStream > xDataStream( xFactory->createInstanceWithArgumentsAndContext( 680 ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.io.SequenceInputStream" ) ), 681 aStreamCreationArgs, m_xContext ), uno::UNO_QUERY_THROW ); 682 683 uno::Sequence<beans::PropertyValue> aSequence(3); 684 aSequence[0] = beans::PropertyValue( ::rtl::OUString::createFromAscii("URL"), 685 0, 686 uno::makeAny(aFileName), 687 beans::PropertyState_DIRECT_VALUE ); 688 aSequence[1] = beans::PropertyValue( ::rtl::OUString::createFromAscii("InputStream"), 689 0, 690 uno::makeAny( xDataStream ), 691 beans::PropertyState_DIRECT_VALUE ); 692 aSequence[2] = beans::PropertyValue( ::rtl::OUString::createFromAscii("InputSequence"), 693 0, 694 uno::makeAny(aDataSequence), 695 beans::PropertyState_DIRECT_VALUE ); 696 697 return aSequence; 698 } 699 700 void Parser::readImage() 701 { 702 sal_Int32 nWidth, nHeight,nMaskColors; 703 readInt32(nWidth); 704 readInt32(nHeight); 705 readInt32(nMaskColors); 706 707 uno::Sequence<beans::PropertyValue> aImg( readImageImpl() ); 708 709 if( nMaskColors ) 710 { 711 uno::Sequence<sal_Int8> aDataSequence(nMaskColors); 712 readBinaryData( aDataSequence ); 713 714 uno::Sequence<uno::Any> aMaskRanges(2); 715 716 uno::Sequence<double> aMinRange(nMaskColors/2); 717 uno::Sequence<double> aMaxRange(nMaskColors/2); 718 for( sal_Int32 i=0; i<nMaskColors/2; ++i ) 719 { 720 aMinRange[i] = aDataSequence[i] / 255.0; 721 aMaxRange[i] = aDataSequence[i+nMaskColors/2] / 255.0; 722 } 723 724 aMaskRanges[0] = uno::makeAny(aMinRange); 725 aMaskRanges[1] = uno::makeAny(aMaxRange); 726 727 m_pSink->drawColorMaskedImage( aImg, aMaskRanges ); 728 } 729 else 730 m_pSink->drawImage( aImg ); 731 } 732 733 void Parser::readMask() 734 { 735 sal_Int32 nWidth, nHeight, nInvert; 736 readInt32(nWidth); 737 readInt32(nHeight); 738 readInt32(nInvert); 739 740 m_pSink->drawMask( readImageImpl(), nInvert ); 741 } 742 743 void Parser::readLink() 744 { 745 geometry::RealRectangle2D aBounds; 746 readDouble(aBounds.X1); 747 readDouble(aBounds.Y1); 748 readDouble(aBounds.X2); 749 readDouble(aBounds.Y2); 750 751 m_pSink->hyperLink( aBounds, 752 rtl::OStringToOUString( lcl_unescapeLineFeeds( 753 m_aLine.copy(m_nCharIndex) ), 754 RTL_TEXTENCODING_UTF8 ) ); 755 // name gobbles up rest of line 756 m_nCharIndex = -1; 757 } 758 759 void Parser::readMaskedImage() 760 { 761 sal_Int32 nWidth, nHeight, nMaskWidth, nMaskHeight, nMaskInvert; 762 readInt32(nWidth); 763 readInt32(nHeight); 764 readInt32(nMaskWidth); 765 readInt32(nMaskHeight); 766 readInt32(nMaskInvert); 767 768 const uno::Sequence<beans::PropertyValue> aImage( readImageImpl() ); 769 const uno::Sequence<beans::PropertyValue> aMask ( readImageImpl() ); 770 m_pSink->drawMaskedImage( aImage, aMask, nMaskInvert != 0 ); 771 } 772 773 void Parser::readSoftMaskedImage() 774 { 775 sal_Int32 nWidth, nHeight, nMaskWidth, nMaskHeight; 776 readInt32(nWidth); 777 readInt32(nHeight); 778 readInt32(nMaskWidth); 779 readInt32(nMaskHeight); 780 781 const uno::Sequence<beans::PropertyValue> aImage( readImageImpl() ); 782 const uno::Sequence<beans::PropertyValue> aMask ( readImageImpl() ); 783 m_pSink->drawAlphaMaskedImage( aImage, aMask ); 784 } 785 786 void Parser::parseLine( const ::rtl::OString& rLine ) 787 { 788 OSL_PRECOND( m_pSink, "Invalid sink" ); 789 OSL_PRECOND( m_pErr, "Invalid filehandle" ); 790 OSL_PRECOND( m_xContext.is(), "Invalid service factory" ); 791 792 m_nNextToken = 0; m_nCharIndex = 0; m_aLine = rLine; 793 uno::Reference<rendering::XPolyPolygon2D> xPoly; 794 const ::rtl::OString& rCmd = readNextToken(); 795 const hash_entry* pEntry = PdfKeywordHash::in_word_set( rCmd.getStr(), 796 rCmd.getLength() ); 797 OSL_ASSERT(pEntry); 798 switch( pEntry->eKey ) 799 { 800 case CLIPPATH: 801 m_pSink->intersectClip(readPath()); break; 802 case DRAWCHAR: 803 readChar(); break; 804 case DRAWIMAGE: 805 readImage(); break; 806 case DRAWLINK: 807 readLink(); break; 808 case DRAWMASK: 809 readMask(); break; 810 case DRAWMASKEDIMAGE: 811 readMaskedImage(); break; 812 case DRAWSOFTMASKEDIMAGE: 813 readSoftMaskedImage(); break; 814 case ENDPAGE: 815 m_pSink->endPage(); break; 816 case ENDTEXTOBJECT: 817 m_pSink->endText(); break; 818 case EOCLIPPATH: 819 m_pSink->intersectEoClip(readPath()); break; 820 case EOFILLPATH: 821 { 822 double area = 0.0; 823 uno::Reference<rendering::XPolyPolygon2D> path = readPath( &area ); 824 m_pSink->eoFillPath(path); 825 // if area is smaller than required, add borders. 826 if(area < minAreaThreshold) 827 m_pSink->strokePath(path); 828 } 829 break; 830 case FILLPATH: 831 { 832 double area = 0.0; 833 uno::Reference<rendering::XPolyPolygon2D> path = readPath( &area ); 834 m_pSink->fillPath(path); 835 // if area is smaller than required, add borders. 836 if(area < minAreaThreshold) 837 m_pSink->strokePath(path); 838 } 839 break; 840 case RESTORESTATE: 841 m_pSink->popState(); break; 842 case SAVESTATE: 843 m_pSink->pushState(); break; 844 case SETPAGENUM: 845 m_pSink->setPageNum( readInt32() ); break; 846 case STARTPAGE: 847 { 848 const double nWidth ( readDouble() ); 849 const double nHeight( readDouble() ); 850 m_pSink->startPage( geometry::RealSize2D( nWidth, nHeight ) ); 851 break; 852 } 853 case STROKEPATH: 854 m_pSink->strokePath(readPath()); break; 855 case UPDATECTM: 856 readTransformation(); break; 857 case UPDATEFILLCOLOR: 858 m_pSink->setFillColor( readColor() ); break; 859 case UPDATEFLATNESS: 860 m_pSink->setFlatness( readDouble( ) ); break; 861 case UPDATEFONT: 862 readFont(); break; 863 case UPDATELINECAP: 864 readLineCap(); break; 865 case UPDATELINEDASH: 866 readLineDash(); break; 867 case UPDATELINEJOIN: 868 readLineJoin(); break; 869 case UPDATELINEWIDTH: 870 m_pSink->setLineWidth( readDouble() );break; 871 case UPDATEMITERLIMIT: 872 m_pSink->setMiterLimit( readDouble() ); break; 873 case UPDATESTROKECOLOR: 874 m_pSink->setStrokeColor( readColor() ); break; 875 case UPDATESTROKEOPACITY: 876 break; 877 case SETTEXTRENDERMODE: 878 m_pSink->setTextRenderMode( readInt32() ); break; 879 880 case NONE: 881 default: 882 OSL_PRECOND(false,"Unknown input"); 883 break; 884 } 885 886 // all consumed? 887 OSL_POSTCOND(m_nCharIndex==-1,"leftover scanner input"); 888 } 889 890 oslFileError readLine( oslFileHandle pFile, ::rtl::OStringBuffer& line ) 891 { 892 OSL_PRECOND( line.getLength() == 0, "line buf not empty" ); 893 894 // TODO(P3): read larger chunks 895 sal_Char aChar('\n'); 896 sal_uInt64 nBytesRead; 897 oslFileError nRes; 898 899 // skip garbage \r \n at start of line 900 while( osl_File_E_None == (nRes=osl_readFile(pFile, &aChar, 1, &nBytesRead)) && 901 nBytesRead == 1 && 902 (aChar == '\n' || aChar == '\r') ) ; 903 904 if( aChar != '\n' && aChar != '\r' ) 905 line.append( aChar ); 906 907 while( osl_File_E_None == (nRes=osl_readFile(pFile, &aChar, 1, &nBytesRead)) && 908 nBytesRead == 1 && aChar != '\n' && aChar != '\r' ) 909 { 910 line.append( aChar ); 911 } 912 913 return nRes; 914 } 915 916 } // namespace 917 918 static bool checkEncryption( const rtl::OUString& i_rPath, 919 const uno::Reference< task::XInteractionHandler >& i_xIHdl, 920 rtl::OUString& io_rPwd, 921 bool& o_rIsEncrypted, 922 const rtl::OUString& i_rDocName 923 ) 924 { 925 bool bSuccess = false; 926 rtl::OString aPDFFile; 927 aPDFFile = rtl::OUStringToOString( i_rPath, osl_getThreadTextEncoding() ); 928 929 pdfparse::PDFReader aParser; 930 boost::scoped_ptr<pdfparse::PDFEntry> pEntry( aParser.read( aPDFFile.getStr() )); 931 if( pEntry ) 932 { 933 pdfparse::PDFFile* pPDFFile = dynamic_cast<pdfparse::PDFFile*>(pEntry.get()); 934 if( pPDFFile ) 935 { 936 o_rIsEncrypted = pPDFFile->isEncrypted(); 937 if( o_rIsEncrypted ) 938 { 939 bool bAuthenticated = false; 940 if( io_rPwd.getLength() ) 941 { 942 rtl::OString aIsoPwd = rtl::OUStringToOString( io_rPwd, 943 RTL_TEXTENCODING_ISO_8859_1 ); 944 bAuthenticated = pPDFFile->setupDecryptionData( aIsoPwd.getStr() ); 945 // trash password string on heap 946 rtl_zeroMemory( (void*)aIsoPwd.getStr(), aIsoPwd.getLength() ); 947 } 948 if( bAuthenticated ) 949 bSuccess = true; 950 else 951 { 952 if( i_xIHdl.is() ) 953 { 954 bool bEntered = false; 955 do 956 { 957 bEntered = getPassword( i_xIHdl, io_rPwd, ! bEntered, i_rDocName ); 958 rtl::OString aIsoPwd = rtl::OUStringToOString( io_rPwd, 959 RTL_TEXTENCODING_ISO_8859_1 ); 960 bAuthenticated = pPDFFile->setupDecryptionData( aIsoPwd.getStr() ); 961 // trash password string on heap 962 rtl_zeroMemory( (void*)aIsoPwd.getStr(), aIsoPwd.getLength() ); 963 } while( bEntered && ! bAuthenticated ); 964 } 965 966 OSL_TRACE( "password: %s\n", bAuthenticated ? "matches" : "does not match" ); 967 bSuccess = bAuthenticated; 968 } 969 // trash password string on heap 970 rtl_zeroMemory( (void*)io_rPwd.getStr(), io_rPwd.getLength()*sizeof(sal_Unicode) ); 971 if( bAuthenticated ) 972 { 973 rtl::OUStringBuffer aBuf( 128 ); 974 aBuf.appendAscii( "_OOO_pdfi_Credentials_" ); 975 aBuf.append( pPDFFile->getDecryptionKey() ); 976 io_rPwd = aBuf.makeStringAndClear(); 977 } 978 } 979 else 980 bSuccess = true; 981 } 982 } 983 return bSuccess; 984 } 985 986 bool xpdf_ImportFromFile( const ::rtl::OUString& rURL, 987 const ContentSinkSharedPtr& rSink, 988 const uno::Reference< task::XInteractionHandler >& xIHdl, 989 const rtl::OUString& rPwd, 990 const uno::Reference< uno::XComponentContext >& xContext ) 991 { 992 OSL_ASSERT(rSink); 993 994 ::rtl::OUString aSysUPath; 995 if( osl_getSystemPathFromFileURL( rURL.pData, &aSysUPath.pData ) != osl_File_E_None ) 996 return false; 997 rtl::OUString aDocName( rURL.copy( rURL.lastIndexOf( sal_Unicode('/') )+1 ) ); 998 999 // check for encryption, if necessary get password 1000 rtl::OUString aPwd( rPwd ); 1001 bool bIsEncrypted = false; 1002 if( checkEncryption( aSysUPath, xIHdl, aPwd, bIsEncrypted, aDocName ) == false ) 1003 return false; 1004 1005 rtl::OUStringBuffer converterURL = rtl::OUString::createFromAscii("xpdfimport"); 1006 1007 // retrieve package location url (xpdfimport executable is located there) 1008 // --------------------------------------------------- 1009 uno::Reference<deployment::XPackageInformationProvider> xProvider( 1010 xContext->getValueByName( 1011 rtl::OUString::createFromAscii("/singletons/com.sun.star.deployment.PackageInformationProvider" )), 1012 uno::UNO_QUERY); 1013 if( xProvider.is() ) 1014 { 1015 converterURL.insert( 1016 0, 1017 rtl::OUString::createFromAscii("/")); 1018 converterURL.insert( 1019 0, 1020 xProvider->getPackageLocation( 1021 rtl::OUString::createFromAscii( 1022 BOOST_PP_STRINGIZE(PDFI_IMPL_IDENTIFIER)))); 1023 } 1024 1025 // spawn separate process to keep LGPL/GPL code apart. 1026 // --------------------------------------------------- 1027 rtl_uString** ppEnv = NULL; 1028 sal_uInt32 nEnv = 0; 1029 1030 #if defined UNX && ! defined MACOSX 1031 rtl::OUString aStr( RTL_CONSTASCII_USTRINGPARAM( "$URE_LIB_DIR" ) ); 1032 rtl_bootstrap_expandMacros( &aStr.pData ); 1033 rtl::OUString aSysPath; 1034 osl_getSystemPathFromFileURL( aStr.pData, &aSysPath.pData ); 1035 rtl::OUStringBuffer aEnvBuf( aStr.getLength() + 20 ); 1036 aEnvBuf.appendAscii( "LD_LIBRARY_PATH=" ); 1037 aEnvBuf.append( aSysPath ); 1038 aStr = aEnvBuf.makeStringAndClear(); 1039 ppEnv = &aStr.pData; 1040 nEnv = 1; 1041 #endif 1042 1043 rtl_uString* args[] = { aSysUPath.pData }; 1044 sal_Int32 nArgs = 1; 1045 1046 oslProcess aProcess; 1047 oslFileHandle pIn = NULL; 1048 oslFileHandle pOut = NULL; 1049 oslFileHandle pErr = NULL; 1050 const oslProcessError eErr = 1051 osl_executeProcess_WithRedirectedIO(converterURL.makeStringAndClear().pData, 1052 args, 1053 nArgs, 1054 osl_Process_SEARCHPATH|osl_Process_HIDDEN, 1055 osl_getCurrentSecurity(), 1056 0, ppEnv, nEnv, 1057 &aProcess, &pIn, &pOut, &pErr); 1058 1059 bool bRet=true; 1060 try 1061 { 1062 if( eErr!=osl_Process_E_None ) 1063 return false; 1064 1065 if( pIn ) 1066 { 1067 rtl::OStringBuffer aBuf(256); 1068 if( bIsEncrypted ) 1069 aBuf.append( rtl::OUStringToOString( aPwd, RTL_TEXTENCODING_ISO_8859_1 ) ); 1070 aBuf.append( '\n' ); 1071 1072 sal_uInt64 nWritten = 0; 1073 osl_writeFile( pIn, aBuf.getStr(), sal_uInt64(aBuf.getLength()), &nWritten ); 1074 } 1075 1076 if( pOut && pErr ) 1077 { 1078 // read results of PDF parser. One line - one call to 1079 // OutputDev. stderr is used for alternate streams, like 1080 // embedded fonts and bitmaps 1081 Parser aParser(rSink,pErr,xContext); 1082 ::rtl::OStringBuffer line; 1083 while( osl_File_E_None == readLine(pOut, line) && line.getLength() ) 1084 aParser.parseLine(line.makeStringAndClear()); 1085 } 1086 } 1087 catch( uno::Exception& ) 1088 { 1089 // crappy C file interface. need manual resource dealloc 1090 bRet = false; 1091 } 1092 1093 if( pIn ) 1094 osl_closeFile(pIn); 1095 if( pOut ) 1096 osl_closeFile(pOut); 1097 if( pErr ) 1098 osl_closeFile(pErr); 1099 osl_freeProcessHandle(aProcess); 1100 return bRet; 1101 } 1102 1103 1104 bool xpdf_ImportFromStream( const uno::Reference< io::XInputStream >& xInput, 1105 const ContentSinkSharedPtr& rSink, 1106 const uno::Reference<task::XInteractionHandler >& xIHdl, 1107 const rtl::OUString& rPwd, 1108 const uno::Reference< uno::XComponentContext >& xContext ) 1109 { 1110 OSL_ASSERT(xInput.is()); 1111 OSL_ASSERT(rSink); 1112 1113 // convert XInputStream to local temp file 1114 oslFileHandle aFile = NULL; 1115 rtl::OUString aURL; 1116 if( osl_createTempFile( NULL, &aFile, &aURL.pData ) != osl_File_E_None ) 1117 return false; 1118 1119 // copy content, buffered... 1120 const sal_uInt32 nBufSize = 4096; 1121 uno::Sequence<sal_Int8> aBuf( nBufSize ); 1122 sal_uInt64 nBytes = 0; 1123 sal_uInt64 nWritten = 0; 1124 bool bSuccess = true; 1125 do 1126 { 1127 try 1128 { 1129 nBytes = xInput->readBytes( aBuf, nBufSize ); 1130 } 1131 catch( com::sun::star::uno::Exception& ) 1132 { 1133 osl_closeFile( aFile ); 1134 throw; 1135 } 1136 if( nBytes > 0 ) 1137 { 1138 osl_writeFile( aFile, aBuf.getConstArray(), nBytes, &nWritten ); 1139 if( nWritten != nBytes ) 1140 { 1141 bSuccess = false; 1142 break; 1143 } 1144 } 1145 } 1146 while( nBytes == nBufSize ); 1147 1148 osl_closeFile( aFile ); 1149 1150 return bSuccess && xpdf_ImportFromFile( aURL, rSink, xIHdl, rPwd, xContext ); 1151 } 1152 1153 } 1154 1155