blob: f5f0758cc05677e3ebaf0fae55ff43285a656c7e [file] [log] [blame]
/**************************************************************
*
* 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 );
}
}