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