bump product version to 5.0.4.1
[LibreOffice.git] / sdext / source / pdfimport / wrapper / wrapper.cxx
blob4215163697e864c8da62cc592d39d4cef04e3a59
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <config_folders.h>
22 #include "contentsink.hxx"
23 #include "pdfparse.hxx"
24 #include "pdfihelper.hxx"
25 #include "wrapper.hxx"
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 "rtl/byteseq.hxx"
38 #include "cppuhelper/exc_hlp.hxx"
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/XColorSpace.hpp"
46 #include "com/sun/star/rendering/XPolyPolygon2D.hpp"
47 #include "com/sun/star/rendering/XBitmap.hpp"
48 #include "com/sun/star/geometry/Matrix2D.hpp"
49 #include "com/sun/star/geometry/AffineMatrix2D.hpp"
50 #include "com/sun/star/geometry/RealRectangle2D.hpp"
51 #include "com/sun/star/task/XInteractionHandler.hpp"
53 #include "basegfx/point/b2dpoint.hxx"
54 #include "basegfx/polygon/b2dpolypolygon.hxx"
55 #include "basegfx/polygon/b2dpolygon.hxx"
56 #include "basegfx/tools/canvastools.hxx"
57 #include "basegfx/tools/unopolypolygon.hxx"
59 #include <vcl/metric.hxx>
60 #include <vcl/font.hxx>
61 #include <vcl/virdev.hxx>
63 #include <boost/scoped_ptr.hpp>
64 #include <unordered_map>
65 #include <string.h>
66 #ifdef WNT
67 #include <stdlib.h>
68 #include <ctype.h>
69 #endif
71 #include "rtl/bootstrap.h"
73 using namespace com::sun::star;
75 namespace pdfi
78 namespace
81 // identifier of the strings coming from the out-of-process xpdf
82 // converter
83 enum parseKey {
84 CLIPPATH,
85 DRAWCHAR,
86 DRAWIMAGE,
87 DRAWLINK,
88 DRAWMASK,
89 DRAWMASKEDIMAGE,
90 DRAWSOFTMASKEDIMAGE,
91 ENDPAGE,
92 ENDTEXTOBJECT,
93 EOCLIPPATH,
94 EOFILLPATH,
95 FILLPATH,
96 HYPERLINK,
97 INTERSECTCLIP,
98 INTERSECTEOCLIP,
99 POPSTATE,
100 PUSHSTATE,
101 RESTORESTATE,
102 SAVESTATE,
103 SETBLENDMODE,
104 SETFILLCOLOR,
105 SETFONT,
106 SETLINECAP,
107 SETLINEDASH,
108 SETLINEJOIN,
109 SETLINEWIDTH,
110 SETMITERLIMIT,
111 SETPAGENUM,
112 SETSTROKECOLOR,
113 SETTEXTRENDERMODE,
114 SETTRANSFORMATION,
115 STARTPAGE,
116 STROKEPATH,
117 UPDATEBLENDMODE,
118 UPDATECTM,
119 UPDATEFILLCOLOR,
120 UPDATEFILLOPACITY,
121 UPDATEFLATNESS,
122 UPDATEFONT,
123 UPDATELINECAP,
124 UPDATELINEDASH,
125 UPDATELINEJOIN,
126 UPDATELINEWIDTH,
127 UPDATEMITERLIMIT,
128 UPDATESTROKECOLOR,
129 UPDATESTROKEOPACITY,
130 NONE
133 #include "hash.cxx"
135 class Parser
137 typedef std::unordered_map< sal_Int64,
138 FontAttributes > FontMapType;
140 const uno::Reference<uno::XComponentContext> m_xContext;
141 const ContentSinkSharedPtr m_pSink;
142 const oslFileHandle m_pErr;
143 OString m_aLine;
144 FontMapType m_aFontMap;
145 sal_Int32 m_nNextToken;
146 sal_Int32 m_nCharIndex;
149 OString readNextToken();
150 void readInt32( sal_Int32& o_Value );
151 sal_Int32 readInt32();
152 void readInt64( sal_Int64& o_Value );
153 void readDouble( double& o_Value );
154 double readDouble();
155 void readBinaryData( uno::Sequence<sal_Int8>& rBuf );
157 uno::Reference<rendering::XPolyPolygon2D> readPath();
159 void readChar();
160 void readLineCap();
161 void readLineDash();
162 void readLineJoin();
163 void readTransformation();
164 rendering::ARGBColor readColor();
165 static void parseFontFamilyName( FontAttributes& aResult );
166 void readFont();
167 uno::Sequence<beans::PropertyValue> readImageImpl();
169 void readImage();
170 void readMask();
171 void readLink();
172 void readMaskedImage();
173 void readSoftMaskedImage();
174 static sal_Int32 parseFontCheckForString(const sal_Unicode* pCopy, sal_Int32 nCopyLen,
175 const char* pAttrib, sal_Int32 nAttribLen,
176 FontAttributes& rResult, bool bItalic, bool bBold);
177 static sal_Int32 parseFontRemoveSuffix(const sal_Unicode* pCopy, sal_Int32 nCopyLen,
178 const char* pAttrib, sal_Int32 nAttribLen);
180 public:
181 Parser( const ContentSinkSharedPtr& rSink,
182 oslFileHandle pErr,
183 const uno::Reference<uno::XComponentContext>& xContext ) :
184 m_xContext(xContext),
185 m_pSink(rSink),
186 m_pErr(pErr),
187 m_aLine(),
188 m_aFontMap(101),
189 m_nNextToken(-1),
190 m_nCharIndex(-1)
193 void parseLine( const OString& rLine );
197 namespace
200 /** Unescapes line-ending characters in input string. These
201 characters are encoded as pairs of characters: '\\' 'n', resp.
202 '\\' 'r'. This function converts them back to '\n', resp. '\r'.
204 OString lcl_unescapeLineFeeds(const OString& i_rStr)
206 const size_t nOrigLen(sal::static_int_cast<size_t>(i_rStr.getLength()));
207 const sal_Char* const pOrig(i_rStr.getStr());
208 sal_Char* const pBuffer(new sal_Char[nOrigLen + 1]);
210 const sal_Char* pRead(pOrig);
211 sal_Char* pWrite(pBuffer);
212 const sal_Char* pCur(pOrig);
213 while ((pCur = strchr(pCur, '\\')) != 0)
215 const sal_Char cNext(pCur[1]);
216 if (cNext == 'n' || cNext == 'r' || cNext == '\\')
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;
225 else
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;
233 // maybe there are some data to copy yet
234 if (sal::static_int_cast<size_t>(pRead - pOrig) < nOrigLen)
236 const size_t nLen(nOrigLen - (pRead - pOrig));
237 strncpy(pWrite, pRead, nLen);
238 pWrite += nLen;
240 *pWrite = '\0';
242 OString aResult(pBuffer);
243 delete[] pBuffer;
244 return aResult;
250 OString Parser::readNextToken()
252 OSL_PRECOND(m_nCharIndex!=-1,"insufficient input");
253 return m_aLine.getToken(m_nNextToken,' ',m_nCharIndex);
256 void Parser::readInt32( sal_Int32& o_Value )
258 o_Value = readNextToken().toInt32();
261 sal_Int32 Parser::readInt32()
263 return readNextToken().toInt32();
266 void Parser::readInt64( sal_Int64& o_Value )
268 o_Value = readNextToken().toInt64();
271 void Parser::readDouble( double& o_Value )
273 o_Value = readNextToken().toDouble();
276 double Parser::readDouble()
278 return readNextToken().toDouble();
281 void Parser::readBinaryData( uno::Sequence<sal_Int8>& rBuf )
283 sal_Int32 nFileLen( rBuf.getLength() );
284 sal_Int8* pBuf( rBuf.getArray() );
285 sal_uInt64 nBytesRead(0);
286 oslFileError nRes=osl_File_E_None;
287 while( nFileLen &&
288 osl_File_E_None == (nRes=osl_readFile( m_pErr, pBuf, nFileLen, &nBytesRead )) )
290 pBuf += nBytesRead;
291 nFileLen -= sal::static_int_cast<sal_Int32>(nBytesRead);
294 OSL_PRECOND(nRes==osl_File_E_None, "inconsistent data");
297 uno::Reference<rendering::XPolyPolygon2D> Parser::readPath()
299 const OString aSubPathMarker( "subpath" );
301 if( readNextToken() != aSubPathMarker )
302 OSL_PRECOND(false, "broken path");
304 basegfx::B2DPolyPolygon aResult;
305 while( m_nCharIndex != -1 )
307 basegfx::B2DPolygon aSubPath;
309 sal_Int32 nClosedFlag;
310 readInt32( nClosedFlag );
311 aSubPath.setClosed( nClosedFlag != 0 );
313 sal_Int32 nContiguousControlPoints(0);
314 sal_Int32 nDummy=m_nCharIndex;
315 OString aCurrToken( m_aLine.getToken(m_nNextToken,' ',nDummy) );
317 while( m_nCharIndex != -1 && aCurrToken != aSubPathMarker )
319 sal_Int32 nCurveFlag;
320 double nX, nY;
321 readDouble( nX );
322 readDouble( nY );
323 readInt32( nCurveFlag );
325 aSubPath.append(basegfx::B2DPoint(nX,nY));
326 if( nCurveFlag )
328 ++nContiguousControlPoints;
330 else if( nContiguousControlPoints )
332 OSL_PRECOND(nContiguousControlPoints==2,"broken bezier path");
334 // have two control points before us. the current one
335 // is a normal point - thus, convert previous points
336 // into bezier segment
337 const sal_uInt32 nPoints( aSubPath.count() );
338 const basegfx::B2DPoint aCtrlA( aSubPath.getB2DPoint(nPoints-3) );
339 const basegfx::B2DPoint aCtrlB( aSubPath.getB2DPoint(nPoints-2) );
340 const basegfx::B2DPoint aEnd( aSubPath.getB2DPoint(nPoints-1) );
341 aSubPath.remove(nPoints-3, 3);
342 aSubPath.appendBezierSegment(aCtrlA, aCtrlB, aEnd);
344 nContiguousControlPoints=0;
347 // one token look-ahead (new subpath or more points?
348 nDummy=m_nCharIndex;
349 aCurrToken = m_aLine.getToken(m_nNextToken,' ',nDummy);
352 aResult.append( aSubPath );
353 if( m_nCharIndex != -1 )
354 readNextToken();
357 return static_cast<rendering::XLinePolyPolygon2D*>(
358 new basegfx::unotools::UnoPolyPolygon(aResult));
361 void Parser::readChar()
363 double fontSize;
364 geometry::Matrix2D aUnoMatrix;
365 geometry::RealRectangle2D aRect;
367 readDouble(aRect.X1);
368 readDouble(aRect.Y1);
369 readDouble(aRect.X2);
370 readDouble(aRect.Y2);
371 readDouble(aUnoMatrix.m00);
372 readDouble(aUnoMatrix.m01);
373 readDouble(aUnoMatrix.m10);
374 readDouble(aUnoMatrix.m11);
375 readDouble(fontSize);
377 OString aChars;
379 if (m_nCharIndex != -1)
380 aChars = lcl_unescapeLineFeeds( m_aLine.copy( m_nCharIndex ) );
382 // chars gobble up rest of line
383 m_nCharIndex = -1;
385 m_pSink->drawGlyphs(OStringToOUString(aChars, RTL_TEXTENCODING_UTF8),
386 aRect, aUnoMatrix, fontSize);
389 void Parser::readLineCap()
391 sal_Int8 nCap(rendering::PathCapType::BUTT);
392 switch( readInt32() )
394 default:
395 // FALLTHROUGH intended
396 case 0: nCap = rendering::PathCapType::BUTT; break;
397 case 1: nCap = rendering::PathCapType::ROUND; break;
398 case 2: nCap = rendering::PathCapType::SQUARE; break;
400 m_pSink->setLineCap(nCap);
403 void Parser::readLineDash()
405 if( m_nCharIndex == -1 )
407 m_pSink->setLineDash( uno::Sequence<double>(), 0.0 );
408 return;
411 const double nOffset(readDouble());
412 const sal_Int32 nLen(readInt32());
414 uno::Sequence<double> aDashArray(nLen);
415 double* pArray=aDashArray.getArray();
416 for( sal_Int32 i=0; i<nLen; ++i )
417 *pArray++ = readDouble();
419 m_pSink->setLineDash( aDashArray, nOffset );
422 void Parser::readLineJoin()
424 sal_Int8 nJoin(rendering::PathJoinType::MITER);
425 switch( readInt32() )
427 default:
428 // FALLTHROUGH intended
429 case 0: nJoin = rendering::PathJoinType::MITER; break;
430 case 1: nJoin = rendering::PathJoinType::ROUND; break;
431 case 2: nJoin = rendering::PathJoinType::BEVEL; break;
433 m_pSink->setLineJoin(nJoin);
436 void Parser::readTransformation()
438 geometry::AffineMatrix2D aMat;
439 readDouble(aMat.m00);
440 readDouble(aMat.m10);
441 readDouble(aMat.m01);
442 readDouble(aMat.m11);
443 readDouble(aMat.m02);
444 readDouble(aMat.m12);
445 m_pSink->setTransformation( aMat );
448 rendering::ARGBColor Parser::readColor()
450 rendering::ARGBColor aRes;
451 readDouble(aRes.Red);
452 readDouble(aRes.Green);
453 readDouble(aRes.Blue);
454 readDouble(aRes.Alpha);
455 return aRes;
458 sal_Int32 Parser::parseFontCheckForString(
459 const sal_Unicode* pCopy, sal_Int32 nCopyLen,
460 const char* pAttrib, sal_Int32 nAttribLen,
461 FontAttributes& rResult, bool bItalic, bool bBold)
463 if (nCopyLen < nAttribLen)
464 return 0;
465 for (sal_Int32 i = 0; i < nAttribLen; ++i)
466 if (tolower(pCopy[i]) != pAttrib[i]
467 && toupper(pCopy[i]) != pAttrib[i])
468 return 0;
469 rResult.isItalic |= bItalic;
470 rResult.isBold |= bBold;
471 return nAttribLen;
474 sal_Int32 Parser::parseFontRemoveSuffix(
475 const sal_Unicode* pCopy, sal_Int32 nCopyLen,
476 const char* pAttrib, sal_Int32 nAttribLen)
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;
486 void Parser::parseFontFamilyName( FontAttributes& rResult )
488 OUStringBuffer aNewFamilyName( rResult.familyName.getLength() );
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] == '+' )
495 pCopy += 7;
496 nLen -= 7;
499 // TODO: Looks like this block needs to be refactored
500 while( nLen )
502 if (parseFontRemoveSuffix(pCopy, nLen, RTL_CONSTASCII_STRINGPARAM("PSMT")))
504 nLen -= RTL_CONSTASCII_LENGTH("PSMT");
506 else if (parseFontRemoveSuffix(pCopy, nLen, RTL_CONSTASCII_STRINGPARAM("MT")))
508 nLen -= RTL_CONSTASCII_LENGTH("MT");
511 if (parseFontCheckForString(pCopy, nLen, RTL_CONSTASCII_STRINGPARAM("Italic"), rResult, true, false))
513 sal_Int32 nAttribLen = RTL_CONSTASCII_LENGTH("Italic");
514 nLen -= nAttribLen;
515 pCopy += nAttribLen;
517 else if (parseFontCheckForString(pCopy, nLen, RTL_CONSTASCII_STRINGPARAM("-Bold"), rResult, false, true))
519 sal_Int32 nAttribLen = RTL_CONSTASCII_LENGTH("-Bold");
520 nLen -= nAttribLen;
521 pCopy += nAttribLen;
523 else if (parseFontCheckForString(pCopy, nLen, RTL_CONSTASCII_STRINGPARAM("Bold"), rResult, false, true))
525 sal_Int32 nAttribLen = RTL_CONSTASCII_LENGTH("Bold");
526 nLen -= nAttribLen;
527 pCopy += nAttribLen;
529 else if (parseFontCheckForString(pCopy, nLen, RTL_CONSTASCII_STRINGPARAM("-Roman"), rResult, false, false))
531 sal_Int32 nAttribLen = RTL_CONSTASCII_LENGTH("-Roman");
532 nLen -= nAttribLen;
533 pCopy += nAttribLen;
535 else if (parseFontCheckForString(pCopy, nLen, RTL_CONSTASCII_STRINGPARAM("-LightOblique"), rResult, true, false))
537 sal_Int32 nAttribLen = RTL_CONSTASCII_LENGTH("-LightOblique");
538 nLen -= nAttribLen;
539 pCopy += nAttribLen;
541 else if (parseFontCheckForString(pCopy, nLen, RTL_CONSTASCII_STRINGPARAM("-BoldOblique"), rResult, true, true))
543 sal_Int32 nAttribLen = RTL_CONSTASCII_LENGTH("-BoldOblique");
544 nLen -= nAttribLen;
545 pCopy += nAttribLen;
547 else if (parseFontCheckForString(pCopy, nLen, RTL_CONSTASCII_STRINGPARAM("-Light"), rResult, false, false))
549 sal_Int32 nAttribLen = RTL_CONSTASCII_LENGTH("-Light");
550 nLen -= nAttribLen;
551 pCopy += nAttribLen;
553 else if (parseFontCheckForString(pCopy, nLen, RTL_CONSTASCII_STRINGPARAM("-Reg"), rResult, false, false))
555 sal_Int32 nAttribLen = RTL_CONSTASCII_LENGTH("-Reg");
556 nLen -= nAttribLen;
557 pCopy += nAttribLen;
559 else if(nLen > 0)
561 if( *pCopy != '-' )
562 aNewFamilyName.append( *pCopy );
563 pCopy++;
564 nLen--;
567 rResult.familyName = aNewFamilyName.makeStringAndClear();
570 void Parser::readFont()
572 OString aFontName;
573 sal_Int64 nFontID;
574 sal_Int32 nIsEmbedded, nIsBold, nIsItalic, nIsUnderline, nFileLen;
575 double nSize;
577 readInt64(nFontID);
578 readInt32(nIsEmbedded);
579 readInt32(nIsBold);
580 readInt32(nIsItalic);
581 readInt32(nIsUnderline);
582 readDouble(nSize);
583 readInt32(nFileLen);
585 nSize = nSize < 0.0 ? -nSize : nSize;
586 aFontName = lcl_unescapeLineFeeds( m_aLine.copy( m_nCharIndex ) );
588 // name gobbles up rest of line
589 m_nCharIndex = -1;
591 FontMapType::const_iterator pFont( m_aFontMap.find(nFontID) );
592 if( pFont != m_aFontMap.end() )
594 OSL_PRECOND(nFileLen==0,"font data for known font");
595 FontAttributes aRes(pFont->second);
596 aRes.size = nSize;
597 m_pSink->setFont( aRes );
599 return;
602 // yet unknown font - get info and add to map
603 FontAttributes aResult( OStringToOUString( aFontName,
604 RTL_TEXTENCODING_UTF8 ),
605 nIsBold != 0,
606 nIsItalic != 0,
607 nIsUnderline != 0,
608 false,
609 nSize,
610 1.0);
612 // extract textual attributes (bold, italic in the name, etc.)
613 parseFontFamilyName(aResult);
614 // need to read font file?
615 if( nFileLen )
617 uno::Sequence<sal_Int8> aFontFile(nFileLen);
618 readBinaryData( aFontFile );
620 awt::FontDescriptor aFD;
621 uno::Sequence< uno::Any > aArgs(1);
622 aArgs[0] <<= aFontFile;
626 uno::Reference< beans::XMaterialHolder > xMat(
627 m_xContext->getServiceManager()->createInstanceWithArgumentsAndContext(
628 OUString( "com.sun.star.awt.FontIdentificator" ),
629 aArgs,
630 m_xContext ),
631 uno::UNO_QUERY );
632 if( xMat.is() )
634 uno::Any aRes( xMat->getMaterial() );
635 if( aRes >>= aFD )
637 if (!aFD.Name.isEmpty())
639 aResult.familyName = aFD.Name;
640 parseFontFamilyName(aResult);
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;
650 catch( uno::Exception& )
654 if( aResult.familyName.isEmpty() )
656 // last fallback
657 aResult.familyName = "Arial";
658 aResult.isUnderline = false;
663 static VclPtr<VirtualDevice> vDev;
664 if (!vDev)
665 vDev = VclPtr<VirtualDevice>::Create();
667 vcl::Font font(aResult.familyName, Size(0, 1000));
668 vDev->SetFont(font);
669 FontMetric metric(vDev->GetFontMetric());
670 aResult.ascent = metric.GetAscent() / 1000.0;
672 m_aFontMap[nFontID] = aResult;
674 aResult.size = nSize;
675 m_pSink->setFont(aResult);
678 uno::Sequence<beans::PropertyValue> Parser::readImageImpl()
680 static const char aJpegMarker[] = "JPEG";
681 static const char aPbmMarker[] = "PBM";
682 static const char aPpmMarker[] = "PPM";
683 static const char aPngMarker[] = "PNG";
684 static const char aJpegFile[] = "DUMMY.JPEG";
685 static const char aPbmFile[] = "DUMMY.PBM";
686 static const char aPpmFile[] = "DUMMY.PPM";
687 static const char aPngFile[] = "DUMMY.PNG";
689 OString aToken = readNextToken();
690 const sal_Int32 nImageSize( readInt32() );
692 OUString aFileName;
693 if( aToken == aPngMarker )
694 aFileName = aPngFile;
695 else if( aToken == aJpegMarker )
696 aFileName = aJpegFile;
697 else if( aToken == aPbmMarker )
698 aFileName = aPbmFile;
699 else
701 SAL_WARN_IF(aToken != aPpmMarker,"sdext.pdfimport","Invalid bitmap format");
702 aFileName = aPpmFile;
705 uno::Sequence<sal_Int8> aDataSequence(nImageSize);
706 readBinaryData( aDataSequence );
708 uno::Sequence< uno::Any > aStreamCreationArgs(1);
709 aStreamCreationArgs[0] <<= aDataSequence;
711 uno::Reference< uno::XComponentContext > xContext( m_xContext, uno::UNO_SET_THROW );
712 uno::Reference< lang::XMultiComponentFactory > xFactory( xContext->getServiceManager(), uno::UNO_SET_THROW );
713 uno::Reference< io::XInputStream > xDataStream( xFactory->createInstanceWithArgumentsAndContext(
714 OUString( "com.sun.star.io.SequenceInputStream" ),
715 aStreamCreationArgs, m_xContext ), uno::UNO_QUERY_THROW );
717 uno::Sequence<beans::PropertyValue> aSequence(3);
718 aSequence[0] = beans::PropertyValue( OUString("URL"),
720 uno::makeAny(aFileName),
721 beans::PropertyState_DIRECT_VALUE );
722 aSequence[1] = beans::PropertyValue( OUString("InputStream"),
724 uno::makeAny( xDataStream ),
725 beans::PropertyState_DIRECT_VALUE );
726 aSequence[2] = beans::PropertyValue( OUString("InputSequence"),
728 uno::makeAny(aDataSequence),
729 beans::PropertyState_DIRECT_VALUE );
731 return aSequence;
734 void Parser::readImage()
736 sal_Int32 nWidth, nHeight,nMaskColors;
737 readInt32(nWidth);
738 readInt32(nHeight);
739 readInt32(nMaskColors);
741 uno::Sequence<beans::PropertyValue> aImg( readImageImpl() );
743 if( nMaskColors )
745 uno::Sequence<sal_Int8> aDataSequence(nMaskColors);
746 readBinaryData( aDataSequence );
748 uno::Sequence<uno::Any> aMaskRanges(2);
750 uno::Sequence<double> aMinRange(nMaskColors/2);
751 uno::Sequence<double> aMaxRange(nMaskColors/2);
752 for( sal_Int32 i=0; i<nMaskColors/2; ++i )
754 aMinRange[i] = aDataSequence[i] / 255.0;
755 aMaxRange[i] = aDataSequence[i+nMaskColors/2] / 255.0;
758 aMaskRanges[0] = uno::makeAny(aMinRange);
759 aMaskRanges[1] = uno::makeAny(aMaxRange);
761 m_pSink->drawColorMaskedImage( aImg, aMaskRanges );
763 else
764 m_pSink->drawImage( aImg );
767 void Parser::readMask()
769 sal_Int32 nWidth, nHeight, nInvert;
770 readInt32(nWidth);
771 readInt32(nHeight);
772 readInt32(nInvert);
774 m_pSink->drawMask( readImageImpl(), nInvert );
777 void Parser::readLink()
779 geometry::RealRectangle2D aBounds;
780 readDouble(aBounds.X1);
781 readDouble(aBounds.Y1);
782 readDouble(aBounds.X2);
783 readDouble(aBounds.Y2);
785 m_pSink->hyperLink( aBounds,
786 OStringToOUString( lcl_unescapeLineFeeds(
787 m_aLine.copy(m_nCharIndex) ),
788 RTL_TEXTENCODING_UTF8 ) );
789 // name gobbles up rest of line
790 m_nCharIndex = -1;
793 void Parser::readMaskedImage()
795 sal_Int32 nWidth, nHeight, nMaskWidth, nMaskHeight, nMaskInvert;
796 readInt32(nWidth);
797 readInt32(nHeight);
798 readInt32(nMaskWidth);
799 readInt32(nMaskHeight);
800 readInt32(nMaskInvert);
802 const uno::Sequence<beans::PropertyValue> aImage( readImageImpl() );
803 const uno::Sequence<beans::PropertyValue> aMask ( readImageImpl() );
804 m_pSink->drawMaskedImage( aImage, aMask, nMaskInvert != 0 );
807 void Parser::readSoftMaskedImage()
809 sal_Int32 nWidth, nHeight, nMaskWidth, nMaskHeight;
810 readInt32(nWidth);
811 readInt32(nHeight);
812 readInt32(nMaskWidth);
813 readInt32(nMaskHeight);
815 const uno::Sequence<beans::PropertyValue> aImage( readImageImpl() );
816 const uno::Sequence<beans::PropertyValue> aMask ( readImageImpl() );
817 m_pSink->drawAlphaMaskedImage( aImage, aMask );
820 void Parser::parseLine( const OString& rLine )
822 OSL_PRECOND( m_pSink, "Invalid sink" );
823 OSL_PRECOND( m_pErr, "Invalid filehandle" );
824 OSL_PRECOND( m_xContext.is(), "Invalid service factory" );
826 m_nNextToken = 0; m_nCharIndex = 0; m_aLine = rLine;
827 uno::Reference<rendering::XPolyPolygon2D> xPoly;
828 const OString& rCmd = readNextToken();
829 const hash_entry* pEntry = PdfKeywordHash::in_word_set( rCmd.getStr(),
830 rCmd.getLength() );
831 OSL_ASSERT(pEntry);
832 switch( pEntry->eKey )
834 case CLIPPATH:
835 m_pSink->intersectClip(readPath()); break;
836 case DRAWCHAR:
837 readChar(); break;
838 case DRAWIMAGE:
839 readImage(); break;
840 case DRAWLINK:
841 readLink(); break;
842 case DRAWMASK:
843 readMask(); break;
844 case DRAWMASKEDIMAGE:
845 readMaskedImage(); break;
846 case DRAWSOFTMASKEDIMAGE:
847 readSoftMaskedImage(); break;
848 case ENDPAGE:
849 m_pSink->endPage(); break;
850 case ENDTEXTOBJECT:
851 m_pSink->endText(); break;
852 case EOCLIPPATH:
853 m_pSink->intersectEoClip(readPath()); break;
854 case EOFILLPATH:
855 m_pSink->eoFillPath(readPath()); break;
856 case FILLPATH:
857 m_pSink->fillPath(readPath()); break;
858 case RESTORESTATE:
859 m_pSink->popState(); break;
860 case SAVESTATE:
861 m_pSink->pushState(); break;
862 case SETPAGENUM:
863 m_pSink->setPageNum( readInt32() ); break;
864 case STARTPAGE:
866 const double nWidth ( readDouble() );
867 const double nHeight( readDouble() );
868 m_pSink->startPage( geometry::RealSize2D( nWidth, nHeight ) );
869 break;
871 case STROKEPATH:
872 m_pSink->strokePath(readPath()); break;
873 case UPDATECTM:
874 readTransformation(); break;
875 case UPDATEFILLCOLOR:
876 m_pSink->setFillColor( readColor() ); break;
877 case UPDATEFLATNESS:
878 m_pSink->setFlatness( readDouble( ) ); break;
879 case UPDATEFONT:
880 readFont(); break;
881 case UPDATELINECAP:
882 readLineCap(); break;
883 case UPDATELINEDASH:
884 readLineDash(); break;
885 case UPDATELINEJOIN:
886 readLineJoin(); break;
887 case UPDATELINEWIDTH:
888 m_pSink->setLineWidth( readDouble() );break;
889 case UPDATEMITERLIMIT:
890 m_pSink->setMiterLimit( readDouble() ); break;
891 case UPDATESTROKECOLOR:
892 m_pSink->setStrokeColor( readColor() ); break;
893 case UPDATESTROKEOPACITY:
894 break;
895 case SETTEXTRENDERMODE:
896 m_pSink->setTextRenderMode( readInt32() ); break;
898 case NONE:
899 default:
900 OSL_PRECOND(false,"Unknown input");
901 break;
904 // all consumed?
905 SAL_WARN_IF(m_nCharIndex!=-1, "sdext.pdfimport", "leftover scanner input");
908 oslFileError readLine( oslFileHandle pFile, OStringBuffer& line )
910 OSL_PRECOND( line.isEmpty(), "line buf not empty" );
912 // TODO(P3): read larger chunks
913 sal_Char aChar('\n');
914 sal_uInt64 nBytesRead;
915 oslFileError nRes;
917 // skip garbage \r \n at start of line
918 while( osl_File_E_None == (nRes=osl_readFile(pFile, &aChar, 1, &nBytesRead)) &&
919 nBytesRead == 1 &&
920 (aChar == '\n' || aChar == '\r') ) ;
922 if( aChar != '\n' && aChar != '\r' )
923 line.append( aChar );
925 while( osl_File_E_None == (nRes=osl_readFile(pFile, &aChar, 1, &nBytesRead)) &&
926 nBytesRead == 1 && aChar != '\n' && aChar != '\r' )
928 line.append( aChar );
931 return nRes;
934 } // namespace
936 static bool checkEncryption( const OUString& i_rPath,
937 const uno::Reference< task::XInteractionHandler >& i_xIHdl,
938 OUString& io_rPwd,
939 bool& o_rIsEncrypted,
940 const OUString& i_rDocName
943 bool bSuccess = false;
944 OString aPDFFile;
945 aPDFFile = OUStringToOString( i_rPath, osl_getThreadTextEncoding() );
947 pdfparse::PDFReader aParser;
948 boost::scoped_ptr<pdfparse::PDFEntry> pEntry( pdfparse::PDFReader::read( aPDFFile.getStr() ));
949 if( pEntry )
951 pdfparse::PDFFile* pPDFFile = dynamic_cast<pdfparse::PDFFile*>(pEntry.get());
952 if( pPDFFile )
954 o_rIsEncrypted = pPDFFile->isEncrypted();
955 if( o_rIsEncrypted )
957 if( pPDFFile->usesSupportedEncryptionFormat() )
959 bool bAuthenticated = false;
960 if( !io_rPwd.isEmpty() )
962 OString aIsoPwd = OUStringToOString( io_rPwd,
963 RTL_TEXTENCODING_ISO_8859_1 );
964 bAuthenticated = pPDFFile->setupDecryptionData( aIsoPwd.getStr() );
966 if( bAuthenticated )
967 bSuccess = true;
968 else
970 if( i_xIHdl.is() )
972 bool bEntered = false;
975 bEntered = getPassword( i_xIHdl, io_rPwd, ! bEntered, i_rDocName );
976 OString aIsoPwd = OUStringToOString( io_rPwd,
977 RTL_TEXTENCODING_ISO_8859_1 );
978 bAuthenticated = pPDFFile->setupDecryptionData( aIsoPwd.getStr() );
979 } while( bEntered && ! bAuthenticated );
982 OSL_TRACE( "password: %s", bAuthenticated ? "matches" : "does not match" );
983 bSuccess = bAuthenticated;
985 if( bAuthenticated )
987 OUStringBuffer aBuf( 128 );
988 aBuf.appendAscii( "_OOO_pdfi_Credentials_" );
989 aBuf.append( pPDFFile->getDecryptionKey() );
990 io_rPwd = aBuf.makeStringAndClear();
993 else if( i_xIHdl.is() )
995 reportUnsupportedEncryptionFormat( i_xIHdl );
996 //TODO: this should either be handled further down the
997 // call stack, or else information that this has already
998 // been handled should be passed down the call stack, so
999 // that SfxBaseModel::load does not show an additional
1000 // "General Error" message box
1003 else
1004 bSuccess = true;
1007 return bSuccess;
1010 bool xpdf_ImportFromFile( const OUString& rURL,
1011 const ContentSinkSharedPtr& rSink,
1012 const uno::Reference< task::XInteractionHandler >& xIHdl,
1013 const OUString& rPwd,
1014 const uno::Reference< uno::XComponentContext >& xContext,
1015 const OUString& rFilterOptions )
1017 OSL_ASSERT(rSink);
1019 OUString aSysUPath;
1020 if( osl_getSystemPathFromFileURL( rURL.pData, &aSysUPath.pData ) != osl_File_E_None )
1022 SAL_WARN(
1023 "sdext.pdfimport",
1024 "getSystemPathFromFileURL(" << rURL << ") failed");
1025 return false;
1027 OUString aDocName( rURL.copy( rURL.lastIndexOf( '/' )+1 ) );
1029 // check for encryption, if necessary get password
1030 OUString aPwd( rPwd );
1031 bool bIsEncrypted = false;
1032 if( !checkEncryption( aSysUPath, xIHdl, aPwd, bIsEncrypted, aDocName ) )
1034 SAL_INFO(
1035 "sdext.pdfimport",
1036 "checkEncryption(" << aSysUPath << ") failed");
1037 return false;
1040 // Determine xpdfimport executable URL:
1041 OUString converterURL("$BRAND_BASE_DIR/" LIBO_BIN_FOLDER "/xpdfimport");
1042 rtl::Bootstrap::expandMacros(converterURL); //TODO: detect failure
1044 // Determine pathname of xpdfimport_err.pdf:
1045 OUString errPathname("$BRAND_BASE_DIR/" LIBO_SHARE_FOLDER "/xpdfimport/xpdfimport_err.pdf");
1046 rtl::Bootstrap::expandMacros(errPathname); //TODO: detect failure
1047 if (osl::FileBase::getSystemPathFromFileURL(errPathname, errPathname)
1048 != osl::FileBase::E_None)
1050 SAL_WARN(
1051 "sdext.pdfimport",
1052 "getSystemPathFromFileURL(" << errPathname << ") failed");
1053 return false;
1056 // spawn separate process to keep LGPL/GPL code apart.
1058 OUString aOptFlag("-o");
1059 rtl_uString* args[] = { aSysUPath.pData, errPathname.pData,
1060 aOptFlag.pData, rFilterOptions.pData };
1061 sal_Int32 nArgs = rFilterOptions.isEmpty() ? 2 : 4;
1063 oslProcess aProcess;
1064 oslFileHandle pIn = NULL;
1065 oslFileHandle pOut = NULL;
1066 oslFileHandle pErr = NULL;
1067 oslSecurity pSecurity = osl_getCurrentSecurity ();
1068 oslProcessError eErr =
1069 osl_executeProcess_WithRedirectedIO(converterURL.pData,
1070 args,
1071 nArgs,
1072 osl_Process_SEARCHPATH|osl_Process_HIDDEN,
1073 pSecurity,
1074 0, 0, 0,
1075 &aProcess, &pIn, &pOut, &pErr);
1076 osl_freeSecurityHandle(pSecurity);
1078 bool bRet=true;
1081 if( eErr!=osl_Process_E_None )
1083 SAL_WARN(
1084 "sdext.pdfimport",
1085 "executeProcess of " << converterURL << " failed with "
1086 << +eErr);
1087 return false;
1090 if( pIn )
1092 OStringBuffer aBuf(256);
1093 if( bIsEncrypted )
1094 aBuf.append( OUStringToOString( aPwd, RTL_TEXTENCODING_ISO_8859_1 ) );
1095 aBuf.append( '\n' );
1097 sal_uInt64 nWritten = 0;
1098 osl_writeFile( pIn, aBuf.getStr(), sal_uInt64(aBuf.getLength()), &nWritten );
1101 if( pOut && pErr )
1103 // read results of PDF parser. One line - one call to
1104 // OutputDev. stderr is used for alternate streams, like
1105 // embedded fonts and bitmaps
1106 Parser aParser(rSink,pErr,xContext);
1107 OStringBuffer line;
1108 while( osl_File_E_None == readLine(pOut, line) && !line.isEmpty() )
1109 aParser.parseLine(line.makeStringAndClear());
1112 catch( uno::Exception& )
1114 // crappy C file interface. need manual resource dealloc
1115 bRet = false;
1118 if( pIn )
1119 osl_closeFile(pIn);
1120 if( pOut )
1121 osl_closeFile(pOut);
1122 if( pErr )
1123 osl_closeFile(pErr);
1124 eErr = osl_joinProcess(aProcess);
1125 if (eErr == osl_Process_E_None)
1127 oslProcessInfo info;
1128 info.Size = sizeof info;
1129 eErr = osl_getProcessInfo(aProcess, osl_Process_EXITCODE, &info);
1130 if (eErr == osl_Process_E_None)
1132 if (info.Code != 0)
1134 SAL_WARN(
1135 "sdext.pdfimport",
1136 "getProcessInfo of " << converterURL
1137 << " failed with exit code " << info.Code);
1138 bRet = false;
1141 else
1143 SAL_WARN(
1144 "sdext.pdfimport",
1145 "getProcessInfo of " << converterURL << " failed with "
1146 << +eErr);
1147 bRet = false;
1150 else
1152 SAL_WARN(
1153 "sdext.pdfimport",
1154 "joinProcess of " << converterURL << " failed with " << +eErr);
1155 bRet = false;
1157 osl_freeProcessHandle(aProcess);
1158 return bRet;
1162 bool xpdf_ImportFromStream( const uno::Reference< io::XInputStream >& xInput,
1163 const ContentSinkSharedPtr& rSink,
1164 const uno::Reference<task::XInteractionHandler >& xIHdl,
1165 const OUString& rPwd,
1166 const uno::Reference< uno::XComponentContext >& xContext,
1167 const OUString& rFilterOptions )
1169 OSL_ASSERT(xInput.is());
1170 OSL_ASSERT(rSink);
1172 // convert XInputStream to local temp file
1173 oslFileHandle aFile = NULL;
1174 OUString aURL;
1175 if( osl_createTempFile( NULL, &aFile, &aURL.pData ) != osl_File_E_None )
1176 return false;
1178 // copy content, buffered...
1179 const sal_uInt32 nBufSize = 4096;
1180 uno::Sequence<sal_Int8> aBuf( nBufSize );
1181 sal_uInt64 nBytes = 0;
1182 sal_uInt64 nWritten = 0;
1183 bool bSuccess = true;
1188 nBytes = xInput->readBytes( aBuf, nBufSize );
1190 catch( com::sun::star::uno::Exception& )
1192 osl_closeFile( aFile );
1193 throw;
1195 if( nBytes > 0 )
1197 osl_writeFile( aFile, aBuf.getConstArray(), nBytes, &nWritten );
1198 if( nWritten != nBytes )
1200 bSuccess = false;
1201 break;
1205 while( nBytes == nBufSize );
1207 osl_closeFile( aFile );
1209 if ( bSuccess )
1210 bSuccess = xpdf_ImportFromFile( aURL, rSink, xIHdl, rPwd, xContext, rFilterOptions );
1211 osl_removeFile( aURL.pData );
1213 return bSuccess;
1218 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */