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