1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
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/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
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 .
18  */
19 
20 #include <config_folders.h>
21 
22 #include <contentsink.hxx>
23 #include <pdfparse.hxx>
24 #include <pdfihelper.hxx>
25 #include <wrapper.hxx>
26 
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>
37 
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>
51 
52 #include <basegfx/point/b2dpoint.hxx>
53 #include <basegfx/polygon/b2dpolypolygon.hxx>
54 #include <basegfx/polygon/b2dpolygon.hxx>
55 #include <basegfx/utils/unopolypolygon.hxx>
56 
57 #include <vcl/metric.hxx>
58 #include <vcl/font.hxx>
59 #include <vcl/virdev.hxx>
60 
61 #include <memory>
62 #include <unordered_map>
63 #include <string.h>
64 #include <stdlib.h>
65 
66 #include <rtl/character.hxx>
67 
68 using namespace com::sun::star;
69 
70 namespace pdfi
71 {
72 
73 namespace
74 {
75 
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
126 };
127 
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
137 
138 class Parser
139 {
140     typedef std::unordered_map< sal_Int64,
141                            FontAttributes > FontMapType;
142 
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;
151 
152 
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 );
160 
161     uno::Reference<rendering::XPolyPolygon2D> readPath();
162 
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();
172 
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);
183 
184 public:
Parser(const ContentSinkSharedPtr & rSink,oslFileHandle pErr,const uno::Reference<uno::XComponentContext> & xContext)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)
195     {}
196 
197     void parseLine( const OString& rLine );
198 };
199 
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'.
203   */
lcl_unescapeLineFeeds(const OString & i_rStr)204 OString lcl_unescapeLineFeeds(const OString& i_rStr)
205 {
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]);
209 
210     const sal_Char* pRead(pOrig);
211     sal_Char* pWrite(pBuffer.get());
212     const sal_Char* pCur(pOrig);
213     while ((pCur = strchr(pCur, '\\')) != nullptr)
214     {
215         const sal_Char cNext(pCur[1]);
216         if (cNext == 'n' || cNext == 'r' || cNext == '\\')
217         {
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;
224         }
225         else
226         {
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;
231         }
232     }
233     // maybe there are some data to copy yet
234     if (sal::static_int_cast<size_t>(pRead - pOrig) < nOrigLen)
235     {
236         const size_t nLen(nOrigLen - (pRead - pOrig));
237         strncpy(pWrite, pRead, nLen);
238         pWrite += nLen;
239     }
240     *pWrite = '\0';
241 
242     OString aResult(pBuffer.get());
243     return aResult;
244 }
245 
readNextToken()246 OString Parser::readNextToken()
247 {
248     OSL_PRECOND(m_nCharIndex!=-1,"insufficient input");
249     return m_aLine.getToken(m_nNextToken,' ',m_nCharIndex);
250 }
251 
readInt32(sal_Int32 & o_Value)252 void Parser::readInt32( sal_Int32& o_Value )
253 {
254     o_Value = readNextToken().toInt32();
255 }
256 
readInt32()257 sal_Int32 Parser::readInt32()
258 {
259     return readNextToken().toInt32();
260 }
261 
readInt64(sal_Int64 & o_Value)262 void Parser::readInt64( sal_Int64& o_Value )
263 {
264     o_Value = readNextToken().toInt64();
265 }
266 
readDouble(double & o_Value)267 void Parser::readDouble( double& o_Value )
268 {
269     o_Value = readNextToken().toDouble();
270 }
271 
readDouble()272 double Parser::readDouble()
273 {
274     return readNextToken().toDouble();
275 }
276 
readBinaryData(uno::Sequence<sal_Int8> & rBuf)277 void Parser::readBinaryData( uno::Sequence<sal_Int8>& rBuf )
278 {
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 )) )
285     {
286         pBuf += nBytesRead;
287         nFileLen -= sal::static_int_cast<sal_Int32>(nBytesRead);
288     }
289 
290     OSL_PRECOND(nRes==osl_File_E_None, "inconsistent data");
291 }
292 
readPath()293 uno::Reference<rendering::XPolyPolygon2D> Parser::readPath()
294 {
295     const OString aSubPathMarker( "subpath" );
296 
297     if( readNextToken() != aSubPathMarker )
298         OSL_PRECOND(false, "broken path");
299 
300     basegfx::B2DPolyPolygon aResult;
301     while( m_nCharIndex != -1 )
302     {
303         basegfx::B2DPolygon aSubPath;
304 
305         sal_Int32 nClosedFlag;
306         readInt32( nClosedFlag );
307         aSubPath.setClosed( nClosedFlag != 0 );
308 
309         sal_Int32 nContiguousControlPoints(0);
310         sal_Int32 nDummy=m_nCharIndex;
311         OString aCurrToken( m_aLine.getToken(m_nNextToken,' ',nDummy) );
312 
313         while( m_nCharIndex != -1 && aCurrToken != aSubPathMarker )
314         {
315             sal_Int32 nCurveFlag;
316             double    nX, nY;
317             readDouble( nX );
318             readDouble( nY );
319             readInt32(  nCurveFlag );
320 
321             aSubPath.append(basegfx::B2DPoint(nX,nY));
322             if( nCurveFlag )
323             {
324                 ++nContiguousControlPoints;
325             }
326             else if( nContiguousControlPoints )
327             {
328                 OSL_PRECOND(nContiguousControlPoints==2,"broken bezier path");
329 
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);
339 
340                 nContiguousControlPoints=0;
341             }
342 
343             // one token look-ahead (new subpath or more points?
344             nDummy=m_nCharIndex;
345             aCurrToken = m_aLine.getToken(m_nNextToken,' ',nDummy);
346         }
347 
348         aResult.append( aSubPath );
349         if( m_nCharIndex != -1 )
350             readNextToken();
351     }
352 
353     return static_cast<rendering::XLinePolyPolygon2D*>(
354         new basegfx::unotools::UnoPolyPolygon(aResult));
355 }
356 
readChar()357 void Parser::readChar()
358 {
359     double fontSize;
360     geometry::Matrix2D aUnoMatrix;
361     geometry::RealRectangle2D aRect;
362 
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);
372 
373     OString aChars;
374 
375     if (m_nCharIndex != -1)
376         aChars = lcl_unescapeLineFeeds( m_aLine.copy( m_nCharIndex ) );
377 
378     // chars gobble up rest of line
379     m_nCharIndex = -1;
380 
381     m_pSink->drawGlyphs(OStringToOUString(aChars, RTL_TEXTENCODING_UTF8),
382         aRect, aUnoMatrix, fontSize);
383 }
384 
readLineCap()385 void Parser::readLineCap()
386 {
387     sal_Int8 nCap(rendering::PathCapType::BUTT);
388     switch( readInt32() )
389     {
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;
394     }
395     m_pSink->setLineCap(nCap);
396 }
397 
readLineDash()398 void Parser::readLineDash()
399 {
400     if( m_nCharIndex == -1 )
401     {
402         m_pSink->setLineDash( uno::Sequence<double>(), 0.0 );
403         return;
404     }
405 
406     const double nOffset(readDouble());
407     const sal_Int32 nLen(readInt32());
408 
409     uno::Sequence<double> aDashArray(nLen);
410     double* pArray=aDashArray.getArray();
411     for( sal_Int32 i=0; i<nLen; ++i )
412         *pArray++ = readDouble();
413 
414     m_pSink->setLineDash( aDashArray, nOffset );
415 }
416 
readLineJoin()417 void Parser::readLineJoin()
418 {
419     sal_Int8 nJoin(rendering::PathJoinType::MITER);
420     switch( readInt32() )
421     {
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;
426     }
427     m_pSink->setLineJoin(nJoin);
428 }
429 
readTransformation()430 void Parser::readTransformation()
431 {
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 );
440 }
441 
readColor()442 rendering::ARGBColor Parser::readColor()
443 {
444     rendering::ARGBColor aRes;
445     readDouble(aRes.Red);
446     readDouble(aRes.Green);
447     readDouble(aRes.Blue);
448     readDouble(aRes.Alpha);
449     return aRes;
450 }
451 
parseFontCheckForString(const sal_Unicode * pCopy,sal_Int32 nCopyLen,const char * pAttrib,sal_Int32 nAttribLen,FontAttributes & rResult,bool bItalic,bool bBold)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)
456 {
457     if (nCopyLen < nAttribLen)
458         return 0;
459     for (sal_Int32 i = 0; i < nAttribLen; ++i)
460     {
461         sal_uInt32 nCode = pAttrib[i];
462         if (rtl::toAsciiLowerCase(pCopy[i]) != nCode
463             && rtl::toAsciiUpperCase(pCopy[i]) != nCode)
464             return 0;
465     }
466     rResult.isItalic |= bItalic;
467     rResult.isBold |= bBold;
468     return nAttribLen;
469 }
470 
parseFontRemoveSuffix(const sal_Unicode * pCopy,sal_Int32 nCopyLen,const char * pAttrib,sal_Int32 nAttribLen)471 sal_Int32 Parser::parseFontRemoveSuffix(
472     const sal_Unicode* pCopy, sal_Int32 nCopyLen,
473     const char* pAttrib, sal_Int32 nAttribLen)
474 {
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;
481 }
482 
parseFontFamilyName(FontAttributes & rResult)483 void Parser::parseFontFamilyName( FontAttributes& rResult )
484 {
485     OUStringBuffer aNewFamilyName( rResult.familyName.getLength() );
486 
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] == '+' )
491     {
492         pCopy += 7;
493         nLen -= 7;
494     }
495 
496     // TODO: Looks like this block needs to be refactored
497     while( nLen )
498     {
499         if (parseFontRemoveSuffix(pCopy, nLen, RTL_CONSTASCII_STRINGPARAM("PSMT")))
500         {
501             nLen -= RTL_CONSTASCII_LENGTH("PSMT");
502         }
503         else if (parseFontRemoveSuffix(pCopy, nLen, RTL_CONSTASCII_STRINGPARAM("MT")))
504         {
505             nLen -= RTL_CONSTASCII_LENGTH("MT");
506         }
507 
508         if (parseFontCheckForString(pCopy, nLen, RTL_CONSTASCII_STRINGPARAM("Italic"), rResult, true, false))
509         {
510             sal_Int32 nAttribLen = RTL_CONSTASCII_LENGTH("Italic");
511             nLen -= nAttribLen;
512             pCopy += nAttribLen;
513         }
514         else if (parseFontCheckForString(pCopy, nLen, RTL_CONSTASCII_STRINGPARAM("-LightOblique"), rResult, true, false))
515         {
516             sal_Int32 nAttribLen = RTL_CONSTASCII_LENGTH("-LightOblique");
517             nLen -= nAttribLen;
518             pCopy += nAttribLen;
519         }
520         else if (parseFontCheckForString(pCopy, nLen, RTL_CONSTASCII_STRINGPARAM("-Light"), rResult, false, false))
521         {
522             sal_Int32 nAttribLen = RTL_CONSTASCII_LENGTH("-Light");
523             nLen -= nAttribLen;
524             pCopy += nAttribLen;
525         }
526         else if (parseFontCheckForString(pCopy, nLen, RTL_CONSTASCII_STRINGPARAM("-BoldOblique"), rResult, true, true))
527         {
528             sal_Int32 nAttribLen = RTL_CONSTASCII_LENGTH("-BoldOblique");
529             nLen -= nAttribLen;
530             pCopy += nAttribLen;
531         }
532         else if (parseFontCheckForString(pCopy, nLen, RTL_CONSTASCII_STRINGPARAM("-Bold"), rResult, false, true))
533         {
534             sal_Int32 nAttribLen = RTL_CONSTASCII_LENGTH("-Bold");
535             nLen -= nAttribLen;
536             pCopy += nAttribLen;
537         }
538         else if (parseFontCheckForString(pCopy, nLen, RTL_CONSTASCII_STRINGPARAM("Bold"), rResult, false, true))
539         {
540             sal_Int32 nAttribLen = RTL_CONSTASCII_LENGTH("Bold");
541             nLen -= nAttribLen;
542             pCopy += nAttribLen;
543         }
544         else if (parseFontCheckForString(pCopy, nLen, RTL_CONSTASCII_STRINGPARAM("-Roman"), rResult, false, false))
545         {
546             sal_Int32 nAttribLen = RTL_CONSTASCII_LENGTH("-Roman");
547             nLen -= nAttribLen;
548             pCopy += nAttribLen;
549         }
550         else if (parseFontCheckForString(pCopy, nLen, RTL_CONSTASCII_STRINGPARAM("-Oblique"), rResult, true, false))
551         {
552             sal_Int32 nAttribLen = RTL_CONSTASCII_LENGTH("-Oblique");
553             nLen -= nAttribLen;
554             pCopy += nAttribLen;
555         }
556         else if (parseFontCheckForString(pCopy, nLen, RTL_CONSTASCII_STRINGPARAM("-Reg"), rResult, false, false))
557         {
558             sal_Int32 nAttribLen = RTL_CONSTASCII_LENGTH("-Reg");
559             nLen -= nAttribLen;
560             pCopy += nAttribLen;
561         }
562         else if(nLen > 0)
563         {
564             if( *pCopy != '-' )
565                 aNewFamilyName.append( *pCopy );
566             pCopy++;
567             nLen--;
568         }
569     }
570     rResult.familyName = aNewFamilyName.makeStringAndClear();
571 }
572 
readFont()573 void Parser::readFont()
574 {
575     OString aFontName;
576     sal_Int64      nFontID;
577     sal_Int32      nIsEmbedded, nIsBold, nIsItalic, nIsUnderline, nFileLen;
578     double         nSize;
579 
580     readInt64(nFontID);
581     readInt32(nIsEmbedded);
582     readInt32(nIsBold);
583     readInt32(nIsItalic);
584     readInt32(nIsUnderline);
585     readDouble(nSize);
586     readInt32(nFileLen);
587 
588     nSize = nSize < 0.0 ? -nSize : nSize;
589     aFontName = lcl_unescapeLineFeeds( m_aLine.copy( m_nCharIndex ) );
590 
591     // name gobbles up rest of line
592     m_nCharIndex = -1;
593 
594     FontMapType::const_iterator pFont( m_aFontMap.find(nFontID) );
595     if( pFont != m_aFontMap.end() )
596     {
597         OSL_PRECOND(nFileLen==0,"font data for known font");
598         FontAttributes aRes(pFont->second);
599         aRes.size = nSize;
600         m_pSink->setFont( aRes );
601 
602         return;
603     }
604 
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);
613 
614     // extract textual attributes (bold, italic in the name, etc.)
615     parseFontFamilyName(aResult);
616     // need to read font file?
617     if( nFileLen )
618     {
619         uno::Sequence<sal_Int8> aFontFile(nFileLen);
620         readBinaryData( aFontFile );
621 
622         awt::FontDescriptor aFD;
623         uno::Sequence< uno::Any > aArgs(1);
624         aArgs[0] <<= aFontFile;
625 
626         try
627         {
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() )
633             {
634                 uno::Any aRes( xMat->getMaterial() );
635                 if( aRes >>= aFD )
636                 {
637                     if (!aFD.Name.isEmpty())
638                     {
639                         aResult.familyName = aFD.Name;
640                         parseFontFamilyName(aResult);
641                     }
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;
647                 }
648             }
649         }
650         catch( uno::Exception& )
651         {
652         }
653 
654         if( aResult.familyName.isEmpty() )
655         {
656             // last fallback
657             aResult.familyName  = "Arial";
658             aResult.isUnderline = false;
659         }
660 
661     }
662 
663     if (!m_xDev)
664         m_xDev.disposeAndReset(VclPtr<VirtualDevice>::Create());
665 
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;
670 
671     m_aFontMap[nFontID] = aResult;
672 
673     aResult.size = nSize;
674     m_pSink->setFont(aResult);
675 }
676 
readImageImpl()677 uno::Sequence<beans::PropertyValue> Parser::readImageImpl()
678 {
679     OString aToken = readNextToken();
680     const sal_Int32 nImageSize( readInt32() );
681 
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
690     {
691         SAL_WARN_IF(aToken != "PPM","sdext.pdfimport","Invalid bitmap format");
692         aFileName = "DUMMY.PPM";
693     }
694 
695     uno::Sequence<sal_Int8> aDataSequence(nImageSize);
696     readBinaryData( aDataSequence );
697 
698     uno::Sequence< uno::Any > aStreamCreationArgs(1);
699     aStreamCreationArgs[0] <<= aDataSequence;
700 
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 );
706 
707     uno::Sequence<beans::PropertyValue> aSequence( comphelper::InitPropertySequence({
708             { "URL", uno::makeAny(aFileName) },
709             { "InputStream", uno::makeAny( xDataStream ) },
710             { "InputSequence", uno::makeAny(aDataSequence) }
711         }));
712 
713     return aSequence;
714 }
715 
readImage()716 void Parser::readImage()
717 {
718     sal_Int32 nWidth, nHeight,nMaskColors;
719     readInt32(nWidth);
720     readInt32(nHeight);
721     readInt32(nMaskColors);
722 
723     uno::Sequence<beans::PropertyValue> aImg( readImageImpl() );
724 
725     if( nMaskColors )
726     {
727         uno::Sequence<sal_Int8> aDataSequence(nMaskColors);
728         readBinaryData( aDataSequence );
729 
730         uno::Sequence<uno::Any> aMaskRanges(2);
731 
732         uno::Sequence<double> aMinRange(nMaskColors/2);
733         uno::Sequence<double> aMaxRange(nMaskColors/2);
734         for( sal_Int32 i=0; i<nMaskColors/2; ++i )
735         {
736             aMinRange[i] = aDataSequence[i] / 255.0;
737             aMaxRange[i] = aDataSequence[i+nMaskColors/2] / 255.0;
738         }
739 
740         aMaskRanges[0] <<= aMinRange;
741         aMaskRanges[1] <<= aMaxRange;
742 
743         m_pSink->drawColorMaskedImage( aImg, aMaskRanges );
744     }
745     else
746         m_pSink->drawImage( aImg );
747 }
748 
readMask()749 void Parser::readMask()
750 {
751     sal_Int32 nWidth, nHeight, nInvert;
752     readInt32(nWidth);
753     readInt32(nHeight);
754     readInt32(nInvert);
755 
756     m_pSink->drawMask( readImageImpl(), nInvert != 0);
757 }
758 
readLink()759 void Parser::readLink()
760 {
761     geometry::RealRectangle2D aBounds;
762     readDouble(aBounds.X1);
763     readDouble(aBounds.Y1);
764     readDouble(aBounds.X2);
765     readDouble(aBounds.Y2);
766 
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;
773 }
774 
readMaskedImage()775 void Parser::readMaskedImage()
776 {
777     sal_Int32 nWidth, nHeight, nMaskWidth, nMaskHeight, nMaskInvert;
778     readInt32(nWidth);
779     readInt32(nHeight);
780     readInt32(nMaskWidth);
781     readInt32(nMaskHeight);
782     readInt32(nMaskInvert);
783 
784     const uno::Sequence<beans::PropertyValue> aImage( readImageImpl() );
785     const uno::Sequence<beans::PropertyValue> aMask ( readImageImpl() );
786     m_pSink->drawMaskedImage( aImage, aMask, nMaskInvert != 0 );
787 }
788 
readSoftMaskedImage()789 void Parser::readSoftMaskedImage()
790 {
791     sal_Int32 nWidth, nHeight, nMaskWidth, nMaskHeight;
792     readInt32(nWidth);
793     readInt32(nHeight);
794     readInt32(nMaskWidth);
795     readInt32(nMaskHeight);
796 
797     const uno::Sequence<beans::PropertyValue> aImage( readImageImpl() );
798     const uno::Sequence<beans::PropertyValue> aMask ( readImageImpl() );
799     m_pSink->drawAlphaMaskedImage( aImage, aMask );
800 }
801 
parseLine(const OString & rLine)802 void Parser::parseLine( const OString& rLine )
803 {
804     OSL_PRECOND( m_pSink,         "Invalid sink" );
805     OSL_PRECOND( m_pErr,          "Invalid filehandle" );
806     OSL_PRECOND( m_xContext.is(), "Invalid service factory" );
807 
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 )
814     {
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:
846         {
847             const double nWidth ( readDouble() );
848             const double nHeight( readDouble() );
849             m_pSink->startPage( geometry::RealSize2D( nWidth, nHeight ) );
850             break;
851         }
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;
878 
879         case NONE:
880         default:
881             OSL_PRECOND(false,"Unknown input");
882             break;
883     }
884 
885     // all consumed?
886     SAL_WARN_IF(m_nCharIndex!=-1, "sdext.pdfimport", "leftover scanner input");
887 }
888 
889 } // namespace
890 
checkEncryption(const OUString & i_rPath,const uno::Reference<task::XInteractionHandler> & i_xIHdl,OUString & io_rPwd,bool & o_rIsEncrypted,const OUString & i_rDocName)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
896                              )
897 {
898     bool bSuccess = false;
899     OString aPDFFile = OUStringToOString( i_rPath, osl_getThreadTextEncoding() );
900 
901     std::unique_ptr<pdfparse::PDFEntry> pEntry( pdfparse::PDFReader::read( aPDFFile.getStr() ));
902     if( pEntry )
903     {
904         pdfparse::PDFFile* pPDFFile = dynamic_cast<pdfparse::PDFFile*>(pEntry.get());
905         if( pPDFFile )
906         {
907             o_rIsEncrypted = pPDFFile->isEncrypted();
908             if( o_rIsEncrypted )
909             {
910                 if( pPDFFile->usesSupportedEncryptionFormat() )
911                 {
912                     bool bAuthenticated = false;
913                     if( !io_rPwd.isEmpty() )
914                     {
915                         OString aIsoPwd = OUStringToOString( io_rPwd,
916                                                                        RTL_TEXTENCODING_ISO_8859_1 );
917                         bAuthenticated = pPDFFile->setupDecryptionData( aIsoPwd.getStr() );
918                     }
919                     if( bAuthenticated )
920                         bSuccess = true;
921                     else
922                     {
923                         if( i_xIHdl.is() )
924                         {
925                             bool bEntered = false;
926                             do
927                             {
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 );
933                         }
934 
935                         bSuccess = bAuthenticated;
936                     }
937                 }
938                 else if( i_xIHdl.is() )
939                 {
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
946                 }
947             }
948             else
949                 bSuccess = true;
950         }
951     }
952     return bSuccess;
953 }
954 
955 class Buffering
956 {
957     static const int SIZE = 64*1024;
958     std::unique_ptr<char[]> aBuffer;
959     oslFileHandle& pOut;
960     size_t pos;
961     sal_uInt64 left;
962 
963 public:
Buffering(oslFileHandle & out)964     explicit Buffering(oslFileHandle& out) : aBuffer(new char[SIZE]), pOut(out), pos(0), left(0) {}
965 
read(char * pChar,short count,sal_uInt64 * pBytesRead)966     oslFileError read(char *pChar, short count, sal_uInt64* pBytesRead)
967     {
968         oslFileError nRes = osl_File_E_None;
969         sal_uInt64 nBytesRead = 0;
970         while (count > 0)
971         {
972             if (left == 0)
973             {
974                 nRes = osl_readFile(pOut, aBuffer.get(), SIZE, &left);
975                 if (nRes != osl_File_E_None || left == 0)
976                 {
977                     *pBytesRead = nBytesRead;
978                     return nRes;
979                 }
980                 pos = 0;
981             }
982             *pChar = aBuffer.get()[pos];
983             --count;
984             ++pos;
985             --left;
986             ++pChar;
987             ++nBytesRead;
988         }
989         *pBytesRead = nBytesRead;
990         return osl_File_E_None;
991     }
992 };
993 
xpdf_ImportFromFile(const OUString & rURL,const ContentSinkSharedPtr & rSink,const uno::Reference<task::XInteractionHandler> & xIHdl,const OUString & rPwd,const uno::Reference<uno::XComponentContext> & xContext,const OUString & rFilterOptions)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)
1000 {
1001     OSL_ASSERT(rSink);
1002 
1003     OUString aSysUPath;
1004     if( osl_getSystemPathFromFileURL( rURL.pData, &aSysUPath.pData ) != osl_File_E_None )
1005     {
1006         SAL_WARN(
1007             "sdext.pdfimport",
1008             "getSystemPathFromFileURL(" << rURL << ") failed");
1009         return false;
1010     }
1011     OUString aDocName( rURL.copy( rURL.lastIndexOf( '/' )+1 ) );
1012 
1013     // check for encryption, if necessary get password
1014     OUString aPwd( rPwd );
1015     bool bIsEncrypted = false;
1016     if( !checkEncryption( aSysUPath, xIHdl, aPwd, bIsEncrypted, aDocName ) )
1017     {
1018         SAL_INFO(
1019             "sdext.pdfimport",
1020             "checkEncryption(" << aSysUPath << ") failed");
1021         return false;
1022     }
1023 
1024     // Determine xpdfimport executable URL:
1025     OUString converterURL("$BRAND_BASE_DIR/" LIBO_BIN_FOLDER "/xpdfimport");
1026     rtl::Bootstrap::expandMacros(converterURL); //TODO: detect failure
1027 
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)
1033     {
1034         SAL_WARN(
1035             "sdext.pdfimport",
1036             "getSystemPathFromFileURL(" << errPathname << ") failed");
1037         return false;
1038     }
1039 
1040     // spawn separate process to keep LGPL/GPL code apart.
1041 
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;
1046 
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);
1061 
1062     bool bRet=true;
1063     try
1064     {
1065         if( eErr!=osl_Process_E_None )
1066         {
1067             SAL_WARN(
1068                 "sdext.pdfimport",
1069                 "executeProcess of " << converterURL << " failed with "
1070                     << +eErr);
1071             return false;
1072         }
1073 
1074         if( pIn )
1075         {
1076             OStringBuffer aBuf(256);
1077             if( bIsEncrypted )
1078                 aBuf.append( OUStringToOString( aPwd, RTL_TEXTENCODING_ISO_8859_1 ) );
1079             aBuf.append( '\n' );
1080 
1081             sal_uInt64 nWritten = 0;
1082             osl_writeFile( pIn, aBuf.getStr(), sal_uInt64(aBuf.getLength()), &nWritten );
1083         }
1084 
1085         if( pOut && pErr )
1086         {
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( ;; )
1094             {
1095                 char aChar('\n');
1096                 sal_uInt64 nBytesRead;
1097                 oslFileError nRes;
1098 
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;
1105 
1106                 if( aChar != '\n' && aChar != '\r' )
1107                     line.append( aChar );
1108 
1109                 while( osl_File_E_None == (nRes = aBuffering.read(&aChar, 1, &nBytesRead)) &&
1110                        nBytesRead == 1 && aChar != '\n' && aChar != '\r' )
1111                 {
1112                     line.append( aChar );
1113                 }
1114                 if ( osl_File_E_None != nRes )
1115                     break;
1116                 if ( line.isEmpty() )
1117                     break;
1118 
1119                 aParser.parseLine(line.makeStringAndClear());
1120             }
1121         }
1122     }
1123     catch( uno::Exception& )
1124     {
1125         // crappy C file interface. need manual resource dealloc
1126         bRet = false;
1127     }
1128 
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)
1137     {
1138         oslProcessInfo info;
1139         info.Size = sizeof info;
1140         eErr = osl_getProcessInfo(aProcess, osl_Process_EXITCODE, &info);
1141         if (eErr == osl_Process_E_None)
1142         {
1143             if (info.Code != 0)
1144             {
1145                 SAL_WARN(
1146                     "sdext.pdfimport",
1147                     "getProcessInfo of " << converterURL
1148                         << " failed with exit code " << info.Code);
1149                 bRet = false;
1150             }
1151         }
1152         else
1153         {
1154             SAL_WARN(
1155                 "sdext.pdfimport",
1156                 "getProcessInfo of " << converterURL << " failed with "
1157                     << +eErr);
1158             bRet = false;
1159         }
1160     }
1161     else
1162     {
1163         SAL_WARN(
1164             "sdext.pdfimport",
1165             "joinProcess of " << converterURL << " failed with " << +eErr);
1166         bRet = false;
1167     }
1168     osl_freeProcessHandle(aProcess);
1169     return bRet;
1170 }
1171 
1172 
xpdf_ImportFromStream(const uno::Reference<io::XInputStream> & xInput,const ContentSinkSharedPtr & rSink,const uno::Reference<task::XInteractionHandler> & xIHdl,const OUString & rPwd,const uno::Reference<uno::XComponentContext> & xContext,const OUString & rFilterOptions)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 )
1179 {
1180     OSL_ASSERT(xInput.is());
1181     OSL_ASSERT(rSink);
1182 
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;
1188 
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;
1195     do
1196     {
1197         try
1198         {
1199             nBytes = xInput->readBytes( aBuf, nBufSize );
1200         }
1201         catch( css::uno::Exception& )
1202         {
1203             osl_closeFile( aFile );
1204             throw;
1205         }
1206         if( nBytes > 0 )
1207         {
1208             osl_writeFile( aFile, aBuf.getConstArray(), nBytes, &nWritten );
1209             if( nWritten != nBytes )
1210             {
1211                 bSuccess = false;
1212                 break;
1213             }
1214         }
1215     }
1216     while( nBytes == nBufSize );
1217 
1218     osl_closeFile( aFile );
1219 
1220     if ( bSuccess )
1221         bSuccess = xpdf_ImportFromFile( aURL, rSink, xIHdl, rPwd, xContext, rFilterOptions );
1222     osl_removeFile( aURL.pData );
1223 
1224     return bSuccess;
1225 }
1226 
1227 }
1228 
1229 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
1230