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:
Parser(const ContentSinkSharedPtr & rSink,oslFileHandle pErr,const uno::Reference<uno::XComponentContext> & xContext)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 */
lcl_unescapeLineFeeds(const rtl::OString & i_rStr)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
readNextToken()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
readInt32(sal_Int32 & o_Value)264 void Parser::readInt32( sal_Int32& o_Value )
265 {
266 o_Value = readNextToken().toInt32();
267 }
268
readInt32()269 sal_Int32 Parser::readInt32()
270 {
271 return readNextToken().toInt32();
272 }
273
readInt64(sal_Int64 & o_Value)274 void Parser::readInt64( sal_Int64& o_Value )
275 {
276 o_Value = readNextToken().toInt64();
277 }
278
readDouble(double & o_Value)279 void Parser::readDouble( double& o_Value )
280 {
281 o_Value = readNextToken().toDouble();
282 }
283
readDouble()284 double Parser::readDouble()
285 {
286 return readNextToken().toDouble();
287 }
288
readBinaryData(uno::Sequence<sal_Int8> & rBuf)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
readPath(double * pArea=NULL)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
readChar()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
readLineCap()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
readLineDash()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
readLineJoin()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
readTransformation()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
readColor()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
parseFontCheckForString(const sal_Unicode * pCopy,const char * s,sal_Int32 & nLen,FontAttributes & aResult,bool bItalic,bool bBold)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
parseFontRemoveSuffix(const sal_Unicode * pCopy,const char * s,sal_Int32 & nLen)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
parseFontFamilyName(FontAttributes & aResult)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
readFont()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
readImageImpl()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
readImage()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
readMask()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
readLink()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
readMaskedImage()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
readSoftMaskedImage()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
parseLine(const::rtl::OString & rLine)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
readLine(oslFileHandle pFile,::rtl::OStringBuffer & line)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
checkEncryption(const rtl::OUString & i_rPath,const uno::Reference<task::XInteractionHandler> & i_xIHdl,rtl::OUString & io_rPwd,bool & o_rIsEncrypted,const rtl::OUString & i_rDocName)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
xpdf_ImportFromFile(const::rtl::OUString & rURL,const ContentSinkSharedPtr & rSink,const uno::Reference<task::XInteractionHandler> & xIHdl,const rtl::OUString & rPwd,const uno::Reference<uno::XComponentContext> & xContext)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
xpdf_ImportFromStream(const uno::Reference<io::XInputStream> & xInput,const ContentSinkSharedPtr & rSink,const uno::Reference<task::XInteractionHandler> & xIHdl,const rtl::OUString & rPwd,const uno::Reference<uno::XComponentContext> & xContext)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
1147