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