bump product version to 4.1.6.2
[LibreOffice.git] / sdext / source / pdfimport / wrapper / wrapper.cxx
blob35766d4c6044d1e0c9b1137b5ca279cd5435096f
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
21 #include "contentsink.hxx"
22 #include "pdfparse.hxx"
23 #include "pdfihelper.hxx"
25 #include "osl/file.h"
26 #include "osl/file.hxx"
27 #include "osl/thread.h"
28 #include "osl/process.h"
29 #include "osl/diagnose.h"
30 #include "rtl/bootstrap.hxx"
31 #include "rtl/ustring.hxx"
32 #include "rtl/ustrbuf.hxx"
33 #include "rtl/strbuf.hxx"
34 #include "rtl/byteseq.hxx"
36 #include "cppuhelper/exc_hlp.hxx"
37 #include "com/sun/star/io/XInputStream.hpp"
38 #include "com/sun/star/uno/XComponentContext.hpp"
39 #include "com/sun/star/awt/FontDescriptor.hpp"
40 #include "com/sun/star/beans/XMaterialHolder.hpp"
41 #include "com/sun/star/rendering/PathCapType.hpp"
42 #include "com/sun/star/rendering/PathJoinType.hpp"
43 #include "com/sun/star/rendering/XColorSpace.hpp"
44 #include "com/sun/star/rendering/XPolyPolygon2D.hpp"
45 #include "com/sun/star/rendering/XBitmap.hpp"
46 #include "com/sun/star/geometry/Matrix2D.hpp"
47 #include "com/sun/star/geometry/AffineMatrix2D.hpp"
48 #include "com/sun/star/geometry/RealRectangle2D.hpp"
49 #include "com/sun/star/task/XInteractionHandler.hpp"
51 #include "basegfx/point/b2dpoint.hxx"
52 #include "basegfx/polygon/b2dpolypolygon.hxx"
53 #include "basegfx/polygon/b2dpolygon.hxx"
54 #include "basegfx/tools/canvastools.hxx"
55 #include "basegfx/tools/unopolypolygon.hxx"
57 #include <boost/scoped_ptr.hpp>
58 #include <boost/unordered_map.hpp>
59 #include <string.h>
60 #ifdef WNT
61 #include <stdlib.h>
62 #include <ctype.h>
63 #endif
65 #include "rtl/bootstrap.h"
67 #include <string.h> // memcmp
69 using namespace com::sun::star;
71 namespace pdfi
74 namespace
77 // identifier of the strings coming from the out-of-process xpdf
78 // converter
79 enum parseKey {
80 CLIPPATH,
81 DRAWCHAR,
82 DRAWIMAGE,
83 DRAWLINK,
84 DRAWMASK,
85 DRAWMASKEDIMAGE,
86 DRAWSOFTMASKEDIMAGE,
87 ENDPAGE,
88 ENDTEXTOBJECT,
89 EOCLIPPATH,
90 EOFILLPATH,
91 FILLPATH,
92 HYPERLINK,
93 INTERSECTCLIP,
94 INTERSECTEOCLIP,
95 POPSTATE,
96 PUSHSTATE,
97 RESTORESTATE,
98 SAVESTATE,
99 SETBLENDMODE,
100 SETFILLCOLOR,
101 SETFONT,
102 SETLINECAP,
103 SETLINEDASH,
104 SETLINEJOIN,
105 SETLINEWIDTH,
106 SETMITERLIMIT,
107 SETPAGENUM,
108 SETSTROKECOLOR,
109 SETTEXTRENDERMODE,
110 SETTRANSFORMATION,
111 STARTPAGE,
112 STROKEPATH,
113 UPDATEBLENDMODE,
114 UPDATECTM,
115 UPDATEFILLCOLOR,
116 UPDATEFILLOPACITY,
117 UPDATEFLATNESS,
118 UPDATEFONT,
119 UPDATELINECAP,
120 UPDATELINEDASH,
121 UPDATELINEJOIN,
122 UPDATELINEWIDTH,
123 UPDATEMITERLIMIT,
124 UPDATESTROKECOLOR,
125 UPDATESTROKEOPACITY,
126 NONE
129 #include "hash.cxx"
131 class Parser
133 typedef boost::unordered_map< sal_Int64,
134 FontAttributes > FontMapType;
136 const uno::Reference<uno::XComponentContext> m_xContext;
137 const ContentSinkSharedPtr m_pSink;
138 const oslFileHandle m_pErr;
139 OString m_aLine;
140 FontMapType m_aFontMap;
141 sal_Int32 m_nNextToken;
142 sal_Int32 m_nCharIndex;
144 const double minAreaThreshold;
145 const double minLineWidth;
147 OString readNextToken();
148 void readInt32( sal_Int32& o_Value );
149 sal_Int32 readInt32();
150 void readInt64( sal_Int64& o_Value );
151 void readDouble( double& o_Value );
152 double readDouble();
153 void readBinaryData( uno::Sequence<sal_Int8>& rBuf );
155 uno::Reference<rendering::XPolyPolygon2D> readPath( double* );
157 void readChar();
158 void readLineCap();
159 void readLineDash();
160 void readLineJoin();
161 void readTransformation();
162 rendering::ARGBColor readColor();
163 void parseFontFamilyName( FontAttributes& aResult );
164 void readFont();
165 uno::Sequence<beans::PropertyValue> readImageImpl();
167 void readImage();
168 void readMask();
169 void readLink();
170 void readMaskedImage();
171 void readSoftMaskedImage();
172 int parseFontCheckForString( const sal_Unicode* pCopy, const char* str, sal_Int32& nLen,
173 FontAttributes& aResult, bool bItalic, bool bBold);
174 int parseFontRemoveSuffix( const sal_Unicode* pCopy, const char* s, sal_Int32& nLen);
177 public:
178 Parser( const ContentSinkSharedPtr& rSink,
179 oslFileHandle pErr,
180 const uno::Reference<uno::XComponentContext>& xContext ) :
181 m_xContext(xContext),
182 m_pSink(rSink),
183 m_pErr(pErr),
184 m_aLine(),
185 m_aFontMap(101),
186 m_nNextToken(-1),
187 m_nCharIndex(-1),
188 minAreaThreshold( 300.0 ),
189 minLineWidth( 12 )
192 void parseLine( const OString& rLine );
196 namespace
199 /** Unescapes line-ending characters in input string. These
200 characters are encoded as pairs of characters: '\\' 'n', resp.
201 '\\' 'r'. This function converts them back to '\n', resp. '\r'.
203 OString lcl_unescapeLineFeeds(const OString& i_rStr)
205 const size_t nOrigLen(sal::static_int_cast<size_t>(i_rStr.getLength()));
206 const sal_Char* const pOrig(i_rStr.getStr());
207 sal_Char* const pBuffer(new sal_Char[nOrigLen + 1]);
209 const sal_Char* pRead(pOrig);
210 sal_Char* pWrite(pBuffer);
211 const sal_Char* pCur(pOrig);
212 while ((pCur = strchr(pCur, '\\')) != 0)
214 const sal_Char cNext(pCur[1]);
215 if (cNext == 'n' || cNext == 'r' || cNext == '\\')
217 const size_t nLen(pCur - pRead);
218 strncpy(pWrite, pRead, nLen);
219 pWrite += nLen;
220 *pWrite = cNext == 'n' ? '\n' : (cNext == 'r' ? '\r' : '\\');
221 ++pWrite;
222 pCur = pRead = pCur + 2;
224 else
226 // Just continue on the next character. The current
227 // block will be copied the next time it goes through the
228 // 'if' branch.
229 ++pCur;
232 // maybe there are some data to copy yet
233 if (sal::static_int_cast<size_t>(pRead - pOrig) < nOrigLen)
235 const size_t nLen(nOrigLen - (pRead - pOrig));
236 strncpy(pWrite, pRead, nLen);
237 pWrite += nLen;
239 *pWrite = '\0';
241 OString aResult(pBuffer);
242 delete[] pBuffer;
243 return aResult;
249 OString Parser::readNextToken()
251 OSL_PRECOND(m_nCharIndex!=-1,"insufficient input");
252 return m_aLine.getToken(m_nNextToken,' ',m_nCharIndex);
255 void Parser::readInt32( sal_Int32& o_Value )
257 o_Value = readNextToken().toInt32();
260 sal_Int32 Parser::readInt32()
262 return readNextToken().toInt32();
265 void Parser::readInt64( sal_Int64& o_Value )
267 o_Value = readNextToken().toInt64();
270 void Parser::readDouble( double& o_Value )
272 o_Value = readNextToken().toDouble();
275 double Parser::readDouble()
277 return readNextToken().toDouble();
280 void Parser::readBinaryData( uno::Sequence<sal_Int8>& rBuf )
282 sal_Int32 nFileLen( rBuf.getLength() );
283 sal_Int8* pBuf( rBuf.getArray() );
284 sal_uInt64 nBytesRead(0);
285 oslFileError nRes=osl_File_E_None;
286 while( nFileLen &&
287 osl_File_E_None == (nRes=osl_readFile( m_pErr, pBuf, nFileLen, &nBytesRead )) )
289 pBuf += nBytesRead;
290 nFileLen -= sal::static_int_cast<sal_Int32>(nBytesRead);
293 OSL_PRECOND(nRes==osl_File_E_None, "inconsistent data");
296 uno::Reference<rendering::XPolyPolygon2D> Parser::readPath( double* pArea = NULL )
298 const OString aSubPathMarker( "subpath" );
300 if( readNextToken() != aSubPathMarker )
301 OSL_PRECOND(false, "broken path");
303 basegfx::B2DPolyPolygon aResult;
304 while( m_nCharIndex != -1 )
306 basegfx::B2DPolygon aSubPath;
308 sal_Int32 nClosedFlag;
309 readInt32( nClosedFlag );
310 aSubPath.setClosed( nClosedFlag != 0 );
312 sal_Int32 nContiguousControlPoints(0);
313 sal_Int32 nDummy=m_nCharIndex;
314 OString aCurrToken( m_aLine.getToken(m_nNextToken,' ',nDummy) );
316 while( m_nCharIndex != -1 && aCurrToken != aSubPathMarker )
318 sal_Int32 nCurveFlag;
319 double nX, nY;
320 readDouble( nX );
321 readDouble( nY );
322 readInt32( nCurveFlag );
324 aSubPath.append(basegfx::B2DPoint(nX,nY));
325 if( nCurveFlag )
327 ++nContiguousControlPoints;
329 else if( nContiguousControlPoints )
331 OSL_PRECOND(nContiguousControlPoints==2,"broken bezier path");
333 // have two control points before us. the current one
334 // is a normal point - thus, convert previous points
335 // into bezier segment
336 const sal_uInt32 nPoints( aSubPath.count() );
337 const basegfx::B2DPoint aCtrlA( aSubPath.getB2DPoint(nPoints-3) );
338 const basegfx::B2DPoint aCtrlB( aSubPath.getB2DPoint(nPoints-2) );
339 const basegfx::B2DPoint aEnd( aSubPath.getB2DPoint(nPoints-1) );
340 aSubPath.remove(nPoints-3, 3);
341 aSubPath.appendBezierSegment(aCtrlA, aCtrlB, aEnd);
343 nContiguousControlPoints=0;
346 // one token look-ahead (new subpath or more points?
347 nDummy=m_nCharIndex;
348 aCurrToken = m_aLine.getToken(m_nNextToken,' ',nDummy);
351 aResult.append( aSubPath );
352 if( m_nCharIndex != -1 )
353 readNextToken();
356 if( pArea )
358 basegfx::B2DRange aRange( aResult.getB2DRange() );
359 if( aRange.getWidth() <= minLineWidth || aRange.getHeight() <= minLineWidth)
360 *pArea = 0.0;
361 else
362 *pArea = aRange.getWidth() * aRange.getHeight();
365 return static_cast<rendering::XLinePolyPolygon2D*>(
366 new basegfx::unotools::UnoPolyPolygon(aResult));
369 void Parser::readChar()
371 geometry::Matrix2D aUnoMatrix;
372 geometry::RealRectangle2D aRect;
374 readDouble(aRect.X1);
375 readDouble(aRect.Y1);
376 readDouble(aRect.X2);
377 readDouble(aRect.Y2);
378 readDouble(aUnoMatrix.m00);
379 readDouble(aUnoMatrix.m01);
380 readDouble(aUnoMatrix.m10);
381 readDouble(aUnoMatrix.m11);
383 OString aChars = lcl_unescapeLineFeeds( m_aLine.copy( m_nCharIndex ) );
385 // chars gobble up rest of line
386 m_nCharIndex = -1;
388 m_pSink->drawGlyphs( OStringToOUString( aChars,
389 RTL_TEXTENCODING_UTF8 ),
390 aRect, aUnoMatrix );
393 void Parser::readLineCap()
395 sal_Int8 nCap(rendering::PathCapType::BUTT);
396 switch( readInt32() )
398 default:
399 // FALLTHROUGH intended
400 case 0: nCap = rendering::PathCapType::BUTT; break;
401 case 1: nCap = rendering::PathCapType::ROUND; break;
402 case 2: nCap = rendering::PathCapType::SQUARE; break;
404 m_pSink->setLineCap(nCap);
407 void Parser::readLineDash()
409 if( m_nCharIndex == -1 )
411 m_pSink->setLineDash( uno::Sequence<double>(), 0.0 );
412 return;
415 const double nOffset(readDouble());
416 const sal_Int32 nLen(readInt32());
418 uno::Sequence<double> aDashArray(nLen);
419 double* pArray=aDashArray.getArray();
420 for( sal_Int32 i=0; i<nLen; ++i )
421 *pArray++ = readDouble();
423 m_pSink->setLineDash( aDashArray, nOffset );
426 void Parser::readLineJoin()
428 sal_Int8 nJoin(rendering::PathJoinType::MITER);
429 switch( readInt32() )
431 default:
432 // FALLTHROUGH intended
433 case 0: nJoin = rendering::PathJoinType::MITER; break;
434 case 1: nJoin = rendering::PathJoinType::ROUND; break;
435 case 2: nJoin = rendering::PathJoinType::BEVEL; break;
437 m_pSink->setLineJoin(nJoin);
440 void Parser::readTransformation()
442 geometry::AffineMatrix2D aMat;
443 readDouble(aMat.m00);
444 readDouble(aMat.m10);
445 readDouble(aMat.m01);
446 readDouble(aMat.m11);
447 readDouble(aMat.m02);
448 readDouble(aMat.m12);
449 m_pSink->setTransformation( aMat );
452 rendering::ARGBColor Parser::readColor()
454 rendering::ARGBColor aRes;
455 readDouble(aRes.Red);
456 readDouble(aRes.Green);
457 readDouble(aRes.Blue);
458 readDouble(aRes.Alpha);
459 return aRes;
462 int Parser::parseFontCheckForString( const sal_Unicode* pCopy, const char* s, sal_Int32& nLen,
463 FontAttributes& aResult, bool bItalic, bool bBold)
465 int l = strlen(s);
466 if (nLen < l)
467 return 0;
468 for (int i = 0; i < l; i++)
469 if (tolower(pCopy[i]) != s[i]
470 && toupper(pCopy[i]) != s[i])
471 return 0;
472 aResult.isItalic = bItalic;
473 aResult.isBold = bBold;
474 nLen -= l;
475 pCopy += l;
476 return l;
479 int Parser::parseFontRemoveSuffix( const sal_Unicode* pCopy, const char* s, sal_Int32& nLen)
481 int l = strlen(s);
482 if (nLen < l)
483 return 0;
484 for (int i = 0; i < l; i++)
485 if ( pCopy[nLen - l + i] != s[i] )
486 return 0;
487 nLen -= l;
488 return l;
491 void Parser::parseFontFamilyName( FontAttributes& aResult )
493 OUStringBuffer aNewFamilyName( aResult.familyName.getLength() );
495 const sal_Unicode* pCopy = aResult.familyName.getStr();
496 sal_Int32 nLen = aResult.familyName.getLength();
497 // parse out truetype subsets (e.g. BAAAAA+Thorndale)
498 if( nLen > 8 && pCopy[6] == sal_Unicode('+') )
500 pCopy += 7;
501 nLen -= 7;
504 while( nLen )
506 if (parseFontRemoveSuffix( pCopy, "PSMT", nLen)) {}
507 else if (parseFontRemoveSuffix( pCopy, "MT", nLen)) {}
509 if (parseFontCheckForString( pCopy, "Italic", nLen, aResult, true, false)) {}
510 else if (parseFontCheckForString( pCopy, "-Bold", nLen, aResult, false, true)) {}
511 else if (parseFontCheckForString( pCopy, "Bold", nLen, aResult, false, true)) {}
512 else if (parseFontCheckForString( pCopy, "-Roman", nLen, aResult, false, false)) {}
513 else if (parseFontCheckForString( pCopy, "-LightOblique", nLen, aResult, true, false)) {}
514 else if (parseFontCheckForString( pCopy, "-BoldOblique", nLen, aResult, true, true)) {}
515 else if (parseFontCheckForString( pCopy, "-Light", nLen, aResult, false, false)) {}
516 else if (parseFontCheckForString( pCopy, "-Reg", nLen, aResult, false, false)) {}
517 else
519 if( *pCopy != '-' )
520 aNewFamilyName.append( *pCopy );
521 pCopy++;
522 nLen--;
525 aResult.familyName = aNewFamilyName.makeStringAndClear();
528 void Parser::readFont()
530 OString aFontName;
531 sal_Int64 nFontID;
532 sal_Int32 nIsEmbedded, nIsBold, nIsItalic, nIsUnderline, nFileLen;
533 double nSize;
535 readInt64(nFontID);
536 readInt32(nIsEmbedded);
537 readInt32(nIsBold);
538 readInt32(nIsItalic);
539 readInt32(nIsUnderline);
540 readDouble(nSize);
541 readInt32(nFileLen);
543 nSize = nSize < 0.0 ? -nSize : nSize;
544 aFontName = lcl_unescapeLineFeeds( m_aLine.copy( m_nCharIndex ) );
546 // name gobbles up rest of line
547 m_nCharIndex = -1;
549 FontMapType::const_iterator pFont( m_aFontMap.find(nFontID) );
550 if( pFont != m_aFontMap.end() )
552 OSL_PRECOND(nFileLen==0,"font data for known font");
553 FontAttributes aRes(pFont->second);
554 aRes.size = nSize;
555 m_pSink->setFont( aRes );
557 return;
560 // yet unknown font - get info and add to map
561 FontAttributes aResult( OStringToOUString( aFontName,
562 RTL_TEXTENCODING_UTF8 ),
563 nIsBold != 0,
564 nIsItalic != 0,
565 nIsUnderline != 0,
566 false,
567 nSize );
569 // extract textual attributes (bold, italic in the name, etc.)
570 parseFontFamilyName(aResult);
571 // need to read font file?
572 if( nFileLen )
574 uno::Sequence<sal_Int8> aFontFile(nFileLen);
575 readBinaryData( aFontFile );
577 awt::FontDescriptor aFD;
578 uno::Sequence< uno::Any > aArgs(1);
579 aArgs[0] <<= aFontFile;
583 uno::Reference< beans::XMaterialHolder > xMat(
584 m_xContext->getServiceManager()->createInstanceWithArgumentsAndContext(
585 OUString( "com.sun.star.awt.FontIdentificator" ),
586 aArgs,
587 m_xContext ),
588 uno::UNO_QUERY );
589 if( xMat.is() )
591 uno::Any aRes( xMat->getMaterial() );
592 if( aRes >>= aFD )
594 aResult.familyName = aFD.Name;
595 parseFontFamilyName(aResult);
596 aResult.isBold = (aFD.Weight > 100.0);
597 aResult.isItalic = (aFD.Slant == awt::FontSlant_OBLIQUE ||
598 aFD.Slant == awt::FontSlant_ITALIC );
599 aResult.isUnderline = false;
600 aResult.size = 0;
604 catch( uno::Exception& )
608 if( aResult.familyName.isEmpty() )
610 // last fallback
611 aResult.familyName = OUString( "Arial" );
612 aResult.isUnderline = false;
616 m_aFontMap[nFontID] = aResult;
618 aResult.size = nSize;
619 m_pSink->setFont(aResult);
622 uno::Sequence<beans::PropertyValue> Parser::readImageImpl()
624 static const OString aJpegMarker( "JPEG" );
625 static const OString aPbmMarker( "PBM" );
626 static const OString aPpmMarker( "PPM" );
627 static const OString aPngMarker( "PNG" );
628 static const OUString aJpegFile( "DUMMY.JPEG" );
629 static const OUString aPbmFile( "DUMMY.PBM" );
630 static const OUString aPpmFile( "DUMMY.PPM" );
631 static const OUString aPngFile( "DUMMY.PNG" );
633 OString aToken = readNextToken();
634 const sal_Int32 nImageSize( readInt32() );
636 OUString aFileName;
637 if( aToken == aPngMarker )
638 aFileName = aPngFile;
639 else if( aToken == aJpegMarker )
640 aFileName = aJpegFile;
641 else if( aToken == aPbmMarker )
642 aFileName = aPbmFile;
643 else
645 SAL_WARN_IF(aToken != aPpmMarker,"sdext.pdfimport","Invalid bitmap format");
646 aFileName = aPpmFile;
649 uno::Sequence<sal_Int8> aDataSequence(nImageSize);
650 readBinaryData( aDataSequence );
652 uno::Sequence< uno::Any > aStreamCreationArgs(1);
653 aStreamCreationArgs[0] <<= aDataSequence;
655 uno::Reference< uno::XComponentContext > xContext( m_xContext, uno::UNO_SET_THROW );
656 uno::Reference< lang::XMultiComponentFactory > xFactory( xContext->getServiceManager(), uno::UNO_SET_THROW );
657 uno::Reference< io::XInputStream > xDataStream( xFactory->createInstanceWithArgumentsAndContext(
658 OUString( "com.sun.star.io.SequenceInputStream" ),
659 aStreamCreationArgs, m_xContext ), uno::UNO_QUERY_THROW );
661 uno::Sequence<beans::PropertyValue> aSequence(3);
662 aSequence[0] = beans::PropertyValue( OUString("URL"),
664 uno::makeAny(aFileName),
665 beans::PropertyState_DIRECT_VALUE );
666 aSequence[1] = beans::PropertyValue( OUString("InputStream"),
668 uno::makeAny( xDataStream ),
669 beans::PropertyState_DIRECT_VALUE );
670 aSequence[2] = beans::PropertyValue( OUString("InputSequence"),
672 uno::makeAny(aDataSequence),
673 beans::PropertyState_DIRECT_VALUE );
675 return aSequence;
678 void Parser::readImage()
680 sal_Int32 nWidth, nHeight,nMaskColors;
681 readInt32(nWidth);
682 readInt32(nHeight);
683 readInt32(nMaskColors);
685 uno::Sequence<beans::PropertyValue> aImg( readImageImpl() );
687 if( nMaskColors )
689 uno::Sequence<sal_Int8> aDataSequence(nMaskColors);
690 readBinaryData( aDataSequence );
692 uno::Sequence<uno::Any> aMaskRanges(2);
694 uno::Sequence<double> aMinRange(nMaskColors/2);
695 uno::Sequence<double> aMaxRange(nMaskColors/2);
696 for( sal_Int32 i=0; i<nMaskColors/2; ++i )
698 aMinRange[i] = aDataSequence[i] / 255.0;
699 aMaxRange[i] = aDataSequence[i+nMaskColors/2] / 255.0;
702 aMaskRanges[0] = uno::makeAny(aMinRange);
703 aMaskRanges[1] = uno::makeAny(aMaxRange);
705 m_pSink->drawColorMaskedImage( aImg, aMaskRanges );
707 else
708 m_pSink->drawImage( aImg );
711 void Parser::readMask()
713 sal_Int32 nWidth, nHeight, nInvert;
714 readInt32(nWidth);
715 readInt32(nHeight);
716 readInt32(nInvert);
718 m_pSink->drawMask( readImageImpl(), nInvert );
721 void Parser::readLink()
723 geometry::RealRectangle2D aBounds;
724 readDouble(aBounds.X1);
725 readDouble(aBounds.Y1);
726 readDouble(aBounds.X2);
727 readDouble(aBounds.Y2);
729 m_pSink->hyperLink( aBounds,
730 OStringToOUString( lcl_unescapeLineFeeds(
731 m_aLine.copy(m_nCharIndex) ),
732 RTL_TEXTENCODING_UTF8 ) );
733 // name gobbles up rest of line
734 m_nCharIndex = -1;
737 void Parser::readMaskedImage()
739 sal_Int32 nWidth, nHeight, nMaskWidth, nMaskHeight, nMaskInvert;
740 readInt32(nWidth);
741 readInt32(nHeight);
742 readInt32(nMaskWidth);
743 readInt32(nMaskHeight);
744 readInt32(nMaskInvert);
746 const uno::Sequence<beans::PropertyValue> aImage( readImageImpl() );
747 const uno::Sequence<beans::PropertyValue> aMask ( readImageImpl() );
748 m_pSink->drawMaskedImage( aImage, aMask, nMaskInvert != 0 );
751 void Parser::readSoftMaskedImage()
753 sal_Int32 nWidth, nHeight, nMaskWidth, nMaskHeight;
754 readInt32(nWidth);
755 readInt32(nHeight);
756 readInt32(nMaskWidth);
757 readInt32(nMaskHeight);
759 const uno::Sequence<beans::PropertyValue> aImage( readImageImpl() );
760 const uno::Sequence<beans::PropertyValue> aMask ( readImageImpl() );
761 m_pSink->drawAlphaMaskedImage( aImage, aMask );
764 void Parser::parseLine( const OString& rLine )
766 OSL_PRECOND( m_pSink, "Invalid sink" );
767 OSL_PRECOND( m_pErr, "Invalid filehandle" );
768 OSL_PRECOND( m_xContext.is(), "Invalid service factory" );
770 m_nNextToken = 0; m_nCharIndex = 0; m_aLine = rLine;
771 uno::Reference<rendering::XPolyPolygon2D> xPoly;
772 const OString& rCmd = readNextToken();
773 const hash_entry* pEntry = PdfKeywordHash::in_word_set( rCmd.getStr(),
774 rCmd.getLength() );
775 OSL_ASSERT(pEntry);
776 switch( pEntry->eKey )
778 case CLIPPATH:
779 m_pSink->intersectClip(readPath()); break;
780 case DRAWCHAR:
781 readChar(); break;
782 case DRAWIMAGE:
783 readImage(); break;
784 case DRAWLINK:
785 readLink(); break;
786 case DRAWMASK:
787 readMask(); break;
788 case DRAWMASKEDIMAGE:
789 readMaskedImage(); break;
790 case DRAWSOFTMASKEDIMAGE:
791 readSoftMaskedImage(); break;
792 case ENDPAGE:
793 m_pSink->endPage(); break;
794 case ENDTEXTOBJECT:
795 m_pSink->endText(); break;
796 case EOCLIPPATH:
797 m_pSink->intersectEoClip(readPath()); break;
798 case EOFILLPATH:
800 double area = 0.0;
801 uno::Reference<rendering::XPolyPolygon2D> path = readPath( &area );
802 m_pSink->eoFillPath(path);
803 // if area is smaller than required, add borders.
804 if(area < minAreaThreshold)
805 m_pSink->strokePath(path);
807 break;
808 case FILLPATH:
810 double area = 0.0;
811 uno::Reference<rendering::XPolyPolygon2D> path = readPath( &area );
812 m_pSink->fillPath(path);
813 // if area is smaller than required, add borders.
814 if(area < minAreaThreshold)
815 m_pSink->strokePath(path);
817 break;
818 case RESTORESTATE:
819 m_pSink->popState(); break;
820 case SAVESTATE:
821 m_pSink->pushState(); break;
822 case SETPAGENUM:
823 m_pSink->setPageNum( readInt32() ); break;
824 case STARTPAGE:
826 const double nWidth ( readDouble() );
827 const double nHeight( readDouble() );
828 m_pSink->startPage( geometry::RealSize2D( nWidth, nHeight ) );
829 break;
831 case STROKEPATH:
832 m_pSink->strokePath(readPath()); break;
833 case UPDATECTM:
834 readTransformation(); break;
835 case UPDATEFILLCOLOR:
836 m_pSink->setFillColor( readColor() ); break;
837 case UPDATEFLATNESS:
838 m_pSink->setFlatness( readDouble( ) ); break;
839 case UPDATEFONT:
840 readFont(); break;
841 case UPDATELINECAP:
842 readLineCap(); break;
843 case UPDATELINEDASH:
844 readLineDash(); break;
845 case UPDATELINEJOIN:
846 readLineJoin(); break;
847 case UPDATELINEWIDTH:
848 m_pSink->setLineWidth( readDouble() );break;
849 case UPDATEMITERLIMIT:
850 m_pSink->setMiterLimit( readDouble() ); break;
851 case UPDATESTROKECOLOR:
852 m_pSink->setStrokeColor( readColor() ); break;
853 case UPDATESTROKEOPACITY:
854 break;
855 case SETTEXTRENDERMODE:
856 m_pSink->setTextRenderMode( readInt32() ); break;
858 case NONE:
859 default:
860 OSL_PRECOND(false,"Unknown input");
861 break;
864 // all consumed?
865 OSL_POSTCOND(m_nCharIndex==-1,"leftover scanner input");
868 oslFileError readLine( oslFileHandle pFile, OStringBuffer& line )
870 OSL_PRECOND( line.isEmpty(), "line buf not empty" );
872 // TODO(P3): read larger chunks
873 sal_Char aChar('\n');
874 sal_uInt64 nBytesRead;
875 oslFileError nRes;
877 // skip garbage \r \n at start of line
878 while( osl_File_E_None == (nRes=osl_readFile(pFile, &aChar, 1, &nBytesRead)) &&
879 nBytesRead == 1 &&
880 (aChar == '\n' || aChar == '\r') ) ;
882 if( aChar != '\n' && aChar != '\r' )
883 line.append( aChar );
885 while( osl_File_E_None == (nRes=osl_readFile(pFile, &aChar, 1, &nBytesRead)) &&
886 nBytesRead == 1 && aChar != '\n' && aChar != '\r' )
888 line.append( aChar );
891 return nRes;
894 } // namespace
896 static bool checkEncryption( const OUString& i_rPath,
897 const uno::Reference< task::XInteractionHandler >& i_xIHdl,
898 OUString& io_rPwd,
899 bool& o_rIsEncrypted,
900 const OUString& i_rDocName
903 bool bSuccess = false;
904 OString aPDFFile;
905 aPDFFile = OUStringToOString( i_rPath, osl_getThreadTextEncoding() );
907 pdfparse::PDFReader aParser;
908 boost::scoped_ptr<pdfparse::PDFEntry> pEntry( aParser.read( aPDFFile.getStr() ));
909 if( pEntry )
911 pdfparse::PDFFile* pPDFFile = dynamic_cast<pdfparse::PDFFile*>(pEntry.get());
912 if( pPDFFile )
914 o_rIsEncrypted = pPDFFile->isEncrypted();
915 if( o_rIsEncrypted )
917 if( pPDFFile->usesSupportedEncryptionFormat() )
919 bool bAuthenticated = false;
920 if( !io_rPwd.isEmpty() )
922 OString aIsoPwd = OUStringToOString( io_rPwd,
923 RTL_TEXTENCODING_ISO_8859_1 );
924 bAuthenticated = pPDFFile->setupDecryptionData( aIsoPwd.getStr() );
926 if( bAuthenticated )
927 bSuccess = true;
928 else
930 if( i_xIHdl.is() )
932 bool bEntered = false;
935 bEntered = getPassword( i_xIHdl, io_rPwd, ! bEntered, i_rDocName );
936 OString aIsoPwd = OUStringToOString( io_rPwd,
937 RTL_TEXTENCODING_ISO_8859_1 );
938 bAuthenticated = pPDFFile->setupDecryptionData( aIsoPwd.getStr() );
939 } while( bEntered && ! bAuthenticated );
942 OSL_TRACE( "password: %s", bAuthenticated ? "matches" : "does not match" );
943 bSuccess = bAuthenticated;
945 if( bAuthenticated )
947 OUStringBuffer aBuf( 128 );
948 aBuf.appendAscii( "_OOO_pdfi_Credentials_" );
949 aBuf.append( pPDFFile->getDecryptionKey() );
950 io_rPwd = aBuf.makeStringAndClear();
953 else if( i_xIHdl.is() )
955 reportUnsupportedEncryptionFormat( i_xIHdl );
956 //TODO: this should either be handled further down the
957 // call stack, or else information that this has already
958 // been handled should be passed down the call stack, so
959 // that SfxBaseModel::load does not show an additional
960 // "General Error" message box
963 else
964 bSuccess = true;
967 return bSuccess;
970 bool xpdf_ImportFromFile( const OUString& rURL,
971 const ContentSinkSharedPtr& rSink,
972 const uno::Reference< task::XInteractionHandler >& xIHdl,
973 const OUString& rPwd,
974 const uno::Reference< uno::XComponentContext >& xContext )
976 OSL_ASSERT(rSink);
978 OUString aSysUPath;
979 if( osl_getSystemPathFromFileURL( rURL.pData, &aSysUPath.pData ) != osl_File_E_None )
981 SAL_WARN(
982 "sdext.pdfimport",
983 "getSystemPathFromFileURL(" << rURL << ") failed");
984 return false;
986 OUString aDocName( rURL.copy( rURL.lastIndexOf( sal_Unicode('/') )+1 ) );
988 // check for encryption, if necessary get password
989 OUString aPwd( rPwd );
990 bool bIsEncrypted = false;
991 if( checkEncryption( aSysUPath, xIHdl, aPwd, bIsEncrypted, aDocName ) == false )
993 SAL_INFO(
994 "sdext.pdfimport",
995 "checkEncryption(" << aSysUPath << ") failed");
996 return false;
999 // Determine xpdfimport executable URL:
1000 OUString converterURL("$BRAND_BASE_DIR/program/xpdfimport");
1001 rtl::Bootstrap::expandMacros(converterURL); //TODO: detect failure
1003 // Determine pathname of xpdfimport_err.pdf:
1004 OUString errPathname("$BRAND_BASE_DIR/share/xpdfimport/xpdfimport_err.pdf");
1005 rtl::Bootstrap::expandMacros(errPathname); //TODO: detect failure
1006 if (osl::FileBase::getSystemPathFromFileURL(errPathname, errPathname)
1007 != osl::FileBase::E_None)
1009 SAL_WARN(
1010 "sdext.pdfimport",
1011 "getSystemPathFromFileURL(" << errPathname << ") failed");
1012 return false;
1015 // spawn separate process to keep LGPL/GPL code apart.
1016 // ---------------------------------------------------
1017 rtl_uString** ppEnv = NULL;
1018 sal_uInt32 nEnv = 0;
1020 #if defined UNX && ! defined MACOSX
1021 OUString aStr( "$URE_LIB_DIR" );
1022 rtl_bootstrap_expandMacros( &aStr.pData );
1023 OUString aSysPath;
1024 osl_getSystemPathFromFileURL( aStr.pData, &aSysPath.pData );
1025 OUStringBuffer aEnvBuf( aStr.getLength() + 20 );
1026 aEnvBuf.appendAscii( "LD_LIBRARY_PATH=" );
1027 aEnvBuf.append( aSysPath );
1028 aStr = aEnvBuf.makeStringAndClear();
1029 ppEnv = &aStr.pData;
1030 nEnv = 1;
1031 #endif
1033 rtl_uString* args[] = { aSysUPath.pData, errPathname.pData };
1034 sal_Int32 nArgs = 2;
1036 oslProcess aProcess;
1037 oslFileHandle pIn = NULL;
1038 oslFileHandle pOut = NULL;
1039 oslFileHandle pErr = NULL;
1040 const oslProcessError eErr =
1041 osl_executeProcess_WithRedirectedIO(converterURL.pData,
1042 args,
1043 nArgs,
1044 osl_Process_SEARCHPATH|osl_Process_HIDDEN,
1045 osl_getCurrentSecurity(),
1046 0, ppEnv, nEnv,
1047 &aProcess, &pIn, &pOut, &pErr);
1049 bool bRet=true;
1052 if( eErr!=osl_Process_E_None )
1054 SAL_WARN(
1055 "sdext.pdfimport",
1056 "executeProcess of " << converterURL << " failed with "
1057 << +eErr);
1058 return false;
1061 if( pIn )
1063 OStringBuffer aBuf(256);
1064 if( bIsEncrypted )
1065 aBuf.append( OUStringToOString( aPwd, RTL_TEXTENCODING_ISO_8859_1 ) );
1066 aBuf.append( '\n' );
1068 sal_uInt64 nWritten = 0;
1069 osl_writeFile( pIn, aBuf.getStr(), sal_uInt64(aBuf.getLength()), &nWritten );
1072 if( pOut && pErr )
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 OStringBuffer line;
1079 while( osl_File_E_None == readLine(pOut, line) && line.getLength() )
1080 aParser.parseLine(line.makeStringAndClear());
1083 catch( uno::Exception& )
1085 // crappy C file interface. need manual resource dealloc
1086 bRet = false;
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;
1100 bool xpdf_ImportFromStream( const uno::Reference< io::XInputStream >& xInput,
1101 const ContentSinkSharedPtr& rSink,
1102 const uno::Reference<task::XInteractionHandler >& xIHdl,
1103 const OUString& rPwd,
1104 const uno::Reference< uno::XComponentContext >& xContext )
1106 OSL_ASSERT(xInput.is());
1107 OSL_ASSERT(rSink);
1109 // convert XInputStream to local temp file
1110 oslFileHandle aFile = NULL;
1111 OUString aURL;
1112 if( osl_createTempFile( NULL, &aFile, &aURL.pData ) != osl_File_E_None )
1113 return false;
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;
1125 nBytes = xInput->readBytes( aBuf, nBufSize );
1127 catch( com::sun::star::uno::Exception& )
1129 osl_closeFile( aFile );
1130 throw;
1132 if( nBytes > 0 )
1134 osl_writeFile( aFile, aBuf.getConstArray(), nBytes, &nWritten );
1135 if( nWritten != nBytes )
1137 bSuccess = false;
1138 break;
1142 while( nBytes == nBufSize );
1144 osl_closeFile( aFile );
1146 if ( bSuccess )
1147 bSuccess = xpdf_ImportFromFile( aURL, rSink, xIHdl, rPwd, xContext );
1148 osl_removeFile( aURL.pData );
1150 return bSuccess;
1155 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */