1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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"
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>
65 #include "rtl/bootstrap.h"
67 #include <string.h> // memcmp
69 using namespace com::sun::star
;
77 // identifier of the strings coming from the out-of-process xpdf
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
;
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
);
153 void readBinaryData( uno::Sequence
<sal_Int8
>& rBuf
);
155 uno::Reference
<rendering::XPolyPolygon2D
> readPath( double* );
161 void readTransformation();
162 rendering::ARGBColor
readColor();
163 void parseFontFamilyName( FontAttributes
& aResult
);
165 uno::Sequence
<beans::PropertyValue
> readImageImpl();
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
);
178 Parser( const ContentSinkSharedPtr
& rSink
,
180 const uno::Reference
<uno::XComponentContext
>& xContext
) :
181 m_xContext(xContext
),
188 minAreaThreshold( 300.0 ),
192 void parseLine( const OString
& rLine
);
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
);
220 *pWrite
= cNext
== 'n' ? '\n' : (cNext
== 'r' ? '\r' : '\\');
222 pCur
= pRead
= pCur
+ 2;
226 // Just continue on the next character. The current
227 // block will be copied the next time it goes through the
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
);
241 OString
aResult(pBuffer
);
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
;
287 osl_File_E_None
== (nRes
=osl_readFile( m_pErr
, pBuf
, nFileLen
, &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
;
322 readInt32( nCurveFlag
);
324 aSubPath
.append(basegfx::B2DPoint(nX
,nY
));
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?
348 aCurrToken
= m_aLine
.getToken(m_nNextToken
,' ',nDummy
);
351 aResult
.append( aSubPath
);
352 if( m_nCharIndex
!= -1 )
358 basegfx::B2DRange
aRange( aResult
.getB2DRange() );
359 if( aRange
.getWidth() <= minLineWidth
|| aRange
.getHeight() <= minLineWidth
)
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
388 m_pSink
->drawGlyphs( OStringToOUString( aChars
,
389 RTL_TEXTENCODING_UTF8
),
393 void Parser::readLineCap()
395 sal_Int8
nCap(rendering::PathCapType::BUTT
);
396 switch( readInt32() )
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 );
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() )
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
);
462 int Parser::parseFontCheckForString( const sal_Unicode
* pCopy
, const char* s
, sal_Int32
& nLen
,
463 FontAttributes
& aResult
, bool bItalic
, bool bBold
)
468 for (int i
= 0; i
< l
; i
++)
469 if (tolower(pCopy
[i
]) != s
[i
]
470 && toupper(pCopy
[i
]) != s
[i
])
472 aResult
.isItalic
= bItalic
;
473 aResult
.isBold
= bBold
;
479 int Parser::parseFontRemoveSuffix( const sal_Unicode
* pCopy
, const char* s
, sal_Int32
& nLen
)
484 for (int i
= 0; i
< l
; i
++)
485 if ( pCopy
[nLen
- l
+ i
] != s
[i
] )
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('+') )
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)) {}
520 aNewFamilyName
.append( *pCopy
);
525 aResult
.familyName
= aNewFamilyName
.makeStringAndClear();
528 void Parser::readFont()
532 sal_Int32 nIsEmbedded
, nIsBold
, nIsItalic
, nIsUnderline
, nFileLen
;
536 readInt32(nIsEmbedded
);
538 readInt32(nIsItalic
);
539 readInt32(nIsUnderline
);
543 nSize
= nSize
< 0.0 ? -nSize
: nSize
;
544 aFontName
= lcl_unescapeLineFeeds( m_aLine
.copy( m_nCharIndex
) );
546 // name gobbles up rest of line
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
);
555 m_pSink
->setFont( aRes
);
560 // yet unknown font - get info and add to map
561 FontAttributes
aResult( OStringToOUString( aFontName
,
562 RTL_TEXTENCODING_UTF8
),
569 // extract textual attributes (bold, italic in the name, etc.)
570 parseFontFamilyName(aResult
);
571 // need to read font file?
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" ),
591 uno::Any
aRes( xMat
->getMaterial() );
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;
604 catch( uno::Exception
& )
608 if( aResult
.familyName
.isEmpty() )
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() );
637 if( aToken
== aPngMarker
)
638 aFileName
= aPngFile
;
639 else if( aToken
== aJpegMarker
)
640 aFileName
= aJpegFile
;
641 else if( aToken
== aPbmMarker
)
642 aFileName
= aPbmFile
;
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
);
678 void Parser::readImage()
680 sal_Int32 nWidth
, nHeight
,nMaskColors
;
683 readInt32(nMaskColors
);
685 uno::Sequence
<beans::PropertyValue
> aImg( readImageImpl() );
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
);
708 m_pSink
->drawImage( aImg
);
711 void Parser::readMask()
713 sal_Int32 nWidth
, nHeight
, 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
737 void Parser::readMaskedImage()
739 sal_Int32 nWidth
, nHeight
, nMaskWidth
, nMaskHeight
, nMaskInvert
;
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
;
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(),
776 switch( pEntry
->eKey
)
779 m_pSink
->intersectClip(readPath()); break;
788 case DRAWMASKEDIMAGE
:
789 readMaskedImage(); break;
790 case DRAWSOFTMASKEDIMAGE
:
791 readSoftMaskedImage(); break;
793 m_pSink
->endPage(); break;
795 m_pSink
->endText(); break;
797 m_pSink
->intersectEoClip(readPath()); break;
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
);
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
);
819 m_pSink
->popState(); break;
821 m_pSink
->pushState(); break;
823 m_pSink
->setPageNum( readInt32() ); break;
826 const double nWidth ( readDouble() );
827 const double nHeight( readDouble() );
828 m_pSink
->startPage( geometry::RealSize2D( nWidth
, nHeight
) );
832 m_pSink
->strokePath(readPath()); break;
834 readTransformation(); break;
835 case UPDATEFILLCOLOR
:
836 m_pSink
->setFillColor( readColor() ); break;
838 m_pSink
->setFlatness( readDouble( ) ); break;
842 readLineCap(); break;
844 readLineDash(); break;
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
:
855 case SETTEXTRENDERMODE
:
856 m_pSink
->setTextRenderMode( readInt32() ); break;
860 OSL_PRECOND(false,"Unknown input");
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
;
877 // skip garbage \r \n at start of line
878 while( osl_File_E_None
== (nRes
=osl_readFile(pFile
, &aChar
, 1, &nBytesRead
)) &&
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
);
896 static bool checkEncryption( const OUString
& i_rPath
,
897 const uno::Reference
< task::XInteractionHandler
>& i_xIHdl
,
899 bool& o_rIsEncrypted
,
900 const OUString
& i_rDocName
903 bool bSuccess
= false;
905 aPDFFile
= OUStringToOString( i_rPath
, osl_getThreadTextEncoding() );
907 pdfparse::PDFReader aParser
;
908 boost::scoped_ptr
<pdfparse::PDFEntry
> pEntry( aParser
.read( aPDFFile
.getStr() ));
911 pdfparse::PDFFile
* pPDFFile
= dynamic_cast<pdfparse::PDFFile
*>(pEntry
.get());
914 o_rIsEncrypted
= pPDFFile
->isEncrypted();
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() );
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
;
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
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
)
979 if( osl_getSystemPathFromFileURL( rURL
.pData
, &aSysUPath
.pData
) != osl_File_E_None
)
983 "getSystemPathFromFileURL(" << rURL
<< ") failed");
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 )
995 "checkEncryption(" << aSysUPath
<< ") failed");
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
)
1011 "getSystemPathFromFileURL(" << errPathname
<< ") failed");
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
);
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
;
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
,
1044 osl_Process_SEARCHPATH
|osl_Process_HIDDEN
,
1045 osl_getCurrentSecurity(),
1047 &aProcess
, &pIn
, &pOut
, &pErr
);
1052 if( eErr
!=osl_Process_E_None
)
1056 "executeProcess of " << converterURL
<< " failed with "
1063 OStringBuffer
aBuf(256);
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
);
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
);
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
1092 osl_closeFile(pOut
);
1094 osl_closeFile(pErr
);
1095 osl_freeProcessHandle(aProcess
);
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());
1109 // convert XInputStream to local temp file
1110 oslFileHandle aFile
= NULL
;
1112 if( osl_createTempFile( NULL
, &aFile
, &aURL
.pData
) != osl_File_E_None
)
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
);
1134 osl_writeFile( aFile
, aBuf
.getConstArray(), nBytes
, &nWritten
);
1135 if( nWritten
!= nBytes
)
1142 while( nBytes
== nBufSize
);
1144 osl_closeFile( aFile
);
1147 bSuccess
= xpdf_ImportFromFile( aURL
, rSink
, xIHdl
, rPwd
, xContext
);
1148 osl_removeFile( aURL
.pData
);
1155 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */