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 if (aResult.familyName.endsWithIgnoreAsciiCase("-VKana"))
634 {
635 parseFontFamilyName(aResult);
636 }
637
638 // Font weight
639 if (aFontReadResult.GetWeight() == WEIGHT_THIN)
640 aResult.fontWeight = u"100";
641 else if (aFontReadResult.GetWeight() == WEIGHT_ULTRALIGHT)
642 aResult.fontWeight = u"200";
643 else if (aFontReadResult.GetWeight() == WEIGHT_LIGHT)
644 aResult.fontWeight = u"300";
645 else if (aFontReadResult.GetWeight() == WEIGHT_SEMILIGHT)
646 aResult.fontWeight = u"350";
647 // no need to check "normal" here as this is default in nFontWeight above
648 else if (aFontReadResult.GetWeight() == WEIGHT_SEMIBOLD)
649 aResult.fontWeight = u"600";
650 else if (aFontReadResult.GetWeight() == WEIGHT_BOLD)
651 aResult.fontWeight = u"bold";
652 else if (aFontReadResult.GetWeight() == WEIGHT_ULTRABOLD)
653 aResult.fontWeight = u"800";
654 else if (aFontReadResult.GetWeight() == WEIGHT_BLACK)
655 aResult.fontWeight = u"900";
656 SAL_INFO("sdext.pdfimport", aResult.fontWeight);
657
658 // Italic
659 aResult.isItalic = (aFontReadResult.GetItalic() == ITALIC_OBLIQUE ||
660 aFontReadResult.GetItalic() == ITALIC_NORMAL);
661 } else // font detection failed
662 {
663 SAL_WARN("sdext.pdfimport",
664 "Font detection from fontFile returned empty result. Guessing font info from font name.");
665 parseFontFamilyName(aResult);
666 }
667
668 } else // no embedded font file - guess font attributes from font name
669 {
670 parseFontFamilyName(aResult);
671 }
672
673 // last fallback
674 if (aResult.familyName.isEmpty())
675 {
676 SAL_WARN("sdext.pdfimport", "Failed to determine the font, using a fallback font Arial.");
677 aResult.familyName = "Arial";
678 }
679
680 if (!m_parser.m_xDev)
681 m_parser.m_xDev.disposeAndReset(VclPtr<VirtualDevice>::Create());
682
683 vcl::Font font(aResult.familyName, Size(0, 1000));
684 m_parser.m_xDev->SetFont(font);
685 FontMetric metric(m_parser.m_xDev->GetFontMetric());
686 aResult.ascent = metric.GetAscent() / 1000.0;
687
688 m_parser.m_aFontMap[nFontID] = aResult;
689
690 aResult.size = nSize;
691 m_parser.m_pSink->setFont(aResult);
692}
693
694uno::Sequence<beans::PropertyValue> LineParser::readImageImpl()
695{
696 std::string_view aToken = readNextToken();
697 const sal_Int32 nImageSize( readInt32() );
698
699 OUString aFileName;
700 if( aToken == "PNG" )
701 aFileName = "DUMMY.PNG";
702 else if( aToken == "JPEG" )
703 aFileName = "DUMMY.JPEG";
704 else if( aToken == "PBM" )
705 aFileName = "DUMMY.PBM";
706 else
707 {
708 SAL_WARN_IF(aToken != "PPM","sdext.pdfimport","Invalid bitmap format");
709 aFileName = "DUMMY.PPM";
710 }
711
712 uno::Sequence<sal_Int8> aDataSequence(nImageSize);
713 readBinaryData( aDataSequence );
714
715 uno::Sequence< uno::Any > aStreamCreationArgs{ uno::Any(aDataSequence) };
716
717 uno::Reference< uno::XComponentContext > xContext( m_parser.m_xContext, uno::UNO_SET_THROW );
718 uno::Reference< lang::XMultiComponentFactory > xFactory( xContext->getServiceManager(), uno::UNO_SET_THROW );
719 uno::Reference< io::XInputStream > xDataStream(
720 xFactory->createInstanceWithArgumentsAndContext( "com.sun.star.io.SequenceInputStream", aStreamCreationArgs, m_parser.m_xContext ),
721 uno::UNO_QUERY_THROW );
722
723 uno::Sequence<beans::PropertyValue> aSequence( comphelper::InitPropertySequence({
724 { "URL", uno::Any(aFileName) },
725 { "InputStream", uno::Any( xDataStream ) },
726 { "InputSequence", uno::Any(aDataSequence) }
727 }));
728
729 return aSequence;
730}
731
732void LineParser::readImage()
733{
734 sal_Int32 nWidth, nHeight,nMaskColors;
735 readInt32(nWidth);
736 readInt32(nHeight);
737 readInt32(nMaskColors);
738
739 uno::Sequence<beans::PropertyValue> aImg( readImageImpl() );
740
741 if( nMaskColors )
742 {
743 uno::Sequence<sal_Int8> aDataSequence(nMaskColors);
744 readBinaryData( aDataSequence );
745
746 uno::Sequence<double> aMinRange(nMaskColors/2);
747 auto pMinRange = aMinRange.getArray();
748 uno::Sequence<double> aMaxRange(nMaskColors/2);
749 auto pMaxRange = aMaxRange.getArray();
750 for( sal_Int32 i=0; i<nMaskColors/2; ++i )
751 {
752 pMinRange[i] = aDataSequence[i] / 255.0;
753 pMaxRange[i] = aDataSequence[i+nMaskColors/2] / 255.0;
754 }
755
756 uno::Sequence<uno::Any> aMaskRanges{ uno::Any(aMinRange), uno::Any(aMaxRange) };
757 m_parser.m_pSink->drawColorMaskedImage( aImg, aMaskRanges );
758 }
759 else
760 m_parser.m_pSink->drawImage( aImg );
761}
762
763void LineParser::readMask()
764{
765 sal_Int32 nWidth, nHeight, nInvert;
766 readInt32(nWidth);
767 readInt32(nHeight);
768 readInt32(nInvert);
769
770 m_parser.m_pSink->drawMask( readImageImpl(), nInvert != 0);
771}
772
773void LineParser::readLink()
774{
775 geometry::RealRectangle2D aBounds;
776 readDouble(aBounds.X1);
777 readDouble(aBounds.Y1);
778 readDouble(aBounds.X2);
779 readDouble(aBounds.Y2);
780
781 m_parser.m_pSink->hyperLink( aBounds,
782 OStringToOUString( lcl_unescapeLineFeeds(
783 m_aLine.substr(m_nCharIndex) ),
784 RTL_TEXTENCODING_UTF8 ) );
785 // name gobbles up rest of line
786 m_nCharIndex = std::string_view::npos;
787}
788
789void LineParser::readMaskedImage()
790{
791 sal_Int32 nWidth, nHeight, nMaskWidth, nMaskHeight, nMaskInvert;
792 readInt32(nWidth);
793 readInt32(nHeight);
794 readInt32(nMaskWidth);
795 readInt32(nMaskHeight);
796 readInt32(nMaskInvert);
797
798 const uno::Sequence<beans::PropertyValue> aImage( readImageImpl() );
799 const uno::Sequence<beans::PropertyValue> aMask ( readImageImpl() );
800 m_parser.m_pSink->drawMaskedImage( aImage, aMask, nMaskInvert != 0 );
801}
802
803void LineParser::readSoftMaskedImage()
804{
805 sal_Int32 nWidth, nHeight, nMaskWidth, nMaskHeight;
806 readInt32(nWidth);
807 readInt32(nHeight);
808 readInt32(nMaskWidth);
809 readInt32(nMaskHeight);
810
811 const uno::Sequence<beans::PropertyValue> aImage( readImageImpl() );
812 const uno::Sequence<beans::PropertyValue> aMask ( readImageImpl() );
813 m_parser.m_pSink->drawAlphaMaskedImage( aImage, aMask );
814}
815
816void Parser::parseLine( std::string_view aLine )
817{
818 OSL_PRECOND( m_pSink, "Invalid sink" );
819 OSL_PRECOND( m_pErr, "Invalid filehandle" );
820 OSL_PRECOND( m_xContext.is(), "Invalid service factory" );
821
822 LineParser lp(*this, aLine);
823 const std::string_view rCmd = lp.readNextToken();
824 const hash_entry* pEntry = PdfKeywordHash::in_word_set( rCmd.data(),
825 rCmd.size() );
826 OSL_ASSERT(pEntry);
827 switch( pEntry->eKey )
828 {
829 case CLIPPATH:
830 m_pSink->intersectClip(lp.readPath()); break;
831 case DRAWCHAR:
832 lp.readChar(); break;
833 case DRAWIMAGE:
834 lp.readImage(); break;
835 case DRAWLINK:
836 lp.readLink(); break;
837 case DRAWMASK:
838 lp.readMask(); break;
839 case DRAWMASKEDIMAGE:
840 lp.readMaskedImage(); break;
841 case DRAWSOFTMASKEDIMAGE:
842 lp.readSoftMaskedImage(); break;
843 case ENDPAGE:
844 m_pSink->endPage(); break;
845 case ENDTEXTOBJECT:
846 m_pSink->endText(); break;
847 case EOCLIPPATH:
848 m_pSink->intersectEoClip(lp.readPath()); break;
849 case EOFILLPATH:
850 m_pSink->eoFillPath(lp.readPath()); break;
851 case FILLPATH:
852 m_pSink->fillPath(lp.readPath()); break;
853 case RESTORESTATE:
854 m_pSink->popState(); break;
855 case SAVESTATE:
856 m_pSink->pushState(); break;
857 case SETPAGENUM:
858 m_pSink->setPageNum( lp.readInt32() ); break;
859 case STARTPAGE:
860 {
861 const double nWidth ( lp.readDouble() );
862 const double nHeight( lp.readDouble() );
863 m_pSink->startPage( geometry::RealSize2D( nWidth, nHeight ) );
864 break;
865 }
866 case STROKEPATH:
867 m_pSink->strokePath(lp.readPath()); break;
868 case UPDATECTM:
869 lp.readTransformation(); break;
870 case UPDATEFILLCOLOR:
871 m_pSink->setFillColor( lp.readColor() ); break;
872 case UPDATEFLATNESS:
873 m_pSink->setFlatness( lp.readDouble( ) ); break;
874 case UPDATEFONT:
875 lp.readFont(); break;
876 case UPDATELINECAP:
877 lp.readLineCap(); break;
878 case UPDATELINEDASH:
879 lp.readLineDash(); break;
880 case UPDATELINEJOIN:
881 lp.readLineJoin(); break;
882 case UPDATELINEWIDTH:
883 m_pSink->setLineWidth( lp.readDouble() );break;
884 case UPDATEMITERLIMIT:
885 m_pSink->setMiterLimit( lp.readDouble() ); break;
886 case UPDATESTROKECOLOR:
887 m_pSink->setStrokeColor( lp.readColor() ); break;
888 case UPDATESTROKEOPACITY:
889 break;
890 case SETTEXTRENDERMODE:
891 m_pSink->setTextRenderMode( lp.readInt32() ); break;
892
893 case NONE:
894 default:
895 OSL_PRECOND(false,"Unknown input");
896 break;
897 }
898
899 // all consumed?
901 lp.m_nCharIndex!=std::string_view::npos, "sdext.pdfimport", "leftover scanner input");
902}
903
904} // namespace
905
906static bool checkEncryption( std::u16string_view i_rPath,
907 const uno::Reference< task::XInteractionHandler >& i_xIHdl,
908 OUString& io_rPwd,
909 bool& o_rIsEncrypted,
910 const OUString& i_rDocName
911 )
912{
913 bool bSuccess = false;
914 OString aPDFFile = OUStringToOString( i_rPath, osl_getThreadTextEncoding() );
915
916 std::unique_ptr<pdfparse::PDFEntry> pEntry( pdfparse::PDFReader::read( aPDFFile.getStr() ));
917 if( pEntry )
918 {
919 pdfparse::PDFFile* pPDFFile = dynamic_cast<pdfparse::PDFFile*>(pEntry.get());
920 if( pPDFFile )
921 {
922 o_rIsEncrypted = pPDFFile->isEncrypted();
923 if( o_rIsEncrypted )
924 {
925 if( pPDFFile->usesSupportedEncryptionFormat() )
926 {
927 bool bAuthenticated = false;
928 if( !io_rPwd.isEmpty() )
929 {
930 OString aIsoPwd = OUStringToOString( io_rPwd,
931 RTL_TEXTENCODING_ISO_8859_1 );
932 bAuthenticated = pPDFFile->setupDecryptionData( aIsoPwd );
933 }
934 if( bAuthenticated )
935 bSuccess = true;
936 else
937 {
938 if( i_xIHdl.is() )
939 {
940 bool bEntered = false;
941 do
942 {
943 bEntered = getPassword( i_xIHdl, io_rPwd, ! bEntered, i_rDocName );
944 OString aIsoPwd = OUStringToOString( io_rPwd,
945 RTL_TEXTENCODING_ISO_8859_1 );
946 bAuthenticated = pPDFFile->setupDecryptionData( aIsoPwd );
947 } while( bEntered && ! bAuthenticated );
948 }
949
950 bSuccess = bAuthenticated;
951 }
952 }
953 else if( i_xIHdl.is() )
954 {
956 //TODO: this should either be handled further down the
957 // call stack, or else information that this has already
958 // been handled should be passed down the call stack, so
959 // that SfxBaseModel::load does not show an additional
960 // "General Error" message box
961 }
962 }
963 else
964 bSuccess = true;
965 }
966 }
967 return bSuccess;
968}
969
970namespace {
971
972class Buffering
973{
974 static const int SIZE = 64*1024;
975 std::unique_ptr<char[]> aBuffer;
976 oslFileHandle& pOut;
977 size_t pos;
978 sal_uInt64 left;
979
980public:
981 explicit Buffering(oslFileHandle& out) : aBuffer(new char[SIZE]), pOut(out), pos(0), left(0) {}
982
983 oslFileError read(char *pChar, short count, sal_uInt64* pBytesRead)
984 {
985 oslFileError nRes = osl_File_E_None;
986 sal_uInt64 nBytesRead = 0;
987 while (count > 0)
988 {
989 if (left == 0)
990 {
991 nRes = osl_readFile(pOut, aBuffer.get(), SIZE, &left);
992 if (nRes != osl_File_E_None || left == 0)
993 {
994 *pBytesRead = nBytesRead;
995 return nRes;
996 }
997 pos = 0;
998 }
999 *pChar = aBuffer.get()[pos];
1000 --count;
1001 ++pos;
1002 --left;
1003 ++pChar;
1004 ++nBytesRead;
1005 }
1006 *pBytesRead = nBytesRead;
1007 return osl_File_E_None;
1008 }
1009};
1010
1011}
1012
1013bool xpdf_ImportFromFile(const OUString& rURL,
1014 const ContentSinkSharedPtr& rSink,
1015 const uno::Reference<task::XInteractionHandler>& xIHdl,
1016 const OUString& rPwd,
1017 const uno::Reference<uno::XComponentContext>& xContext,
1018 const OUString& rFilterOptions)
1019{
1020 OSL_ASSERT(rSink);
1021
1022 OUString aSysUPath;
1023 if( osl_getSystemPathFromFileURL( rURL.pData, &aSysUPath.pData ) != osl_File_E_None )
1024 {
1025 SAL_WARN(
1026 "sdext.pdfimport",
1027 "getSystemPathFromFileURL(" << rURL << ") failed");
1028 return false;
1029 }
1030 OUString aDocName( rURL.copy( rURL.lastIndexOf( '/' )+1 ) );
1031
1032 // check for encryption, if necessary get password
1033 OUString aPwd( rPwd );
1034 bool bIsEncrypted = false;
1035 if( !checkEncryption( aSysUPath, xIHdl, aPwd, bIsEncrypted, aDocName ) )
1036 {
1037 SAL_INFO(
1038 "sdext.pdfimport",
1039 "checkEncryption(" << aSysUPath << ") failed");
1040 return false;
1041 }
1042
1043 // Determine xpdfimport executable URL:
1044 OUString converterURL("$BRAND_BASE_DIR/" LIBO_BIN_FOLDER "/xpdfimport");
1045 rtl::Bootstrap::expandMacros(converterURL); //TODO: detect failure
1046
1047 // Determine pathname of xpdfimport_err.pdf:
1048 OUString errPathname("$BRAND_BASE_DIR/" LIBO_SHARE_FOLDER "/xpdfimport/xpdfimport_err.pdf");
1049 rtl::Bootstrap::expandMacros(errPathname); //TODO: detect failure
1050 if (osl::FileBase::getSystemPathFromFileURL(errPathname, errPathname)
1051 != osl::FileBase::E_None)
1052 {
1053 SAL_WARN(
1054 "sdext.pdfimport",
1055 "getSystemPathFromFileURL(" << errPathname << ") failed");
1056 return false;
1057 }
1058
1059 // spawn separate process to keep LGPL/GPL code apart.
1060
1061 OUString aOptFlag("-o");
1062 rtl_uString* args[] = { aSysUPath.pData, errPathname.pData,
1063 aOptFlag.pData, rFilterOptions.pData };
1064 sal_Int32 nArgs = rFilterOptions.isEmpty() ? 2 : 4;
1065
1066 oslProcess aProcess;
1067 oslFileHandle pIn = nullptr;
1068 oslFileHandle pOut = nullptr;
1069 oslFileHandle pErr = nullptr;
1070 oslSecurity pSecurity = osl_getCurrentSecurity ();
1071 oslProcessError eErr =
1072 osl_executeProcess_WithRedirectedIO(converterURL.pData,
1073 args,
1074 nArgs,
1075 osl_Process_SEARCHPATH|osl_Process_HIDDEN,
1076 pSecurity,
1077 nullptr, nullptr, 0,
1078 &aProcess, &pIn, &pOut, &pErr);
1079 osl_freeSecurityHandle(pSecurity);
1080
1081 bool bRet=true;
1082 try
1083 {
1084 if( eErr!=osl_Process_E_None )
1085 {
1086 SAL_WARN(
1087 "sdext.pdfimport",
1088 "executeProcess of " << converterURL << " failed with "
1089 << +eErr);
1090 return false;
1091 }
1092
1093 if( pIn )
1094 {
1095 OStringBuffer aBuf(256);
1096 if( bIsEncrypted )
1097 aBuf.append( OUStringToOString( aPwd, RTL_TEXTENCODING_ISO_8859_1 ) );
1098 aBuf.append( '\n' );
1099
1100 sal_uInt64 nWritten = 0;
1101 osl_writeFile( pIn, aBuf.getStr(), sal_uInt64(aBuf.getLength()), &nWritten );
1102 }
1103
1104 if( pOut && pErr )
1105 {
1106 // read results of PDF parser. One line - one call to
1107 // OutputDev. stderr is used for alternate streams, like
1108 // embedded fonts and bitmaps
1109 Parser aParser(rSink,pErr,xContext);
1110 Buffering aBuffering(pOut);
1111 OStringBuffer line;
1112 for( ;; )
1113 {
1114 char aChar('\n');
1115 sal_uInt64 nBytesRead;
1116 oslFileError nRes;
1117
1118 // skip garbage \r \n at start of line
1119 for (;;)
1120 {
1121 nRes = aBuffering.read(&aChar, 1, &nBytesRead);
1122 if (osl_File_E_None != nRes || nBytesRead != 1 || (aChar != '\n' && aChar != '\r') )
1123 break;
1124 }
1125 if ( osl_File_E_None != nRes )
1126 break;
1127
1128 if( aChar != '\n' && aChar != '\r' )
1129 line.append( aChar );
1130
1131 for (;;)
1132 {
1133 nRes = aBuffering.read(&aChar, 1, &nBytesRead);
1134 if ( osl_File_E_None != nRes || nBytesRead != 1 || aChar == '\n' || aChar == '\r' )
1135 break;
1136 line.append( aChar );
1137 }
1138 if ( osl_File_E_None != nRes )
1139 break;
1140 if ( line.isEmpty() )
1141 break;
1142
1143 aParser.parseLine(line);
1144 line.setLength(0);
1145 }
1146 }
1147 }
1148 catch( uno::Exception& )
1149 {
1150 // crappy C file interface. need manual resource dealloc
1151 bRet = false;
1152 }
1153
1154 if( pIn )
1155 osl_closeFile(pIn);
1156 if( pOut )
1157 osl_closeFile(pOut);
1158 if( pErr )
1159 osl_closeFile(pErr);
1160 eErr = osl_joinProcess(aProcess);
1161 if (eErr == osl_Process_E_None)
1162 {
1163 oslProcessInfo info;
1164 info.Size = sizeof info;
1165 eErr = osl_getProcessInfo(aProcess, osl_Process_EXITCODE, &info);
1166 if (eErr == osl_Process_E_None)
1167 {
1168 if (info.Code != 0)
1169 {
1170 SAL_WARN(
1171 "sdext.pdfimport",
1172 "getProcessInfo of " << converterURL
1173 << " failed with exit code " << info.Code);
1174 bRet = false;
1175 }
1176 }
1177 else
1178 {
1179 SAL_WARN(
1180 "sdext.pdfimport",
1181 "getProcessInfo of " << converterURL << " failed with "
1182 << +eErr);
1183 bRet = false;
1184 }
1185 }
1186 else
1187 {
1188 SAL_WARN(
1189 "sdext.pdfimport",
1190 "joinProcess of " << converterURL << " failed with " << +eErr);
1191 bRet = false;
1192 }
1193 osl_freeProcessHandle(aProcess);
1194 return bRet;
1195}
1196
1197
1198bool xpdf_ImportFromStream( const uno::Reference< io::XInputStream >& xInput,
1199 const ContentSinkSharedPtr& rSink,
1200 const uno::Reference<task::XInteractionHandler >& xIHdl,
1201 const OUString& rPwd,
1202 const uno::Reference< uno::XComponentContext >& xContext,
1203 const OUString& rFilterOptions )
1204{
1205 OSL_ASSERT(xInput.is());
1206 OSL_ASSERT(rSink);
1207
1208 // convert XInputStream to local temp file
1209 oslFileHandle aFile = nullptr;
1210 OUString aURL;
1211 if( osl_createTempFile( nullptr, &aFile, &aURL.pData ) != osl_File_E_None )
1212 return false;
1213
1214 // copy content, buffered...
1215 const sal_uInt32 nBufSize = 4096;
1216 uno::Sequence<sal_Int8> aBuf( nBufSize );
1217 sal_uInt64 nBytes = 0;
1218 sal_uInt64 nWritten = 0;
1219 bool bSuccess = true;
1220 do
1221 {
1222 try
1223 {
1224 nBytes = xInput->readBytes( aBuf, nBufSize );
1225 }
1226 catch( css::uno::Exception& )
1227 {
1228 osl_closeFile( aFile );
1229 throw;
1230 }
1231 if( nBytes > 0 )
1232 {
1233 osl_writeFile( aFile, aBuf.getConstArray(), nBytes, &nWritten );
1234 if( nWritten != nBytes )
1235 {
1236 bSuccess = false;
1237 break;
1238 }
1239 }
1240 }
1241 while( nBytes == nBufSize );
1242
1243 osl_closeFile( aFile );
1244
1245 if ( bSuccess )
1246 bSuccess = xpdf_ImportFromFile( aURL, rSink, xIHdl, rPwd, xContext, rFilterOptions );
1247 osl_removeFile( aURL.pData );
1248
1249 return bSuccess;
1250}
1251
1252}
1253
1254/* 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:906
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
constexpr tools::Long SIZE
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:976
const oslFileHandle m_pErr
Definition: wrapper.cxx:146
size_t pos
Definition: wrapper.cxx:977
std::string_view m_aLine
Definition: wrapper.cxx:164
Parser & m_parser
Definition: wrapper.cxx:163
std::unique_ptr< char[]> aBuffer
Definition: wrapper.cxx:975
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
FontMapType m_aFontMap
Definition: wrapper.cxx:147
const char * pChar