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