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 <sal/log.hxx>
38 #include <comphelper/propertysequence.hxx>
39 #include <com/sun/star/io/XInputStream.hpp>
40 #include <com/sun/star/uno/XComponentContext.hpp>
41 #include <com/sun/star/awt/FontDescriptor.hpp>
42 #include <com/sun/star/beans/XMaterialHolder.hpp>
43 #include <com/sun/star/rendering/PathCapType.hpp>
44 #include <com/sun/star/rendering/PathJoinType.hpp>
45 #include <com/sun/star/rendering/XPolyPolygon2D.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/geometry/RealSize2D.hpp>
50 #include <com/sun/star/task/XInteractionHandler.hpp>
52 #include <basegfx/point/b2dpoint.hxx>
53 #include <basegfx/polygon/b2dpolypolygon.hxx>
54 #include <basegfx/polygon/b2dpolygon.hxx>
55 #include <basegfx/utils/unopolypolygon.hxx>
57 #include <vcl/metric.hxx>
58 #include <vcl/font.hxx>
59 #include <vcl/virdev.hxx>
62 #include <unordered_map>
66 #include <rtl/character.hxx>
68 using namespace com::sun::star
;
76 // identifier of the strings coming from the out-of-process xpdf
128 #if defined _MSC_VER && defined __clang__
129 #pragma clang diagnostic push
130 #pragma clang diagnostic ignored "-Wdeprecated-register"
131 #pragma clang diagnostic ignored "-Wextra-tokens"
134 #if defined _MSC_VER && defined __clang__
135 #pragma clang diagnostic pop
140 typedef std::unordered_map
< sal_Int64
,
141 FontAttributes
> FontMapType
;
143 ScopedVclPtr
<VirtualDevice
> m_xDev
;
144 const uno::Reference
<uno::XComponentContext
> m_xContext
;
145 const ContentSinkSharedPtr m_pSink
;
146 const oslFileHandle m_pErr
;
148 FontMapType m_aFontMap
;
149 sal_Int32 m_nNextToken
;
150 sal_Int32 m_nCharIndex
;
153 OString
readNextToken();
154 void readInt32( sal_Int32
& o_Value
);
155 sal_Int32
readInt32();
156 void readInt64( sal_Int64
& o_Value
);
157 void readDouble( double& o_Value
);
159 void readBinaryData( uno::Sequence
<sal_Int8
>& rBuf
);
161 uno::Reference
<rendering::XPolyPolygon2D
> readPath();
167 void readTransformation();
168 rendering::ARGBColor
readColor();
169 static void parseFontFamilyName( FontAttributes
& aResult
);
171 uno::Sequence
<beans::PropertyValue
> readImageImpl();
176 void readMaskedImage();
177 void readSoftMaskedImage();
178 static sal_Int32
parseFontCheckForString(const sal_Unicode
* pCopy
, sal_Int32 nCopyLen
,
179 const char* pAttrib
, sal_Int32 nAttribLen
,
180 FontAttributes
& rResult
, bool bItalic
, bool bBold
);
181 static sal_Int32
parseFontRemoveSuffix(const sal_Unicode
* pCopy
, sal_Int32 nCopyLen
,
182 const char* pAttrib
, sal_Int32 nAttribLen
);
185 Parser( const ContentSinkSharedPtr
& rSink
,
187 const uno::Reference
<uno::XComponentContext
>& xContext
) :
188 m_xContext(xContext
),
197 void parseLine( const OString
& rLine
);
200 /** Unescapes line-ending characters in input string. These
201 characters are encoded as pairs of characters: '\\' 'n', resp.
202 '\\' 'r'. This function converts them back to '\n', resp. '\r'.
204 OString
lcl_unescapeLineFeeds(const OString
& i_rStr
)
206 const size_t nOrigLen(sal::static_int_cast
<size_t>(i_rStr
.getLength()));
207 const sal_Char
* const pOrig(i_rStr
.getStr());
208 std::unique_ptr
<sal_Char
[]> pBuffer(new sal_Char
[nOrigLen
+ 1]);
210 const sal_Char
* pRead(pOrig
);
211 sal_Char
* pWrite(pBuffer
.get());
212 const sal_Char
* pCur(pOrig
);
213 while ((pCur
= strchr(pCur
, '\\')) != nullptr)
215 const sal_Char
cNext(pCur
[1]);
216 if (cNext
== 'n' || cNext
== 'r' || cNext
== '\\')
218 const size_t nLen(pCur
- pRead
);
219 strncpy(pWrite
, pRead
, nLen
);
221 *pWrite
= cNext
== 'n' ? '\n' : (cNext
== 'r' ? '\r' : '\\');
223 pCur
= pRead
= pCur
+ 2;
227 // Just continue on the next character. The current
228 // block will be copied the next time it goes through the
233 // maybe there are some data to copy yet
234 if (sal::static_int_cast
<size_t>(pRead
- pOrig
) < nOrigLen
)
236 const size_t nLen(nOrigLen
- (pRead
- pOrig
));
237 strncpy(pWrite
, pRead
, nLen
);
242 OString
aResult(pBuffer
.get());
246 OString
Parser::readNextToken()
248 OSL_PRECOND(m_nCharIndex
!=-1,"insufficient input");
249 return m_aLine
.getToken(m_nNextToken
,' ',m_nCharIndex
);
252 void Parser::readInt32( sal_Int32
& o_Value
)
254 o_Value
= readNextToken().toInt32();
257 sal_Int32
Parser::readInt32()
259 return readNextToken().toInt32();
262 void Parser::readInt64( sal_Int64
& o_Value
)
264 o_Value
= readNextToken().toInt64();
267 void Parser::readDouble( double& o_Value
)
269 o_Value
= readNextToken().toDouble();
272 double Parser::readDouble()
274 return readNextToken().toDouble();
277 void Parser::readBinaryData( uno::Sequence
<sal_Int8
>& rBuf
)
279 sal_Int32
nFileLen( rBuf
.getLength() );
280 sal_Int8
* pBuf( rBuf
.getArray() );
281 sal_uInt64
nBytesRead(0);
282 oslFileError nRes
=osl_File_E_None
;
284 osl_File_E_None
== (nRes
=osl_readFile( m_pErr
, pBuf
, nFileLen
, &nBytesRead
)) )
287 nFileLen
-= sal::static_int_cast
<sal_Int32
>(nBytesRead
);
290 OSL_PRECOND(nRes
==osl_File_E_None
, "inconsistent data");
293 uno::Reference
<rendering::XPolyPolygon2D
> Parser::readPath()
295 const OString
aSubPathMarker( "subpath" );
297 if( readNextToken() != aSubPathMarker
)
298 OSL_PRECOND(false, "broken path");
300 basegfx::B2DPolyPolygon aResult
;
301 while( m_nCharIndex
!= -1 )
303 basegfx::B2DPolygon aSubPath
;
305 sal_Int32 nClosedFlag
;
306 readInt32( nClosedFlag
);
307 aSubPath
.setClosed( nClosedFlag
!= 0 );
309 sal_Int32
nContiguousControlPoints(0);
310 sal_Int32 nDummy
=m_nCharIndex
;
311 OString
aCurrToken( m_aLine
.getToken(m_nNextToken
,' ',nDummy
) );
313 while( m_nCharIndex
!= -1 && aCurrToken
!= aSubPathMarker
)
315 sal_Int32 nCurveFlag
;
319 readInt32( nCurveFlag
);
321 aSubPath
.append(basegfx::B2DPoint(nX
,nY
));
324 ++nContiguousControlPoints
;
326 else if( nContiguousControlPoints
)
328 OSL_PRECOND(nContiguousControlPoints
==2,"broken bezier path");
330 // have two control points before us. the current one
331 // is a normal point - thus, convert previous points
332 // into bezier segment
333 const sal_uInt32
nPoints( aSubPath
.count() );
334 const basegfx::B2DPoint
aCtrlA( aSubPath
.getB2DPoint(nPoints
-3) );
335 const basegfx::B2DPoint
aCtrlB( aSubPath
.getB2DPoint(nPoints
-2) );
336 const basegfx::B2DPoint
aEnd( aSubPath
.getB2DPoint(nPoints
-1) );
337 aSubPath
.remove(nPoints
-3, 3);
338 aSubPath
.appendBezierSegment(aCtrlA
, aCtrlB
, aEnd
);
340 nContiguousControlPoints
=0;
343 // one token look-ahead (new subpath or more points?
345 aCurrToken
= m_aLine
.getToken(m_nNextToken
,' ',nDummy
);
348 aResult
.append( aSubPath
);
349 if( m_nCharIndex
!= -1 )
353 return static_cast<rendering::XLinePolyPolygon2D
*>(
354 new basegfx::unotools::UnoPolyPolygon(aResult
));
357 void Parser::readChar()
360 geometry::Matrix2D aUnoMatrix
;
361 geometry::RealRectangle2D aRect
;
363 readDouble(aRect
.X1
);
364 readDouble(aRect
.Y1
);
365 readDouble(aRect
.X2
);
366 readDouble(aRect
.Y2
);
367 readDouble(aUnoMatrix
.m00
);
368 readDouble(aUnoMatrix
.m01
);
369 readDouble(aUnoMatrix
.m10
);
370 readDouble(aUnoMatrix
.m11
);
371 readDouble(fontSize
);
375 if (m_nCharIndex
!= -1)
376 aChars
= lcl_unescapeLineFeeds( m_aLine
.copy( m_nCharIndex
) );
378 // chars gobble up rest of line
381 m_pSink
->drawGlyphs(OStringToOUString(aChars
, RTL_TEXTENCODING_UTF8
),
382 aRect
, aUnoMatrix
, fontSize
);
385 void Parser::readLineCap()
387 sal_Int8
nCap(rendering::PathCapType::BUTT
);
388 switch( readInt32() )
391 case 0: nCap
= rendering::PathCapType::BUTT
; break;
392 case 1: nCap
= rendering::PathCapType::ROUND
; break;
393 case 2: nCap
= rendering::PathCapType::SQUARE
; break;
395 m_pSink
->setLineCap(nCap
);
398 void Parser::readLineDash()
400 if( m_nCharIndex
== -1 )
402 m_pSink
->setLineDash( uno::Sequence
<double>(), 0.0 );
406 const double nOffset(readDouble());
407 const sal_Int32
nLen(readInt32());
409 uno::Sequence
<double> aDashArray(nLen
);
410 double* pArray
=aDashArray
.getArray();
411 for( sal_Int32 i
=0; i
<nLen
; ++i
)
412 *pArray
++ = readDouble();
414 m_pSink
->setLineDash( aDashArray
, nOffset
);
417 void Parser::readLineJoin()
419 sal_Int8
nJoin(rendering::PathJoinType::MITER
);
420 switch( readInt32() )
423 case 0: nJoin
= rendering::PathJoinType::MITER
; break;
424 case 1: nJoin
= rendering::PathJoinType::ROUND
; break;
425 case 2: nJoin
= rendering::PathJoinType::BEVEL
; break;
427 m_pSink
->setLineJoin(nJoin
);
430 void Parser::readTransformation()
432 geometry::AffineMatrix2D aMat
;
433 readDouble(aMat
.m00
);
434 readDouble(aMat
.m10
);
435 readDouble(aMat
.m01
);
436 readDouble(aMat
.m11
);
437 readDouble(aMat
.m02
);
438 readDouble(aMat
.m12
);
439 m_pSink
->setTransformation( aMat
);
442 rendering::ARGBColor
Parser::readColor()
444 rendering::ARGBColor aRes
;
445 readDouble(aRes
.Red
);
446 readDouble(aRes
.Green
);
447 readDouble(aRes
.Blue
);
448 readDouble(aRes
.Alpha
);
452 sal_Int32
Parser::parseFontCheckForString(
453 const sal_Unicode
* pCopy
, sal_Int32 nCopyLen
,
454 const char* pAttrib
, sal_Int32 nAttribLen
,
455 FontAttributes
& rResult
, bool bItalic
, bool bBold
)
457 if (nCopyLen
< nAttribLen
)
459 for (sal_Int32 i
= 0; i
< nAttribLen
; ++i
)
461 sal_uInt32 nCode
= pAttrib
[i
];
462 if (rtl::toAsciiLowerCase(pCopy
[i
]) != nCode
463 && rtl::toAsciiUpperCase(pCopy
[i
]) != nCode
)
466 rResult
.isItalic
|= bItalic
;
467 rResult
.isBold
|= bBold
;
471 sal_Int32
Parser::parseFontRemoveSuffix(
472 const sal_Unicode
* pCopy
, sal_Int32 nCopyLen
,
473 const char* pAttrib
, sal_Int32 nAttribLen
)
475 if (nCopyLen
< nAttribLen
)
477 for (sal_Int32 i
= 0; i
< nAttribLen
; ++i
)
478 if ( pCopy
[nCopyLen
- nAttribLen
+ i
] != pAttrib
[i
] )
483 void Parser::parseFontFamilyName( FontAttributes
& rResult
)
485 OUStringBuffer
aNewFamilyName( rResult
.familyName
.getLength() );
487 const sal_Unicode
* pCopy
= rResult
.familyName
.getStr();
488 sal_Int32 nLen
= rResult
.familyName
.getLength();
489 // parse out truetype subsets (e.g. BAAAAA+Thorndale)
490 if( nLen
> 8 && pCopy
[6] == '+' )
496 // TODO: Looks like this block needs to be refactored
499 if (parseFontRemoveSuffix(pCopy
, nLen
, RTL_CONSTASCII_STRINGPARAM("PSMT")))
501 nLen
-= RTL_CONSTASCII_LENGTH("PSMT");
503 else if (parseFontRemoveSuffix(pCopy
, nLen
, RTL_CONSTASCII_STRINGPARAM("MT")))
505 nLen
-= RTL_CONSTASCII_LENGTH("MT");
508 if (parseFontCheckForString(pCopy
, nLen
, RTL_CONSTASCII_STRINGPARAM("Italic"), rResult
, true, false))
510 sal_Int32 nAttribLen
= RTL_CONSTASCII_LENGTH("Italic");
514 else if (parseFontCheckForString(pCopy
, nLen
, RTL_CONSTASCII_STRINGPARAM("-LightOblique"), rResult
, true, false))
516 sal_Int32 nAttribLen
= RTL_CONSTASCII_LENGTH("-LightOblique");
520 else if (parseFontCheckForString(pCopy
, nLen
, RTL_CONSTASCII_STRINGPARAM("-Light"), rResult
, false, false))
522 sal_Int32 nAttribLen
= RTL_CONSTASCII_LENGTH("-Light");
526 else if (parseFontCheckForString(pCopy
, nLen
, RTL_CONSTASCII_STRINGPARAM("-BoldOblique"), rResult
, true, true))
528 sal_Int32 nAttribLen
= RTL_CONSTASCII_LENGTH("-BoldOblique");
532 else if (parseFontCheckForString(pCopy
, nLen
, RTL_CONSTASCII_STRINGPARAM("-Bold"), rResult
, false, true))
534 sal_Int32 nAttribLen
= RTL_CONSTASCII_LENGTH("-Bold");
538 else if (parseFontCheckForString(pCopy
, nLen
, RTL_CONSTASCII_STRINGPARAM("Bold"), rResult
, false, true))
540 sal_Int32 nAttribLen
= RTL_CONSTASCII_LENGTH("Bold");
544 else if (parseFontCheckForString(pCopy
, nLen
, RTL_CONSTASCII_STRINGPARAM("-Roman"), rResult
, false, false))
546 sal_Int32 nAttribLen
= RTL_CONSTASCII_LENGTH("-Roman");
550 else if (parseFontCheckForString(pCopy
, nLen
, RTL_CONSTASCII_STRINGPARAM("-Oblique"), rResult
, true, false))
552 sal_Int32 nAttribLen
= RTL_CONSTASCII_LENGTH("-Oblique");
556 else if (parseFontCheckForString(pCopy
, nLen
, RTL_CONSTASCII_STRINGPARAM("-Reg"), rResult
, false, false))
558 sal_Int32 nAttribLen
= RTL_CONSTASCII_LENGTH("-Reg");
565 aNewFamilyName
.append( *pCopy
);
570 rResult
.familyName
= aNewFamilyName
.makeStringAndClear();
573 void Parser::readFont()
577 sal_Int32 nIsEmbedded
, nIsBold
, nIsItalic
, nIsUnderline
, nFileLen
;
581 readInt32(nIsEmbedded
);
583 readInt32(nIsItalic
);
584 readInt32(nIsUnderline
);
588 nSize
= nSize
< 0.0 ? -nSize
: nSize
;
589 aFontName
= lcl_unescapeLineFeeds( m_aLine
.copy( m_nCharIndex
) );
591 // name gobbles up rest of line
594 FontMapType::const_iterator
pFont( m_aFontMap
.find(nFontID
) );
595 if( pFont
!= m_aFontMap
.end() )
597 OSL_PRECOND(nFileLen
==0,"font data for known font");
598 FontAttributes
aRes(pFont
->second
);
600 m_pSink
->setFont( aRes
);
605 // yet unknown font - get info and add to map
606 FontAttributes
aResult( OStringToOUString( aFontName
,
607 RTL_TEXTENCODING_UTF8
),
614 // extract textual attributes (bold, italic in the name, etc.)
615 parseFontFamilyName(aResult
);
616 // need to read font file?
619 uno::Sequence
<sal_Int8
> aFontFile(nFileLen
);
620 readBinaryData( aFontFile
);
622 awt::FontDescriptor aFD
;
623 uno::Sequence
< uno::Any
> aArgs(1);
624 aArgs
[0] <<= aFontFile
;
628 uno::Reference
< beans::XMaterialHolder
> xMat(
629 m_xContext
->getServiceManager()->createInstanceWithArgumentsAndContext(
630 "com.sun.star.awt.FontIdentificator", aArgs
, m_xContext
),
634 uno::Any
aRes( xMat
->getMaterial() );
637 if (!aFD
.Name
.isEmpty())
639 aResult
.familyName
= aFD
.Name
;
640 parseFontFamilyName(aResult
);
642 aResult
.isBold
= (aFD
.Weight
> 100.0);
643 aResult
.isItalic
= (aFD
.Slant
== awt::FontSlant_OBLIQUE
||
644 aFD
.Slant
== awt::FontSlant_ITALIC
);
645 aResult
.isUnderline
= false;
650 catch( uno::Exception
& )
654 if( aResult
.familyName
.isEmpty() )
657 aResult
.familyName
= "Arial";
658 aResult
.isUnderline
= false;
664 m_xDev
.disposeAndReset(VclPtr
<VirtualDevice
>::Create());
666 vcl::Font
font(aResult
.familyName
, Size(0, 1000));
667 m_xDev
->SetFont(font
);
668 FontMetric
metric(m_xDev
->GetFontMetric());
669 aResult
.ascent
= metric
.GetAscent() / 1000.0;
671 m_aFontMap
[nFontID
] = aResult
;
673 aResult
.size
= nSize
;
674 m_pSink
->setFont(aResult
);
677 uno::Sequence
<beans::PropertyValue
> Parser::readImageImpl()
679 OString aToken
= readNextToken();
680 const sal_Int32
nImageSize( readInt32() );
683 if( aToken
== "PNG" )
684 aFileName
= "DUMMY.PNG";
685 else if( aToken
== "JPEG" )
686 aFileName
= "DUMMY.JPEG";
687 else if( aToken
== "PBM" )
688 aFileName
= "DUMMY.PBM";
691 SAL_WARN_IF(aToken
!= "PPM","sdext.pdfimport","Invalid bitmap format");
692 aFileName
= "DUMMY.PPM";
695 uno::Sequence
<sal_Int8
> aDataSequence(nImageSize
);
696 readBinaryData( aDataSequence
);
698 uno::Sequence
< uno::Any
> aStreamCreationArgs(1);
699 aStreamCreationArgs
[0] <<= aDataSequence
;
701 uno::Reference
< uno::XComponentContext
> xContext( m_xContext
, uno::UNO_SET_THROW
);
702 uno::Reference
< lang::XMultiComponentFactory
> xFactory( xContext
->getServiceManager(), uno::UNO_SET_THROW
);
703 uno::Reference
< io::XInputStream
> xDataStream(
704 xFactory
->createInstanceWithArgumentsAndContext( "com.sun.star.io.SequenceInputStream", aStreamCreationArgs
, m_xContext
),
705 uno::UNO_QUERY_THROW
);
707 uno::Sequence
<beans::PropertyValue
> aSequence( comphelper::InitPropertySequence({
708 { "URL", uno::makeAny(aFileName
) },
709 { "InputStream", uno::makeAny( xDataStream
) },
710 { "InputSequence", uno::makeAny(aDataSequence
) }
716 void Parser::readImage()
718 sal_Int32 nWidth
, nHeight
,nMaskColors
;
721 readInt32(nMaskColors
);
723 uno::Sequence
<beans::PropertyValue
> aImg( readImageImpl() );
727 uno::Sequence
<sal_Int8
> aDataSequence(nMaskColors
);
728 readBinaryData( aDataSequence
);
730 uno::Sequence
<uno::Any
> aMaskRanges(2);
732 uno::Sequence
<double> aMinRange(nMaskColors
/2);
733 uno::Sequence
<double> aMaxRange(nMaskColors
/2);
734 for( sal_Int32 i
=0; i
<nMaskColors
/2; ++i
)
736 aMinRange
[i
] = aDataSequence
[i
] / 255.0;
737 aMaxRange
[i
] = aDataSequence
[i
+nMaskColors
/2] / 255.0;
740 aMaskRanges
[0] <<= aMinRange
;
741 aMaskRanges
[1] <<= aMaxRange
;
743 m_pSink
->drawColorMaskedImage( aImg
, aMaskRanges
);
746 m_pSink
->drawImage( aImg
);
749 void Parser::readMask()
751 sal_Int32 nWidth
, nHeight
, nInvert
;
756 m_pSink
->drawMask( readImageImpl(), nInvert
!= 0);
759 void Parser::readLink()
761 geometry::RealRectangle2D aBounds
;
762 readDouble(aBounds
.X1
);
763 readDouble(aBounds
.Y1
);
764 readDouble(aBounds
.X2
);
765 readDouble(aBounds
.Y2
);
767 m_pSink
->hyperLink( aBounds
,
768 OStringToOUString( lcl_unescapeLineFeeds(
769 m_aLine
.copy(m_nCharIndex
) ),
770 RTL_TEXTENCODING_UTF8
) );
771 // name gobbles up rest of line
775 void Parser::readMaskedImage()
777 sal_Int32 nWidth
, nHeight
, nMaskWidth
, nMaskHeight
, nMaskInvert
;
780 readInt32(nMaskWidth
);
781 readInt32(nMaskHeight
);
782 readInt32(nMaskInvert
);
784 const uno::Sequence
<beans::PropertyValue
> aImage( readImageImpl() );
785 const uno::Sequence
<beans::PropertyValue
> aMask ( readImageImpl() );
786 m_pSink
->drawMaskedImage( aImage
, aMask
, nMaskInvert
!= 0 );
789 void Parser::readSoftMaskedImage()
791 sal_Int32 nWidth
, nHeight
, nMaskWidth
, nMaskHeight
;
794 readInt32(nMaskWidth
);
795 readInt32(nMaskHeight
);
797 const uno::Sequence
<beans::PropertyValue
> aImage( readImageImpl() );
798 const uno::Sequence
<beans::PropertyValue
> aMask ( readImageImpl() );
799 m_pSink
->drawAlphaMaskedImage( aImage
, aMask
);
802 void Parser::parseLine( const OString
& rLine
)
804 OSL_PRECOND( m_pSink
, "Invalid sink" );
805 OSL_PRECOND( m_pErr
, "Invalid filehandle" );
806 OSL_PRECOND( m_xContext
.is(), "Invalid service factory" );
808 m_nNextToken
= 0; m_nCharIndex
= 0; m_aLine
= rLine
;
809 const OString
& rCmd
= readNextToken();
810 const hash_entry
* pEntry
= PdfKeywordHash::in_word_set( rCmd
.getStr(),
813 switch( pEntry
->eKey
)
816 m_pSink
->intersectClip(readPath()); break;
825 case DRAWMASKEDIMAGE
:
826 readMaskedImage(); break;
827 case DRAWSOFTMASKEDIMAGE
:
828 readSoftMaskedImage(); break;
830 m_pSink
->endPage(); break;
832 m_pSink
->endText(); break;
834 m_pSink
->intersectEoClip(readPath()); break;
836 m_pSink
->eoFillPath(readPath()); break;
838 m_pSink
->fillPath(readPath()); break;
840 m_pSink
->popState(); break;
842 m_pSink
->pushState(); break;
844 m_pSink
->setPageNum( readInt32() ); break;
847 const double nWidth ( readDouble() );
848 const double nHeight( readDouble() );
849 m_pSink
->startPage( geometry::RealSize2D( nWidth
, nHeight
) );
853 m_pSink
->strokePath(readPath()); break;
855 readTransformation(); break;
856 case UPDATEFILLCOLOR
:
857 m_pSink
->setFillColor( readColor() ); break;
859 m_pSink
->setFlatness( readDouble( ) ); break;
863 readLineCap(); break;
865 readLineDash(); break;
867 readLineJoin(); break;
868 case UPDATELINEWIDTH
:
869 m_pSink
->setLineWidth( readDouble() );break;
870 case UPDATEMITERLIMIT
:
871 m_pSink
->setMiterLimit( readDouble() ); break;
872 case UPDATESTROKECOLOR
:
873 m_pSink
->setStrokeColor( readColor() ); break;
874 case UPDATESTROKEOPACITY
:
876 case SETTEXTRENDERMODE
:
877 m_pSink
->setTextRenderMode( readInt32() ); break;
881 OSL_PRECOND(false,"Unknown input");
886 SAL_WARN_IF(m_nCharIndex
!=-1, "sdext.pdfimport", "leftover scanner input");
891 static bool checkEncryption( const OUString
& i_rPath
,
892 const uno::Reference
< task::XInteractionHandler
>& i_xIHdl
,
894 bool& o_rIsEncrypted
,
895 const OUString
& i_rDocName
898 bool bSuccess
= false;
899 OString aPDFFile
= OUStringToOString( i_rPath
, osl_getThreadTextEncoding() );
901 std::unique_ptr
<pdfparse::PDFEntry
> pEntry( pdfparse::PDFReader::read( aPDFFile
.getStr() ));
904 pdfparse::PDFFile
* pPDFFile
= dynamic_cast<pdfparse::PDFFile
*>(pEntry
.get());
907 o_rIsEncrypted
= pPDFFile
->isEncrypted();
910 if( pPDFFile
->usesSupportedEncryptionFormat() )
912 bool bAuthenticated
= false;
913 if( !io_rPwd
.isEmpty() )
915 OString aIsoPwd
= OUStringToOString( io_rPwd
,
916 RTL_TEXTENCODING_ISO_8859_1
);
917 bAuthenticated
= pPDFFile
->setupDecryptionData( aIsoPwd
.getStr() );
925 bool bEntered
= false;
928 bEntered
= getPassword( i_xIHdl
, io_rPwd
, ! bEntered
, i_rDocName
);
929 OString aIsoPwd
= OUStringToOString( io_rPwd
,
930 RTL_TEXTENCODING_ISO_8859_1
);
931 bAuthenticated
= pPDFFile
->setupDecryptionData( aIsoPwd
.getStr() );
932 } while( bEntered
&& ! bAuthenticated
);
935 bSuccess
= bAuthenticated
;
938 else if( i_xIHdl
.is() )
940 reportUnsupportedEncryptionFormat( i_xIHdl
);
941 //TODO: this should either be handled further down the
942 // call stack, or else information that this has already
943 // been handled should be passed down the call stack, so
944 // that SfxBaseModel::load does not show an additional
945 // "General Error" message box
957 static const int SIZE
= 64*1024;
958 std::unique_ptr
<char[]> aBuffer
;
964 explicit Buffering(oslFileHandle
& out
) : aBuffer(new char[SIZE
]), pOut(out
), pos(0), left(0) {}
966 oslFileError
read(char *pChar
, short count
, sal_uInt64
* pBytesRead
)
968 oslFileError nRes
= osl_File_E_None
;
969 sal_uInt64 nBytesRead
= 0;
974 nRes
= osl_readFile(pOut
, aBuffer
.get(), SIZE
, &left
);
975 if (nRes
!= osl_File_E_None
|| left
== 0)
977 *pBytesRead
= nBytesRead
;
982 *pChar
= aBuffer
.get()[pos
];
989 *pBytesRead
= nBytesRead
;
990 return osl_File_E_None
;
994 bool xpdf_ImportFromFile(const OUString
& rURL
,
995 const ContentSinkSharedPtr
& rSink
,
996 const uno::Reference
<task::XInteractionHandler
>& xIHdl
,
997 const OUString
& rPwd
,
998 const uno::Reference
<uno::XComponentContext
>& xContext
,
999 const OUString
& rFilterOptions
)
1004 if( osl_getSystemPathFromFileURL( rURL
.pData
, &aSysUPath
.pData
) != osl_File_E_None
)
1008 "getSystemPathFromFileURL(" << rURL
<< ") failed");
1011 OUString
aDocName( rURL
.copy( rURL
.lastIndexOf( '/' )+1 ) );
1013 // check for encryption, if necessary get password
1014 OUString
aPwd( rPwd
);
1015 bool bIsEncrypted
= false;
1016 if( !checkEncryption( aSysUPath
, xIHdl
, aPwd
, bIsEncrypted
, aDocName
) )
1020 "checkEncryption(" << aSysUPath
<< ") failed");
1024 // Determine xpdfimport executable URL:
1025 OUString
converterURL("$BRAND_BASE_DIR/" LIBO_BIN_FOLDER
"/xpdfimport");
1026 rtl::Bootstrap::expandMacros(converterURL
); //TODO: detect failure
1028 // Determine pathname of xpdfimport_err.pdf:
1029 OUString
errPathname("$BRAND_BASE_DIR/" LIBO_SHARE_FOLDER
"/xpdfimport/xpdfimport_err.pdf");
1030 rtl::Bootstrap::expandMacros(errPathname
); //TODO: detect failure
1031 if (osl::FileBase::getSystemPathFromFileURL(errPathname
, errPathname
)
1032 != osl::FileBase::E_None
)
1036 "getSystemPathFromFileURL(" << errPathname
<< ") failed");
1040 // spawn separate process to keep LGPL/GPL code apart.
1042 OUString
aOptFlag("-o");
1043 rtl_uString
* args
[] = { aSysUPath
.pData
, errPathname
.pData
,
1044 aOptFlag
.pData
, rFilterOptions
.pData
};
1045 sal_Int32 nArgs
= rFilterOptions
.isEmpty() ? 2 : 4;
1047 oslProcess aProcess
;
1048 oslFileHandle pIn
= nullptr;
1049 oslFileHandle pOut
= nullptr;
1050 oslFileHandle pErr
= nullptr;
1051 oslSecurity pSecurity
= osl_getCurrentSecurity ();
1052 oslProcessError eErr
=
1053 osl_executeProcess_WithRedirectedIO(converterURL
.pData
,
1056 osl_Process_SEARCHPATH
|osl_Process_HIDDEN
,
1058 nullptr, nullptr, 0,
1059 &aProcess
, &pIn
, &pOut
, &pErr
);
1060 osl_freeSecurityHandle(pSecurity
);
1065 if( eErr
!=osl_Process_E_None
)
1069 "executeProcess of " << converterURL
<< " failed with "
1076 OStringBuffer
aBuf(256);
1078 aBuf
.append( OUStringToOString( aPwd
, RTL_TEXTENCODING_ISO_8859_1
) );
1079 aBuf
.append( '\n' );
1081 sal_uInt64 nWritten
= 0;
1082 osl_writeFile( pIn
, aBuf
.getStr(), sal_uInt64(aBuf
.getLength()), &nWritten
);
1087 // read results of PDF parser. One line - one call to
1088 // OutputDev. stderr is used for alternate streams, like
1089 // embedded fonts and bitmaps
1090 Parser
aParser(rSink
,pErr
,xContext
);
1091 Buffering
aBuffering(pOut
);
1096 sal_uInt64 nBytesRead
;
1099 // skip garbage \r \n at start of line
1100 while( osl_File_E_None
== (nRes
= aBuffering
.read(&aChar
, 1, &nBytesRead
)) &&
1102 (aChar
== '\n' || aChar
== '\r') ) ;
1103 if ( osl_File_E_None
!= nRes
)
1106 if( aChar
!= '\n' && aChar
!= '\r' )
1107 line
.append( aChar
);
1109 while( osl_File_E_None
== (nRes
= aBuffering
.read(&aChar
, 1, &nBytesRead
)) &&
1110 nBytesRead
== 1 && aChar
!= '\n' && aChar
!= '\r' )
1112 line
.append( aChar
);
1114 if ( osl_File_E_None
!= nRes
)
1116 if ( line
.isEmpty() )
1119 aParser
.parseLine(line
.makeStringAndClear());
1123 catch( uno::Exception
& )
1125 // crappy C file interface. need manual resource dealloc
1132 osl_closeFile(pOut
);
1134 osl_closeFile(pErr
);
1135 eErr
= osl_joinProcess(aProcess
);
1136 if (eErr
== osl_Process_E_None
)
1138 oslProcessInfo info
;
1139 info
.Size
= sizeof info
;
1140 eErr
= osl_getProcessInfo(aProcess
, osl_Process_EXITCODE
, &info
);
1141 if (eErr
== osl_Process_E_None
)
1147 "getProcessInfo of " << converterURL
1148 << " failed with exit code " << info
.Code
);
1156 "getProcessInfo of " << converterURL
<< " failed with "
1165 "joinProcess of " << converterURL
<< " failed with " << +eErr
);
1168 osl_freeProcessHandle(aProcess
);
1173 bool xpdf_ImportFromStream( const uno::Reference
< io::XInputStream
>& xInput
,
1174 const ContentSinkSharedPtr
& rSink
,
1175 const uno::Reference
<task::XInteractionHandler
>& xIHdl
,
1176 const OUString
& rPwd
,
1177 const uno::Reference
< uno::XComponentContext
>& xContext
,
1178 const OUString
& rFilterOptions
)
1180 OSL_ASSERT(xInput
.is());
1183 // convert XInputStream to local temp file
1184 oslFileHandle aFile
= nullptr;
1186 if( osl_createTempFile( nullptr, &aFile
, &aURL
.pData
) != osl_File_E_None
)
1189 // copy content, buffered...
1190 const sal_uInt32 nBufSize
= 4096;
1191 uno::Sequence
<sal_Int8
> aBuf( nBufSize
);
1192 sal_uInt64 nBytes
= 0;
1193 sal_uInt64 nWritten
= 0;
1194 bool bSuccess
= true;
1199 nBytes
= xInput
->readBytes( aBuf
, nBufSize
);
1201 catch( css::uno::Exception
& )
1203 osl_closeFile( aFile
);
1208 osl_writeFile( aFile
, aBuf
.getConstArray(), nBytes
, &nWritten
);
1209 if( nWritten
!= nBytes
)
1216 while( nBytes
== nBufSize
);
1218 osl_closeFile( aFile
);
1221 bSuccess
= xpdf_ImportFromFile( aURL
, rSink
, xIHdl
, rPwd
, xContext
, rFilterOptions
);
1222 osl_removeFile( aURL
.pData
);
1229 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */