Version 6.1.0.2, tag libreoffice-6.1.0.2
[LibreOffice.git] / sdext / source / pdfimport / wrapper / wrapper.cxx
blob7d3d68901e371bb90c2590545c32601f21b52870
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 <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>
65 #include <memory>
66 #include <unordered_map>
67 #include <string.h>
68 #include <stdlib.h>
70 #include <rtl/bootstrap.h>
72 #include <rtl/character.hxx>
74 using namespace com::sun::star;
76 namespace pdfi
79 namespace
82 // identifier of the strings coming from the out-of-process xpdf
83 // converter
84 enum parseKey {
85 CLIPPATH,
86 DRAWCHAR,
87 DRAWIMAGE,
88 DRAWLINK,
89 DRAWMASK,
90 DRAWMASKEDIMAGE,
91 DRAWSOFTMASKEDIMAGE,
92 ENDPAGE,
93 ENDTEXTOBJECT,
94 EOCLIPPATH,
95 EOFILLPATH,
96 FILLPATH,
97 HYPERLINK,
98 INTERSECTCLIP,
99 INTERSECTEOCLIP,
100 POPSTATE,
101 PUSHSTATE,
102 RESTORESTATE,
103 SAVESTATE,
104 SETBLENDMODE,
105 SETFILLCOLOR,
106 SETFONT,
107 SETLINECAP,
108 SETLINEDASH,
109 SETLINEJOIN,
110 SETLINEWIDTH,
111 SETMITERLIMIT,
112 SETPAGENUM,
113 SETSTROKECOLOR,
114 SETTEXTRENDERMODE,
115 SETTRANSFORMATION,
116 STARTPAGE,
117 STROKEPATH,
118 UPDATEBLENDMODE,
119 UPDATECTM,
120 UPDATEFILLCOLOR,
121 UPDATEFILLOPACITY,
122 UPDATEFLATNESS,
123 UPDATEFONT,
124 UPDATELINECAP,
125 UPDATELINEDASH,
126 UPDATELINEJOIN,
127 UPDATELINEWIDTH,
128 UPDATEMITERLIMIT,
129 UPDATESTROKECOLOR,
130 UPDATESTROKEOPACITY,
131 NONE
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"
138 #endif
139 #include <hash.cxx>
140 #if defined _MSC_VER && defined __clang__
141 #pragma clang diagnostic pop
142 #endif
144 class Parser
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;
153 OString m_aLine;
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 );
164 double readDouble();
165 void readBinaryData( uno::Sequence<sal_Int8>& rBuf );
167 uno::Reference<rendering::XPolyPolygon2D> readPath();
169 void readChar();
170 void readLineCap();
171 void readLineDash();
172 void readLineJoin();
173 void readTransformation();
174 rendering::ARGBColor readColor();
175 static void parseFontFamilyName( FontAttributes& aResult );
176 void readFont();
177 uno::Sequence<beans::PropertyValue> readImageImpl();
179 void readImage();
180 void readMask();
181 void readLink();
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);
190 public:
191 Parser( const ContentSinkSharedPtr& rSink,
192 oslFileHandle pErr,
193 const uno::Reference<uno::XComponentContext>& xContext ) :
194 m_xContext(xContext),
195 m_pSink(rSink),
196 m_pErr(pErr),
197 m_aLine(),
198 m_aFontMap(101),
199 m_nNextToken(-1),
200 m_nCharIndex(-1)
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);
226 pWrite += nLen;
227 *pWrite = cNext == 'n' ? '\n' : (cNext == 'r' ? '\r' : '\\');
228 ++pWrite;
229 pCur = pRead = pCur + 2;
231 else
233 // Just continue on the next character. The current
234 // block will be copied the next time it goes through the
235 // 'if' branch.
236 ++pCur;
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);
244 pWrite += nLen;
246 *pWrite = '\0';
248 OString aResult(pBuffer.get());
249 return aResult;
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;
289 while( nFileLen &&
290 osl_File_E_None == (nRes=osl_readFile( m_pErr, pBuf, nFileLen, &nBytesRead )) )
292 pBuf += 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;
322 double nX, nY;
323 readDouble( nX );
324 readDouble( nY );
325 readInt32( nCurveFlag );
327 aSubPath.append(basegfx::B2DPoint(nX,nY));
328 if( nCurveFlag )
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?
350 nDummy=m_nCharIndex;
351 aCurrToken = m_aLine.getToken(m_nNextToken,' ',nDummy);
354 aResult.append( aSubPath );
355 if( m_nCharIndex != -1 )
356 readNextToken();
359 return static_cast<rendering::XLinePolyPolygon2D*>(
360 new basegfx::unotools::UnoPolyPolygon(aResult));
363 void Parser::readChar()
365 double fontSize;
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);
379 OString aChars;
381 if (m_nCharIndex != -1)
382 aChars = lcl_unescapeLineFeeds( m_aLine.copy( m_nCharIndex ) );
384 // chars gobble up rest of line
385 m_nCharIndex = -1;
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() )
396 default:
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 );
410 return;
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() )
429 default:
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);
457 return aRes;
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)
466 return 0;
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)
472 return 0;
474 rResult.isItalic |= bItalic;
475 rResult.isBold |= bBold;
476 return nAttribLen;
479 sal_Int32 Parser::parseFontRemoveSuffix(
480 const sal_Unicode* pCopy, sal_Int32 nCopyLen,
481 const char* pAttrib, sal_Int32 nAttribLen)
483 if (nCopyLen < nAttribLen)
484 return 0;
485 for (sal_Int32 i = 0; i < nAttribLen; ++i)
486 if ( pCopy[nCopyLen - nAttribLen + i] != pAttrib[i] )
487 return 0;
488 return nAttribLen;
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] == '+' )
500 pCopy += 7;
501 nLen -= 7;
504 // TODO: Looks like this block needs to be refactored
505 while( nLen )
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");
519 nLen -= nAttribLen;
520 pCopy += nAttribLen;
522 else if (parseFontCheckForString(pCopy, nLen, RTL_CONSTASCII_STRINGPARAM("-LightOblique"), rResult, true, false))
524 sal_Int32 nAttribLen = RTL_CONSTASCII_LENGTH("-LightOblique");
525 nLen -= nAttribLen;
526 pCopy += nAttribLen;
528 else if (parseFontCheckForString(pCopy, nLen, RTL_CONSTASCII_STRINGPARAM("-Light"), rResult, false, false))
530 sal_Int32 nAttribLen = RTL_CONSTASCII_LENGTH("-Light");
531 nLen -= nAttribLen;
532 pCopy += nAttribLen;
534 else if (parseFontCheckForString(pCopy, nLen, RTL_CONSTASCII_STRINGPARAM("-BoldOblique"), rResult, true, true))
536 sal_Int32 nAttribLen = RTL_CONSTASCII_LENGTH("-BoldOblique");
537 nLen -= nAttribLen;
538 pCopy += nAttribLen;
540 else if (parseFontCheckForString(pCopy, nLen, RTL_CONSTASCII_STRINGPARAM("-Bold"), rResult, false, true))
542 sal_Int32 nAttribLen = RTL_CONSTASCII_LENGTH("-Bold");
543 nLen -= nAttribLen;
544 pCopy += nAttribLen;
546 else if (parseFontCheckForString(pCopy, nLen, RTL_CONSTASCII_STRINGPARAM("Bold"), rResult, false, true))
548 sal_Int32 nAttribLen = RTL_CONSTASCII_LENGTH("Bold");
549 nLen -= nAttribLen;
550 pCopy += nAttribLen;
552 else if (parseFontCheckForString(pCopy, nLen, RTL_CONSTASCII_STRINGPARAM("-Roman"), rResult, false, false))
554 sal_Int32 nAttribLen = RTL_CONSTASCII_LENGTH("-Roman");
555 nLen -= nAttribLen;
556 pCopy += nAttribLen;
558 else if (parseFontCheckForString(pCopy, nLen, RTL_CONSTASCII_STRINGPARAM("-Oblique"), rResult, true, false))
560 sal_Int32 nAttribLen = RTL_CONSTASCII_LENGTH("-Oblique");
561 nLen -= nAttribLen;
562 pCopy += nAttribLen;
564 else if (parseFontCheckForString(pCopy, nLen, RTL_CONSTASCII_STRINGPARAM("-Reg"), rResult, false, false))
566 sal_Int32 nAttribLen = RTL_CONSTASCII_LENGTH("-Reg");
567 nLen -= nAttribLen;
568 pCopy += nAttribLen;
570 else if(nLen > 0)
572 if( *pCopy != '-' )
573 aNewFamilyName.append( *pCopy );
574 pCopy++;
575 nLen--;
578 rResult.familyName = aNewFamilyName.makeStringAndClear();
581 void Parser::readFont()
583 OString aFontName;
584 sal_Int64 nFontID;
585 sal_Int32 nIsEmbedded, nIsBold, nIsItalic, nIsUnderline, nFileLen;
586 double nSize;
588 readInt64(nFontID);
589 readInt32(nIsEmbedded);
590 readInt32(nIsBold);
591 readInt32(nIsItalic);
592 readInt32(nIsUnderline);
593 readDouble(nSize);
594 readInt32(nFileLen);
596 nSize = nSize < 0.0 ? -nSize : nSize;
597 aFontName = lcl_unescapeLineFeeds( m_aLine.copy( m_nCharIndex ) );
599 // name gobbles up rest of line
600 m_nCharIndex = -1;
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);
607 aRes.size = nSize;
608 m_pSink->setFont( aRes );
610 return;
613 // yet unknown font - get info and add to map
614 FontAttributes aResult( OStringToOUString( aFontName,
615 RTL_TEXTENCODING_UTF8 ),
616 nIsBold != 0,
617 nIsItalic != 0,
618 nIsUnderline != 0,
619 nSize,
620 1.0);
622 // extract textual attributes (bold, italic in the name, etc.)
623 parseFontFamilyName(aResult);
624 // need to read font file?
625 if( nFileLen )
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 ),
639 uno::UNO_QUERY );
640 if( xMat.is() )
642 uno::Any aRes( xMat->getMaterial() );
643 if( aRes >>= aFD )
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;
654 aResult.size = 0;
658 catch( uno::Exception& )
662 if( aResult.familyName.isEmpty() )
664 // last fallback
665 aResult.familyName = "Arial";
666 aResult.isUnderline = false;
671 if (!m_xDev)
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() );
690 OUString aFileName;
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";
697 else
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) }
719 }));
721 return aSequence;
724 void Parser::readImage()
726 sal_Int32 nWidth, nHeight,nMaskColors;
727 readInt32(nWidth);
728 readInt32(nHeight);
729 readInt32(nMaskColors);
731 uno::Sequence<beans::PropertyValue> aImg( readImageImpl() );
733 if( nMaskColors )
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 );
753 else
754 m_pSink->drawImage( aImg );
757 void Parser::readMask()
759 sal_Int32 nWidth, nHeight, nInvert;
760 readInt32(nWidth);
761 readInt32(nHeight);
762 readInt32(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
780 m_nCharIndex = -1;
783 void Parser::readMaskedImage()
785 sal_Int32 nWidth, nHeight, nMaskWidth, nMaskHeight, nMaskInvert;
786 readInt32(nWidth);
787 readInt32(nHeight);
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;
800 readInt32(nWidth);
801 readInt32(nHeight);
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(),
819 rCmd.getLength() );
820 OSL_ASSERT(pEntry);
821 switch( pEntry->eKey )
823 case CLIPPATH:
824 m_pSink->intersectClip(readPath()); break;
825 case DRAWCHAR:
826 readChar(); break;
827 case DRAWIMAGE:
828 readImage(); break;
829 case DRAWLINK:
830 readLink(); break;
831 case DRAWMASK:
832 readMask(); break;
833 case DRAWMASKEDIMAGE:
834 readMaskedImage(); break;
835 case DRAWSOFTMASKEDIMAGE:
836 readSoftMaskedImage(); break;
837 case ENDPAGE:
838 m_pSink->endPage(); break;
839 case ENDTEXTOBJECT:
840 m_pSink->endText(); break;
841 case EOCLIPPATH:
842 m_pSink->intersectEoClip(readPath()); break;
843 case EOFILLPATH:
844 m_pSink->eoFillPath(readPath()); break;
845 case FILLPATH:
846 m_pSink->fillPath(readPath()); break;
847 case RESTORESTATE:
848 m_pSink->popState(); break;
849 case SAVESTATE:
850 m_pSink->pushState(); break;
851 case SETPAGENUM:
852 m_pSink->setPageNum( readInt32() ); break;
853 case STARTPAGE:
855 const double nWidth ( readDouble() );
856 const double nHeight( readDouble() );
857 m_pSink->startPage( geometry::RealSize2D( nWidth, nHeight ) );
858 break;
860 case STROKEPATH:
861 m_pSink->strokePath(readPath()); break;
862 case UPDATECTM:
863 readTransformation(); break;
864 case UPDATEFILLCOLOR:
865 m_pSink->setFillColor( readColor() ); break;
866 case UPDATEFLATNESS:
867 m_pSink->setFlatness( readDouble( ) ); break;
868 case UPDATEFONT:
869 readFont(); break;
870 case UPDATELINECAP:
871 readLineCap(); break;
872 case UPDATELINEDASH:
873 readLineDash(); break;
874 case UPDATELINEJOIN:
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:
883 break;
884 case SETTEXTRENDERMODE:
885 m_pSink->setTextRenderMode( readInt32() ); break;
887 case NONE:
888 default:
889 OSL_PRECOND(false,"Unknown input");
890 break;
893 // all consumed?
894 SAL_WARN_IF(m_nCharIndex!=-1, "sdext.pdfimport", "leftover scanner input");
897 } // namespace
899 static bool checkEncryption( const OUString& i_rPath,
900 const uno::Reference< task::XInteractionHandler >& i_xIHdl,
901 OUString& io_rPwd,
902 bool& o_rIsEncrypted,
903 const OUString& i_rDocName
906 bool bSuccess = false;
907 OString aPDFFile;
908 aPDFFile = OUStringToOString( i_rPath, osl_getThreadTextEncoding() );
910 pdfparse::PDFReader aParser;
911 std::unique_ptr<pdfparse::PDFEntry> pEntry( pdfparse::PDFReader::read( aPDFFile.getStr() ));
912 if( pEntry )
914 pdfparse::PDFFile* pPDFFile = dynamic_cast<pdfparse::PDFFile*>(pEntry.get());
915 if( pPDFFile )
917 o_rIsEncrypted = pPDFFile->isEncrypted();
918 if( o_rIsEncrypted )
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() );
929 if( bAuthenticated )
930 bSuccess = true;
931 else
933 if( i_xIHdl.is() )
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
958 else
959 bSuccess = true;
962 return bSuccess;
965 class Buffering
967 static const int SIZE = 64*1024;
968 std::unique_ptr<char[]> aBuffer;
969 oslFileHandle& pOut;
970 size_t pos;
971 sal_uInt64 left;
973 public:
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;
980 while (count > 0)
982 if (left == 0)
984 nRes = osl_readFile(pOut, aBuffer.get(), SIZE, &left);
985 if (nRes != osl_File_E_None || left == 0)
987 *pBytesRead = nBytesRead;
988 return nRes;
990 pos = 0;
992 *pChar = aBuffer.get()[pos];
993 --count;
994 ++pos;
995 --left;
996 ++pChar;
997 ++nBytesRead;
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 )
1019 OSL_ASSERT(rSink);
1021 OUString aSysUPath;
1022 if( osl_getSystemPathFromFileURL( rURL.pData, &aSysUPath.pData ) != osl_File_E_None )
1024 SAL_WARN(
1025 "sdext.pdfimport",
1026 "getSystemPathFromFileURL(" << rURL << ") failed");
1027 return false;
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 ) )
1036 SAL_INFO(
1037 "sdext.pdfimport",
1038 "checkEncryption(" << aSysUPath << ") failed");
1039 return false;
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)
1065 SAL_WARN(
1066 "sdext.pdfimport",
1067 "getSystemPathFromFileURL(" << errPathname << ") failed");
1068 return false;
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,
1085 args,
1086 nArgs,
1087 osl_Process_SEARCHPATH|osl_Process_HIDDEN,
1088 pSecurity,
1089 nullptr, nullptr, 0,
1090 &aProcess, &pIn, &pOut, &pErr);
1091 osl_freeSecurityHandle(pSecurity);
1093 bool bRet=true;
1096 if( eErr!=osl_Process_E_None )
1098 SAL_WARN(
1099 "sdext.pdfimport",
1100 "executeProcess of " << converterURL << " failed with "
1101 << +eErr);
1102 return false;
1105 if( pIn )
1107 OStringBuffer aBuf(256);
1108 if( bIsEncrypted )
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 );
1116 if( pOut && pErr )
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);
1123 OStringBuffer line;
1124 for( ;; )
1126 char aChar('\n');
1127 sal_uInt64 nBytesRead;
1128 oslFileError nRes;
1130 // skip garbage \r \n at start of line
1131 while( osl_File_E_None == (nRes = aBuffering.read(&aChar, 1, &nBytesRead)) &&
1132 nBytesRead == 1 &&
1133 (aChar == '\n' || aChar == '\r') ) ;
1134 if ( osl_File_E_None != nRes )
1135 break;
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 )
1146 break;
1147 if ( line.isEmpty() )
1148 break;
1150 aParser.parseLine(line.makeStringAndClear());
1154 catch( uno::Exception& )
1156 // crappy C file interface. need manual resource dealloc
1157 bRet = false;
1160 if( pIn )
1161 osl_closeFile(pIn);
1162 if( pOut )
1163 osl_closeFile(pOut);
1164 if( pErr )
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)
1174 if (info.Code != 0)
1176 SAL_WARN(
1177 "sdext.pdfimport",
1178 "getProcessInfo of " << converterURL
1179 << " failed with exit code " << info.Code);
1180 bRet = false;
1183 else
1185 SAL_WARN(
1186 "sdext.pdfimport",
1187 "getProcessInfo of " << converterURL << " failed with "
1188 << +eErr);
1189 bRet = false;
1192 else
1194 SAL_WARN(
1195 "sdext.pdfimport",
1196 "joinProcess of " << converterURL << " failed with " << +eErr);
1197 bRet = false;
1199 osl_freeProcessHandle(aProcess);
1200 return bRet;
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());
1212 OSL_ASSERT(rSink);
1214 // convert XInputStream to local temp file
1215 oslFileHandle aFile = nullptr;
1216 OUString aURL;
1217 if( osl_createTempFile( nullptr, &aFile, &aURL.pData ) != osl_File_E_None )
1218 return false;
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 );
1235 throw;
1237 if( nBytes > 0 )
1239 osl_writeFile( aFile, aBuf.getConstArray(), nBytes, &nWritten );
1240 if( nWritten != nBytes )
1242 bSuccess = false;
1243 break;
1247 while( nBytes == nBufSize );
1249 osl_closeFile( aFile );
1251 if ( bSuccess )
1252 bSuccess = xpdf_ImportFromFile( aURL, rSink, xIHdl, rPwd, xContext, rFilterOptions );
1253 osl_removeFile( aURL.pData );
1255 return bSuccess;
1260 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */