summaryrefslogtreecommitdiff
path: root/sdext/source/pdfimport/wrapper/wrapper.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'sdext/source/pdfimport/wrapper/wrapper.cxx')
-rw-r--r--sdext/source/pdfimport/wrapper/wrapper.cxx1160
1 files changed, 1160 insertions, 0 deletions
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: */