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