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