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