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 .
20 #include <config_folders.h>
22 #include <contentsink.hxx>
23 #include <pdfparse.hxx>
24 #include <pdfihelper.hxx>
25 #include <wrapper.hxx>
28 #include <osl/file.hxx>
29 #include <osl/thread.h>
30 #include <osl/process.h>
31 #include <osl/diagnose.h>
32 #include <rtl/bootstrap.hxx>
33 #include <rtl/ustring.hxx>
34 #include <rtl/ustrbuf.hxx>
35 #include <rtl/strbuf.hxx>
36 #include <rtl/byteseq.hxx>
38 #include <comphelper/propertysequence.hxx>
39 #include <cppuhelper/exc_hlp.hxx>
40 #include <com/sun/star/io/XInputStream.hpp>
41 #include <com/sun/star/uno/XComponentContext.hpp>
42 #include <com/sun/star/awt/FontDescriptor.hpp>
43 #include <com/sun/star/beans/XMaterialHolder.hpp>
44 #include <com/sun/star/rendering/PathCapType.hpp>
45 #include <com/sun/star/rendering/PathJoinType.hpp>
46 #include <com/sun/star/rendering/XColorSpace.hpp>
47 #include <com/sun/star/rendering/XPolyPolygon2D.hpp>
48 #include <com/sun/star/rendering/XBitmap.hpp>
49 #include <com/sun/star/geometry/Matrix2D.hpp>
50 #include <com/sun/star/geometry/AffineMatrix2D.hpp>
51 #include <com/sun/star/geometry/RealRectangle2D.hpp>
52 #include <com/sun/star/task/XInteractionHandler.hpp>
54 #include <basegfx/point/b2dpoint.hxx>
55 #include <basegfx/polygon/b2dpolypolygon.hxx>
56 #include <basegfx/polygon/b2dpolygon.hxx>
57 #include <basegfx/utils/canvastools.hxx>
58 #include <basegfx/utils/unopolypolygon.hxx>
60 #include <vcl/metric.hxx>
61 #include <vcl/font.hxx>
62 #include <vcl/virdev.hxx>
63 #include <vcl/lazydelete.hxx>
66 #include <unordered_map>
70 #include <rtl/bootstrap.h>
72 #include <rtl/character.hxx>
74 using namespace com::sun::star
;
82 // identifier of the strings coming from the out-of-process xpdf
134 #if defined _MSC_VER && defined __clang__
135 #pragma clang diagnostic push
136 #pragma clang diagnostic ignored "-Wdeprecated-register"
137 #pragma clang diagnostic ignored "-Wextra-tokens"
140 #if defined _MSC_VER && defined __clang__
141 #pragma clang diagnostic pop
146 typedef std::unordered_map
< sal_Int64
,
147 FontAttributes
> FontMapType
;
149 ScopedVclPtr
<VirtualDevice
> m_xDev
;
150 const uno::Reference
<uno::XComponentContext
> m_xContext
;
151 const ContentSinkSharedPtr m_pSink
;
152 const oslFileHandle m_pErr
;
154 FontMapType m_aFontMap
;
155 sal_Int32 m_nNextToken
;
156 sal_Int32 m_nCharIndex
;
159 OString
readNextToken();
160 void readInt32( sal_Int32
& o_Value
);
161 sal_Int32
readInt32();
162 void readInt64( sal_Int64
& o_Value
);
163 void readDouble( double& o_Value
);
165 void readBinaryData( uno::Sequence
<sal_Int8
>& rBuf
);
167 uno::Reference
<rendering::XPolyPolygon2D
> readPath();
173 void readTransformation();
174 rendering::ARGBColor
readColor();
175 static void parseFontFamilyName( FontAttributes
& aResult
);
177 uno::Sequence
<beans::PropertyValue
> readImageImpl();
182 void readMaskedImage();
183 void readSoftMaskedImage();
184 static sal_Int32
parseFontCheckForString(const sal_Unicode
* pCopy
, sal_Int32 nCopyLen
,
185 const char* pAttrib
, sal_Int32 nAttribLen
,
186 FontAttributes
& rResult
, bool bItalic
, bool bBold
);
187 static sal_Int32
parseFontRemoveSuffix(const sal_Unicode
* pCopy
, sal_Int32 nCopyLen
,
188 const char* pAttrib
, sal_Int32 nAttribLen
);
191 Parser( const ContentSinkSharedPtr
& rSink
,
193 const uno::Reference
<uno::XComponentContext
>& xContext
) :
194 m_xContext(xContext
),
203 void parseLine( const OString
& rLine
);
206 /** Unescapes line-ending characters in input string. These
207 characters are encoded as pairs of characters: '\\' 'n', resp.
208 '\\' 'r'. This function converts them back to '\n', resp. '\r'.
210 OString
lcl_unescapeLineFeeds(const OString
& i_rStr
)
212 const size_t nOrigLen(sal::static_int_cast
<size_t>(i_rStr
.getLength()));
213 const sal_Char
* const pOrig(i_rStr
.getStr());
214 std::unique_ptr
<sal_Char
[]> pBuffer(new sal_Char
[nOrigLen
+ 1]);
216 const sal_Char
* pRead(pOrig
);
217 sal_Char
* pWrite(pBuffer
.get());
218 const sal_Char
* pCur(pOrig
);
219 while ((pCur
= strchr(pCur
, '\\')) != nullptr)
221 const sal_Char
cNext(pCur
[1]);
222 if (cNext
== 'n' || cNext
== 'r' || cNext
== '\\')
224 const size_t nLen(pCur
- pRead
);
225 strncpy(pWrite
, pRead
, nLen
);
227 *pWrite
= cNext
== 'n' ? '\n' : (cNext
== 'r' ? '\r' : '\\');
229 pCur
= pRead
= pCur
+ 2;
233 // Just continue on the next character. The current
234 // block will be copied the next time it goes through the
239 // maybe there are some data to copy yet
240 if (sal::static_int_cast
<size_t>(pRead
- pOrig
) < nOrigLen
)
242 const size_t nLen(nOrigLen
- (pRead
- pOrig
));
243 strncpy(pWrite
, pRead
, nLen
);
248 OString
aResult(pBuffer
.get());
252 OString
Parser::readNextToken()
254 OSL_PRECOND(m_nCharIndex
!=-1,"insufficient input");
255 return m_aLine
.getToken(m_nNextToken
,' ',m_nCharIndex
);
258 void Parser::readInt32( sal_Int32
& o_Value
)
260 o_Value
= readNextToken().toInt32();
263 sal_Int32
Parser::readInt32()
265 return readNextToken().toInt32();
268 void Parser::readInt64( sal_Int64
& o_Value
)
270 o_Value
= readNextToken().toInt64();
273 void Parser::readDouble( double& o_Value
)
275 o_Value
= readNextToken().toDouble();
278 double Parser::readDouble()
280 return readNextToken().toDouble();
283 void Parser::readBinaryData( uno::Sequence
<sal_Int8
>& rBuf
)
285 sal_Int32
nFileLen( rBuf
.getLength() );
286 sal_Int8
* pBuf( rBuf
.getArray() );
287 sal_uInt64
nBytesRead(0);
288 oslFileError nRes
=osl_File_E_None
;
290 osl_File_E_None
== (nRes
=osl_readFile( m_pErr
, pBuf
, nFileLen
, &nBytesRead
)) )
293 nFileLen
-= sal::static_int_cast
<sal_Int32
>(nBytesRead
);
296 OSL_PRECOND(nRes
==osl_File_E_None
, "inconsistent data");
299 uno::Reference
<rendering::XPolyPolygon2D
> Parser::readPath()
301 const OString
aSubPathMarker( "subpath" );
303 if( readNextToken() != aSubPathMarker
)
304 OSL_PRECOND(false, "broken path");
306 basegfx::B2DPolyPolygon aResult
;
307 while( m_nCharIndex
!= -1 )
309 basegfx::B2DPolygon aSubPath
;
311 sal_Int32 nClosedFlag
;
312 readInt32( nClosedFlag
);
313 aSubPath
.setClosed( nClosedFlag
!= 0 );
315 sal_Int32
nContiguousControlPoints(0);
316 sal_Int32 nDummy
=m_nCharIndex
;
317 OString
aCurrToken( m_aLine
.getToken(m_nNextToken
,' ',nDummy
) );
319 while( m_nCharIndex
!= -1 && aCurrToken
!= aSubPathMarker
)
321 sal_Int32 nCurveFlag
;
325 readInt32( nCurveFlag
);
327 aSubPath
.append(basegfx::B2DPoint(nX
,nY
));
330 ++nContiguousControlPoints
;
332 else if( nContiguousControlPoints
)
334 OSL_PRECOND(nContiguousControlPoints
==2,"broken bezier path");
336 // have two control points before us. the current one
337 // is a normal point - thus, convert previous points
338 // into bezier segment
339 const sal_uInt32
nPoints( aSubPath
.count() );
340 const basegfx::B2DPoint
aCtrlA( aSubPath
.getB2DPoint(nPoints
-3) );
341 const basegfx::B2DPoint
aCtrlB( aSubPath
.getB2DPoint(nPoints
-2) );
342 const basegfx::B2DPoint
aEnd( aSubPath
.getB2DPoint(nPoints
-1) );
343 aSubPath
.remove(nPoints
-3, 3);
344 aSubPath
.appendBezierSegment(aCtrlA
, aCtrlB
, aEnd
);
346 nContiguousControlPoints
=0;
349 // one token look-ahead (new subpath or more points?
351 aCurrToken
= m_aLine
.getToken(m_nNextToken
,' ',nDummy
);
354 aResult
.append( aSubPath
);
355 if( m_nCharIndex
!= -1 )
359 return static_cast<rendering::XLinePolyPolygon2D
*>(
360 new basegfx::unotools::UnoPolyPolygon(aResult
));
363 void Parser::readChar()
366 geometry::Matrix2D aUnoMatrix
;
367 geometry::RealRectangle2D aRect
;
369 readDouble(aRect
.X1
);
370 readDouble(aRect
.Y1
);
371 readDouble(aRect
.X2
);
372 readDouble(aRect
.Y2
);
373 readDouble(aUnoMatrix
.m00
);
374 readDouble(aUnoMatrix
.m01
);
375 readDouble(aUnoMatrix
.m10
);
376 readDouble(aUnoMatrix
.m11
);
377 readDouble(fontSize
);
381 if (m_nCharIndex
!= -1)
382 aChars
= lcl_unescapeLineFeeds( m_aLine
.copy( m_nCharIndex
) );
384 // chars gobble up rest of line
387 m_pSink
->drawGlyphs(OStringToOUString(aChars
, RTL_TEXTENCODING_UTF8
),
388 aRect
, aUnoMatrix
, fontSize
);
391 void Parser::readLineCap()
393 sal_Int8
nCap(rendering::PathCapType::BUTT
);
394 switch( readInt32() )
397 // FALLTHROUGH intended
398 case 0: nCap
= rendering::PathCapType::BUTT
; break;
399 case 1: nCap
= rendering::PathCapType::ROUND
; break;
400 case 2: nCap
= rendering::PathCapType::SQUARE
; break;
402 m_pSink
->setLineCap(nCap
);
405 void Parser::readLineDash()
407 if( m_nCharIndex
== -1 )
409 m_pSink
->setLineDash( uno::Sequence
<double>(), 0.0 );
413 const double nOffset(readDouble());
414 const sal_Int32
nLen(readInt32());
416 uno::Sequence
<double> aDashArray(nLen
);
417 double* pArray
=aDashArray
.getArray();
418 for( sal_Int32 i
=0; i
<nLen
; ++i
)
419 *pArray
++ = readDouble();
421 m_pSink
->setLineDash( aDashArray
, nOffset
);
424 void Parser::readLineJoin()
426 sal_Int8
nJoin(rendering::PathJoinType::MITER
);
427 switch( readInt32() )
430 // FALLTHROUGH intended
431 case 0: nJoin
= rendering::PathJoinType::MITER
; break;
432 case 1: nJoin
= rendering::PathJoinType::ROUND
; break;
433 case 2: nJoin
= rendering::PathJoinType::BEVEL
; break;
435 m_pSink
->setLineJoin(nJoin
);
438 void Parser::readTransformation()
440 geometry::AffineMatrix2D aMat
;
441 readDouble(aMat
.m00
);
442 readDouble(aMat
.m10
);
443 readDouble(aMat
.m01
);
444 readDouble(aMat
.m11
);
445 readDouble(aMat
.m02
);
446 readDouble(aMat
.m12
);
447 m_pSink
->setTransformation( aMat
);
450 rendering::ARGBColor
Parser::readColor()
452 rendering::ARGBColor aRes
;
453 readDouble(aRes
.Red
);
454 readDouble(aRes
.Green
);
455 readDouble(aRes
.Blue
);
456 readDouble(aRes
.Alpha
);
460 sal_Int32
Parser::parseFontCheckForString(
461 const sal_Unicode
* pCopy
, sal_Int32 nCopyLen
,
462 const char* pAttrib
, sal_Int32 nAttribLen
,
463 FontAttributes
& rResult
, bool bItalic
, bool bBold
)
465 if (nCopyLen
< nAttribLen
)
467 for (sal_Int32 i
= 0; i
< nAttribLen
; ++i
)
469 sal_uInt32 nCode
= pAttrib
[i
];
470 if (rtl::toAsciiLowerCase(pCopy
[i
]) != nCode
471 && rtl::toAsciiUpperCase(pCopy
[i
]) != nCode
)
474 rResult
.isItalic
|= bItalic
;
475 rResult
.isBold
|= bBold
;
479 sal_Int32
Parser::parseFontRemoveSuffix(
480 const sal_Unicode
* pCopy
, sal_Int32 nCopyLen
,
481 const char* pAttrib
, sal_Int32 nAttribLen
)
483 if (nCopyLen
< nAttribLen
)
485 for (sal_Int32 i
= 0; i
< nAttribLen
; ++i
)
486 if ( pCopy
[nCopyLen
- nAttribLen
+ i
] != pAttrib
[i
] )
491 void Parser::parseFontFamilyName( FontAttributes
& rResult
)
493 OUStringBuffer
aNewFamilyName( rResult
.familyName
.getLength() );
495 const sal_Unicode
* pCopy
= rResult
.familyName
.getStr();
496 sal_Int32 nLen
= rResult
.familyName
.getLength();
497 // parse out truetype subsets (e.g. BAAAAA+Thorndale)
498 if( nLen
> 8 && pCopy
[6] == '+' )
504 // TODO: Looks like this block needs to be refactored
507 if (parseFontRemoveSuffix(pCopy
, nLen
, RTL_CONSTASCII_STRINGPARAM("PSMT")))
509 nLen
-= RTL_CONSTASCII_LENGTH("PSMT");
511 else if (parseFontRemoveSuffix(pCopy
, nLen
, RTL_CONSTASCII_STRINGPARAM("MT")))
513 nLen
-= RTL_CONSTASCII_LENGTH("MT");
516 if (parseFontCheckForString(pCopy
, nLen
, RTL_CONSTASCII_STRINGPARAM("Italic"), rResult
, true, false))
518 sal_Int32 nAttribLen
= RTL_CONSTASCII_LENGTH("Italic");
522 else if (parseFontCheckForString(pCopy
, nLen
, RTL_CONSTASCII_STRINGPARAM("-LightOblique"), rResult
, true, false))
524 sal_Int32 nAttribLen
= RTL_CONSTASCII_LENGTH("-LightOblique");
528 else if (parseFontCheckForString(pCopy
, nLen
, RTL_CONSTASCII_STRINGPARAM("-Light"), rResult
, false, false))
530 sal_Int32 nAttribLen
= RTL_CONSTASCII_LENGTH("-Light");
534 else if (parseFontCheckForString(pCopy
, nLen
, RTL_CONSTASCII_STRINGPARAM("-BoldOblique"), rResult
, true, true))
536 sal_Int32 nAttribLen
= RTL_CONSTASCII_LENGTH("-BoldOblique");
540 else if (parseFontCheckForString(pCopy
, nLen
, RTL_CONSTASCII_STRINGPARAM("-Bold"), rResult
, false, true))
542 sal_Int32 nAttribLen
= RTL_CONSTASCII_LENGTH("-Bold");
546 else if (parseFontCheckForString(pCopy
, nLen
, RTL_CONSTASCII_STRINGPARAM("Bold"), rResult
, false, true))
548 sal_Int32 nAttribLen
= RTL_CONSTASCII_LENGTH("Bold");
552 else if (parseFontCheckForString(pCopy
, nLen
, RTL_CONSTASCII_STRINGPARAM("-Roman"), rResult
, false, false))
554 sal_Int32 nAttribLen
= RTL_CONSTASCII_LENGTH("-Roman");
558 else if (parseFontCheckForString(pCopy
, nLen
, RTL_CONSTASCII_STRINGPARAM("-Oblique"), rResult
, true, false))
560 sal_Int32 nAttribLen
= RTL_CONSTASCII_LENGTH("-Oblique");
564 else if (parseFontCheckForString(pCopy
, nLen
, RTL_CONSTASCII_STRINGPARAM("-Reg"), rResult
, false, false))
566 sal_Int32 nAttribLen
= RTL_CONSTASCII_LENGTH("-Reg");
573 aNewFamilyName
.append( *pCopy
);
578 rResult
.familyName
= aNewFamilyName
.makeStringAndClear();
581 void Parser::readFont()
585 sal_Int32 nIsEmbedded
, nIsBold
, nIsItalic
, nIsUnderline
, nFileLen
;
589 readInt32(nIsEmbedded
);
591 readInt32(nIsItalic
);
592 readInt32(nIsUnderline
);
596 nSize
= nSize
< 0.0 ? -nSize
: nSize
;
597 aFontName
= lcl_unescapeLineFeeds( m_aLine
.copy( m_nCharIndex
) );
599 // name gobbles up rest of line
602 FontMapType::const_iterator
pFont( m_aFontMap
.find(nFontID
) );
603 if( pFont
!= m_aFontMap
.end() )
605 OSL_PRECOND(nFileLen
==0,"font data for known font");
606 FontAttributes
aRes(pFont
->second
);
608 m_pSink
->setFont( aRes
);
613 // yet unknown font - get info and add to map
614 FontAttributes
aResult( OStringToOUString( aFontName
,
615 RTL_TEXTENCODING_UTF8
),
622 // extract textual attributes (bold, italic in the name, etc.)
623 parseFontFamilyName(aResult
);
624 // need to read font file?
627 uno::Sequence
<sal_Int8
> aFontFile(nFileLen
);
628 readBinaryData( aFontFile
);
630 awt::FontDescriptor aFD
;
631 uno::Sequence
< uno::Any
> aArgs(1);
632 aArgs
[0] <<= aFontFile
;
636 uno::Reference
< beans::XMaterialHolder
> xMat(
637 m_xContext
->getServiceManager()->createInstanceWithArgumentsAndContext(
638 "com.sun.star.awt.FontIdentificator", aArgs
, m_xContext
),
642 uno::Any
aRes( xMat
->getMaterial() );
645 if (!aFD
.Name
.isEmpty())
647 aResult
.familyName
= aFD
.Name
;
648 parseFontFamilyName(aResult
);
650 aResult
.isBold
= (aFD
.Weight
> 100.0);
651 aResult
.isItalic
= (aFD
.Slant
== awt::FontSlant_OBLIQUE
||
652 aFD
.Slant
== awt::FontSlant_ITALIC
);
653 aResult
.isUnderline
= false;
658 catch( uno::Exception
& )
662 if( aResult
.familyName
.isEmpty() )
665 aResult
.familyName
= "Arial";
666 aResult
.isUnderline
= false;
672 m_xDev
.disposeAndReset(VclPtr
<VirtualDevice
>::Create());
674 vcl::Font
font(aResult
.familyName
, Size(0, 1000));
675 m_xDev
->SetFont(font
);
676 FontMetric
metric(m_xDev
->GetFontMetric());
677 aResult
.ascent
= metric
.GetAscent() / 1000.0;
679 m_aFontMap
[nFontID
] = aResult
;
681 aResult
.size
= nSize
;
682 m_pSink
->setFont(aResult
);
685 uno::Sequence
<beans::PropertyValue
> Parser::readImageImpl()
687 OString aToken
= readNextToken();
688 const sal_Int32
nImageSize( readInt32() );
691 if( aToken
== "PNG" )
692 aFileName
= "DUMMY.PNG";
693 else if( aToken
== "JPEG" )
694 aFileName
= "DUMMY.JPEG";
695 else if( aToken
== "PBM" )
696 aFileName
= "DUMMY.PBM";
699 SAL_WARN_IF(aToken
!= "PPM","sdext.pdfimport","Invalid bitmap format");
700 aFileName
= "DUMMY.PPM";
703 uno::Sequence
<sal_Int8
> aDataSequence(nImageSize
);
704 readBinaryData( aDataSequence
);
706 uno::Sequence
< uno::Any
> aStreamCreationArgs(1);
707 aStreamCreationArgs
[0] <<= aDataSequence
;
709 uno::Reference
< uno::XComponentContext
> xContext( m_xContext
, uno::UNO_SET_THROW
);
710 uno::Reference
< lang::XMultiComponentFactory
> xFactory( xContext
->getServiceManager(), uno::UNO_SET_THROW
);
711 uno::Reference
< io::XInputStream
> xDataStream(
712 xFactory
->createInstanceWithArgumentsAndContext( "com.sun.star.io.SequenceInputStream", aStreamCreationArgs
, m_xContext
),
713 uno::UNO_QUERY_THROW
);
715 uno::Sequence
<beans::PropertyValue
> aSequence( comphelper::InitPropertySequence({
716 { "URL", uno::makeAny(aFileName
) },
717 { "InputStream", uno::makeAny( xDataStream
) },
718 { "InputSequence", uno::makeAny(aDataSequence
) }
724 void Parser::readImage()
726 sal_Int32 nWidth
, nHeight
,nMaskColors
;
729 readInt32(nMaskColors
);
731 uno::Sequence
<beans::PropertyValue
> aImg( readImageImpl() );
735 uno::Sequence
<sal_Int8
> aDataSequence(nMaskColors
);
736 readBinaryData( aDataSequence
);
738 uno::Sequence
<uno::Any
> aMaskRanges(2);
740 uno::Sequence
<double> aMinRange(nMaskColors
/2);
741 uno::Sequence
<double> aMaxRange(nMaskColors
/2);
742 for( sal_Int32 i
=0; i
<nMaskColors
/2; ++i
)
744 aMinRange
[i
] = aDataSequence
[i
] / 255.0;
745 aMaxRange
[i
] = aDataSequence
[i
+nMaskColors
/2] / 255.0;
748 aMaskRanges
[0] <<= aMinRange
;
749 aMaskRanges
[1] <<= aMaxRange
;
751 m_pSink
->drawColorMaskedImage( aImg
, aMaskRanges
);
754 m_pSink
->drawImage( aImg
);
757 void Parser::readMask()
759 sal_Int32 nWidth
, nHeight
, nInvert
;
764 m_pSink
->drawMask( readImageImpl(), nInvert
!= 0);
767 void Parser::readLink()
769 geometry::RealRectangle2D aBounds
;
770 readDouble(aBounds
.X1
);
771 readDouble(aBounds
.Y1
);
772 readDouble(aBounds
.X2
);
773 readDouble(aBounds
.Y2
);
775 m_pSink
->hyperLink( aBounds
,
776 OStringToOUString( lcl_unescapeLineFeeds(
777 m_aLine
.copy(m_nCharIndex
) ),
778 RTL_TEXTENCODING_UTF8
) );
779 // name gobbles up rest of line
783 void Parser::readMaskedImage()
785 sal_Int32 nWidth
, nHeight
, nMaskWidth
, nMaskHeight
, nMaskInvert
;
788 readInt32(nMaskWidth
);
789 readInt32(nMaskHeight
);
790 readInt32(nMaskInvert
);
792 const uno::Sequence
<beans::PropertyValue
> aImage( readImageImpl() );
793 const uno::Sequence
<beans::PropertyValue
> aMask ( readImageImpl() );
794 m_pSink
->drawMaskedImage( aImage
, aMask
, nMaskInvert
!= 0 );
797 void Parser::readSoftMaskedImage()
799 sal_Int32 nWidth
, nHeight
, nMaskWidth
, nMaskHeight
;
802 readInt32(nMaskWidth
);
803 readInt32(nMaskHeight
);
805 const uno::Sequence
<beans::PropertyValue
> aImage( readImageImpl() );
806 const uno::Sequence
<beans::PropertyValue
> aMask ( readImageImpl() );
807 m_pSink
->drawAlphaMaskedImage( aImage
, aMask
);
810 void Parser::parseLine( const OString
& rLine
)
812 OSL_PRECOND( m_pSink
, "Invalid sink" );
813 OSL_PRECOND( m_pErr
, "Invalid filehandle" );
814 OSL_PRECOND( m_xContext
.is(), "Invalid service factory" );
816 m_nNextToken
= 0; m_nCharIndex
= 0; m_aLine
= rLine
;
817 const OString
& rCmd
= readNextToken();
818 const hash_entry
* pEntry
= PdfKeywordHash::in_word_set( rCmd
.getStr(),
821 switch( pEntry
->eKey
)
824 m_pSink
->intersectClip(readPath()); break;
833 case DRAWMASKEDIMAGE
:
834 readMaskedImage(); break;
835 case DRAWSOFTMASKEDIMAGE
:
836 readSoftMaskedImage(); break;
838 m_pSink
->endPage(); break;
840 m_pSink
->endText(); break;
842 m_pSink
->intersectEoClip(readPath()); break;
844 m_pSink
->eoFillPath(readPath()); break;
846 m_pSink
->fillPath(readPath()); break;
848 m_pSink
->popState(); break;
850 m_pSink
->pushState(); break;
852 m_pSink
->setPageNum( readInt32() ); break;
855 const double nWidth ( readDouble() );
856 const double nHeight( readDouble() );
857 m_pSink
->startPage( geometry::RealSize2D( nWidth
, nHeight
) );
861 m_pSink
->strokePath(readPath()); break;
863 readTransformation(); break;
864 case UPDATEFILLCOLOR
:
865 m_pSink
->setFillColor( readColor() ); break;
867 m_pSink
->setFlatness( readDouble( ) ); break;
871 readLineCap(); break;
873 readLineDash(); break;
875 readLineJoin(); break;
876 case UPDATELINEWIDTH
:
877 m_pSink
->setLineWidth( readDouble() );break;
878 case UPDATEMITERLIMIT
:
879 m_pSink
->setMiterLimit( readDouble() ); break;
880 case UPDATESTROKECOLOR
:
881 m_pSink
->setStrokeColor( readColor() ); break;
882 case UPDATESTROKEOPACITY
:
884 case SETTEXTRENDERMODE
:
885 m_pSink
->setTextRenderMode( readInt32() ); break;
889 OSL_PRECOND(false,"Unknown input");
894 SAL_WARN_IF(m_nCharIndex
!=-1, "sdext.pdfimport", "leftover scanner input");
899 static bool checkEncryption( const OUString
& i_rPath
,
900 const uno::Reference
< task::XInteractionHandler
>& i_xIHdl
,
902 bool& o_rIsEncrypted
,
903 const OUString
& i_rDocName
906 bool bSuccess
= false;
908 aPDFFile
= OUStringToOString( i_rPath
, osl_getThreadTextEncoding() );
910 pdfparse::PDFReader aParser
;
911 std::unique_ptr
<pdfparse::PDFEntry
> pEntry( pdfparse::PDFReader::read( aPDFFile
.getStr() ));
914 pdfparse::PDFFile
* pPDFFile
= dynamic_cast<pdfparse::PDFFile
*>(pEntry
.get());
917 o_rIsEncrypted
= pPDFFile
->isEncrypted();
920 if( pPDFFile
->usesSupportedEncryptionFormat() )
922 bool bAuthenticated
= false;
923 if( !io_rPwd
.isEmpty() )
925 OString aIsoPwd
= OUStringToOString( io_rPwd
,
926 RTL_TEXTENCODING_ISO_8859_1
);
927 bAuthenticated
= pPDFFile
->setupDecryptionData( aIsoPwd
.getStr() );
935 bool bEntered
= false;
938 bEntered
= getPassword( i_xIHdl
, io_rPwd
, ! bEntered
, i_rDocName
);
939 OString aIsoPwd
= OUStringToOString( io_rPwd
,
940 RTL_TEXTENCODING_ISO_8859_1
);
941 bAuthenticated
= pPDFFile
->setupDecryptionData( aIsoPwd
.getStr() );
942 } while( bEntered
&& ! bAuthenticated
);
945 bSuccess
= bAuthenticated
;
948 else if( i_xIHdl
.is() )
950 reportUnsupportedEncryptionFormat( i_xIHdl
);
951 //TODO: this should either be handled further down the
952 // call stack, or else information that this has already
953 // been handled should be passed down the call stack, so
954 // that SfxBaseModel::load does not show an additional
955 // "General Error" message box
967 static const int SIZE
= 64*1024;
968 std::unique_ptr
<char[]> aBuffer
;
974 explicit Buffering(oslFileHandle
& out
) : aBuffer(new char[SIZE
]), pOut(out
), pos(0), left(0) {}
976 oslFileError
read(char *pChar
, short count
, sal_uInt64
* pBytesRead
)
978 oslFileError nRes
= osl_File_E_None
;
979 sal_uInt64 nBytesRead
= 0;
984 nRes
= osl_readFile(pOut
, aBuffer
.get(), SIZE
, &left
);
985 if (nRes
!= osl_File_E_None
|| left
== 0)
987 *pBytesRead
= nBytesRead
;
992 *pChar
= aBuffer
.get()[pos
];
999 *pBytesRead
= nBytesRead
;
1000 return osl_File_E_None
;
1004 bool xpdf_ImportFromFile_Poppler(const OUString
& aSysUPath
,
1005 const ContentSinkSharedPtr
& rSink
,
1006 const uno::Reference
<task::XInteractionHandler
>& xIHdl
,
1007 const bool bIsEncrypted
,
1008 const OUString
& aPwd
,
1009 const uno::Reference
<uno::XComponentContext
>& xContext
,
1010 const OUString
& rFilterOptions
);
1012 bool xpdf_ImportFromFile( const OUString
& rURL
,
1013 const ContentSinkSharedPtr
& rSink
,
1014 const uno::Reference
< task::XInteractionHandler
>& xIHdl
,
1015 const OUString
& rPwd
,
1016 const uno::Reference
< uno::XComponentContext
>& xContext
,
1017 const OUString
& rFilterOptions
)
1022 if( osl_getSystemPathFromFileURL( rURL
.pData
, &aSysUPath
.pData
) != osl_File_E_None
)
1026 "getSystemPathFromFileURL(" << rURL
<< ") failed");
1029 OUString
aDocName( rURL
.copy( rURL
.lastIndexOf( '/' )+1 ) );
1031 // check for encryption, if necessary get password
1032 OUString
aPwd( rPwd
);
1033 bool bIsEncrypted
= false;
1034 if( !checkEncryption( aSysUPath
, xIHdl
, aPwd
, bIsEncrypted
, aDocName
) )
1038 "checkEncryption(" << aSysUPath
<< ") failed");
1042 return xpdf_ImportFromFile_Poppler(aSysUPath
, rSink
, xIHdl
, bIsEncrypted
, aPwd
, xContext
, rFilterOptions
);
1045 /// Parse PDf file using libpoppler, which is quite limited
1046 /// to be phased out in favor of pdfium.
1047 bool xpdf_ImportFromFile_Poppler(const OUString
& aSysUPath
,
1048 const ContentSinkSharedPtr
& rSink
,
1049 const uno::Reference
<task::XInteractionHandler
>& /*xIHdl*/,
1050 const bool bIsEncrypted
,
1051 const OUString
& aPwd
,
1052 const uno::Reference
<uno::XComponentContext
>& xContext
,
1053 const OUString
& rFilterOptions
)
1055 // Determine xpdfimport executable URL:
1056 OUString
converterURL("$BRAND_BASE_DIR/" LIBO_BIN_FOLDER
"/xpdfimport");
1057 rtl::Bootstrap::expandMacros(converterURL
); //TODO: detect failure
1059 // Determine pathname of xpdfimport_err.pdf:
1060 OUString
errPathname("$BRAND_BASE_DIR/" LIBO_SHARE_FOLDER
"/xpdfimport/xpdfimport_err.pdf");
1061 rtl::Bootstrap::expandMacros(errPathname
); //TODO: detect failure
1062 if (osl::FileBase::getSystemPathFromFileURL(errPathname
, errPathname
)
1063 != osl::FileBase::E_None
)
1067 "getSystemPathFromFileURL(" << errPathname
<< ") failed");
1071 // spawn separate process to keep LGPL/GPL code apart.
1073 OUString
aOptFlag("-o");
1074 rtl_uString
* args
[] = { aSysUPath
.pData
, errPathname
.pData
,
1075 aOptFlag
.pData
, rFilterOptions
.pData
};
1076 sal_Int32 nArgs
= rFilterOptions
.isEmpty() ? 2 : 4;
1078 oslProcess aProcess
;
1079 oslFileHandle pIn
= nullptr;
1080 oslFileHandle pOut
= nullptr;
1081 oslFileHandle pErr
= nullptr;
1082 oslSecurity pSecurity
= osl_getCurrentSecurity ();
1083 oslProcessError eErr
=
1084 osl_executeProcess_WithRedirectedIO(converterURL
.pData
,
1087 osl_Process_SEARCHPATH
|osl_Process_HIDDEN
,
1089 nullptr, nullptr, 0,
1090 &aProcess
, &pIn
, &pOut
, &pErr
);
1091 osl_freeSecurityHandle(pSecurity
);
1096 if( eErr
!=osl_Process_E_None
)
1100 "executeProcess of " << converterURL
<< " failed with "
1107 OStringBuffer
aBuf(256);
1109 aBuf
.append( OUStringToOString( aPwd
, RTL_TEXTENCODING_ISO_8859_1
) );
1110 aBuf
.append( '\n' );
1112 sal_uInt64 nWritten
= 0;
1113 osl_writeFile( pIn
, aBuf
.getStr(), sal_uInt64(aBuf
.getLength()), &nWritten
);
1118 // read results of PDF parser. One line - one call to
1119 // OutputDev. stderr is used for alternate streams, like
1120 // embedded fonts and bitmaps
1121 Parser
aParser(rSink
,pErr
,xContext
);
1122 Buffering
aBuffering(pOut
);
1127 sal_uInt64 nBytesRead
;
1130 // skip garbage \r \n at start of line
1131 while( osl_File_E_None
== (nRes
= aBuffering
.read(&aChar
, 1, &nBytesRead
)) &&
1133 (aChar
== '\n' || aChar
== '\r') ) ;
1134 if ( osl_File_E_None
!= nRes
)
1137 if( aChar
!= '\n' && aChar
!= '\r' )
1138 line
.append( aChar
);
1140 while( osl_File_E_None
== (nRes
= aBuffering
.read(&aChar
, 1, &nBytesRead
)) &&
1141 nBytesRead
== 1 && aChar
!= '\n' && aChar
!= '\r' )
1143 line
.append( aChar
);
1145 if ( osl_File_E_None
!= nRes
)
1147 if ( line
.isEmpty() )
1150 aParser
.parseLine(line
.makeStringAndClear());
1154 catch( uno::Exception
& )
1156 // crappy C file interface. need manual resource dealloc
1163 osl_closeFile(pOut
);
1165 osl_closeFile(pErr
);
1166 eErr
= osl_joinProcess(aProcess
);
1167 if (eErr
== osl_Process_E_None
)
1169 oslProcessInfo info
;
1170 info
.Size
= sizeof info
;
1171 eErr
= osl_getProcessInfo(aProcess
, osl_Process_EXITCODE
, &info
);
1172 if (eErr
== osl_Process_E_None
)
1178 "getProcessInfo of " << converterURL
1179 << " failed with exit code " << info
.Code
);
1187 "getProcessInfo of " << converterURL
<< " failed with "
1196 "joinProcess of " << converterURL
<< " failed with " << +eErr
);
1199 osl_freeProcessHandle(aProcess
);
1204 bool xpdf_ImportFromStream( const uno::Reference
< io::XInputStream
>& xInput
,
1205 const ContentSinkSharedPtr
& rSink
,
1206 const uno::Reference
<task::XInteractionHandler
>& xIHdl
,
1207 const OUString
& rPwd
,
1208 const uno::Reference
< uno::XComponentContext
>& xContext
,
1209 const OUString
& rFilterOptions
)
1211 OSL_ASSERT(xInput
.is());
1214 // convert XInputStream to local temp file
1215 oslFileHandle aFile
= nullptr;
1217 if( osl_createTempFile( nullptr, &aFile
, &aURL
.pData
) != osl_File_E_None
)
1220 // copy content, buffered...
1221 const sal_uInt32 nBufSize
= 4096;
1222 uno::Sequence
<sal_Int8
> aBuf( nBufSize
);
1223 sal_uInt64 nBytes
= 0;
1224 sal_uInt64 nWritten
= 0;
1225 bool bSuccess
= true;
1230 nBytes
= xInput
->readBytes( aBuf
, nBufSize
);
1232 catch( css::uno::Exception
& )
1234 osl_closeFile( aFile
);
1239 osl_writeFile( aFile
, aBuf
.getConstArray(), nBytes
, &nWritten
);
1240 if( nWritten
!= nBytes
)
1247 while( nBytes
== nBufSize
);
1249 osl_closeFile( aFile
);
1252 bSuccess
= xpdf_ImportFromFile( aURL
, rSink
, xIHdl
, rPwd
, xContext
, rFilterOptions
);
1253 osl_removeFile( aURL
.pData
);
1260 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */