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