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