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