| /************************************************************** |
| * |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| * |
| *************************************************************/ |
| |
| |
| |
| // MARKER(update_precomp.py): autogen include statement, do not remove |
| #include "precompiled_sdext.hxx" |
| |
| #include "contentsink.hxx" |
| #include "pdfparse.hxx" |
| #include "pdfihelper.hxx" |
| |
| #include "osl/file.h" |
| #include "osl/thread.h" |
| #include "osl/process.h" |
| #include "osl/diagnose.h" |
| #include "rtl/ustring.hxx" |
| #include "rtl/ustrbuf.hxx" |
| #include "rtl/strbuf.hxx" |
| #include "rtl/byteseq.hxx" |
| |
| #include "cppuhelper/exc_hlp.hxx" |
| #include "com/sun/star/io/XInputStream.hpp" |
| #include "com/sun/star/uno/XComponentContext.hpp" |
| #include "com/sun/star/awt/FontDescriptor.hpp" |
| #include "com/sun/star/deployment/XPackageInformationProvider.hpp" |
| #include "com/sun/star/beans/XMaterialHolder.hpp" |
| #include "com/sun/star/rendering/PathCapType.hpp" |
| #include "com/sun/star/rendering/PathJoinType.hpp" |
| #include "com/sun/star/rendering/XColorSpace.hpp" |
| #include "com/sun/star/rendering/XPolyPolygon2D.hpp" |
| #include "com/sun/star/rendering/XBitmap.hpp" |
| #include "com/sun/star/geometry/Matrix2D.hpp" |
| #include "com/sun/star/geometry/AffineMatrix2D.hpp" |
| #include "com/sun/star/geometry/RealRectangle2D.hpp" |
| #include "com/sun/star/task/XInteractionHandler.hpp" |
| |
| #include "basegfx/point/b2dpoint.hxx" |
| #include "basegfx/polygon/b2dpolypolygon.hxx" |
| #include "basegfx/polygon/b2dpolygon.hxx" |
| #include "basegfx/tools/canvastools.hxx" |
| #include "basegfx/tools/unopolypolygon.hxx" |
| |
| #include <boost/bind.hpp> |
| #include <boost/preprocessor/stringize.hpp> |
| #include <boost/scoped_ptr.hpp> |
| #include <boost/scoped_array.hpp> |
| |
| #include <hash_map> |
| #include <string.h> |
| #ifdef WNT |
| #include <stdlib.h> |
| #include <ctype.h> |
| #endif |
| |
| #include "rtl/bootstrap.h" |
| |
| #include <string.h> // memcmp |
| |
| #ifndef PDFI_IMPL_IDENTIFIER |
| # error define implementation name for pdfi extension, please! |
| #endif |
| |
| using namespace com::sun::star; |
| |
| namespace pdfi |
| { |
| |
| namespace |
| { |
| |
| // identifier of the strings coming from the out-of-process xpdf |
| // converter |
| enum parseKey { |
| CLIPPATH, |
| DRAWCHAR, |
| DRAWIMAGE, |
| DRAWLINK, |
| DRAWMASK, |
| DRAWMASKEDIMAGE, |
| DRAWSOFTMASKEDIMAGE, |
| ENDPAGE, |
| ENDTEXTOBJECT, |
| EOCLIPPATH, |
| EOFILLPATH, |
| FILLPATH, |
| HYPERLINK, |
| INTERSECTCLIP, |
| INTERSECTEOCLIP, |
| POPSTATE, |
| PUSHSTATE, |
| RESTORESTATE, |
| SAVESTATE, |
| SETBLENDMODE, |
| SETFILLCOLOR, |
| SETFONT, |
| SETLINECAP, |
| SETLINEDASH, |
| SETLINEJOIN, |
| SETLINEWIDTH, |
| SETMITERLIMIT, |
| SETPAGENUM, |
| SETSTROKECOLOR, |
| SETTEXTRENDERMODE, |
| SETTRANSFORMATION, |
| STARTPAGE, |
| STROKEPATH, |
| UPDATEBLENDMODE, |
| UPDATECTM, |
| UPDATEFILLCOLOR, |
| UPDATEFILLOPACITY, |
| UPDATEFLATNESS, |
| UPDATEFONT, |
| UPDATELINECAP, |
| UPDATELINEDASH, |
| UPDATELINEJOIN, |
| UPDATELINEWIDTH, |
| UPDATEMITERLIMIT, |
| UPDATESTROKECOLOR, |
| UPDATESTROKEOPACITY, |
| NONE |
| }; |
| |
| #include "hash.cxx" |
| |
| class Parser |
| { |
| typedef std::hash_map< sal_Int64, |
| FontAttributes > FontMapType; |
| |
| const uno::Reference<uno::XComponentContext> m_xContext; |
| const ContentSinkSharedPtr m_pSink; |
| const oslFileHandle m_pErr; |
| ::rtl::OString m_aLine; |
| FontMapType m_aFontMap; |
| sal_Int32 m_nNextToken; |
| sal_Int32 m_nCharIndex; |
| |
| const double minAreaThreshold; |
| const double minLineWidth; |
| |
| ::rtl::OString readNextToken(); |
| void readInt32( sal_Int32& o_Value ); |
| sal_Int32 readInt32(); |
| void readInt64( sal_Int64& o_Value ); |
| void readDouble( double& o_Value ); |
| double readDouble(); |
| void readBinaryData( uno::Sequence<sal_Int8>& rBuf ); |
| |
| uno::Reference<rendering::XPolyPolygon2D> readPath( double* ); |
| |
| void readChar(); |
| void readLineCap(); |
| void readLineDash(); |
| void readLineJoin(); |
| void readTransformation(); |
| rendering::ARGBColor readColor(); |
| void parseFontFamilyName( FontAttributes& aResult ); |
| void readFont(); |
| uno::Sequence<beans::PropertyValue> readImageImpl(); |
| |
| void readImage(); |
| void readMask(); |
| void readLink(); |
| void readMaskedImage(); |
| void readSoftMaskedImage(); |
| int parseFontCheckForString( const sal_Unicode* pCopy, const char* str, sal_Int32& nLen, |
| FontAttributes& aResult, bool bItalic, bool bBold); |
| int parseFontRemoveSuffix( const sal_Unicode* pCopy, const char* s, sal_Int32& nLen); |
| |
| |
| public: |
| Parser( const ContentSinkSharedPtr& rSink, |
| oslFileHandle pErr, |
| const uno::Reference<uno::XComponentContext>& xContext ) : |
| m_xContext(xContext), |
| m_pSink(rSink), |
| m_pErr(pErr), |
| m_aLine(), |
| m_aFontMap(101), |
| m_nNextToken(-1), |
| m_nCharIndex(-1), |
| minAreaThreshold( 300.0 ), |
| minLineWidth( 12 ) |
| {} |
| |
| void parseLine( const ::rtl::OString& rLine ); |
| }; |
| |
| |
| namespace |
| { |
| |
| /** Unescapes line-ending characters in input string. These |
| characters are encoded as pairs of characters: '\\' 'n', resp. |
| '\\' 'r'. This function converts them back to '\n', resp. '\r'. |
| */ |
| rtl::OString lcl_unescapeLineFeeds(const rtl::OString& i_rStr) |
| { |
| const size_t nOrigLen(sal::static_int_cast<size_t>(i_rStr.getLength())); |
| const sal_Char* const pOrig(i_rStr.getStr()); |
| sal_Char* const pBuffer(new sal_Char[nOrigLen + 1]); |
| |
| const sal_Char* pRead(pOrig); |
| sal_Char* pWrite(pBuffer); |
| const sal_Char* pCur(pOrig); |
| while ((pCur = strchr(pCur, '\\')) != 0) |
| { |
| const sal_Char cNext(pCur[1]); |
| if (cNext == 'n' || cNext == 'r' || cNext == '\\') |
| { |
| const size_t nLen(pCur - pRead); |
| strncpy(pWrite, pRead, nLen); |
| pWrite += nLen; |
| *pWrite = cNext == 'n' ? '\n' : (cNext == 'r' ? '\r' : '\\'); |
| ++pWrite; |
| pCur = pRead = pCur + 2; |
| } |
| else |
| { |
| // Just continue on the next character. The current |
| // block will be copied the next time it goes through the |
| // 'if' branch. |
| ++pCur; |
| } |
| } |
| // maybe there are some data to copy yet |
| if (sal::static_int_cast<size_t>(pRead - pOrig) < nOrigLen) |
| { |
| const size_t nLen(nOrigLen - (pRead - pOrig)); |
| strncpy(pWrite, pRead, nLen); |
| pWrite += nLen; |
| } |
| *pWrite = '\0'; |
| |
| rtl::OString aResult(pBuffer); |
| delete[] pBuffer; |
| return aResult; |
| } |
| |
| } |
| |
| |
| ::rtl::OString Parser::readNextToken() |
| { |
| OSL_PRECOND(m_nCharIndex!=-1,"insufficient input"); |
| return m_aLine.getToken(m_nNextToken,' ',m_nCharIndex); |
| } |
| |
| void Parser::readInt32( sal_Int32& o_Value ) |
| { |
| o_Value = readNextToken().toInt32(); |
| } |
| |
| sal_Int32 Parser::readInt32() |
| { |
| return readNextToken().toInt32(); |
| } |
| |
| void Parser::readInt64( sal_Int64& o_Value ) |
| { |
| o_Value = readNextToken().toInt64(); |
| } |
| |
| void Parser::readDouble( double& o_Value ) |
| { |
| o_Value = readNextToken().toDouble(); |
| } |
| |
| double Parser::readDouble() |
| { |
| return readNextToken().toDouble(); |
| } |
| |
| void Parser::readBinaryData( uno::Sequence<sal_Int8>& rBuf ) |
| { |
| sal_Int32 nFileLen( rBuf.getLength() ); |
| sal_Int8* pBuf( rBuf.getArray() ); |
| sal_uInt64 nBytesRead(0); |
| oslFileError nRes=osl_File_E_None; |
| while( nFileLen && |
| osl_File_E_None == (nRes=osl_readFile( m_pErr, pBuf, nFileLen, &nBytesRead )) ) |
| { |
| pBuf += nBytesRead; |
| nFileLen -= sal::static_int_cast<sal_Int32>(nBytesRead); |
| } |
| |
| OSL_PRECOND(nRes==osl_File_E_None, "inconsistent data"); |
| } |
| |
| uno::Reference<rendering::XPolyPolygon2D> Parser::readPath( double* pArea = NULL ) |
| { |
| const rtl::OString aSubPathMarker( "subpath" ); |
| |
| if( 0 != readNextToken().compareTo( aSubPathMarker ) ) |
| OSL_PRECOND(false, "broken path"); |
| |
| basegfx::B2DPolyPolygon aResult; |
| while( m_nCharIndex != -1 ) |
| { |
| basegfx::B2DPolygon aSubPath; |
| |
| sal_Int32 nClosedFlag; |
| readInt32( nClosedFlag ); |
| aSubPath.setClosed( nClosedFlag != 0 ); |
| |
| sal_Int32 nContiguousControlPoints(0); |
| sal_Int32 nDummy=m_nCharIndex; |
| rtl::OString aCurrToken( m_aLine.getToken(m_nNextToken,' ',nDummy) ); |
| |
| while( m_nCharIndex != -1 && 0 != aCurrToken.compareTo(aSubPathMarker) ) |
| { |
| sal_Int32 nCurveFlag; |
| double nX, nY; |
| readDouble( nX ); |
| readDouble( nY ); |
| readInt32( nCurveFlag ); |
| |
| aSubPath.append(basegfx::B2DPoint(nX,nY)); |
| if( nCurveFlag ) |
| { |
| ++nContiguousControlPoints; |
| } |
| else if( nContiguousControlPoints ) |
| { |
| OSL_PRECOND(nContiguousControlPoints==2,"broken bezier path"); |
| |
| // have two control points before us. the current one |
| // is a normal point - thus, convert previous points |
| // into bezier segment |
| const sal_uInt32 nPoints( aSubPath.count() ); |
| const basegfx::B2DPoint aCtrlA( aSubPath.getB2DPoint(nPoints-3) ); |
| const basegfx::B2DPoint aCtrlB( aSubPath.getB2DPoint(nPoints-2) ); |
| const basegfx::B2DPoint aEnd( aSubPath.getB2DPoint(nPoints-1) ); |
| aSubPath.remove(nPoints-3, 3); |
| aSubPath.appendBezierSegment(aCtrlA, aCtrlB, aEnd); |
| |
| nContiguousControlPoints=0; |
| } |
| |
| // one token look-ahead (new subpath or more points? |
| nDummy=m_nCharIndex; |
| aCurrToken = m_aLine.getToken(m_nNextToken,' ',nDummy); |
| } |
| |
| aResult.append( aSubPath ); |
| if( m_nCharIndex != -1 ) |
| readNextToken(); |
| } |
| |
| if( pArea ) |
| { |
| basegfx::B2DRange aRange( aResult.getB2DRange() ); |
| if( aRange.getWidth() <= minLineWidth || aRange.getHeight() <= minLineWidth) |
| *pArea = 0.0; |
| else |
| *pArea = aRange.getWidth() * aRange.getHeight(); |
| } |
| |
| return static_cast<rendering::XLinePolyPolygon2D*>( |
| new basegfx::unotools::UnoPolyPolygon(aResult)); |
| } |
| |
| void Parser::readChar() |
| { |
| geometry::Matrix2D aUnoMatrix; |
| geometry::RealRectangle2D aRect; |
| |
| readDouble(aRect.X1); |
| readDouble(aRect.Y1); |
| readDouble(aRect.X2); |
| readDouble(aRect.Y2); |
| readDouble(aUnoMatrix.m00); |
| readDouble(aUnoMatrix.m01); |
| readDouble(aUnoMatrix.m10); |
| readDouble(aUnoMatrix.m11); |
| |
| rtl::OString aChars = lcl_unescapeLineFeeds( m_aLine.copy( m_nCharIndex ) ); |
| |
| // chars gobble up rest of line |
| m_nCharIndex = -1; |
| |
| m_pSink->drawGlyphs( rtl::OStringToOUString( aChars, |
| RTL_TEXTENCODING_UTF8 ), |
| aRect, aUnoMatrix ); |
| } |
| |
| void Parser::readLineCap() |
| { |
| sal_Int8 nCap(rendering::PathCapType::BUTT); |
| switch( readInt32() ) |
| { |
| default: |
| // FALLTHROUGH intended |
| case 0: nCap = rendering::PathCapType::BUTT; break; |
| case 1: nCap = rendering::PathCapType::ROUND; break; |
| case 2: nCap = rendering::PathCapType::SQUARE; break; |
| } |
| m_pSink->setLineCap(nCap); |
| } |
| |
| void Parser::readLineDash() |
| { |
| if( m_nCharIndex == -1 ) |
| { |
| m_pSink->setLineDash( uno::Sequence<double>(), 0.0 ); |
| return; |
| } |
| |
| const double nOffset(readDouble()); |
| const sal_Int32 nLen(readInt32()); |
| |
| uno::Sequence<double> aDashArray(nLen); |
| double* pArray=aDashArray.getArray(); |
| for( sal_Int32 i=0; i<nLen; ++i ) |
| *pArray++ = readDouble(); |
| |
| m_pSink->setLineDash( aDashArray, nOffset ); |
| } |
| |
| void Parser::readLineJoin() |
| { |
| sal_Int8 nJoin(rendering::PathJoinType::MITER); |
| switch( readInt32() ) |
| { |
| default: |
| // FALLTHROUGH intended |
| case 0: nJoin = rendering::PathJoinType::MITER; break; |
| case 1: nJoin = rendering::PathJoinType::ROUND; break; |
| case 2: nJoin = rendering::PathJoinType::BEVEL; break; |
| } |
| m_pSink->setLineJoin(nJoin); |
| } |
| |
| void Parser::readTransformation() |
| { |
| geometry::AffineMatrix2D aMat; |
| readDouble(aMat.m00); |
| readDouble(aMat.m10); |
| readDouble(aMat.m01); |
| readDouble(aMat.m11); |
| readDouble(aMat.m02); |
| readDouble(aMat.m12); |
| m_pSink->setTransformation( aMat ); |
| } |
| |
| rendering::ARGBColor Parser::readColor() |
| { |
| rendering::ARGBColor aRes; |
| readDouble(aRes.Red); |
| readDouble(aRes.Green); |
| readDouble(aRes.Blue); |
| readDouble(aRes.Alpha); |
| return aRes; |
| } |
| |
| int Parser::parseFontCheckForString( const sal_Unicode* pCopy, const char* s, sal_Int32& nLen, |
| FontAttributes& aResult, bool bItalic, bool bBold) |
| { |
| int l = strlen(s); |
| if (nLen < l) |
| return 0; |
| for (int i = 0; i < l; i++) |
| if (tolower(pCopy[i]) != s[i] |
| && toupper(pCopy[i]) != s[i]) |
| return 0; |
| aResult.isItalic = bItalic; |
| aResult.isBold = bBold; |
| nLen -= l; |
| pCopy += l; |
| return l; |
| } |
| |
| int Parser::parseFontRemoveSuffix( const sal_Unicode* pCopy, const char* s, sal_Int32& nLen) |
| { |
| int l = strlen(s); |
| if (nLen < l) |
| return 0; |
| for (int i = 0; i < l; i++) |
| if ( pCopy[nLen - l + i] != s[i] ) |
| return 0; |
| nLen -= l; |
| return l; |
| } |
| |
| void Parser::parseFontFamilyName( FontAttributes& aResult ) |
| { |
| rtl::OUStringBuffer aNewFamilyName( aResult.familyName.getLength() ); |
| |
| const sal_Unicode* pCopy = aResult.familyName.getStr(); |
| sal_Int32 nLen = aResult.familyName.getLength(); |
| // parse out truetype subsets (e.g. BAAAAA+Thorndale) |
| if( nLen > 8 && pCopy[6] == sal_Unicode('+') ) |
| { |
| pCopy += 7; |
| nLen -= 7; |
| } |
| |
| while( nLen ) |
| { |
| if (parseFontRemoveSuffix( pCopy, "PSMT", nLen)) {} |
| else if (parseFontRemoveSuffix( pCopy, "MT", nLen)) {} |
| |
| if (parseFontCheckForString( pCopy, "Italic", nLen, aResult, true, false)) {} |
| else if (parseFontCheckForString( pCopy, "-Bold", nLen, aResult, false, true)) {} |
| else if (parseFontCheckForString( pCopy, "Bold", nLen, aResult, false, true)) {} |
| else if (parseFontCheckForString( pCopy, "-Roman", nLen, aResult, false, false)) {} |
| else if (parseFontCheckForString( pCopy, "-LightOblique", nLen, aResult, true, false)) {} |
| else if (parseFontCheckForString( pCopy, "-BoldOblique", nLen, aResult, true, true)) {} |
| else if (parseFontCheckForString( pCopy, "-Light", nLen, aResult, false, false)) {} |
| else if (parseFontCheckForString( pCopy, "-Reg", nLen, aResult, false, false)) {} |
| else |
| { |
| if( *pCopy != '-' ) |
| aNewFamilyName.append( *pCopy ); |
| pCopy++; |
| nLen--; |
| } |
| } |
| aResult.familyName = aNewFamilyName.makeStringAndClear(); |
| } |
| |
| void Parser::readFont() |
| { |
| ::rtl::OString aFontName; |
| sal_Int64 nFontID; |
| sal_Int32 nIsEmbedded, nIsBold, nIsItalic, nIsUnderline, nFileLen; |
| double nSize; |
| |
| readInt64(nFontID); |
| readInt32(nIsEmbedded); |
| readInt32(nIsBold); |
| readInt32(nIsItalic); |
| readInt32(nIsUnderline); |
| readDouble(nSize); |
| readInt32(nFileLen); |
| |
| nSize = nSize < 0.0 ? -nSize : nSize; |
| aFontName = lcl_unescapeLineFeeds( m_aLine.copy( m_nCharIndex ) ); |
| |
| // name gobbles up rest of line |
| m_nCharIndex = -1; |
| |
| FontMapType::const_iterator pFont( m_aFontMap.find(nFontID) ); |
| if( pFont != m_aFontMap.end() ) |
| { |
| OSL_PRECOND(nFileLen==0,"font data for known font"); |
| FontAttributes aRes(pFont->second); |
| aRes.size = nSize; |
| m_pSink->setFont( aRes ); |
| |
| return; |
| } |
| |
| // yet unknown font - get info and add to map |
| FontAttributes aResult( rtl::OStringToOUString( aFontName, |
| RTL_TEXTENCODING_UTF8 ), |
| nIsBold != 0, |
| nIsItalic != 0, |
| nIsUnderline != 0, |
| false, |
| nSize ); |
| |
| // extract textual attributes (bold, italic in the name, etc.) |
| parseFontFamilyName(aResult); |
| // need to read font file? |
| if( nFileLen ) |
| { |
| uno::Sequence<sal_Int8> aFontFile(nFileLen); |
| readBinaryData( aFontFile ); |
| |
| awt::FontDescriptor aFD; |
| uno::Sequence< uno::Any > aArgs(1); |
| aArgs[0] <<= aFontFile; |
| |
| try |
| { |
| uno::Reference< beans::XMaterialHolder > xMat( |
| m_xContext->getServiceManager()->createInstanceWithArgumentsAndContext( |
| rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.awt.FontIdentificator" ) ), |
| aArgs, |
| m_xContext ), |
| uno::UNO_QUERY ); |
| if( xMat.is() ) |
| { |
| uno::Any aRes( xMat->getMaterial() ); |
| if( aRes >>= aFD ) |
| { |
| aResult.familyName = aFD.Name; |
| parseFontFamilyName(aResult); |
| aResult.isBold = (aFD.Weight > 100.0); |
| aResult.isItalic = (aFD.Slant == awt::FontSlant_OBLIQUE || |
| aFD.Slant == awt::FontSlant_ITALIC ); |
| aResult.isUnderline = false; |
| aResult.size = 0; |
| } |
| } |
| } |
| catch( uno::Exception& ) |
| { |
| } |
| |
| if( !aResult.familyName.getLength() ) |
| { |
| // last fallback |
| aResult.familyName = ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "Arial" ) ); |
| aResult.isUnderline = false; |
| } |
| |
| } |
| m_aFontMap[nFontID] = aResult; |
| |
| aResult.size = nSize; |
| m_pSink->setFont(aResult); |
| } |
| |
| uno::Sequence<beans::PropertyValue> Parser::readImageImpl() |
| { |
| static const rtl::OString aJpegMarker( "JPEG" ); |
| static const rtl::OString aPbmMarker( "PBM" ); |
| static const rtl::OString aPpmMarker( "PPM" ); |
| static const rtl::OString aPngMarker( "PNG" ); |
| static const rtl::OUString aJpegFile( |
| RTL_CONSTASCII_USTRINGPARAM( "DUMMY.JPEG" )); |
| static const rtl::OUString aPbmFile( |
| RTL_CONSTASCII_USTRINGPARAM( "DUMMY.PBM" )); |
| static const rtl::OUString aPpmFile( |
| RTL_CONSTASCII_USTRINGPARAM( "DUMMY.PPM" )); |
| static const rtl::OUString aPngFile( |
| RTL_CONSTASCII_USTRINGPARAM( "DUMMY.PNG" )); |
| |
| rtl::OString aToken = readNextToken(); |
| const sal_Int32 nImageSize( readInt32() ); |
| |
| rtl::OUString aFileName; |
| if( aToken.compareTo( aPngMarker ) == 0 ) |
| aFileName = aPngFile; |
| else if( aToken.compareTo( aJpegMarker ) == 0 ) |
| aFileName = aJpegFile; |
| else if( aToken.compareTo( aPbmMarker ) == 0 ) |
| aFileName = aPbmFile; |
| else |
| { |
| OSL_PRECOND( aToken.compareTo( aPpmMarker ) == 0, |
| "Invalid bitmap format" ); |
| aFileName = aPpmFile; |
| } |
| |
| uno::Sequence<sal_Int8> aDataSequence(nImageSize); |
| readBinaryData( aDataSequence ); |
| |
| uno::Sequence< uno::Any > aStreamCreationArgs(1); |
| aStreamCreationArgs[0] <<= aDataSequence; |
| |
| uno::Reference< uno::XComponentContext > xContext( m_xContext, uno::UNO_SET_THROW ); |
| uno::Reference< lang::XMultiComponentFactory > xFactory( xContext->getServiceManager(), uno::UNO_SET_THROW ); |
| uno::Reference< io::XInputStream > xDataStream( xFactory->createInstanceWithArgumentsAndContext( |
| ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.io.SequenceInputStream" ) ), |
| aStreamCreationArgs, m_xContext ), uno::UNO_QUERY_THROW ); |
| |
| uno::Sequence<beans::PropertyValue> aSequence(3); |
| aSequence[0] = beans::PropertyValue( ::rtl::OUString::createFromAscii("URL"), |
| 0, |
| uno::makeAny(aFileName), |
| beans::PropertyState_DIRECT_VALUE ); |
| aSequence[1] = beans::PropertyValue( ::rtl::OUString::createFromAscii("InputStream"), |
| 0, |
| uno::makeAny( xDataStream ), |
| beans::PropertyState_DIRECT_VALUE ); |
| aSequence[2] = beans::PropertyValue( ::rtl::OUString::createFromAscii("InputSequence"), |
| 0, |
| uno::makeAny(aDataSequence), |
| beans::PropertyState_DIRECT_VALUE ); |
| |
| return aSequence; |
| } |
| |
| void Parser::readImage() |
| { |
| sal_Int32 nWidth, nHeight,nMaskColors; |
| readInt32(nWidth); |
| readInt32(nHeight); |
| readInt32(nMaskColors); |
| |
| uno::Sequence<beans::PropertyValue> aImg( readImageImpl() ); |
| |
| if( nMaskColors ) |
| { |
| uno::Sequence<sal_Int8> aDataSequence(nMaskColors); |
| readBinaryData( aDataSequence ); |
| |
| uno::Sequence<uno::Any> aMaskRanges(2); |
| |
| uno::Sequence<double> aMinRange(nMaskColors/2); |
| uno::Sequence<double> aMaxRange(nMaskColors/2); |
| for( sal_Int32 i=0; i<nMaskColors/2; ++i ) |
| { |
| aMinRange[i] = aDataSequence[i] / 255.0; |
| aMaxRange[i] = aDataSequence[i+nMaskColors/2] / 255.0; |
| } |
| |
| aMaskRanges[0] = uno::makeAny(aMinRange); |
| aMaskRanges[1] = uno::makeAny(aMaxRange); |
| |
| m_pSink->drawColorMaskedImage( aImg, aMaskRanges ); |
| } |
| else |
| m_pSink->drawImage( aImg ); |
| } |
| |
| void Parser::readMask() |
| { |
| sal_Int32 nWidth, nHeight, nInvert; |
| readInt32(nWidth); |
| readInt32(nHeight); |
| readInt32(nInvert); |
| |
| m_pSink->drawMask( readImageImpl(), nInvert ); |
| } |
| |
| void Parser::readLink() |
| { |
| geometry::RealRectangle2D aBounds; |
| readDouble(aBounds.X1); |
| readDouble(aBounds.Y1); |
| readDouble(aBounds.X2); |
| readDouble(aBounds.Y2); |
| |
| m_pSink->hyperLink( aBounds, |
| rtl::OStringToOUString( lcl_unescapeLineFeeds( |
| m_aLine.copy(m_nCharIndex) ), |
| RTL_TEXTENCODING_UTF8 ) ); |
| // name gobbles up rest of line |
| m_nCharIndex = -1; |
| } |
| |
| void Parser::readMaskedImage() |
| { |
| sal_Int32 nWidth, nHeight, nMaskWidth, nMaskHeight, nMaskInvert; |
| readInt32(nWidth); |
| readInt32(nHeight); |
| readInt32(nMaskWidth); |
| readInt32(nMaskHeight); |
| readInt32(nMaskInvert); |
| |
| const uno::Sequence<beans::PropertyValue> aImage( readImageImpl() ); |
| const uno::Sequence<beans::PropertyValue> aMask ( readImageImpl() ); |
| m_pSink->drawMaskedImage( aImage, aMask, nMaskInvert != 0 ); |
| } |
| |
| void Parser::readSoftMaskedImage() |
| { |
| sal_Int32 nWidth, nHeight, nMaskWidth, nMaskHeight; |
| readInt32(nWidth); |
| readInt32(nHeight); |
| readInt32(nMaskWidth); |
| readInt32(nMaskHeight); |
| |
| const uno::Sequence<beans::PropertyValue> aImage( readImageImpl() ); |
| const uno::Sequence<beans::PropertyValue> aMask ( readImageImpl() ); |
| m_pSink->drawAlphaMaskedImage( aImage, aMask ); |
| } |
| |
| void Parser::parseLine( const ::rtl::OString& rLine ) |
| { |
| OSL_PRECOND( m_pSink, "Invalid sink" ); |
| OSL_PRECOND( m_pErr, "Invalid filehandle" ); |
| OSL_PRECOND( m_xContext.is(), "Invalid service factory" ); |
| |
| m_nNextToken = 0; m_nCharIndex = 0; m_aLine = rLine; |
| uno::Reference<rendering::XPolyPolygon2D> xPoly; |
| const ::rtl::OString& rCmd = readNextToken(); |
| const hash_entry* pEntry = PdfKeywordHash::in_word_set( rCmd.getStr(), |
| rCmd.getLength() ); |
| OSL_ASSERT(pEntry); |
| switch( pEntry->eKey ) |
| { |
| case CLIPPATH: |
| m_pSink->intersectClip(readPath()); break; |
| case DRAWCHAR: |
| readChar(); break; |
| case DRAWIMAGE: |
| readImage(); break; |
| case DRAWLINK: |
| readLink(); break; |
| case DRAWMASK: |
| readMask(); break; |
| case DRAWMASKEDIMAGE: |
| readMaskedImage(); break; |
| case DRAWSOFTMASKEDIMAGE: |
| readSoftMaskedImage(); break; |
| case ENDPAGE: |
| m_pSink->endPage(); break; |
| case ENDTEXTOBJECT: |
| m_pSink->endText(); break; |
| case EOCLIPPATH: |
| m_pSink->intersectEoClip(readPath()); break; |
| case EOFILLPATH: |
| { |
| double area = 0.0; |
| uno::Reference<rendering::XPolyPolygon2D> path = readPath( &area ); |
| m_pSink->eoFillPath(path); |
| // if area is smaller than required, add borders. |
| if(area < minAreaThreshold) |
| m_pSink->strokePath(path); |
| } |
| break; |
| case FILLPATH: |
| { |
| double area = 0.0; |
| uno::Reference<rendering::XPolyPolygon2D> path = readPath( &area ); |
| m_pSink->fillPath(path); |
| // if area is smaller than required, add borders. |
| if(area < minAreaThreshold) |
| m_pSink->strokePath(path); |
| } |
| break; |
| case RESTORESTATE: |
| m_pSink->popState(); break; |
| case SAVESTATE: |
| m_pSink->pushState(); break; |
| case SETPAGENUM: |
| m_pSink->setPageNum( readInt32() ); break; |
| case STARTPAGE: |
| { |
| const double nWidth ( readDouble() ); |
| const double nHeight( readDouble() ); |
| m_pSink->startPage( geometry::RealSize2D( nWidth, nHeight ) ); |
| break; |
| } |
| case STROKEPATH: |
| m_pSink->strokePath(readPath()); break; |
| case UPDATECTM: |
| readTransformation(); break; |
| case UPDATEFILLCOLOR: |
| m_pSink->setFillColor( readColor() ); break; |
| case UPDATEFLATNESS: |
| m_pSink->setFlatness( readDouble( ) ); break; |
| case UPDATEFONT: |
| readFont(); break; |
| case UPDATELINECAP: |
| readLineCap(); break; |
| case UPDATELINEDASH: |
| readLineDash(); break; |
| case UPDATELINEJOIN: |
| readLineJoin(); break; |
| case UPDATELINEWIDTH: |
| m_pSink->setLineWidth( readDouble() );break; |
| case UPDATEMITERLIMIT: |
| m_pSink->setMiterLimit( readDouble() ); break; |
| case UPDATESTROKECOLOR: |
| m_pSink->setStrokeColor( readColor() ); break; |
| case UPDATESTROKEOPACITY: |
| break; |
| case SETTEXTRENDERMODE: |
| m_pSink->setTextRenderMode( readInt32() ); break; |
| |
| case NONE: |
| default: |
| OSL_PRECOND(false,"Unknown input"); |
| break; |
| } |
| |
| // all consumed? |
| OSL_POSTCOND(m_nCharIndex==-1,"leftover scanner input"); |
| } |
| |
| oslFileError readLine( oslFileHandle pFile, ::rtl::OStringBuffer& line ) |
| { |
| OSL_PRECOND( line.getLength() == 0, "line buf not empty" ); |
| |
| // TODO(P3): read larger chunks |
| sal_Char aChar('\n'); |
| sal_uInt64 nBytesRead; |
| oslFileError nRes; |
| |
| // skip garbage \r \n at start of line |
| while( osl_File_E_None == (nRes=osl_readFile(pFile, &aChar, 1, &nBytesRead)) && |
| nBytesRead == 1 && |
| (aChar == '\n' || aChar == '\r') ) ; |
| |
| if( aChar != '\n' && aChar != '\r' ) |
| line.append( aChar ); |
| |
| while( osl_File_E_None == (nRes=osl_readFile(pFile, &aChar, 1, &nBytesRead)) && |
| nBytesRead == 1 && aChar != '\n' && aChar != '\r' ) |
| { |
| line.append( aChar ); |
| } |
| |
| return nRes; |
| } |
| |
| } // namespace |
| |
| static bool checkEncryption( const rtl::OUString& i_rPath, |
| const uno::Reference< task::XInteractionHandler >& i_xIHdl, |
| rtl::OUString& io_rPwd, |
| bool& o_rIsEncrypted, |
| const rtl::OUString& i_rDocName |
| ) |
| { |
| bool bSuccess = false; |
| rtl::OString aPDFFile; |
| aPDFFile = rtl::OUStringToOString( i_rPath, osl_getThreadTextEncoding() ); |
| |
| pdfparse::PDFReader aParser; |
| boost::scoped_ptr<pdfparse::PDFEntry> pEntry( aParser.read( aPDFFile.getStr() )); |
| if( pEntry ) |
| { |
| pdfparse::PDFFile* pPDFFile = dynamic_cast<pdfparse::PDFFile*>(pEntry.get()); |
| if( pPDFFile ) |
| { |
| o_rIsEncrypted = pPDFFile->isEncrypted(); |
| if( o_rIsEncrypted ) |
| { |
| bool bAuthenticated = false; |
| if( io_rPwd.getLength() ) |
| { |
| rtl::OString aIsoPwd = rtl::OUStringToOString( io_rPwd, |
| RTL_TEXTENCODING_ISO_8859_1 ); |
| bAuthenticated = pPDFFile->setupDecryptionData( aIsoPwd.getStr() ); |
| // trash password string on heap |
| rtl_zeroMemory( (void*)aIsoPwd.getStr(), aIsoPwd.getLength() ); |
| } |
| if( bAuthenticated ) |
| bSuccess = true; |
| else |
| { |
| if( i_xIHdl.is() ) |
| { |
| bool bEntered = false; |
| do |
| { |
| bEntered = getPassword( i_xIHdl, io_rPwd, ! bEntered, i_rDocName ); |
| rtl::OString aIsoPwd = rtl::OUStringToOString( io_rPwd, |
| RTL_TEXTENCODING_ISO_8859_1 ); |
| bAuthenticated = pPDFFile->setupDecryptionData( aIsoPwd.getStr() ); |
| // trash password string on heap |
| rtl_zeroMemory( (void*)aIsoPwd.getStr(), aIsoPwd.getLength() ); |
| } while( bEntered && ! bAuthenticated ); |
| } |
| |
| OSL_TRACE( "password: %s\n", bAuthenticated ? "matches" : "does not match" ); |
| bSuccess = bAuthenticated; |
| } |
| // trash password string on heap |
| rtl_zeroMemory( (void*)io_rPwd.getStr(), io_rPwd.getLength()*sizeof(sal_Unicode) ); |
| if( bAuthenticated ) |
| { |
| rtl::OUStringBuffer aBuf( 128 ); |
| aBuf.appendAscii( "_OOO_pdfi_Credentials_" ); |
| aBuf.append( pPDFFile->getDecryptionKey() ); |
| io_rPwd = aBuf.makeStringAndClear(); |
| } |
| } |
| else |
| bSuccess = true; |
| } |
| } |
| return bSuccess; |
| } |
| |
| bool xpdf_ImportFromFile( const ::rtl::OUString& rURL, |
| const ContentSinkSharedPtr& rSink, |
| const uno::Reference< task::XInteractionHandler >& xIHdl, |
| const rtl::OUString& rPwd, |
| const uno::Reference< uno::XComponentContext >& xContext ) |
| { |
| OSL_ASSERT(rSink); |
| |
| ::rtl::OUString aSysUPath; |
| if( osl_getSystemPathFromFileURL( rURL.pData, &aSysUPath.pData ) != osl_File_E_None ) |
| return false; |
| rtl::OUString aDocName( rURL.copy( rURL.lastIndexOf( sal_Unicode('/') )+1 ) ); |
| |
| // check for encryption, if necessary get password |
| rtl::OUString aPwd( rPwd ); |
| bool bIsEncrypted = false; |
| if( checkEncryption( aSysUPath, xIHdl, aPwd, bIsEncrypted, aDocName ) == false ) |
| return false; |
| |
| rtl::OUStringBuffer converterURL = rtl::OUString::createFromAscii("xpdfimport"); |
| |
| // retrieve package location url (xpdfimport executable is located there) |
| // --------------------------------------------------- |
| uno::Reference<deployment::XPackageInformationProvider> xProvider( |
| xContext->getValueByName( |
| rtl::OUString::createFromAscii("/singletons/com.sun.star.deployment.PackageInformationProvider" )), |
| uno::UNO_QUERY); |
| if( xProvider.is() ) |
| { |
| converterURL.insert( |
| 0, |
| rtl::OUString::createFromAscii("/")); |
| converterURL.insert( |
| 0, |
| xProvider->getPackageLocation( |
| rtl::OUString::createFromAscii( |
| BOOST_PP_STRINGIZE(PDFI_IMPL_IDENTIFIER)))); |
| } |
| |
| // spawn separate process to keep LGPL/GPL code apart. |
| // --------------------------------------------------- |
| rtl_uString** ppEnv = NULL; |
| sal_uInt32 nEnv = 0; |
| |
| #if defined UNX && ! defined MACOSX |
| rtl::OUString aStr( RTL_CONSTASCII_USTRINGPARAM( "$URE_LIB_DIR" ) ); |
| rtl_bootstrap_expandMacros( &aStr.pData ); |
| rtl::OUString aSysPath; |
| osl_getSystemPathFromFileURL( aStr.pData, &aSysPath.pData ); |
| rtl::OUStringBuffer aEnvBuf( aStr.getLength() + 20 ); |
| aEnvBuf.appendAscii( "LD_LIBRARY_PATH=" ); |
| aEnvBuf.append( aSysPath ); |
| aStr = aEnvBuf.makeStringAndClear(); |
| ppEnv = &aStr.pData; |
| nEnv = 1; |
| #endif |
| |
| rtl_uString* args[] = { aSysUPath.pData }; |
| sal_Int32 nArgs = 1; |
| |
| oslProcess aProcess; |
| oslFileHandle pIn = NULL; |
| oslFileHandle pOut = NULL; |
| oslFileHandle pErr = NULL; |
| const oslProcessError eErr = |
| osl_executeProcess_WithRedirectedIO(converterURL.makeStringAndClear().pData, |
| args, |
| nArgs, |
| osl_Process_SEARCHPATH|osl_Process_HIDDEN, |
| osl_getCurrentSecurity(), |
| 0, ppEnv, nEnv, |
| &aProcess, &pIn, &pOut, &pErr); |
| |
| bool bRet=true; |
| try |
| { |
| if( eErr!=osl_Process_E_None ) |
| return false; |
| |
| if( pIn ) |
| { |
| rtl::OStringBuffer aBuf(256); |
| if( bIsEncrypted ) |
| aBuf.append( rtl::OUStringToOString( aPwd, RTL_TEXTENCODING_ISO_8859_1 ) ); |
| aBuf.append( '\n' ); |
| |
| sal_uInt64 nWritten = 0; |
| osl_writeFile( pIn, aBuf.getStr(), sal_uInt64(aBuf.getLength()), &nWritten ); |
| } |
| |
| if( pOut && pErr ) |
| { |
| // read results of PDF parser. One line - one call to |
| // OutputDev. stderr is used for alternate streams, like |
| // embedded fonts and bitmaps |
| Parser aParser(rSink,pErr,xContext); |
| ::rtl::OStringBuffer line; |
| while( osl_File_E_None == readLine(pOut, line) && line.getLength() ) |
| aParser.parseLine(line.makeStringAndClear()); |
| } |
| } |
| catch( uno::Exception& ) |
| { |
| // crappy C file interface. need manual resource dealloc |
| bRet = false; |
| } |
| |
| if( pIn ) |
| osl_closeFile(pIn); |
| if( pOut ) |
| osl_closeFile(pOut); |
| if( pErr ) |
| osl_closeFile(pErr); |
| osl_freeProcessHandle(aProcess); |
| return bRet; |
| } |
| |
| |
| bool xpdf_ImportFromStream( const uno::Reference< io::XInputStream >& xInput, |
| const ContentSinkSharedPtr& rSink, |
| const uno::Reference<task::XInteractionHandler >& xIHdl, |
| const rtl::OUString& rPwd, |
| const uno::Reference< uno::XComponentContext >& xContext ) |
| { |
| OSL_ASSERT(xInput.is()); |
| OSL_ASSERT(rSink); |
| |
| // convert XInputStream to local temp file |
| oslFileHandle aFile = NULL; |
| rtl::OUString aURL; |
| if( osl_createTempFile( NULL, &aFile, &aURL.pData ) != osl_File_E_None ) |
| return false; |
| |
| // copy content, buffered... |
| const sal_uInt32 nBufSize = 4096; |
| uno::Sequence<sal_Int8> aBuf( nBufSize ); |
| sal_uInt64 nBytes = 0; |
| sal_uInt64 nWritten = 0; |
| bool bSuccess = true; |
| do |
| { |
| try |
| { |
| nBytes = xInput->readBytes( aBuf, nBufSize ); |
| } |
| catch( com::sun::star::uno::Exception& ) |
| { |
| osl_closeFile( aFile ); |
| throw; |
| } |
| if( nBytes > 0 ) |
| { |
| osl_writeFile( aFile, aBuf.getConstArray(), nBytes, &nWritten ); |
| if( nWritten != nBytes ) |
| { |
| bSuccess = false; |
| break; |
| } |
| } |
| } |
| while( nBytes == nBufSize ); |
| |
| osl_closeFile( aFile ); |
| |
| return bSuccess && xpdf_ImportFromFile( aURL, rSink, xIHdl, rPwd, xContext ); |
| } |
| |
| } |
| |