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 <o3tl/string_view.hxx>
28 #include <osl/file.h>
29 #include <osl/file.hxx>
30 #include <osl/thread.h>
31 #include <osl/process.h>
32 #include <osl/diagnose.h>
33 #include <rtl/bootstrap.hxx>
34 #include <rtl/ustring.hxx>
35 #include <rtl/ustrbuf.hxx>
36 #include <rtl/strbuf.hxx>
37 #include <sal/log.hxx>
38 
39 #include <comphelper/propertysequence.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/XPolyPolygon2D.hpp>
47 #include <com/sun/star/geometry/Matrix2D.hpp>
48 #include <com/sun/star/geometry/AffineMatrix2D.hpp>
49 #include <com/sun/star/geometry/RealRectangle2D.hpp>
50 #include <com/sun/star/geometry/RealSize2D.hpp>
51 #include <com/sun/star/task/XInteractionHandler.hpp>
52 #include <tools/diagnose_ex.h>
53 
54 #include <basegfx/point/b2dpoint.hxx>
55 #include <basegfx/polygon/b2dpolypolygon.hxx>
56 #include <basegfx/polygon/b2dpolygon.hxx>
57 #include <basegfx/utils/unopolypolygon.hxx>
58 
59 #include <vcl/metric.hxx>
60 #include <vcl/font.hxx>
61 #include <vcl/virdev.hxx>
62 
63 #include <cstddef>
64 #include <memory>
65 #include <string_view>
66 #include <unordered_map>
67 #include <string.h>
68 #include <stdlib.h>
69 
70 #include <rtl/character.hxx>
71 
72 using namespace com::sun::star;
73 
74 namespace pdfi
75 {
76 
77 namespace
78 {
79 
80 // identifier of the strings coming from the out-of-process xpdf
81 // converter
82 enum parseKey {
83     CLIPPATH,
84     DRAWCHAR,
85     DRAWIMAGE,
86     DRAWLINK,
87     DRAWMASK,
88     DRAWMASKEDIMAGE,
89     DRAWSOFTMASKEDIMAGE,
90     ENDPAGE,
91     ENDTEXTOBJECT,
92     EOCLIPPATH,
93     EOFILLPATH,
94     FILLPATH,
95     HYPERLINK,
96     INTERSECTCLIP,
97     INTERSECTEOCLIP,
98     POPSTATE,
99     PUSHSTATE,
100     RESTORESTATE,
101     SAVESTATE,
102     SETBLENDMODE,
103     SETFILLCOLOR,
104     SETFONT,
105     SETLINECAP,
106     SETLINEDASH,
107     SETLINEJOIN,
108     SETLINEWIDTH,
109     SETMITERLIMIT,
110     SETPAGENUM,
111     SETSTROKECOLOR,
112     SETTEXTRENDERMODE,
113     SETTRANSFORMATION,
114     STARTPAGE,
115     STROKEPATH,
116     UPDATEBLENDMODE,
117     UPDATECTM,
118     UPDATEFILLCOLOR,
119     UPDATEFILLOPACITY,
120     UPDATEFLATNESS,
121     UPDATEFONT,
122     UPDATELINECAP,
123     UPDATELINEDASH,
124     UPDATELINEJOIN,
125     UPDATELINEWIDTH,
126     UPDATEMITERLIMIT,
127     UPDATESTROKECOLOR,
128     UPDATESTROKEOPACITY,
129     NONE
130 };
131 
132 #if defined _MSC_VER && defined __clang__
133 #pragma clang diagnostic push
134 #pragma clang diagnostic ignored "-Wdeprecated-register"
135 #pragma clang diagnostic ignored "-Wextra-tokens"
136 #endif
137 #include <hash.cxx>
138 #if defined _MSC_VER && defined __clang__
139 #pragma clang diagnostic pop
140 #endif
141 
142 class Parser
143 {
144     friend class LineParser;
145 
146     typedef std::unordered_map< sal_Int64,
147                            FontAttributes > FontMapType;
148 
149     ScopedVclPtr<VirtualDevice> m_xDev;
150     const uno::Reference<uno::XComponentContext> m_xContext;
151     const ContentSinkSharedPtr                   m_pSink;
152     const oslFileHandle                          m_pErr;
153     FontMapType                                  m_aFontMap;
154 
155 public:
Parser(const ContentSinkSharedPtr & rSink,oslFileHandle pErr,const uno::Reference<uno::XComponentContext> & xContext)156     Parser( const ContentSinkSharedPtr&                   rSink,
157             oslFileHandle                                 pErr,
158             const uno::Reference<uno::XComponentContext>& xContext ) :
159         m_xContext(xContext),
160         m_pSink(rSink),
161         m_pErr(pErr),
162         m_aFontMap(101)
163     {}
164 
165     void parseLine( const OString& rLine );
166 };
167 
168 class LineParser {
169     Parser & m_parser;
170     OString                               m_aLine;
171 
172     static sal_Int32 parseFontCheckForString(const sal_Unicode* pCopy, sal_Int32 nCopyLen,
173                                       const char* pAttrib, sal_Int32 nAttribLen,
174                                       FontAttributes& rResult, bool bItalic, bool bBold);
175     static sal_Int32 parseFontRemoveSuffix(const sal_Unicode* pCopy, sal_Int32 nCopyLen,
176                               const char* pAttrib, sal_Int32 nAttribLen);
177     static void          parseFontFamilyName( FontAttributes& aResult );
178 
179     void           readInt32( sal_Int32& o_Value );
180     void           readInt64( sal_Int64& o_Value );
181     void           readDouble( double& o_Value );
182     void           readBinaryData( uno::Sequence<sal_Int8>& rBuf );
183 
184     uno::Sequence<beans::PropertyValue> readImageImpl();
185 
186 public:
187     std::size_t m_nCharIndex = 0;
188 
LineParser(Parser & parser,OString const & line)189     LineParser(Parser & parser, OString const & line): m_parser(parser), m_aLine(line) {}
190 
191     std::string_view readNextToken();
192     sal_Int32      readInt32();
193     double         readDouble();
194 
195     uno::Reference<rendering::XPolyPolygon2D> readPath();
196 
197     void                 readChar();
198     void                 readLineCap();
199     void                 readLineDash();
200     void                 readLineJoin();
201     void                 readTransformation();
202     rendering::ARGBColor readColor();
203     void                 readFont();
204 
205     void                 readImage();
206     void                 readMask();
207     void                 readLink();
208     void                 readMaskedImage();
209     void                 readSoftMaskedImage();
210 };
211 
212 /** Unescapes line-ending characters in input string. These
213     characters are encoded as pairs of characters: '\\' 'n', resp.
214     '\\' 'r'. This function converts them back to '\n', resp. '\r'.
215   */
lcl_unescapeLineFeeds(std::string_view i_rStr)216 OString lcl_unescapeLineFeeds(std::string_view i_rStr)
217 {
218     const size_t nOrigLen(i_rStr.size());
219     const char* const pOrig(i_rStr.data());
220     std::unique_ptr<char[]> pBuffer(new char[nOrigLen + 1]);
221 
222     const char* pRead(pOrig);
223     char* pWrite(pBuffer.get());
224     const char* pCur(pOrig);
225     while ((pCur = strchr(pCur, '\\')) != nullptr)
226     {
227         const char cNext(pCur[1]);
228         if (cNext == 'n' || cNext == 'r' || cNext == '\\')
229         {
230             const size_t nLen(pCur - pRead);
231             strncpy(pWrite, pRead, nLen);
232             pWrite += nLen;
233             *pWrite = cNext == 'n' ? '\n' : (cNext == 'r' ? '\r' : '\\');
234             ++pWrite;
235             pCur = pRead = pCur + 2;
236         }
237         else
238         {
239             // Just continue on the next character. The current
240             // block will be copied the next time it goes through the
241             // 'if' branch.
242             ++pCur;
243         }
244     }
245     // maybe there are some data to copy yet
246     if (sal::static_int_cast<size_t>(pRead - pOrig) < nOrigLen)
247     {
248         const size_t nLen(nOrigLen - (pRead - pOrig));
249         strncpy(pWrite, pRead, nLen);
250         pWrite += nLen;
251     }
252     *pWrite = '\0';
253 
254     OString aResult(pBuffer.get());
255     return aResult;
256 }
257 
readNextToken()258 std::string_view LineParser::readNextToken()
259 {
260     if (m_nCharIndex == std::string_view::npos) {
261         SAL_WARN("sdext.pdfimport", "insufficient input");
262         return {};
263     }
264     return o3tl::getToken(m_aLine,' ',m_nCharIndex);
265 }
266 
readInt32(sal_Int32 & o_Value)267 void LineParser::readInt32( sal_Int32& o_Value )
268 {
269     std::string_view tok = readNextToken();
270     sal_Int64 n = rtl_str_toInt64_WithLength(tok.data(), 10, tok.size());
271     if (n < SAL_MIN_INT32 || n > SAL_MAX_INT32)
272         n = 0;
273     o_Value = n;
274 }
275 
readInt32()276 sal_Int32 LineParser::readInt32()
277 {
278     std::string_view tok = readNextToken();
279     sal_Int64 n =rtl_str_toInt64_WithLength(tok.data(), 10, tok.size());
280     if (n < SAL_MIN_INT32 || n > SAL_MAX_INT32)
281         n = 0;
282     return n;
283 }
284 
readInt64(sal_Int64 & o_Value)285 void LineParser::readInt64( sal_Int64& o_Value )
286 {
287     std::string_view tok = readNextToken();
288     o_Value = rtl_str_toInt64_WithLength(tok.data(), 10, tok.size());
289 }
290 
readDouble(double & o_Value)291 void LineParser::readDouble( double& o_Value )
292 {
293     std::string_view tok = readNextToken();
294     o_Value = rtl_math_stringToDouble(tok.data(), tok.data() + tok.size(), '.', 0,
295                                    nullptr, nullptr);
296 }
297 
readDouble()298 double LineParser::readDouble()
299 {
300     std::string_view tok = readNextToken();
301     return rtl_math_stringToDouble(tok.data(), tok.data() + tok.size(), '.', 0,
302                                    nullptr, nullptr);
303 }
304 
readBinaryData(uno::Sequence<sal_Int8> & rBuf)305 void LineParser::readBinaryData( uno::Sequence<sal_Int8>& rBuf )
306 {
307     sal_Int32 nFileLen( rBuf.getLength() );
308     sal_Int8*           pBuf( rBuf.getArray() );
309     sal_uInt64          nBytesRead(0);
310     oslFileError        nRes=osl_File_E_None;
311     while( nFileLen )
312     {
313         nRes = osl_readFile( m_parser.m_pErr, pBuf, nFileLen, &nBytesRead );
314         if (osl_File_E_None != nRes )
315             break;
316         pBuf += nBytesRead;
317         nFileLen -= sal::static_int_cast<sal_Int32>(nBytesRead);
318     }
319 
320     OSL_PRECOND(nRes==osl_File_E_None, "inconsistent data");
321 }
322 
readPath()323 uno::Reference<rendering::XPolyPolygon2D> LineParser::readPath()
324 {
325     static const std::string_view aSubPathMarker( "subpath" );
326 
327     if( readNextToken() != aSubPathMarker )
328         OSL_PRECOND(false, "broken path");
329 
330     basegfx::B2DPolyPolygon aResult;
331     while( m_nCharIndex != std::string_view::npos )
332     {
333         basegfx::B2DPolygon aSubPath;
334 
335         sal_Int32 nClosedFlag;
336         readInt32( nClosedFlag );
337         aSubPath.setClosed( nClosedFlag != 0 );
338 
339         sal_Int32 nContiguousControlPoints(0);
340 
341         while( m_nCharIndex != std::string_view::npos )
342         {
343             std::size_t nDummy=m_nCharIndex;
344             if (o3tl::getToken(m_aLine,' ',nDummy) == aSubPathMarker) {
345                 break;
346             }
347 
348             sal_Int32 nCurveFlag;
349             double    nX, nY;
350             readDouble( nX );
351             readDouble( nY );
352             readInt32(  nCurveFlag );
353 
354             aSubPath.append(basegfx::B2DPoint(nX,nY));
355             if( nCurveFlag )
356             {
357                 ++nContiguousControlPoints;
358             }
359             else if( nContiguousControlPoints )
360             {
361                 OSL_PRECOND(nContiguousControlPoints==2,"broken bezier path");
362 
363                 // have two control points before us. the current one
364                 // is a normal point - thus, convert previous points
365                 // into bezier segment
366                 const sal_uInt32 nPoints( aSubPath.count() );
367                 const basegfx::B2DPoint aCtrlA( aSubPath.getB2DPoint(nPoints-3) );
368                 const basegfx::B2DPoint aCtrlB( aSubPath.getB2DPoint(nPoints-2) );
369                 const basegfx::B2DPoint aEnd( aSubPath.getB2DPoint(nPoints-1) );
370                 aSubPath.remove(nPoints-3, 3);
371                 aSubPath.appendBezierSegment(aCtrlA, aCtrlB, aEnd);
372 
373                 nContiguousControlPoints=0;
374             }
375         }
376 
377         aResult.append( aSubPath );
378         if( m_nCharIndex != std::string_view::npos )
379             readNextToken();
380     }
381 
382     return static_cast<rendering::XLinePolyPolygon2D*>(
383         new basegfx::unotools::UnoPolyPolygon(aResult));
384 }
385 
readChar()386 void LineParser::readChar()
387 {
388     double fontSize;
389     geometry::Matrix2D aUnoMatrix;
390     geometry::RealRectangle2D aRect;
391 
392     readDouble(aRect.X1);
393     readDouble(aRect.Y1);
394     readDouble(aRect.X2);
395     readDouble(aRect.Y2);
396     readDouble(aUnoMatrix.m00);
397     readDouble(aUnoMatrix.m01);
398     readDouble(aUnoMatrix.m10);
399     readDouble(aUnoMatrix.m11);
400     readDouble(fontSize);
401 
402     OString aChars;
403 
404     if (m_nCharIndex != std::string_view::npos)
405         aChars = lcl_unescapeLineFeeds( m_aLine.subView( m_nCharIndex ) );
406 
407     // chars gobble up rest of line
408     m_nCharIndex = std::string_view::npos;
409 
410     m_parser.m_pSink->drawGlyphs(OStringToOUString(aChars, RTL_TEXTENCODING_UTF8),
411         aRect, aUnoMatrix, fontSize);
412 }
413 
readLineCap()414 void LineParser::readLineCap()
415 {
416     sal_Int8 nCap(rendering::PathCapType::BUTT);
417     switch( readInt32() )
418     {
419         default:
420         case 0: nCap = rendering::PathCapType::BUTT; break;
421         case 1: nCap = rendering::PathCapType::ROUND; break;
422         case 2: nCap = rendering::PathCapType::SQUARE; break;
423     }
424     m_parser.m_pSink->setLineCap(nCap);
425 }
426 
readLineDash()427 void LineParser::readLineDash()
428 {
429     if( m_nCharIndex == std::string_view::npos )
430     {
431         m_parser.m_pSink->setLineDash( uno::Sequence<double>(), 0.0 );
432         return;
433     }
434 
435     const double nOffset(readDouble());
436     const sal_Int32 nLen(readInt32());
437 
438     uno::Sequence<double> aDashArray(nLen);
439     double* pArray=aDashArray.getArray();
440     for( sal_Int32 i=0; i<nLen; ++i )
441         *pArray++ = readDouble();
442 
443     m_parser.m_pSink->setLineDash( aDashArray, nOffset );
444 }
445 
readLineJoin()446 void LineParser::readLineJoin()
447 {
448     sal_Int8 nJoin(rendering::PathJoinType::MITER);
449     switch( readInt32() )
450     {
451         default:
452         case 0: nJoin = rendering::PathJoinType::MITER; break;
453         case 1: nJoin = rendering::PathJoinType::ROUND; break;
454         case 2: nJoin = rendering::PathJoinType::BEVEL; break;
455     }
456     m_parser.m_pSink->setLineJoin(nJoin);
457 }
458 
readTransformation()459 void LineParser::readTransformation()
460 {
461     geometry::AffineMatrix2D aMat;
462     readDouble(aMat.m00);
463     readDouble(aMat.m10);
464     readDouble(aMat.m01);
465     readDouble(aMat.m11);
466     readDouble(aMat.m02);
467     readDouble(aMat.m12);
468     m_parser.m_pSink->setTransformation( aMat );
469 }
470 
readColor()471 rendering::ARGBColor LineParser::readColor()
472 {
473     rendering::ARGBColor aRes;
474     readDouble(aRes.Red);
475     readDouble(aRes.Green);
476     readDouble(aRes.Blue);
477     readDouble(aRes.Alpha);
478     return aRes;
479 }
480 
parseFontCheckForString(const sal_Unicode * pCopy,sal_Int32 nCopyLen,const char * pAttrib,sal_Int32 nAttribLen,FontAttributes & rResult,bool bItalic,bool bBold)481 sal_Int32 LineParser::parseFontCheckForString(
482     const sal_Unicode* pCopy, sal_Int32 nCopyLen,
483     const char* pAttrib, sal_Int32 nAttribLen,
484     FontAttributes& rResult, bool bItalic, bool bBold)
485 {
486     if (nCopyLen < nAttribLen)
487         return 0;
488     for (sal_Int32 i = 0; i < nAttribLen; ++i)
489     {
490         sal_uInt32 nCode = pAttrib[i];
491         if (rtl::toAsciiLowerCase(pCopy[i]) != nCode
492             && rtl::toAsciiUpperCase(pCopy[i]) != nCode)
493             return 0;
494     }
495     rResult.isItalic |= bItalic;
496     rResult.isBold |= bBold;
497     return nAttribLen;
498 }
499 
parseFontRemoveSuffix(const sal_Unicode * pCopy,sal_Int32 nCopyLen,const char * pAttrib,sal_Int32 nAttribLen)500 sal_Int32 LineParser::parseFontRemoveSuffix(
501     const sal_Unicode* pCopy, sal_Int32 nCopyLen,
502     const char* pAttrib, sal_Int32 nAttribLen)
503 {
504     if (nCopyLen < nAttribLen)
505         return 0;
506     for (sal_Int32 i = 0; i < nAttribLen; ++i)
507         if ( pCopy[nCopyLen - nAttribLen + i] != pAttrib[i] )
508             return 0;
509     return nAttribLen;
510 }
511 
parseFontFamilyName(FontAttributes & rResult)512 void LineParser::parseFontFamilyName( FontAttributes& rResult )
513 {
514     OUStringBuffer aNewFamilyName( rResult.familyName.getLength() );
515 
516     const sal_Unicode* pCopy = rResult.familyName.getStr();
517     sal_Int32 nLen = rResult.familyName.getLength();
518 
519     // TODO: Looks like this block needs to be refactored
520     while( nLen )
521     {
522         if (parseFontRemoveSuffix(pCopy, nLen, RTL_CONSTASCII_STRINGPARAM("PSMT")))
523         {
524             nLen -= RTL_CONSTASCII_LENGTH("PSMT");
525         }
526         else if (parseFontRemoveSuffix(pCopy, nLen, RTL_CONSTASCII_STRINGPARAM("MT")))
527         {
528             nLen -= RTL_CONSTASCII_LENGTH("MT");
529         }
530 
531         if (parseFontCheckForString(pCopy, nLen, RTL_CONSTASCII_STRINGPARAM("Italic"), rResult, true, false))
532         {
533             sal_Int32 nAttribLen = RTL_CONSTASCII_LENGTH("Italic");
534             nLen -= nAttribLen;
535             pCopy += nAttribLen;
536         }
537         else if (parseFontCheckForString(pCopy, nLen, RTL_CONSTASCII_STRINGPARAM("-LightOblique"), rResult, true, false))
538         {
539             sal_Int32 nAttribLen = RTL_CONSTASCII_LENGTH("-LightOblique");
540             nLen -= nAttribLen;
541             pCopy += nAttribLen;
542         }
543         else if (parseFontCheckForString(pCopy, nLen, RTL_CONSTASCII_STRINGPARAM("-Light"), rResult, false, false))
544         {
545             sal_Int32 nAttribLen = RTL_CONSTASCII_LENGTH("-Light");
546             nLen -= nAttribLen;
547             pCopy += nAttribLen;
548         }
549         else if (parseFontCheckForString(pCopy, nLen, RTL_CONSTASCII_STRINGPARAM("-BoldOblique"), rResult, true, true))
550         {
551             sal_Int32 nAttribLen = RTL_CONSTASCII_LENGTH("-BoldOblique");
552             nLen -= nAttribLen;
553             pCopy += nAttribLen;
554         }
555         else if (parseFontCheckForString(pCopy, nLen, RTL_CONSTASCII_STRINGPARAM("-Bold"), rResult, false, true))
556         {
557             sal_Int32 nAttribLen = RTL_CONSTASCII_LENGTH("-Bold");
558             nLen -= nAttribLen;
559             pCopy += nAttribLen;
560         }
561         else if (parseFontCheckForString(pCopy, nLen, RTL_CONSTASCII_STRINGPARAM("Bold"), rResult, false, true))
562         {
563             sal_Int32 nAttribLen = RTL_CONSTASCII_LENGTH("Bold");
564             nLen -= nAttribLen;
565             pCopy += nAttribLen;
566         }
567         else if (parseFontCheckForString(pCopy, nLen, RTL_CONSTASCII_STRINGPARAM("-Roman"), rResult, false, false))
568         {
569             sal_Int32 nAttribLen = RTL_CONSTASCII_LENGTH("-Roman");
570             nLen -= nAttribLen;
571             pCopy += nAttribLen;
572         }
573         else if (parseFontCheckForString(pCopy, nLen, RTL_CONSTASCII_STRINGPARAM("-Oblique"), rResult, true, false))
574         {
575             sal_Int32 nAttribLen = RTL_CONSTASCII_LENGTH("-Oblique");
576             nLen -= nAttribLen;
577             pCopy += nAttribLen;
578         }
579         else if (parseFontCheckForString(pCopy, nLen, RTL_CONSTASCII_STRINGPARAM("-Reg"), rResult, false, false))
580         {
581             sal_Int32 nAttribLen = RTL_CONSTASCII_LENGTH("-Reg");
582             nLen -= nAttribLen;
583             pCopy += nAttribLen;
584         }
585         else if(nLen > 0)
586         {
587             if( *pCopy != '-' )
588                 aNewFamilyName.append( *pCopy );
589             pCopy++;
590             nLen--;
591         }
592     }
593     rResult.familyName = aNewFamilyName.makeStringAndClear();
594 }
595 
readFont()596 void LineParser::readFont()
597 {
598     /*
599     xpdf line is like (separated by space):
600     updateFont <FontID> <isEmbedded> <isBold> <isItalic> <isUnderline> <TransformedFontSize> <nEmbedSize> <FontName>
601     updateFont 14       1            0        0          0             1200.000000           23068        TimesNewRomanPSMT
602 
603     If nEmbedSize > 0, then a fontFile is followed as a stream.
604     */
605 
606     OString        aFontName;
607     sal_Int64      nFontID;
608     sal_Int32      nIsEmbedded, nIsBold, nIsItalic, nIsUnderline, nFileLen;
609     double         nSize;
610 
611     readInt64(nFontID);     // read FontID
612     readInt32(nIsEmbedded); // read isEmbedded
613     readInt32(nIsBold);     // read isBold
614     readInt32(nIsItalic);   // read isItalic
615     readInt32(nIsUnderline);// read isUnderline
616     readDouble(nSize);      // read TransformedFontSize
617     readInt32(nFileLen);    // read nEmbedSize
618 
619     nSize = nSize < 0.0 ? -nSize : nSize;
620     // Read FontName. From the current position to the end (any white spaces will be included).
621     aFontName = lcl_unescapeLineFeeds(m_aLine.subView(m_nCharIndex));
622 
623     // name gobbles up rest of line
624     m_nCharIndex = std::string_view::npos;
625 
626     // Check if this font is already in our font map list.
627     // If yes, update the font size and skip.
628     Parser::FontMapType::const_iterator pFont( m_parser.m_aFontMap.find(nFontID) );
629     if( pFont != m_parser.m_aFontMap.end() )
630     {
631         OSL_PRECOND(nFileLen==0,"font data for known font");
632         FontAttributes aRes(pFont->second);
633         aRes.size = nSize;
634         m_parser.m_pSink->setFont( aRes );
635 
636         return;
637     }
638 
639     // yet unknown font - get info and add to map
640     FontAttributes aResult( OStringToOUString( aFontName, RTL_TEXTENCODING_UTF8 ),
641                             nIsBold != 0,
642                             nIsItalic != 0,
643                             nIsUnderline != 0,
644                             nSize,
645                             1.0);
646 
647     /* The above font attributes (fontName, bold, italic) are based on
648        xpdf line output and may not be reliable. To get correct attributes,
649        we do the following:
650     1. Read the embeded font file and determine the attributes based on the
651        font file.
652     2. If we failed to read the font file, or empty result is returned, then
653        determine the font attributes from the font name.
654     3. If all these attemps have failed, then use a fallback font.
655     */
656     if (nFileLen > 0)
657     {
658         uno::Sequence<sal_Int8> aFontFile(nFileLen);
659         readBinaryData(aFontFile);  // Read fontFile.
660 
661         uno::Sequence<uno::Any> aArgs(1);
662         awt::FontDescriptor aFontDescriptor;
663         aArgs[0] <<= aFontFile;
664 
665         try
666         {
667             uno::Reference<beans::XMaterialHolder> xHolder(
668                 m_parser.m_xContext->getServiceManager()->createInstanceWithArgumentsAndContext(
669                     "com.sun.star.awt.FontIdentificator", aArgs, m_parser.m_xContext),
670                 uno::UNO_QUERY);
671             if (xHolder.is())
672             {
673                 uno::Any aFontReadResult(xHolder->getMaterial());
674                 aFontReadResult >>= aFontDescriptor;
675                 if (!aFontDescriptor.Name.isEmpty())
676                 {
677                     aResult.familyName = aFontDescriptor.Name;
678                     // tdf#143959: there are cases when the family name returned by font descriptor
679                     // is like "AAAAAA+TimesNewRoman,Bold". In this case, use the font name
680                     // determined by parseFontFamilyName instead, but still determine the font
681                     // attributes (bold italic etc) from the font descriptor.
682                     if (aResult.familyName.getLength() > 7 and aResult.familyName.indexOf(u"+", 6) == 6)
683                     {
684                         aResult.familyName = aResult.familyName.copy(7, aResult.familyName.getLength() - 7);
685                         parseFontFamilyName(aResult);
686                     }
687                     aResult.isBold = (aFontDescriptor.Weight > 100.0);
688                     aResult.isItalic = (aFontDescriptor.Slant == awt::FontSlant_OBLIQUE ||
689                                         aFontDescriptor.Slant == awt::FontSlant_ITALIC);
690                 } else
691                 {
692                     SAL_WARN("sdext.pdfimport",
693                         "Font detection from fontFile returned empty result.\
694                         Guessing font info from font name.");
695                     parseFontFamilyName(aResult);
696                 }
697             } else
698             {
699                 SAL_WARN("sdext.pdfimport",
700                     "Failed to run FontIdentificator service.\
701                     Guessing font info from font name.");
702                 parseFontFamilyName(aResult);
703             }
704         } catch (uno::Exception&)
705         {
706             TOOLS_WARN_EXCEPTION("sdext.pdfimport", "Exception when trying to read font file.");
707             parseFontFamilyName(aResult);
708         }
709     } else
710         parseFontFamilyName(aResult);
711 
712     // last fallback
713     if (aResult.familyName.isEmpty())
714     {
715         SAL_WARN("sdext.pdfimport", "Failed to determine the font, using a fallback font Arial.");
716         aResult.familyName = "Arial";
717     }
718 
719     if (!m_parser.m_xDev)
720         m_parser.m_xDev.disposeAndReset(VclPtr<VirtualDevice>::Create());
721 
722     vcl::Font font(aResult.familyName, Size(0, 1000));
723     m_parser.m_xDev->SetFont(font);
724     FontMetric metric(m_parser.m_xDev->GetFontMetric());
725     aResult.ascent = metric.GetAscent() / 1000.0;
726 
727     m_parser.m_aFontMap[nFontID] = aResult;
728 
729     aResult.size = nSize;
730     m_parser.m_pSink->setFont(aResult);
731 }
732 
readImageImpl()733 uno::Sequence<beans::PropertyValue> LineParser::readImageImpl()
734 {
735     std::string_view aToken = readNextToken();
736     const sal_Int32 nImageSize( readInt32() );
737 
738     OUString           aFileName;
739     if( aToken == "PNG" )
740         aFileName = "DUMMY.PNG";
741     else if( aToken == "JPEG" )
742         aFileName = "DUMMY.JPEG";
743     else if( aToken == "PBM" )
744         aFileName = "DUMMY.PBM";
745     else
746     {
747         SAL_WARN_IF(aToken != "PPM","sdext.pdfimport","Invalid bitmap format");
748         aFileName = "DUMMY.PPM";
749     }
750 
751     uno::Sequence<sal_Int8> aDataSequence(nImageSize);
752     readBinaryData( aDataSequence );
753 
754     uno::Sequence< uno::Any > aStreamCreationArgs(1);
755     aStreamCreationArgs[0] <<= aDataSequence;
756 
757     uno::Reference< uno::XComponentContext > xContext( m_parser.m_xContext, uno::UNO_SET_THROW );
758     uno::Reference< lang::XMultiComponentFactory > xFactory( xContext->getServiceManager(), uno::UNO_SET_THROW );
759     uno::Reference< io::XInputStream > xDataStream(
760         xFactory->createInstanceWithArgumentsAndContext( "com.sun.star.io.SequenceInputStream", aStreamCreationArgs, m_parser.m_xContext ),
761         uno::UNO_QUERY_THROW );
762 
763     uno::Sequence<beans::PropertyValue> aSequence( comphelper::InitPropertySequence({
764             { "URL", uno::makeAny(aFileName) },
765             { "InputStream", uno::makeAny( xDataStream ) },
766             { "InputSequence", uno::makeAny(aDataSequence) }
767         }));
768 
769     return aSequence;
770 }
771 
readImage()772 void LineParser::readImage()
773 {
774     sal_Int32 nWidth, nHeight,nMaskColors;
775     readInt32(nWidth);
776     readInt32(nHeight);
777     readInt32(nMaskColors);
778 
779     uno::Sequence<beans::PropertyValue> aImg( readImageImpl() );
780 
781     if( nMaskColors )
782     {
783         uno::Sequence<sal_Int8> aDataSequence(nMaskColors);
784         readBinaryData( aDataSequence );
785 
786         uno::Sequence<uno::Any> aMaskRanges(2);
787 
788         uno::Sequence<double> aMinRange(nMaskColors/2);
789         uno::Sequence<double> aMaxRange(nMaskColors/2);
790         for( sal_Int32 i=0; i<nMaskColors/2; ++i )
791         {
792             aMinRange[i] = aDataSequence[i] / 255.0;
793             aMaxRange[i] = aDataSequence[i+nMaskColors/2] / 255.0;
794         }
795 
796         aMaskRanges[0] <<= aMinRange;
797         aMaskRanges[1] <<= aMaxRange;
798 
799         m_parser.m_pSink->drawColorMaskedImage( aImg, aMaskRanges );
800     }
801     else
802         m_parser.m_pSink->drawImage( aImg );
803 }
804 
readMask()805 void LineParser::readMask()
806 {
807     sal_Int32 nWidth, nHeight, nInvert;
808     readInt32(nWidth);
809     readInt32(nHeight);
810     readInt32(nInvert);
811 
812     m_parser.m_pSink->drawMask( readImageImpl(), nInvert != 0);
813 }
814 
readLink()815 void LineParser::readLink()
816 {
817     geometry::RealRectangle2D aBounds;
818     readDouble(aBounds.X1);
819     readDouble(aBounds.Y1);
820     readDouble(aBounds.X2);
821     readDouble(aBounds.Y2);
822 
823     m_parser.m_pSink->hyperLink( aBounds,
824                         OStringToOUString( lcl_unescapeLineFeeds(
825                                 m_aLine.subView(m_nCharIndex) ),
826                                 RTL_TEXTENCODING_UTF8 ) );
827     // name gobbles up rest of line
828     m_nCharIndex = std::string_view::npos;
829 }
830 
readMaskedImage()831 void LineParser::readMaskedImage()
832 {
833     sal_Int32 nWidth, nHeight, nMaskWidth, nMaskHeight, nMaskInvert;
834     readInt32(nWidth);
835     readInt32(nHeight);
836     readInt32(nMaskWidth);
837     readInt32(nMaskHeight);
838     readInt32(nMaskInvert);
839 
840     const uno::Sequence<beans::PropertyValue> aImage( readImageImpl() );
841     const uno::Sequence<beans::PropertyValue> aMask ( readImageImpl() );
842     m_parser.m_pSink->drawMaskedImage( aImage, aMask, nMaskInvert != 0 );
843 }
844 
readSoftMaskedImage()845 void LineParser::readSoftMaskedImage()
846 {
847     sal_Int32 nWidth, nHeight, nMaskWidth, nMaskHeight;
848     readInt32(nWidth);
849     readInt32(nHeight);
850     readInt32(nMaskWidth);
851     readInt32(nMaskHeight);
852 
853     const uno::Sequence<beans::PropertyValue> aImage( readImageImpl() );
854     const uno::Sequence<beans::PropertyValue> aMask ( readImageImpl() );
855     m_parser.m_pSink->drawAlphaMaskedImage( aImage, aMask );
856 }
857 
parseLine(const OString & rLine)858 void Parser::parseLine( const OString& rLine )
859 {
860     OSL_PRECOND( m_pSink,         "Invalid sink" );
861     OSL_PRECOND( m_pErr,          "Invalid filehandle" );
862     OSL_PRECOND( m_xContext.is(), "Invalid service factory" );
863 
864     LineParser lp(*this, rLine);
865     const std::string_view rCmd = lp.readNextToken();
866     const hash_entry* pEntry = PdfKeywordHash::in_word_set( rCmd.data(),
867                                                             rCmd.size() );
868     OSL_ASSERT(pEntry);
869     switch( pEntry->eKey )
870     {
871         case CLIPPATH:
872             m_pSink->intersectClip(lp.readPath()); break;
873         case DRAWCHAR:
874             lp.readChar(); break;
875         case DRAWIMAGE:
876             lp.readImage(); break;
877         case DRAWLINK:
878             lp.readLink(); break;
879         case DRAWMASK:
880             lp.readMask(); break;
881         case DRAWMASKEDIMAGE:
882             lp.readMaskedImage(); break;
883         case DRAWSOFTMASKEDIMAGE:
884             lp.readSoftMaskedImage(); break;
885         case ENDPAGE:
886             m_pSink->endPage(); break;
887         case ENDTEXTOBJECT:
888             m_pSink->endText(); break;
889         case EOCLIPPATH:
890             m_pSink->intersectEoClip(lp.readPath()); break;
891         case EOFILLPATH:
892             m_pSink->eoFillPath(lp.readPath()); break;
893         case FILLPATH:
894             m_pSink->fillPath(lp.readPath()); break;
895         case RESTORESTATE:
896             m_pSink->popState(); break;
897         case SAVESTATE:
898             m_pSink->pushState(); break;
899         case SETPAGENUM:
900             m_pSink->setPageNum( lp.readInt32() ); break;
901         case STARTPAGE:
902         {
903             const double nWidth ( lp.readDouble() );
904             const double nHeight( lp.readDouble() );
905             m_pSink->startPage( geometry::RealSize2D( nWidth, nHeight ) );
906             break;
907         }
908         case STROKEPATH:
909             m_pSink->strokePath(lp.readPath()); break;
910         case UPDATECTM:
911             lp.readTransformation(); break;
912         case UPDATEFILLCOLOR:
913             m_pSink->setFillColor( lp.readColor() ); break;
914         case UPDATEFLATNESS:
915             m_pSink->setFlatness( lp.readDouble( ) ); break;
916         case UPDATEFONT:
917             lp.readFont(); break;
918         case UPDATELINECAP:
919             lp.readLineCap(); break;
920         case UPDATELINEDASH:
921             lp.readLineDash(); break;
922         case UPDATELINEJOIN:
923             lp.readLineJoin(); break;
924         case UPDATELINEWIDTH:
925             m_pSink->setLineWidth( lp.readDouble() );break;
926         case UPDATEMITERLIMIT:
927             m_pSink->setMiterLimit( lp.readDouble() ); break;
928         case UPDATESTROKECOLOR:
929             m_pSink->setStrokeColor( lp.readColor() ); break;
930         case UPDATESTROKEOPACITY:
931             break;
932         case SETTEXTRENDERMODE:
933             m_pSink->setTextRenderMode( lp.readInt32() ); break;
934 
935         case NONE:
936         default:
937             OSL_PRECOND(false,"Unknown input");
938             break;
939     }
940 
941     // all consumed?
942     SAL_WARN_IF(
943         lp.m_nCharIndex!=std::string_view::npos, "sdext.pdfimport", "leftover scanner input");
944 }
945 
946 } // namespace
947 
checkEncryption(std::u16string_view i_rPath,const uno::Reference<task::XInteractionHandler> & i_xIHdl,OUString & io_rPwd,bool & o_rIsEncrypted,const OUString & i_rDocName)948 static bool checkEncryption( std::u16string_view                           i_rPath,
949                              const uno::Reference< task::XInteractionHandler >& i_xIHdl,
950                              OUString&                                     io_rPwd,
951                              bool&                                              o_rIsEncrypted,
952                              const OUString&                               i_rDocName
953                              )
954 {
955     bool bSuccess = false;
956     OString aPDFFile = OUStringToOString( i_rPath, osl_getThreadTextEncoding() );
957 
958     std::unique_ptr<pdfparse::PDFEntry> pEntry( pdfparse::PDFReader::read( aPDFFile.getStr() ));
959     if( pEntry )
960     {
961         pdfparse::PDFFile* pPDFFile = dynamic_cast<pdfparse::PDFFile*>(pEntry.get());
962         if( pPDFFile )
963         {
964             o_rIsEncrypted = pPDFFile->isEncrypted();
965             if( o_rIsEncrypted )
966             {
967                 if( pPDFFile->usesSupportedEncryptionFormat() )
968                 {
969                     bool bAuthenticated = false;
970                     if( !io_rPwd.isEmpty() )
971                     {
972                         OString aIsoPwd = OUStringToOString( io_rPwd,
973                                                                        RTL_TEXTENCODING_ISO_8859_1 );
974                         bAuthenticated = pPDFFile->setupDecryptionData( aIsoPwd.getStr() );
975                     }
976                     if( bAuthenticated )
977                         bSuccess = true;
978                     else
979                     {
980                         if( i_xIHdl.is() )
981                         {
982                             bool bEntered = false;
983                             do
984                             {
985                                 bEntered = getPassword( i_xIHdl, io_rPwd, ! bEntered, i_rDocName );
986                                 OString aIsoPwd = OUStringToOString( io_rPwd,
987                                                                                RTL_TEXTENCODING_ISO_8859_1 );
988                                 bAuthenticated = pPDFFile->setupDecryptionData( aIsoPwd.getStr() );
989                             } while( bEntered && ! bAuthenticated );
990                         }
991 
992                         bSuccess = bAuthenticated;
993                     }
994                 }
995                 else if( i_xIHdl.is() )
996                 {
997                     reportUnsupportedEncryptionFormat( i_xIHdl );
998                         //TODO: this should either be handled further down the
999                         // call stack, or else information that this has already
1000                         // been handled should be passed down the call stack, so
1001                         // that SfxBaseModel::load does not show an additional
1002                         // "General Error" message box
1003                 }
1004             }
1005             else
1006                 bSuccess = true;
1007         }
1008     }
1009     return bSuccess;
1010 }
1011 
1012 namespace {
1013 
1014 class Buffering
1015 {
1016     static const int SIZE = 64*1024;
1017     std::unique_ptr<char[]> aBuffer;
1018     oslFileHandle& pOut;
1019     size_t pos;
1020     sal_uInt64 left;
1021 
1022 public:
Buffering(oslFileHandle & out)1023     explicit Buffering(oslFileHandle& out) : aBuffer(new char[SIZE]), pOut(out), pos(0), left(0) {}
1024 
read(char * pChar,short count,sal_uInt64 * pBytesRead)1025     oslFileError read(char *pChar, short count, sal_uInt64* pBytesRead)
1026     {
1027         oslFileError nRes = osl_File_E_None;
1028         sal_uInt64 nBytesRead = 0;
1029         while (count > 0)
1030         {
1031             if (left == 0)
1032             {
1033                 nRes = osl_readFile(pOut, aBuffer.get(), SIZE, &left);
1034                 if (nRes != osl_File_E_None || left == 0)
1035                 {
1036                     *pBytesRead = nBytesRead;
1037                     return nRes;
1038                 }
1039                 pos = 0;
1040             }
1041             *pChar = aBuffer.get()[pos];
1042             --count;
1043             ++pos;
1044             --left;
1045             ++pChar;
1046             ++nBytesRead;
1047         }
1048         *pBytesRead = nBytesRead;
1049         return osl_File_E_None;
1050     }
1051 };
1052 
1053 }
1054 
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)1055 bool xpdf_ImportFromFile(const OUString& rURL,
1056                          const ContentSinkSharedPtr& rSink,
1057                          const uno::Reference<task::XInteractionHandler>& xIHdl,
1058                          const OUString& rPwd,
1059                          const uno::Reference<uno::XComponentContext>& xContext,
1060                          const OUString& rFilterOptions)
1061 {
1062     OSL_ASSERT(rSink);
1063 
1064     OUString aSysUPath;
1065     if( osl_getSystemPathFromFileURL( rURL.pData, &aSysUPath.pData ) != osl_File_E_None )
1066     {
1067         SAL_WARN(
1068             "sdext.pdfimport",
1069             "getSystemPathFromFileURL(" << rURL << ") failed");
1070         return false;
1071     }
1072     OUString aDocName( rURL.copy( rURL.lastIndexOf( '/' )+1 ) );
1073 
1074     // check for encryption, if necessary get password
1075     OUString aPwd( rPwd );
1076     bool bIsEncrypted = false;
1077     if( !checkEncryption( aSysUPath, xIHdl, aPwd, bIsEncrypted, aDocName ) )
1078     {
1079         SAL_INFO(
1080             "sdext.pdfimport",
1081             "checkEncryption(" << aSysUPath << ") failed");
1082         return false;
1083     }
1084 
1085     // Determine xpdfimport executable URL:
1086     OUString converterURL("$BRAND_BASE_DIR/" LIBO_BIN_FOLDER "/xpdfimport");
1087     rtl::Bootstrap::expandMacros(converterURL); //TODO: detect failure
1088 
1089     // Determine pathname of xpdfimport_err.pdf:
1090     OUString errPathname("$BRAND_BASE_DIR/" LIBO_SHARE_FOLDER "/xpdfimport/xpdfimport_err.pdf");
1091     rtl::Bootstrap::expandMacros(errPathname); //TODO: detect failure
1092     if (osl::FileBase::getSystemPathFromFileURL(errPathname, errPathname)
1093         != osl::FileBase::E_None)
1094     {
1095         SAL_WARN(
1096             "sdext.pdfimport",
1097             "getSystemPathFromFileURL(" << errPathname << ") failed");
1098         return false;
1099     }
1100 
1101     // spawn separate process to keep LGPL/GPL code apart.
1102 
1103     OUString aOptFlag("-o");
1104     rtl_uString*  args[] = { aSysUPath.pData, errPathname.pData,
1105                              aOptFlag.pData, rFilterOptions.pData };
1106     sal_Int32 nArgs = rFilterOptions.isEmpty() ? 2 : 4;
1107 
1108     oslProcess    aProcess;
1109     oslFileHandle pIn  = nullptr;
1110     oslFileHandle pOut = nullptr;
1111     oslFileHandle pErr = nullptr;
1112     oslSecurity pSecurity = osl_getCurrentSecurity ();
1113     oslProcessError eErr =
1114         osl_executeProcess_WithRedirectedIO(converterURL.pData,
1115                                             args,
1116                                             nArgs,
1117                                             osl_Process_SEARCHPATH|osl_Process_HIDDEN,
1118                                             pSecurity,
1119                                             nullptr, nullptr, 0,
1120                                             &aProcess, &pIn, &pOut, &pErr);
1121     osl_freeSecurityHandle(pSecurity);
1122 
1123     bool bRet=true;
1124     try
1125     {
1126         if( eErr!=osl_Process_E_None )
1127         {
1128             SAL_WARN(
1129                 "sdext.pdfimport",
1130                 "executeProcess of " << converterURL << " failed with "
1131                     << +eErr);
1132             return false;
1133         }
1134 
1135         if( pIn )
1136         {
1137             OStringBuffer aBuf(256);
1138             if( bIsEncrypted )
1139                 aBuf.append( OUStringToOString( aPwd, RTL_TEXTENCODING_ISO_8859_1 ) );
1140             aBuf.append( '\n' );
1141 
1142             sal_uInt64 nWritten = 0;
1143             osl_writeFile( pIn, aBuf.getStr(), sal_uInt64(aBuf.getLength()), &nWritten );
1144         }
1145 
1146         if( pOut && pErr )
1147         {
1148             // read results of PDF parser. One line - one call to
1149             // OutputDev. stderr is used for alternate streams, like
1150             // embedded fonts and bitmaps
1151             Parser aParser(rSink,pErr,xContext);
1152             Buffering aBuffering(pOut);
1153             OStringBuffer line;
1154             for( ;; )
1155             {
1156                 char aChar('\n');
1157                 sal_uInt64 nBytesRead;
1158                 oslFileError nRes;
1159 
1160                 // skip garbage \r \n at start of line
1161                 for (;;)
1162                 {
1163                     nRes = aBuffering.read(&aChar, 1, &nBytesRead);
1164                     if (osl_File_E_None != nRes || nBytesRead != 1 || (aChar != '\n' && aChar != '\r') )
1165                         break;
1166                 }
1167                 if ( osl_File_E_None != nRes )
1168                     break;
1169 
1170                 if( aChar != '\n' && aChar != '\r' )
1171                     line.append( aChar );
1172 
1173                 for (;;)
1174                 {
1175                     nRes = aBuffering.read(&aChar, 1, &nBytesRead);
1176                     if ( osl_File_E_None != nRes || nBytesRead != 1 || aChar == '\n' || aChar == '\r' )
1177                         break;
1178                     line.append( aChar );
1179                 }
1180                 if ( osl_File_E_None != nRes )
1181                     break;
1182                 if ( line.isEmpty() )
1183                     break;
1184 
1185                 aParser.parseLine(line.makeStringAndClear());
1186             }
1187         }
1188     }
1189     catch( uno::Exception& )
1190     {
1191         // crappy C file interface. need manual resource dealloc
1192         bRet = false;
1193     }
1194 
1195     if( pIn )
1196         osl_closeFile(pIn);
1197     if( pOut )
1198         osl_closeFile(pOut);
1199     if( pErr )
1200         osl_closeFile(pErr);
1201     eErr = osl_joinProcess(aProcess);
1202     if (eErr == osl_Process_E_None)
1203     {
1204         oslProcessInfo info;
1205         info.Size = sizeof info;
1206         eErr = osl_getProcessInfo(aProcess, osl_Process_EXITCODE, &info);
1207         if (eErr == osl_Process_E_None)
1208         {
1209             if (info.Code != 0)
1210             {
1211                 SAL_WARN(
1212                     "sdext.pdfimport",
1213                     "getProcessInfo of " << converterURL
1214                         << " failed with exit code " << info.Code);
1215                 bRet = false;
1216             }
1217         }
1218         else
1219         {
1220             SAL_WARN(
1221                 "sdext.pdfimport",
1222                 "getProcessInfo of " << converterURL << " failed with "
1223                     << +eErr);
1224             bRet = false;
1225         }
1226     }
1227     else
1228     {
1229         SAL_WARN(
1230             "sdext.pdfimport",
1231             "joinProcess of " << converterURL << " failed with " << +eErr);
1232         bRet = false;
1233     }
1234     osl_freeProcessHandle(aProcess);
1235     return bRet;
1236 }
1237 
1238 
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)1239 bool xpdf_ImportFromStream( const uno::Reference< io::XInputStream >&         xInput,
1240                             const ContentSinkSharedPtr&                       rSink,
1241                             const uno::Reference<task::XInteractionHandler >& xIHdl,
1242                             const OUString&                              rPwd,
1243                             const uno::Reference< uno::XComponentContext >&   xContext,
1244                             const OUString&                                   rFilterOptions )
1245 {
1246     OSL_ASSERT(xInput.is());
1247     OSL_ASSERT(rSink);
1248 
1249     // convert XInputStream to local temp file
1250     oslFileHandle aFile = nullptr;
1251     OUString aURL;
1252     if( osl_createTempFile( nullptr, &aFile, &aURL.pData ) != osl_File_E_None )
1253         return false;
1254 
1255     // copy content, buffered...
1256     const sal_uInt32 nBufSize = 4096;
1257     uno::Sequence<sal_Int8> aBuf( nBufSize );
1258     sal_uInt64 nBytes = 0;
1259     sal_uInt64 nWritten = 0;
1260     bool bSuccess = true;
1261     do
1262     {
1263         try
1264         {
1265             nBytes = xInput->readBytes( aBuf, nBufSize );
1266         }
1267         catch( css::uno::Exception& )
1268         {
1269             osl_closeFile( aFile );
1270             throw;
1271         }
1272         if( nBytes > 0 )
1273         {
1274             osl_writeFile( aFile, aBuf.getConstArray(), nBytes, &nWritten );
1275             if( nWritten != nBytes )
1276             {
1277                 bSuccess = false;
1278                 break;
1279             }
1280         }
1281     }
1282     while( nBytes == nBufSize );
1283 
1284     osl_closeFile( aFile );
1285 
1286     if ( bSuccess )
1287         bSuccess = xpdf_ImportFromFile( aURL, rSink, xIHdl, rPwd, xContext, rFilterOptions );
1288     osl_removeFile( aURL.pData );
1289 
1290     return bSuccess;
1291 }
1292 
1293 }
1294 
1295 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
1296