/* -*- 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include 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 #if defined _MSC_VER && defined __clang__ #pragma clang diagnostic pop #endif class Parser { typedef std::unordered_map< sal_Int64, FontAttributes > FontMapType; ScopedVclPtr m_xDev; const uno::Reference 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& rBuf ); uno::Reference readPath(); void readChar(); void readLineCap(); void readLineDash(); void readLineJoin(); void readTransformation(); rendering::ARGBColor readColor(); static void parseFontFamilyName( FontAttributes& aResult ); void readFont(); uno::Sequence 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& 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(i_rStr.getLength())); const sal_Char* const pOrig(i_rStr.getStr()); std::unique_ptr 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(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& 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(nBytesRead); } OSL_PRECOND(nRes==osl_File_E_None, "inconsistent data"); } uno::Reference 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( 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(), 0.0 ); return; } const double nOffset(readDouble()); const sal_Int32 nLen(readInt32()); uno::Sequence aDashArray(nLen); double* pArray=aDashArray.getArray(); for( sal_Int32 i=0; isetLineDash( 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 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::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 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 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 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 aImg( readImageImpl() ); if( nMaskColors ) { uno::Sequence aDataSequence(nMaskColors); readBinaryData( aDataSequence ); uno::Sequence aMaskRanges(2); uno::Sequence aMinRange(nMaskColors/2); uno::Sequence aMaxRange(nMaskColors/2); for( sal_Int32 i=0; idrawColorMaskedImage( 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 aImage( readImageImpl() ); const uno::Sequence 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 aImage( readImageImpl() ); const uno::Sequence 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 pEntry( pdfparse::PDFReader::read( aPDFFile.getStr() )); if( pEntry ) { pdfparse::PDFFile* pPDFFile = dynamic_cast(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 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& xIHdl, const OUString& rPwd, const uno::Reference& 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& 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 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: */