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