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