diff options
Diffstat (limited to 'sdext/source/pdfimport/wrapper')
-rwxr-xr-x | sdext/source/pdfimport/wrapper/keyword_list | 53 | ||||
-rwxr-xr-x | sdext/source/pdfimport/wrapper/makefile.mk | 59 | ||||
-rw-r--r-- | sdext/source/pdfimport/wrapper/wrapper.cxx | 1160 |
3 files changed, 1272 insertions, 0 deletions
diff --git a/sdext/source/pdfimport/wrapper/keyword_list b/sdext/source/pdfimport/wrapper/keyword_list new file mode 100755 index 000000000000..bf15ddb2460f --- /dev/null +++ b/sdext/source/pdfimport/wrapper/keyword_list @@ -0,0 +1,53 @@ +struct hash_entry { const char* name; enum parseKey eKey; }; + +#if defined __GNUC__ +#pragma GCC system_header +#endif + +%% +clipPath,CLIPPATH +drawChar,DRAWCHAR +drawImage,DRAWIMAGE +drawLink,DRAWLINK +drawMask,DRAWMASK +drawMaskedImage,DRAWMASKEDIMAGE +drawSoftMaskedImage,DRAWSOFTMASKEDIMAGE +endPage,ENDPAGE +endTextObject,ENDTEXTOBJECT +eoClipPath,EOCLIPPATH +eoFillPath,EOFILLPATH +fillPath,FILLPATH +hyperLink,HYPERLINK +intersectClip,INTERSECTCLIP +intersectEoClip,INTERSECTEOCLIP +popState,POPSTATE +pushState,PUSHSTATE +restoreState,RESTORESTATE +saveState,SAVESTATE +setBlendMode,SETBLENDMODE +setFillColor,SETFILLCOLOR +setFont,SETFONT +setLineCap,SETLINECAP +setLineDash,SETLINEDASH +setLineJoin,SETLINEJOIN +setLineWidth,SETLINEWIDTH +setMiterLimit,SETMITERLIMIT +setPageNum,SETPAGENUM +setStrokeColor,SETSTROKECOLOR +setTextRenderMode,SETTEXTRENDERMODE +setTransformation,SETTRANSFORMATION +startPage,STARTPAGE +strokePath,STROKEPATH +updateBlendMode,UPDATEBLENDMODE +updateCtm,UPDATECTM +updateFillColor,UPDATEFILLCOLOR +updateFillOpacity,UPDATEFILLOPACITY +updateFlatness,UPDATEFLATNESS +updateFont,UPDATEFONT +updateLineCap,UPDATELINECAP +updateLineDash,UPDATELINEDASH +updateLineJoin,UPDATELINEJOIN +updateLineWidth,UPDATELINEWIDTH +updateMiterLimit,UPDATEMITERLIMIT +updateStrokeColor,UPDATESTROKECOLOR +updateStrokeOpacity,UPDATESTROKEOPACITY diff --git a/sdext/source/pdfimport/wrapper/makefile.mk b/sdext/source/pdfimport/wrapper/makefile.mk new file mode 100755 index 000000000000..b2fbdfaa72e8 --- /dev/null +++ b/sdext/source/pdfimport/wrapper/makefile.mk @@ -0,0 +1,59 @@ +#************************************************************************* +# +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# Copyright 2000, 2010 Oracle and/or its affiliates. +# +# OpenOffice.org - a multi-platform office productivity suite +# +# This file is part of OpenOffice.org. +# +# OpenOffice.org is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License version 3 +# only, as published by the Free Software Foundation. +# +# OpenOffice.org is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License version 3 for more details +# (a copy is included in the LICENSE file that accompanied this code). +# +# You should have received a copy of the GNU Lesser General Public License +# version 3 along with OpenOffice.org. If not, see +# <http://www.openoffice.org/license.html> +# for a copy of the LGPLv3 License. +# +#************************************************************************* + +PRJ=..$/..$/.. + +PRJNAME=sdext +TARGET=xpdfwrapper +ENABLE_EXCEPTIONS=TRUE + +# --- Settings ----------------------------------------------------- + +.INCLUDE: settings.mk +.INCLUDE: ../pdfisettings.pmk + +CDEFS+= -DPDFI_IMPL_IDENTIFIER=$(IMPLEMENTATION_IDENTIFIER) + +# --- Files -------------------------------------------------------- + +SLOFILES=\ + $(SLO)$/wrapper.obj + +# --- Targets ------------------------------------------------------ + +.IF "$(ENABLE_PDFIMPORT)" == "NO" +@all: + @echo "PDF Import extension disabled." +.ENDIF + +.INCLUDE : target.mk + +$(SLO)$/wrapper.obj: $(INCCOM)$/hash.cxx + +$(INCCOM)$/hash.cxx: keyword_list + $(GPERF) -C -t -l -L C++ -m 20 -Z PdfKeywordHash -k'4-5,$$' keyword_list > $@ + diff --git a/sdext/source/pdfimport/wrapper/wrapper.cxx b/sdext/source/pdfimport/wrapper/wrapper.cxx new file mode 100644 index 000000000000..098ee584c910 --- /dev/null +++ b/sdext/source/pdfimport/wrapper/wrapper.cxx @@ -0,0 +1,1160 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 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 <boost/unordered_map.hpp> +#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 boost::unordered_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(RTL_CONSTASCII_USTRINGPARAM("URL")), + 0, + uno::makeAny(aFileName), + beans::PropertyState_DIRECT_VALUE ); + aSequence[1] = beans::PropertyValue( ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("InputStream")), + 0, + uno::makeAny( xDataStream ), + beans::PropertyState_DIRECT_VALUE ); + aSequence[2] = beans::PropertyValue( ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("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(RTL_CONSTASCII_USTRINGPARAM("xpdfimport")); + + // retrieve package location url (xpdfimport executable is located there) + // --------------------------------------------------- + uno::Reference<deployment::XPackageInformationProvider> xProvider( + xContext->getValueByName( + rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("/singletons/com.sun.star.deployment.PackageInformationProvider"))), + uno::UNO_QUERY); + if( xProvider.is() ) + { + converterURL.insert( + 0, + rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("/"))); + 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 ); + + if ( bSuccess ) + bSuccess = xpdf_ImportFromFile( aURL, rSink, xIHdl, rPwd, xContext ); + osl_removeFile( aURL.pData ); + + return bSuccess; +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |