diff options
Diffstat (limited to '')
-rw-r--r-- | sdext/source/pdfimport/wrapper/wrapper.cxx | 1240 |
1 files changed, 1240 insertions, 0 deletions
diff --git a/sdext/source/pdfimport/wrapper/wrapper.cxx b/sdext/source/pdfimport/wrapper/wrapper.cxx new file mode 100644 index 000000000..e850e5a6c --- /dev/null +++ b/sdext/source/pdfimport/wrapper/wrapper.cxx @@ -0,0 +1,1240 @@ +/* -*- 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 <sal/log.hxx> + +#include <comphelper/propertysequence.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/XPolyPolygon2D.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/geometry/RealSize2D.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/unopolypolygon.hxx> + +#include <vcl/metric.hxx> +#include <vcl/font.hxx> +#include <vcl/virdev.hxx> + +#include <memory> +#include <unordered_map> +#include <string.h> +#include <stdlib.h> + +#include <rtl/character.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 char* const pOrig(i_rStr.getStr()); + std::unique_ptr<char[]> pBuffer(new char[nOrigLen + 1]); + + const char* pRead(pOrig); + char* pWrite(pBuffer.get()); + const char* pCur(pOrig); + while ((pCur = strchr(pCur, '\\')) != nullptr) + { + const 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 ) + { + nRes = osl_readFile( m_pErr, pBuf, nFileLen, &nBytesRead ); + if (osl_File_E_None != nRes ) + break; + 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: + 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: + 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 = OUStringToOString( i_rPath, osl_getThreadTextEncoding() ); + + 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; +} + +namespace { + +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 + for (;;) + { + nRes = aBuffering.read(&aChar, 1, &nBytesRead); + if (osl_File_E_None != nRes || nBytesRead != 1 || !(aChar == '\n' || aChar == '\r') ) + break; + } + if ( osl_File_E_None != nRes ) + break; + + if( aChar != '\n' && aChar != '\r' ) + line.append( aChar ); + + for (;;) + { + nRes = aBuffering.read(&aChar, 1, &nBytesRead); + if ( osl_File_E_None != nRes || nBytesRead != 1 || aChar == '\n' || aChar == '\r' ) + break; + 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: */ |