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