/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* This file is part of the LibreOffice project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* This file incorporates work covered by the following license notice:
*
* 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 .
*/
#include <config_folders.h>
#include <contentsink.hxx>
#include <pdfparse.hxx>
#include <pdfihelper.hxx>
#include <wrapper.hxx>
#include <osl/file.h>
#include <osl/file.hxx>
#include <osl/thread.h>
#include <osl/process.h>
#include <osl/diagnose.h>
#include <rtl/bootstrap.hxx>
#include <rtl/ustring.hxx>
#include <rtl/ustrbuf.hxx>
#include <rtl/strbuf.hxx>
#include <rtl/byteseq.hxx>
#include <sal/log.hxx>
#include <comphelper/lok.hxx>
#include <comphelper/propertysequence.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/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/utils/canvastools.hxx>
#include <basegfx/utils/unopolypolygon.hxx>
#include <vcl/metric.hxx>
#include <vcl/font.hxx>
#include <vcl/virdev.hxx>
#include <vcl/lazydelete.hxx>
#include <memory>
#include <unordered_map>
#include <string.h>
#include <stdlib.h>
#include <rtl/bootstrap.h>
#include <rtl/character.hxx>
#include <vcl/bitmapaccess.hxx>
#include <vcl/bitmap.hxx>
#include <vcl/graph.hxx>
#include <vcl/pdfread.hxx>
#include <vcl/pngwrite.hxx>
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
};
#if defined _MSC_VER && defined __clang__
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-register"
#pragma clang diagnostic ignored "-Wextra-tokens"
#endif
#include <hash.cxx>
#if defined _MSC_VER && defined __clang__
#pragma clang diagnostic pop
#endif
class Parser
{
typedef std::unordered_map< sal_Int64,
FontAttributes > FontMapType;
ScopedVclPtr<VirtualDevice> m_xDev;
const uno::Reference<uno::XComponentContext> m_xContext;
const ContentSinkSharedPtr m_pSink;
const oslFileHandle m_pErr;
OString m_aLine;
FontMapType m_aFontMap;
sal_Int32 m_nNextToken;
sal_Int32 m_nCharIndex;
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();
void readChar();
void readLineCap();
void readLineDash();
void readLineJoin();
void readTransformation();
rendering::ARGBColor readColor();
static void parseFontFamilyName( FontAttributes& aResult );
void readFont();
uno::Sequence<beans::PropertyValue> readImageImpl();
void readImage();
void readMask();
void readLink();
void readMaskedImage();
void readSoftMaskedImage();
static sal_Int32 parseFontCheckForString(const sal_Unicode* pCopy, sal_Int32 nCopyLen,
const char* pAttrib, sal_Int32 nAttribLen,
FontAttributes& rResult, bool bItalic, bool bBold);
static sal_Int32 parseFontRemoveSuffix(const sal_Unicode* pCopy, sal_Int32 nCopyLen,
const char* pAttrib, sal_Int32 nAttribLen);
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)
{}
void parseLine( const OString& rLine );
};
/** 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'.
*/
OString lcl_unescapeLineFeeds(const OString& i_rStr)
{
const size_t nOrigLen(sal::static_int_cast<size_t>(i_rStr.getLength()));
const sal_Char* const pOrig(i_rStr.getStr());
std::unique_ptr<sal_Char[]> pBuffer(new sal_Char[nOrigLen + 1]);
const sal_Char* pRead(pOrig);
sal_Char* pWrite(pBuffer.get());
const sal_Char* pCur(pOrig);
while ((pCur = strchr(pCur, '\\')) != nullptr)
{
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';
OString aResult(pBuffer.get());
return aResult;
}
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()
{
const OString aSubPathMarker( "subpath" );
if( readNextToken() != 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;
OString aCurrToken( m_aLine.getToken(m_nNextToken,' ',nDummy) );
while( m_nCharIndex != -1 && aCurrToken != 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();
}
return static_cast<rendering::XLinePolyPolygon2D*>(
new basegfx::unotools::UnoPolyPolygon(aResult));
}
void Parser::readChar()
{
double fontSize;
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);
readDouble(fontSize);
OString aChars;
if (m_nCharIndex != -1)
aChars = lcl_unescapeLineFeeds( m_aLine.copy( m_nCharIndex ) );
// chars gobble up rest of line
m_nCharIndex = -1;
m_pSink->drawGlyphs(OStringToOUString(aChars, RTL_TEXTENCODING_UTF8),
aRect, aUnoMatrix, fontSize);
}
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;
}
sal_Int32 Parser::parseFontCheckForString(
const sal_Unicode* pCopy, sal_Int32 nCopyLen,
const char* pAttrib, sal_Int32 nAttribLen,
FontAttributes& rResult, bool bItalic, bool bBold)
{
if (nCopyLen < nAttribLen)
return 0;
for (sal_Int32 i = 0; i < nAttribLen; ++i)
{
sal_uInt32 nCode = pAttrib[i];
if (rtl::toAsciiLowerCase(pCopy[i]) != nCode
&& rtl::toAsciiUpperCase(pCopy[i]) != nCode)
return 0;
}
rResult.isItalic |= bItalic;
rResult.isBold |= bBold;
return nAttribLen;
}
sal_Int32 Parser::parseFontRemoveSuffix(
const sal_Unicode* pCopy, sal_Int32 nCopyLen,
const char* pAttrib, sal_Int32 nAttribLen)
{
if (nCopyLen < nAttribLen)
return 0;
for (sal_Int32 i = 0; i < nAttribLen; ++i)
if ( pCopy[nCopyLen - nAttribLen + i] != pAttrib[i] )
return 0;
return nAttribLen;
}
void Parser::parseFontFamilyName( FontAttributes& rResult )
{
OUStringBuffer aNewFamilyName( rResult.familyName.getLength() );
const sal_Unicode* pCopy = rResult.familyName.getStr();
sal_Int32 nLen = rResult.familyName.getLength();
// parse out truetype subsets (e.g. BAAAAA+Thorndale)
if( nLen > 8 && pCopy[6] == '+' )
{
pCopy += 7;
nLen -= 7;
}
// TODO: Looks like this block needs to be refactored
while( nLen )
{
if (parseFontRemoveSuffix(pCopy, nLen, RTL_CONSTASCII_STRINGPARAM("PSMT")))
{
nLen -= RTL_CONSTASCII_LENGTH("PSMT");
}
else if (parseFontRemoveSuffix(pCopy, nLen, RTL_CONSTASCII_STRINGPARAM("MT")))
{
nLen -= RTL_CONSTASCII_LENGTH("MT");
}
if (parseFontCheckForString(pCopy, nLen, RTL_CONSTASCII_STRINGPARAM("Italic"), rResult, true, false))
{
sal_Int32 nAttribLen = RTL_CONSTASCII_LENGTH("Italic");
nLen -= nAttribLen;
pCopy += nAttribLen;
}
else if (parseFontCheckForString(pCopy, nLen, RTL_CONSTASCII_STRINGPARAM("-LightOblique"), rResult, true, false))
{
sal_Int32 nAttribLen = RTL_CONSTASCII_LENGTH("-LightOblique");
nLen -= nAttribLen;
pCopy += nAttribLen;
}
else if (parseFontCheckForString(pCopy, nLen, RTL_CONSTASCII_STRINGPARAM("-Light"), rResult, false, false))
{
sal_Int32 nAttribLen = RTL_CONSTASCII_LENGTH("-Light");
nLen -= nAttribLen;
pCopy += nAttribLen;
}
else if (parseFontCheckForString(pCopy, nLen, RTL_CONSTASCII_STRINGPARAM("-BoldOblique"), rResult, true, true))
{
sal_Int32 nAttribLen = RTL_CONSTASCII_LENGTH("-BoldOblique");
nLen -= nAttribLen;
pCopy += nAttribLen;
}
else if (parseFontCheckForString(pCopy, nLen, RTL_CONSTASCII_STRINGPARAM("-Bold"), rResult, false, true))
{
sal_Int32 nAttribLen = RTL_CONSTASCII_LENGTH("-Bold");
nLen -= nAttribLen;
pCopy += nAttribLen;
}
else if (parseFontCheckForString(pCopy, nLen, RTL_CONSTASCII_STRINGPARAM("Bold"), rResult, false, true))
{
sal_Int32 nAttribLen = RTL_CONSTASCII_LENGTH("Bold");
nLen -= nAttribLen;
pCopy += nAttribLen;
}
else if (parseFontCheckForString(pCopy, nLen, RTL_CONSTASCII_STRINGPARAM("-Roman"), rResult, false, false))
{
sal_Int32 nAttribLen = RTL_CONSTASCII_LENGTH("-Roman");
nLen -= nAttribLen;
pCopy += nAttribLen;
}
else if (parseFontCheckForString(pCopy, nLen, RTL_CONSTASCII_STRINGPARAM("-Oblique"), rResult, true, false))
{
sal_Int32 nAttribLen = RTL_CONSTASCII_LENGTH("-Oblique");
nLen -= nAttribLen;
pCopy += nAttribLen;
}
else if (parseFontCheckForString(pCopy, nLen, RTL_CONSTASCII_STRINGPARAM("-Reg"), rResult, false, false))
{
sal_Int32 nAttribLen = RTL_CONSTASCII_LENGTH("-Reg");
nLen -= nAttribLen;
pCopy += nAttribLen;
}
else if(nLen > 0)
{
if( *pCopy != '-' )
aNewFamilyName.append( *pCopy );
pCopy++;
nLen--;
}
}
rResult.familyName = aNewFamilyName.makeStringAndClear();
}
void Parser::readFont()
{
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( OStringToOUString( aFontName,
RTL_TEXTENCODING_UTF8 ),
nIsBold != 0,
nIsItalic != 0,
nIsUnderline != 0,
nSize,
1.0);
// 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(
"com.sun.star.awt.FontIdentificator", aArgs, m_xContext ),
uno::UNO_QUERY );
if( xMat.is() )
{
uno::Any aRes( xMat->getMaterial() );
if( aRes >>= aFD )
{
if (!aFD.Name.isEmpty())
{
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.isEmpty() )
{
// last fallback
aResult.familyName = "Arial";
aResult.isUnderline = false;
}
}
if (!m_xDev)
m_xDev.disposeAndReset(VclPtr<VirtualDevice>::Create());
vcl::Font font(aResult.familyName, Size(0, 1000));
m_xDev->SetFont(font);
FontMetric metric(m_xDev->GetFontMetric());
aResult.ascent = metric.GetAscent() / 1000.0;
m_aFontMap[nFontID] = aResult;
aResult.size = nSize;
m_pSink->setFont(aResult);
}
uno::Sequence<beans::PropertyValue> Parser::readImageImpl()
{
OString aToken = readNextToken();
const sal_Int32 nImageSize( readInt32() );
OUString aFileName;
if( aToken == "PNG" )
aFileName = "DUMMY.PNG";
else if( aToken == "JPEG" )
aFileName = "DUMMY.JPEG";
else if( aToken == "PBM" )
aFileName = "DUMMY.PBM";
else
{
SAL_WARN_IF(aToken != "PPM","sdext.pdfimport","Invalid bitmap format");
aFileName = "DUMMY.PPM";
}
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( "com.sun.star.io.SequenceInputStream", aStreamCreationArgs, m_xContext ),
uno::UNO_QUERY_THROW );
uno::Sequence<beans::PropertyValue> aSequence( comphelper::InitPropertySequence({
{ "URL", uno::makeAny(aFileName) },
{ "InputStream", uno::makeAny( xDataStream ) },
{ "InputSequence", uno::makeAny(aDataSequence) }
}));
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] <<= aMinRange;
aMaskRanges[1] <<= 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 != 0);
}
void Parser::readLink()
{
geometry::RealRectangle2D aBounds;
readDouble(aBounds.X1);
readDouble(aBounds.Y1);
readDouble(aBounds.X2);
readDouble(aBounds.Y2);
m_pSink->hyperLink( aBounds,
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 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;
const 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:
m_pSink->eoFillPath(readPath()); break;
case FILLPATH:
m_pSink->fillPath(readPath()); 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?
SAL_WARN_IF(m_nCharIndex!=-1, "sdext.pdfimport", "leftover scanner input");
}
} // namespace
static bool checkEncryption( const OUString& i_rPath,
const uno::Reference< task::XInteractionHandler >& i_xIHdl,
OUString& io_rPwd,
bool& o_rIsEncrypted,
const OUString& i_rDocName
)
{
bool bSuccess = false;
OString aPDFFile;
aPDFFile = OUStringToOString( i_rPath, osl_getThreadTextEncoding() );
pdfparse::PDFReader aParser;
std::unique_ptr<pdfparse::PDFEntry> pEntry( pdfparse::PDFReader::read( aPDFFile.getStr() ));
if( pEntry )
{
pdfparse::PDFFile* pPDFFile = dynamic_cast<pdfparse::PDFFile*>(pEntry.get());
if( pPDFFile )
{
o_rIsEncrypted = pPDFFile->isEncrypted();
if( o_rIsEncrypted )
{
if( pPDFFile->usesSupportedEncryptionFormat() )
{
bool bAuthenticated = false;
if( !io_rPwd.isEmpty() )
{
OString aIsoPwd = OUStringToOString( io_rPwd,
RTL_TEXTENCODING_ISO_8859_1 );
bAuthenticated = pPDFFile->setupDecryptionData( aIsoPwd.getStr() );
}
if( bAuthenticated )
bSuccess = true;
else
{
if( i_xIHdl.is() )
{
bool bEntered = false;
do
{
bEntered = getPassword( i_xIHdl, io_rPwd, ! bEntered, i_rDocName );
OString aIsoPwd = OUStringToOString( io_rPwd,
RTL_TEXTENCODING_ISO_8859_1 );
bAuthenticated = pPDFFile->setupDecryptionData( aIsoPwd.getStr() );
} while( bEntered && ! bAuthenticated );
}
bSuccess = bAuthenticated;
}
}
else if( i_xIHdl.is() )
{
reportUnsupportedEncryptionFormat( i_xIHdl );
//TODO: this should either be handled further down the
// call stack, or else information that this has already
// been handled should be passed down the call stack, so
// that SfxBaseModel::load does not show an additional
// "General Error" message box
}
}
else
bSuccess = true;
}
}
return bSuccess;
}
class Buffering
{
static const int SIZE = 64*1024;
std::unique_ptr<char[]> aBuffer;
oslFileHandle& pOut;
size_t pos;
sal_uInt64 left;
public:
explicit Buffering(oslFileHandle& out) : aBuffer(new char[SIZE]), pOut(out), pos(0), left(0) {}
oslFileError read(char *pChar, short count, sal_uInt64* pBytesRead)
{
oslFileError nRes = osl_File_E_None;
sal_uInt64 nBytesRead = 0;
while (count > 0)
{
if (left == 0)
{
nRes = osl_readFile(pOut, aBuffer.get(), SIZE, &left);
if (nRes != osl_File_E_None || left == 0)
{
*pBytesRead = nBytesRead;
return nRes;
}
pos = 0;
}
*pChar = aBuffer.get()[pos];
--count;
++pos;
--left;
++pChar;
++nBytesRead;
}
*pBytesRead = nBytesRead;
return osl_File_E_None;
}
};
bool xpdf_ImportFromFile(const OUString& rURL,
const ContentSinkSharedPtr& rSink,
const uno::Reference<task::XInteractionHandler>& xIHdl,
const OUString& rPwd,
const uno::Reference<uno::XComponentContext>& xContext,
const OUString& rFilterOptions)
{
OSL_ASSERT(rSink);
OUString aSysUPath;
if( osl_getSystemPathFromFileURL( rURL.pData, &aSysUPath.pData ) != osl_File_E_None )
{
SAL_WARN(
"sdext.pdfimport",
"getSystemPathFromFileURL(" << rURL << ") failed");
return false;
}
OUString aDocName( rURL.copy( rURL.lastIndexOf( '/' )+1 ) );
// check for encryption, if necessary get password
OUString aPwd( rPwd );
bool bIsEncrypted = false;
if( !checkEncryption( aSysUPath, xIHdl, aPwd, bIsEncrypted, aDocName ) )
{
SAL_INFO(
"sdext.pdfimport",
"checkEncryption(" << aSysUPath << ") failed");
return false;
}
// Determine xpdfimport executable URL:
OUString converterURL("$BRAND_BASE_DIR/" LIBO_BIN_FOLDER "/xpdfimport");
rtl::Bootstrap::expandMacros(converterURL); //TODO: detect failure
// Determine pathname of xpdfimport_err.pdf:
OUString errPathname("$BRAND_BASE_DIR/" LIBO_SHARE_FOLDER "/xpdfimport/xpdfimport_err.pdf");
rtl::Bootstrap::expandMacros(errPathname); //TODO: detect failure
if (osl::FileBase::getSystemPathFromFileURL(errPathname, errPathname)
!= osl::FileBase::E_None)
{
SAL_WARN(
"sdext.pdfimport",
"getSystemPathFromFileURL(" << errPathname << ") failed");
return false;
}
// spawn separate process to keep LGPL/GPL code apart.
OUString aOptFlag("-o");
rtl_uString* args[] = { aSysUPath.pData, errPathname.pData,
aOptFlag.pData, rFilterOptions.pData };
sal_Int32 nArgs = rFilterOptions.isEmpty() ? 2 : 4;
oslProcess aProcess;
oslFileHandle pIn = nullptr;
oslFileHandle pOut = nullptr;
oslFileHandle pErr = nullptr;
oslSecurity pSecurity = osl_getCurrentSecurity ();
oslProcessError eErr =
osl_executeProcess_WithRedirectedIO(converterURL.pData,
args,
nArgs,
osl_Process_SEARCHPATH|osl_Process_HIDDEN,
pSecurity,
nullptr, nullptr, 0,
&aProcess, &pIn, &pOut, &pErr);
osl_freeSecurityHandle(pSecurity);
bool bRet=true;
try
{
if( eErr!=osl_Process_E_None )
{
SAL_WARN(
"sdext.pdfimport",
"executeProcess of " << converterURL << " failed with "
<< +eErr);
return false;
}
if( pIn )
{
OStringBuffer aBuf(256);
if( bIsEncrypted )
aBuf.append( 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);
Buffering aBuffering(pOut);
OStringBuffer line;
for( ;; )
{
char aChar('\n');
sal_uInt64 nBytesRead;
oslFileError nRes;
// skip garbage \r \n at start of line
while( osl_File_E_None == (nRes = aBuffering.read(&aChar, 1, &nBytesRead)) &&
nBytesRead == 1 &&
(aChar == '\n' || aChar == '\r') ) ;
if ( osl_File_E_None != nRes )
break;
if( aChar != '\n' && aChar != '\r' )
line.append( aChar );
while( osl_File_E_None == (nRes = aBuffering.read(&aChar, 1, &nBytesRead)) &&
nBytesRead == 1 && aChar != '\n' && aChar != '\r' )
{
line.append( aChar );
}
if ( osl_File_E_None != nRes )
break;
if ( line.isEmpty() )
break;
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);
eErr = osl_joinProcess(aProcess);
if (eErr == osl_Process_E_None)
{
oslProcessInfo info;
info.Size = sizeof info;
eErr = osl_getProcessInfo(aProcess, osl_Process_EXITCODE, &info);
if (eErr == osl_Process_E_None)
{
if (info.Code != 0)
{
SAL_WARN(
"sdext.pdfimport",
"getProcessInfo of " << converterURL
<< " failed with exit code " << info.Code);
bRet = false;
}
}
else
{
SAL_WARN(
"sdext.pdfimport",
"getProcessInfo of " << converterURL << " failed with "
<< +eErr);
bRet = false;
}
}
else
{
SAL_WARN(
"sdext.pdfimport",
"joinProcess of " << converterURL << " failed with " << +eErr);
bRet = false;
}
osl_freeProcessHandle(aProcess);
return bRet;
}
bool xpdf_ImportFromStream( const uno::Reference< io::XInputStream >& xInput,
const ContentSinkSharedPtr& rSink,
const uno::Reference<task::XInteractionHandler >& xIHdl,
const OUString& rPwd,
const uno::Reference< uno::XComponentContext >& xContext,
const OUString& rFilterOptions )
{
OSL_ASSERT(xInput.is());
OSL_ASSERT(rSink);
// convert XInputStream to local temp file
oslFileHandle aFile = nullptr;
OUString aURL;
if( osl_createTempFile( nullptr, &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( css::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, rFilterOptions );
osl_removeFile( aURL.pData );
return bSuccess;
}
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
↑ V572 It is odd that the object which was created using 'new' operator is immediately cast to another type.