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