vcl: allow for overriding the default PDF rendering resolution
[LibreOffice.git] / sdext / source / pdfimport / wrapper / wrapper.cxx
blob3d36e77110beba556e808bb06459a9d0a1db1543
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 .
20 #include <config_folders.h>
22 #include <contentsink.hxx>
23 #include <pdfparse.hxx>
24 #include <pdfihelper.hxx>
25 #include <wrapper.hxx>
27 #include <osl/file.h>
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>
61 #include <memory>
62 #include <unordered_map>
63 #include <string.h>
64 #include <stdlib.h>
66 #include <rtl/character.hxx>
68 using namespace com::sun::star;
70 namespace pdfi
73 namespace
76 // identifier of the strings coming from the out-of-process xpdf
77 // converter
78 enum parseKey {
79 CLIPPATH,
80 DRAWCHAR,
81 DRAWIMAGE,
82 DRAWLINK,
83 DRAWMASK,
84 DRAWMASKEDIMAGE,
85 DRAWSOFTMASKEDIMAGE,
86 ENDPAGE,
87 ENDTEXTOBJECT,
88 EOCLIPPATH,
89 EOFILLPATH,
90 FILLPATH,
91 HYPERLINK,
92 INTERSECTCLIP,
93 INTERSECTEOCLIP,
94 POPSTATE,
95 PUSHSTATE,
96 RESTORESTATE,
97 SAVESTATE,
98 SETBLENDMODE,
99 SETFILLCOLOR,
100 SETFONT,
101 SETLINECAP,
102 SETLINEDASH,
103 SETLINEJOIN,
104 SETLINEWIDTH,
105 SETMITERLIMIT,
106 SETPAGENUM,
107 SETSTROKECOLOR,
108 SETTEXTRENDERMODE,
109 SETTRANSFORMATION,
110 STARTPAGE,
111 STROKEPATH,
112 UPDATEBLENDMODE,
113 UPDATECTM,
114 UPDATEFILLCOLOR,
115 UPDATEFILLOPACITY,
116 UPDATEFLATNESS,
117 UPDATEFONT,
118 UPDATELINECAP,
119 UPDATELINEDASH,
120 UPDATELINEJOIN,
121 UPDATELINEWIDTH,
122 UPDATEMITERLIMIT,
123 UPDATESTROKECOLOR,
124 UPDATESTROKEOPACITY,
125 NONE
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"
132 #endif
133 #include <hash.cxx>
134 #if defined _MSC_VER && defined __clang__
135 #pragma clang diagnostic pop
136 #endif
138 class Parser
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;
147 OString m_aLine;
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 );
158 double readDouble();
159 void readBinaryData( uno::Sequence<sal_Int8>& rBuf );
161 uno::Reference<rendering::XPolyPolygon2D> readPath();
163 void readChar();
164 void readLineCap();
165 void readLineDash();
166 void readLineJoin();
167 void readTransformation();
168 rendering::ARGBColor readColor();
169 static void parseFontFamilyName( FontAttributes& aResult );
170 void readFont();
171 uno::Sequence<beans::PropertyValue> readImageImpl();
173 void readImage();
174 void readMask();
175 void readLink();
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);
184 public:
185 Parser( const ContentSinkSharedPtr& rSink,
186 oslFileHandle pErr,
187 const uno::Reference<uno::XComponentContext>& xContext ) :
188 m_xContext(xContext),
189 m_pSink(rSink),
190 m_pErr(pErr),
191 m_aLine(),
192 m_aFontMap(101),
193 m_nNextToken(-1),
194 m_nCharIndex(-1)
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);
220 pWrite += nLen;
221 *pWrite = cNext == 'n' ? '\n' : (cNext == 'r' ? '\r' : '\\');
222 ++pWrite;
223 pCur = pRead = pCur + 2;
225 else
227 // Just continue on the next character. The current
228 // block will be copied the next time it goes through the
229 // 'if' branch.
230 ++pCur;
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);
238 pWrite += nLen;
240 *pWrite = '\0';
242 OString aResult(pBuffer.get());
243 return aResult;
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;
283 while( nFileLen &&
284 osl_File_E_None == (nRes=osl_readFile( m_pErr, pBuf, nFileLen, &nBytesRead )) )
286 pBuf += 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;
316 double nX, nY;
317 readDouble( nX );
318 readDouble( nY );
319 readInt32( nCurveFlag );
321 aSubPath.append(basegfx::B2DPoint(nX,nY));
322 if( nCurveFlag )
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?
344 nDummy=m_nCharIndex;
345 aCurrToken = m_aLine.getToken(m_nNextToken,' ',nDummy);
348 aResult.append( aSubPath );
349 if( m_nCharIndex != -1 )
350 readNextToken();
353 return static_cast<rendering::XLinePolyPolygon2D*>(
354 new basegfx::unotools::UnoPolyPolygon(aResult));
357 void Parser::readChar()
359 double fontSize;
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);
373 OString aChars;
375 if (m_nCharIndex != -1)
376 aChars = lcl_unescapeLineFeeds( m_aLine.copy( m_nCharIndex ) );
378 // chars gobble up rest of line
379 m_nCharIndex = -1;
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() )
390 default:
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 );
403 return;
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() )
422 default:
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);
449 return aRes;
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)
458 return 0;
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)
464 return 0;
466 rResult.isItalic |= bItalic;
467 rResult.isBold |= bBold;
468 return nAttribLen;
471 sal_Int32 Parser::parseFontRemoveSuffix(
472 const sal_Unicode* pCopy, sal_Int32 nCopyLen,
473 const char* pAttrib, sal_Int32 nAttribLen)
475 if (nCopyLen < nAttribLen)
476 return 0;
477 for (sal_Int32 i = 0; i < nAttribLen; ++i)
478 if ( pCopy[nCopyLen - nAttribLen + i] != pAttrib[i] )
479 return 0;
480 return nAttribLen;
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] == '+' )
492 pCopy += 7;
493 nLen -= 7;
496 // TODO: Looks like this block needs to be refactored
497 while( nLen )
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");
511 nLen -= nAttribLen;
512 pCopy += nAttribLen;
514 else if (parseFontCheckForString(pCopy, nLen, RTL_CONSTASCII_STRINGPARAM("-LightOblique"), rResult, true, false))
516 sal_Int32 nAttribLen = RTL_CONSTASCII_LENGTH("-LightOblique");
517 nLen -= nAttribLen;
518 pCopy += nAttribLen;
520 else if (parseFontCheckForString(pCopy, nLen, RTL_CONSTASCII_STRINGPARAM("-Light"), rResult, false, false))
522 sal_Int32 nAttribLen = RTL_CONSTASCII_LENGTH("-Light");
523 nLen -= nAttribLen;
524 pCopy += nAttribLen;
526 else if (parseFontCheckForString(pCopy, nLen, RTL_CONSTASCII_STRINGPARAM("-BoldOblique"), rResult, true, true))
528 sal_Int32 nAttribLen = RTL_CONSTASCII_LENGTH("-BoldOblique");
529 nLen -= nAttribLen;
530 pCopy += nAttribLen;
532 else if (parseFontCheckForString(pCopy, nLen, RTL_CONSTASCII_STRINGPARAM("-Bold"), rResult, false, true))
534 sal_Int32 nAttribLen = RTL_CONSTASCII_LENGTH("-Bold");
535 nLen -= nAttribLen;
536 pCopy += nAttribLen;
538 else if (parseFontCheckForString(pCopy, nLen, RTL_CONSTASCII_STRINGPARAM("Bold"), rResult, false, true))
540 sal_Int32 nAttribLen = RTL_CONSTASCII_LENGTH("Bold");
541 nLen -= nAttribLen;
542 pCopy += nAttribLen;
544 else if (parseFontCheckForString(pCopy, nLen, RTL_CONSTASCII_STRINGPARAM("-Roman"), rResult, false, false))
546 sal_Int32 nAttribLen = RTL_CONSTASCII_LENGTH("-Roman");
547 nLen -= nAttribLen;
548 pCopy += nAttribLen;
550 else if (parseFontCheckForString(pCopy, nLen, RTL_CONSTASCII_STRINGPARAM("-Oblique"), rResult, true, false))
552 sal_Int32 nAttribLen = RTL_CONSTASCII_LENGTH("-Oblique");
553 nLen -= nAttribLen;
554 pCopy += nAttribLen;
556 else if (parseFontCheckForString(pCopy, nLen, RTL_CONSTASCII_STRINGPARAM("-Reg"), rResult, false, false))
558 sal_Int32 nAttribLen = RTL_CONSTASCII_LENGTH("-Reg");
559 nLen -= nAttribLen;
560 pCopy += nAttribLen;
562 else if(nLen > 0)
564 if( *pCopy != '-' )
565 aNewFamilyName.append( *pCopy );
566 pCopy++;
567 nLen--;
570 rResult.familyName = aNewFamilyName.makeStringAndClear();
573 void Parser::readFont()
575 OString aFontName;
576 sal_Int64 nFontID;
577 sal_Int32 nIsEmbedded, nIsBold, nIsItalic, nIsUnderline, nFileLen;
578 double nSize;
580 readInt64(nFontID);
581 readInt32(nIsEmbedded);
582 readInt32(nIsBold);
583 readInt32(nIsItalic);
584 readInt32(nIsUnderline);
585 readDouble(nSize);
586 readInt32(nFileLen);
588 nSize = nSize < 0.0 ? -nSize : nSize;
589 aFontName = lcl_unescapeLineFeeds( m_aLine.copy( m_nCharIndex ) );
591 // name gobbles up rest of line
592 m_nCharIndex = -1;
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);
599 aRes.size = nSize;
600 m_pSink->setFont( aRes );
602 return;
605 // yet unknown font - get info and add to map
606 FontAttributes aResult( OStringToOUString( aFontName,
607 RTL_TEXTENCODING_UTF8 ),
608 nIsBold != 0,
609 nIsItalic != 0,
610 nIsUnderline != 0,
611 nSize,
612 1.0);
614 // extract textual attributes (bold, italic in the name, etc.)
615 parseFontFamilyName(aResult);
616 // need to read font file?
617 if( nFileLen )
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 ),
631 uno::UNO_QUERY );
632 if( xMat.is() )
634 uno::Any aRes( xMat->getMaterial() );
635 if( aRes >>= aFD )
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;
646 aResult.size = 0;
650 catch( uno::Exception& )
654 if( aResult.familyName.isEmpty() )
656 // last fallback
657 aResult.familyName = "Arial";
658 aResult.isUnderline = false;
663 if (!m_xDev)
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() );
682 OUString aFileName;
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";
689 else
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) }
711 }));
713 return aSequence;
716 void Parser::readImage()
718 sal_Int32 nWidth, nHeight,nMaskColors;
719 readInt32(nWidth);
720 readInt32(nHeight);
721 readInt32(nMaskColors);
723 uno::Sequence<beans::PropertyValue> aImg( readImageImpl() );
725 if( nMaskColors )
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 );
745 else
746 m_pSink->drawImage( aImg );
749 void Parser::readMask()
751 sal_Int32 nWidth, nHeight, nInvert;
752 readInt32(nWidth);
753 readInt32(nHeight);
754 readInt32(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
772 m_nCharIndex = -1;
775 void Parser::readMaskedImage()
777 sal_Int32 nWidth, nHeight, nMaskWidth, nMaskHeight, nMaskInvert;
778 readInt32(nWidth);
779 readInt32(nHeight);
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;
792 readInt32(nWidth);
793 readInt32(nHeight);
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(),
811 rCmd.getLength() );
812 OSL_ASSERT(pEntry);
813 switch( pEntry->eKey )
815 case CLIPPATH:
816 m_pSink->intersectClip(readPath()); break;
817 case DRAWCHAR:
818 readChar(); break;
819 case DRAWIMAGE:
820 readImage(); break;
821 case DRAWLINK:
822 readLink(); break;
823 case DRAWMASK:
824 readMask(); break;
825 case DRAWMASKEDIMAGE:
826 readMaskedImage(); break;
827 case DRAWSOFTMASKEDIMAGE:
828 readSoftMaskedImage(); break;
829 case ENDPAGE:
830 m_pSink->endPage(); break;
831 case ENDTEXTOBJECT:
832 m_pSink->endText(); break;
833 case EOCLIPPATH:
834 m_pSink->intersectEoClip(readPath()); break;
835 case EOFILLPATH:
836 m_pSink->eoFillPath(readPath()); break;
837 case FILLPATH:
838 m_pSink->fillPath(readPath()); break;
839 case RESTORESTATE:
840 m_pSink->popState(); break;
841 case SAVESTATE:
842 m_pSink->pushState(); break;
843 case SETPAGENUM:
844 m_pSink->setPageNum( readInt32() ); break;
845 case STARTPAGE:
847 const double nWidth ( readDouble() );
848 const double nHeight( readDouble() );
849 m_pSink->startPage( geometry::RealSize2D( nWidth, nHeight ) );
850 break;
852 case STROKEPATH:
853 m_pSink->strokePath(readPath()); break;
854 case UPDATECTM:
855 readTransformation(); break;
856 case UPDATEFILLCOLOR:
857 m_pSink->setFillColor( readColor() ); break;
858 case UPDATEFLATNESS:
859 m_pSink->setFlatness( readDouble( ) ); break;
860 case UPDATEFONT:
861 readFont(); break;
862 case UPDATELINECAP:
863 readLineCap(); break;
864 case UPDATELINEDASH:
865 readLineDash(); break;
866 case UPDATELINEJOIN:
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:
875 break;
876 case SETTEXTRENDERMODE:
877 m_pSink->setTextRenderMode( readInt32() ); break;
879 case NONE:
880 default:
881 OSL_PRECOND(false,"Unknown input");
882 break;
885 // all consumed?
886 SAL_WARN_IF(m_nCharIndex!=-1, "sdext.pdfimport", "leftover scanner input");
889 } // namespace
891 static bool checkEncryption( const OUString& i_rPath,
892 const uno::Reference< task::XInteractionHandler >& i_xIHdl,
893 OUString& io_rPwd,
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() ));
902 if( pEntry )
904 pdfparse::PDFFile* pPDFFile = dynamic_cast<pdfparse::PDFFile*>(pEntry.get());
905 if( pPDFFile )
907 o_rIsEncrypted = pPDFFile->isEncrypted();
908 if( o_rIsEncrypted )
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() );
919 if( bAuthenticated )
920 bSuccess = true;
921 else
923 if( i_xIHdl.is() )
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
948 else
949 bSuccess = true;
952 return bSuccess;
955 class Buffering
957 static const int SIZE = 64*1024;
958 std::unique_ptr<char[]> aBuffer;
959 oslFileHandle& pOut;
960 size_t pos;
961 sal_uInt64 left;
963 public:
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;
970 while (count > 0)
972 if (left == 0)
974 nRes = osl_readFile(pOut, aBuffer.get(), SIZE, &left);
975 if (nRes != osl_File_E_None || left == 0)
977 *pBytesRead = nBytesRead;
978 return nRes;
980 pos = 0;
982 *pChar = aBuffer.get()[pos];
983 --count;
984 ++pos;
985 --left;
986 ++pChar;
987 ++nBytesRead;
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)
1001 OSL_ASSERT(rSink);
1003 OUString aSysUPath;
1004 if( osl_getSystemPathFromFileURL( rURL.pData, &aSysUPath.pData ) != osl_File_E_None )
1006 SAL_WARN(
1007 "sdext.pdfimport",
1008 "getSystemPathFromFileURL(" << rURL << ") failed");
1009 return false;
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 ) )
1018 SAL_INFO(
1019 "sdext.pdfimport",
1020 "checkEncryption(" << aSysUPath << ") failed");
1021 return false;
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)
1034 SAL_WARN(
1035 "sdext.pdfimport",
1036 "getSystemPathFromFileURL(" << errPathname << ") failed");
1037 return false;
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,
1054 args,
1055 nArgs,
1056 osl_Process_SEARCHPATH|osl_Process_HIDDEN,
1057 pSecurity,
1058 nullptr, nullptr, 0,
1059 &aProcess, &pIn, &pOut, &pErr);
1060 osl_freeSecurityHandle(pSecurity);
1062 bool bRet=true;
1065 if( eErr!=osl_Process_E_None )
1067 SAL_WARN(
1068 "sdext.pdfimport",
1069 "executeProcess of " << converterURL << " failed with "
1070 << +eErr);
1071 return false;
1074 if( pIn )
1076 OStringBuffer aBuf(256);
1077 if( bIsEncrypted )
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 );
1085 if( pOut && pErr )
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);
1092 OStringBuffer line;
1093 for( ;; )
1095 char aChar('\n');
1096 sal_uInt64 nBytesRead;
1097 oslFileError nRes;
1099 // skip garbage \r \n at start of line
1100 while( osl_File_E_None == (nRes = aBuffering.read(&aChar, 1, &nBytesRead)) &&
1101 nBytesRead == 1 &&
1102 (aChar == '\n' || aChar == '\r') ) ;
1103 if ( osl_File_E_None != nRes )
1104 break;
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 )
1115 break;
1116 if ( line.isEmpty() )
1117 break;
1119 aParser.parseLine(line.makeStringAndClear());
1123 catch( uno::Exception& )
1125 // crappy C file interface. need manual resource dealloc
1126 bRet = false;
1129 if( pIn )
1130 osl_closeFile(pIn);
1131 if( pOut )
1132 osl_closeFile(pOut);
1133 if( pErr )
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)
1143 if (info.Code != 0)
1145 SAL_WARN(
1146 "sdext.pdfimport",
1147 "getProcessInfo of " << converterURL
1148 << " failed with exit code " << info.Code);
1149 bRet = false;
1152 else
1154 SAL_WARN(
1155 "sdext.pdfimport",
1156 "getProcessInfo of " << converterURL << " failed with "
1157 << +eErr);
1158 bRet = false;
1161 else
1163 SAL_WARN(
1164 "sdext.pdfimport",
1165 "joinProcess of " << converterURL << " failed with " << +eErr);
1166 bRet = false;
1168 osl_freeProcessHandle(aProcess);
1169 return bRet;
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());
1181 OSL_ASSERT(rSink);
1183 // convert XInputStream to local temp file
1184 oslFileHandle aFile = nullptr;
1185 OUString aURL;
1186 if( osl_createTempFile( nullptr, &aFile, &aURL.pData ) != osl_File_E_None )
1187 return false;
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 );
1204 throw;
1206 if( nBytes > 0 )
1208 osl_writeFile( aFile, aBuf.getConstArray(), nBytes, &nWritten );
1209 if( nWritten != nBytes )
1211 bSuccess = false;
1212 break;
1216 while( nBytes == nBufSize );
1218 osl_closeFile( aFile );
1220 if ( bSuccess )
1221 bSuccess = xpdf_ImportFromFile( aURL, rSink, xIHdl, rPwd, xContext, rFilterOptions );
1222 osl_removeFile( aURL.pData );
1224 return bSuccess;
1229 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */