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