diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:06:44 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:06:44 +0000 |
commit | ed5640d8b587fbcfed7dd7967f3de04b37a76f26 (patch) | |
tree | 7a5f7c6c9d02226d7471cb3cc8fbbf631b415303 /sdext/source/pdfimport/xpdfwrapper | |
parent | Initial commit. (diff) | |
download | libreoffice-ed5640d8b587fbcfed7dd7967f3de04b37a76f26.tar.xz libreoffice-ed5640d8b587fbcfed7dd7967f3de04b37a76f26.zip |
Adding upstream version 4:7.4.7.upstream/4%7.4.7upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'sdext/source/pdfimport/xpdfwrapper')
-rw-r--r-- | sdext/source/pdfimport/xpdfwrapper/pdfioutdev_gpl.cxx | 1168 | ||||
-rw-r--r-- | sdext/source/pdfimport/xpdfwrapper/pdfioutdev_gpl.hxx | 302 | ||||
-rw-r--r-- | sdext/source/pdfimport/xpdfwrapper/pnghelper.cxx | 404 | ||||
-rw-r--r-- | sdext/source/pdfimport/xpdfwrapper/pnghelper.hxx | 84 | ||||
-rw-r--r-- | sdext/source/pdfimport/xpdfwrapper/wrapper_gpl.cxx | 188 |
5 files changed, 2146 insertions, 0 deletions
diff --git a/sdext/source/pdfimport/xpdfwrapper/pdfioutdev_gpl.cxx b/sdext/source/pdfimport/xpdfwrapper/pdfioutdev_gpl.cxx new file mode 100644 index 000000000..3ad139b65 --- /dev/null +++ b/sdext/source/pdfimport/xpdfwrapper/pdfioutdev_gpl.cxx @@ -0,0 +1,1168 @@ +/* -*- 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 "pdfioutdev_gpl.hxx" +#include "pnghelper.hxx" + +#include <stdlib.h> +#include <stdio.h> +#include <assert.h> +#include <math.h> + +#include <memory> +#include <vector> + +// sigh, UTF8.h was removed in poppler-0.21.0 and put back in 0.21.1, then renamed to UnicodeMapFuncs.h in 0.62.0 +// FIXME: we can't use #if POPPLER_CHECK_VERSION(0, 21, 0) && !POPPLER_CHECK_VERSION(0, 21, 1) +// because the internal poppler does not provide poppler-version.h and the macro always returns 0 +#if POPPLER_CHECK_VERSION(0, 62, 0) +#include <UnicodeMapFuncs.h> +#elif POPPLER_CHECK_VERSION(0, 21, 1) +#include <UTF8.h> +#elif POPPLER_CHECK_VERSION(0, 21, 0) +#include "UTF.h" +#else +#include "UTF8.h" +#endif + +#ifdef _WIN32 +# define snprintf _snprintf + +#if defined __GNUC__ +#pragma GCC diagnostic warning "-Wformat" +#pragma GCC diagnostic warning "-Wformat-extra-args" +#endif +#endif + +/* SYNC STREAMS + ============ + + We stream human-readable tokens to stdout, and binary data (fonts, + bitmaps) to g_binary_out. Another process reads from those pipes, and + there lies the rub: things can deadlock, if the two involved + processes access the pipes in different order. At any point in + time, both processes must access the same pipe. To ensure this, + data must be flushed to the OS before writing to a different pipe, + otherwise not-yet-written data will leave the reading process + waiting on the wrong pipe. + */ + +namespace pdfi +{ + +/// cut off very small numbers & clamp value to zero +static double normalize( double val ) +{ + return fabs(val) < 0.0000001 ? 0.0 : val; +} + +namespace +{ + +/** Escapes line-ending characters (\n and \r) in input string. + */ +std::vector<char> lcl_escapeLineFeeds(const char* const i_pStr) +{ + size_t nLength(strlen(i_pStr)); + std::vector<char> aBuffer; + aBuffer.reserve(2*nLength+1); + + const char* pRead = i_pStr; + while( nLength-- ) + { + if( *pRead == '\r' ) + { + aBuffer.push_back('\\'); + aBuffer.push_back('r'); + } + else if( *pRead == '\n' ) + { + aBuffer.push_back('\\'); + aBuffer.push_back('n'); + } + else if( *pRead == '\\' ) + { + aBuffer.push_back('\\'); + aBuffer.push_back('\\'); + } + else + aBuffer.push_back(*pRead); + pRead++; + } + aBuffer.push_back(0); + + return aBuffer; +} + +} + +/// for the temp char buffer the header gets snprintfed in +#define WRITE_BUFFER_SIZE 1024 + +/// for the initial std::vector capacity when copying stream from xpdf +#define WRITE_BUFFER_INITIAL_CAPACITY (1024*100) + +static void initBuf(OutputBuffer& io_rBuffer) +{ + io_rBuffer.reserve(WRITE_BUFFER_INITIAL_CAPACITY); +} + +static void writeBinaryBuffer( const OutputBuffer& rBuffer ) +{ + // ---sync point--- see SYNC STREAMS above + fflush(stdout); + + // put buffer to stderr + if( !rBuffer.empty() ) + if( fwrite(rBuffer.data(), sizeof(char), + rBuffer.size(), g_binary_out) != static_cast<size_t>(rBuffer.size()) ) + exit(1); // error + + // ---sync point--- see SYNC STREAMS above + fflush(g_binary_out); +} + +static bool ExtractJpegData(Stream* str, OutputBuffer& outBuf) +{ + int bytesToMarker = 0; + int bytesToLen = -1; + bool collectBytes = false; + int startOfScan = 0; + int b1 = -1; + for (; ; ) + { + const int b2 = b1; + b1 = str->getChar(); + + if (b1 == -1) + return false; + + if (collectBytes) + { + outBuf.push_back(static_cast<Output_t>(b1)); + + bytesToMarker--; + bytesToLen--; + } + + if (bytesToMarker == 0) + { + if (startOfScan == 1) + { + bytesToMarker = -1; + startOfScan = 2; + } + else if (b2 == 0xFF) + { + if (b1 == 0xD8) + { + collectBytes = true; + bytesToMarker = 2; + + outBuf.push_back(Output_t(0xFF)); + outBuf.push_back(Output_t(0xD8)); + } + else + { + bytesToLen = 2; + } + if (b1 == 0xDA) + { + startOfScan = 1; + } + } + else if (collectBytes) + { + return false; + } + } + + if (bytesToLen == 0) + { + bytesToMarker = b2 * 256 + b1; + } + + if (startOfScan == 2) + if ((b2 == 0xFF) && (b1 == 0xD9)) + return true; + } +} + +static void writeJpeg_( OutputBuffer& o_rOutputBuf, Stream* str ) +{ + // dump JPEG file as-is +#if POPPLER_CHECK_VERSION(0, 17, 3) + str = str->getNextStream(); +#else + str = ((DCTStream *)str)->getRawStream(); +#endif + str->reset(); + + o_rOutputBuf.clear(); + ExtractJpegData(str, o_rOutputBuf); + + printf( " JPEG %d", static_cast<int>(o_rOutputBuf.size()) ); + printf("\n"); + + str->close(); +} + +static void writePbm_(OutputBuffer& o_rOutputBuf, Stream* str, int width, int height, bool bInvert ) +{ + // write as PBM (char by char, to avoid stdlib lineend messing) + o_rOutputBuf.clear(); + o_rOutputBuf.resize(WRITE_BUFFER_SIZE); + o_rOutputBuf[0] = 'P'; + o_rOutputBuf[1] = '4'; + o_rOutputBuf[2] = 0x0A; + char *pAsCharPtr = reinterpret_cast<char *>(&o_rOutputBuf[3]); + int nOutLen = snprintf(pAsCharPtr, WRITE_BUFFER_SIZE-10, "%d %d", width, height); + if( nOutLen < 0 ) + nOutLen = WRITE_BUFFER_SIZE-10; + o_rOutputBuf[3+nOutLen] =0x0A; + o_rOutputBuf[3+nOutLen+1]=0; + + const int header_size = 3+nOutLen+1; + const int size = height * ((width + 7) / 8); + + printf( " PBM %d", size + header_size ); + printf("\n"); + + // trim buffer to exact header length + o_rOutputBuf.resize(header_size); + + // initialize stream + str->reset(); + + // copy the raw stream + if( bInvert ) + { + for( int i=0; i<size; ++i) + o_rOutputBuf.push_back(static_cast<char>(str->getChar() ^ 0xff)); + } + else + { + for( int i=0; i<size; ++i) + o_rOutputBuf.push_back(static_cast<char>(str->getChar())); + } + + str->close(); +} + +static void writePpm_( OutputBuffer& o_rOutputBuf, + Stream* str, + int width, + int height, + GfxImageColorMap* colorMap ) +{ + // write as PPM (char by char, to avoid stdlib lineend messing) + o_rOutputBuf.clear(); + o_rOutputBuf.resize(WRITE_BUFFER_SIZE); + o_rOutputBuf[0] = 'P'; + o_rOutputBuf[1] = '6'; + o_rOutputBuf[2] = '\n'; + char *pAsCharPtr = reinterpret_cast<char *>(&o_rOutputBuf[3]); + int nOutLen = snprintf(pAsCharPtr, WRITE_BUFFER_SIZE-10, "%d %d", width, height); + if( nOutLen < 0 ) + nOutLen = WRITE_BUFFER_SIZE-10; + o_rOutputBuf[3+nOutLen] ='\n'; + o_rOutputBuf[3+nOutLen+1]='2'; + o_rOutputBuf[3+nOutLen+2]='5'; + o_rOutputBuf[3+nOutLen+3]='5'; + o_rOutputBuf[3+nOutLen+4]='\n'; + o_rOutputBuf[3+nOutLen+5]=0; + + const int header_size = 3+nOutLen+5; + const int size = width*height*3 + header_size; + + printf( " PPM %d", size ); + printf("\n"); + + // trim buffer to exact header size + o_rOutputBuf.resize(header_size); + + // initialize stream + unsigned char *p; + GfxRGB rgb; + std::unique_ptr<ImageStream> imgStr( + new ImageStream(str, + width, + colorMap->getNumPixelComps(), + colorMap->getBits())); + imgStr->reset(); + + for( int y=0; y<height; ++y) + { + p = imgStr->getLine(); + for( int x=0; x<width; ++x) + { + colorMap->getRGB(p, &rgb); + o_rOutputBuf.push_back(colToByte(rgb.r)); + o_rOutputBuf.push_back(colToByte(rgb.g)); + o_rOutputBuf.push_back(colToByte(rgb.b)); + + p +=colorMap->getNumPixelComps(); + } + } +} + +// call this only for 1 bit image streams ! +static void writePng_( OutputBuffer& o_rOutputBuf, + Stream* str, + int width, + int height, + GfxRGB const & zeroColor, + GfxRGB const & oneColor, + bool bIsMask ) +{ + o_rOutputBuf.clear(); + + // get png image + PngHelper::createPng( o_rOutputBuf, str, width, height, zeroColor, oneColor, bIsMask ); + + printf( " PNG %d", static_cast<int>(o_rOutputBuf.size()) ); + printf("\n"); +} + +static void writePng_( OutputBuffer& o_rOutputBuf, + Stream* str, + int width, int height, GfxImageColorMap* colorMap, + Stream* maskStr, + int maskWidth, int maskHeight, GfxImageColorMap* maskColorMap ) +{ + o_rOutputBuf.clear(); + + // get png image + PngHelper::createPng( o_rOutputBuf, str, width, height, colorMap, maskStr, maskWidth, maskHeight, maskColorMap ); + + printf( " PNG %d", static_cast<int>(o_rOutputBuf.size()) ); + printf("\n"); +} + +static void writePng_( OutputBuffer& o_rOutputBuf, + Stream* str, + int width, int height, GfxImageColorMap* colorMap, + Stream* maskStr, + int maskWidth, int maskHeight, bool maskInvert ) +{ + o_rOutputBuf.clear(); + + // get png image + PngHelper::createPng( o_rOutputBuf, str, width, height, colorMap, maskStr, maskWidth, maskHeight, maskInvert ); + + printf( " PNG %d", static_cast<int>(o_rOutputBuf.size()) ); + printf("\n"); +} + +// stolen from ImageOutputDev.cc +static void writeMask_( OutputBuffer& o_rOutputBuf, Stream* str, int width, int height, bool bInvert ) +{ + if( str->getKind() == strDCT ) + writeJpeg_(o_rOutputBuf, str); + else + writePbm_(o_rOutputBuf, str, width, height, bInvert ); +} + +static void writeImage_( OutputBuffer& o_rOutputBuf, + Stream* str, + int width, + int height, + GfxImageColorMap* colorMap ) +{ + // dump JPEG file + if( str->getKind() == strDCT && + (colorMap->getNumPixelComps() == 1 || + colorMap->getNumPixelComps() == 3) ) + { + writeJpeg_(o_rOutputBuf, str); + } + else if (colorMap->getNumPixelComps() == 1 && + colorMap->getBits() == 1) + { + // this is a two color bitmap, write a png + // provide default colors + GfxRGB zeroColor = { 0, 0, 0 }, + oneColor = { byteToCol( 0xff ), byteToCol( 0xff ), byteToCol( 0xff ) }; + if( colorMap->getColorSpace()->getMode() == csIndexed || colorMap->getColorSpace()->getMode() == csDeviceGray ) + { + unsigned char nIndex = 0; + colorMap->getRGB( &nIndex, &zeroColor ); + nIndex = 1; + colorMap->getRGB( &nIndex, &oneColor ); + } + writePng_( o_rOutputBuf, str, width, height, zeroColor, oneColor, false); + } + else + writePpm_( o_rOutputBuf, str, width, height, colorMap ); +} + +// forwarders + + +static void writeImageLF( OutputBuffer& o_rOutputBuf, + Stream* str, + int width, + int height, + GfxImageColorMap* colorMap ) { writeImage_(o_rOutputBuf,str,width,height,colorMap); } +static void writeMaskLF( OutputBuffer& o_rOutputBuf, + Stream* str, + int width, + int height, + bool bInvert ) { writeMask_(o_rOutputBuf,str,width,height,bInvert); } + + +int PDFOutDev::parseFont( long long nNewId, GfxFont* gfxFont, const GfxState* state ) const +{ + FontAttributes aNewFont; + int nSize = 0; + +#if POPPLER_CHECK_VERSION(20, 12, 0) + std::string familyName = gfxFont->getNameWithoutSubsetTag(); +#else +#if POPPLER_CHECK_VERSION(0, 71, 0) // GooString::toStr() + std::string familyName = gfxFont->getName()->toStr(); +#else + const GooString* gooString = gfxFont->getName(); + std::string familyName = std::string(gooString->getCString(), gooString->getLength()); +#endif + if (familyName.length() > 7 && familyName.at(6) == '+') + { + familyName = familyName.substr(7); + } +#endif + if( familyName != "" ) + { + aNewFont.familyName.clear(); +#if POPPLER_CHECK_VERSION(0, 83, 0) // GooString::append(const std::string&) + aNewFont.familyName.append( familyName ); +#else + aNewFont.familyName.append( familyName.c_str() ); +#endif + } + else + { + aNewFont.familyName.clear(); + aNewFont.familyName.append( "Arial" ); + } + + aNewFont.maFontWeight = gfxFont->getWeight(); + aNewFont.isItalic = gfxFont->isItalic(); +#if POPPLER_CHECK_VERSION(0, 83, 0) // const added to getTransformedFontSize + aNewFont.size = state->getTransformedFontSize(); +#else + aNewFont.size = const_cast<GfxState*>(state)->getTransformedFontSize(); +#endif + aNewFont.isUnderline = false; + + if( gfxFont->getType() == fontTrueType || gfxFont->getType() == fontType1 ) + { + // TODO(P3): Unfortunately, need to read stream twice, since + // we must write byte count to stdout before +#if POPPLER_CHECK_VERSION(22, 6, 0) + std::optional<std::vector<unsigned char>> pBuf = gfxFont->readEmbFontFile( m_pDoc->getXRef() ); + if ( pBuf ) + { + aNewFont.isEmbedded = true; + nSize = pBuf->size(); + } +#else + char* pBuf = gfxFont->readEmbFontFile( m_pDoc->getXRef(), &nSize ); + if( pBuf ) + { + aNewFont.isEmbedded = true; + gfree(pBuf); + } +#endif + } + + m_aFontMap[ nNewId ] = aNewFont; + return nSize; +} + +void PDFOutDev::writeFontFile( GfxFont* gfxFont ) const +{ + if( gfxFont->getType() != fontTrueType && gfxFont->getType() != fontType1 ) + return; + + int nSize = 0; +#if POPPLER_CHECK_VERSION(22, 6, 0) + std::optional<std::vector<unsigned char>> pBuf = gfxFont->readEmbFontFile( m_pDoc->getXRef() ); + if ( pBuf ) + nSize = pBuf->size(); + if ( nSize == 0 ) + return; +#else + char* pBuf = gfxFont->readEmbFontFile( m_pDoc->getXRef(), &nSize ); + if( !pBuf ) + return; +#endif + + // ---sync point--- see SYNC STREAMS above + fflush(stdout); + +#if POPPLER_CHECK_VERSION(22, 6, 0) + if( fwrite(pBuf->data(), sizeof(*pBuf->data()), nSize, g_binary_out) != static_cast<size_t>(nSize) ) + { + exit(1); // error + } + // ---sync point--- see SYNC STREAMS above + fflush(g_binary_out); +#else + if( fwrite(pBuf, sizeof(char), nSize, g_binary_out) != static_cast<size_t>(nSize) ) + { + gfree(pBuf); + exit(1); // error + } + // ---sync point--- see SYNC STREAMS above + fflush(g_binary_out); + gfree(pBuf); +#endif +} + +#if POPPLER_CHECK_VERSION(0, 83, 0) +void PDFOutDev::printPath( const GfxPath* pPath ) +#else +void PDFOutDev::printPath( GfxPath* pPath ) +#endif +{ + int nSubPaths = pPath ? pPath->getNumSubpaths() : 0; + for( int i=0; i<nSubPaths; i++ ) + { +#if POPPLER_CHECK_VERSION(0, 83, 0) + const +#endif + GfxSubpath* pSub = pPath->getSubpath( i ); + const int nPoints = pSub->getNumPoints(); + + printf( " subpath %d", pSub->isClosed() ); + + for( int n=0; n<nPoints; ++n ) + { + printf( " %f %f %d", + normalize(pSub->getX(n)), + normalize(pSub->getY(n)), + pSub->getCurve(n) ); + } + } +} + +PDFOutDev::PDFOutDev( PDFDoc* pDoc ) : + m_pDoc( pDoc ), + m_pUtf8Map( new UnicodeMap("UTF-8", true, &mapUTF8) ), + m_bSkipImages(false) +{ +} +PDFOutDev::~PDFOutDev() +{ +} + +void PDFOutDev::startPage(int /*pageNum*/, GfxState* state +#if POPPLER_CHECK_VERSION(0, 23, 0) || POPPLER_CHECK_VERSION(0, 24, 0) + , XRef* /*xref*/ +#endif +) +{ + assert(state); + printf("startPage %f %f\n", + normalize(state->getPageWidth()), + normalize(state->getPageHeight())); +} + +void PDFOutDev::endPage() +{ + printf("endPage\n"); +} + +#if POPPLER_CHECK_VERSION(0, 19, 0) +void PDFOutDev::processLink(AnnotLink *link) +#elif POPPLER_CHECK_VERSION(0, 17, 0) +void PDFOutDev::processLink(AnnotLink *link, Catalog *) +#else +void PDFOutDev::processLink(Link* link, Catalog*) +#endif +{ + assert(link); + + double x1,x2,y1,y2; + link->getRect( &x1, &y1, &x2, &y2 ); + + LinkAction* pAction = link->getAction(); + if (!(pAction && pAction->getKind() == actionURI)) + return; + +#if POPPLER_CHECK_VERSION(0, 86, 0) + const char* pURI = static_cast<LinkURI*>(pAction)->getURI().c_str(); +#elif POPPLER_CHECK_VERSION(0, 72, 0) + const char* pURI = static_cast<LinkURI*>(pAction)->getURI()->c_str(); +#else + const char* pURI = static_cast<LinkURI*>(pAction)->getURI()->getCString(); +#endif + + std::vector<char> aEsc( lcl_escapeLineFeeds(pURI) ); + + printf( "drawLink %f %f %f %f %s\n", + normalize(x1), + normalize(y1), + normalize(x2), + normalize(y2), + aEsc.data() ); +} + +void PDFOutDev::saveState(GfxState*) +{ + printf( "saveState\n" ); +} + +void PDFOutDev::restoreState(GfxState*) +{ + printf( "restoreState\n" ); +} + +#if POPPLER_CHECK_VERSION(0, 71, 0) +void PDFOutDev::setDefaultCTM(const double *pMat) +#else +void PDFOutDev::setDefaultCTM(double *pMat) +#endif +{ + assert(pMat); + + OutputDev::setDefaultCTM(pMat); + + printf( "updateCtm %f %f %f %f %f %f\n", + normalize(pMat[0]), + normalize(pMat[1]), + normalize(pMat[2]), + normalize(pMat[3]), + normalize(pMat[4]), + normalize(pMat[5]) ); +} + +void PDFOutDev::updateCTM(GfxState* state, + double, double, + double, double, + double, double) +{ + assert(state); + + const double* const pMat = state->getCTM(); + assert(pMat); + + printf( "updateCtm %f %f %f %f %f %f\n", + normalize(pMat[0]), + normalize(pMat[1]), + normalize(pMat[2]), + normalize(pMat[3]), + normalize(pMat[4]), + normalize(pMat[5]) ); +} + +void PDFOutDev::updateLineDash(GfxState *state) +{ + if (m_bSkipImages) + return; + assert(state); + + int arrayLen; double startOffset; +#if POPPLER_CHECK_VERSION(22, 9, 0) + const std::vector<double> &dash = state->getLineDash(&startOffset); + const double* dashArray = dash.data(); + arrayLen = dash.size(); +#else + double* dashArray; + state->getLineDash(&dashArray, &arrayLen, &startOffset); +#endif + + printf( "updateLineDash" ); + if( arrayLen && dashArray ) + { + printf( " %f %d", normalize(startOffset), arrayLen ); + for( int i=0; i<arrayLen; ++i ) + printf( " %f", normalize(*dashArray++) ); + } + printf( "\n" ); +} + +void PDFOutDev::updateFlatness(GfxState *state) +{ + if (m_bSkipImages) + return; + assert(state); + printf( "updateFlatness %d\n", state->getFlatness() ); +} + +void PDFOutDev::updateLineJoin(GfxState *state) +{ + if (m_bSkipImages) + return; + assert(state); + printf( "updateLineJoin %d\n", state->getLineJoin() ); +} + +void PDFOutDev::updateLineCap(GfxState *state) +{ + if (m_bSkipImages) + return; + assert(state); + printf( "updateLineCap %d\n", state->getLineCap() ); +} + +void PDFOutDev::updateMiterLimit(GfxState *state) +{ + if (m_bSkipImages) + return; + assert(state); + printf( "updateMiterLimit %f\n", normalize(state->getMiterLimit()) ); +} + +void PDFOutDev::updateLineWidth(GfxState *state) +{ + if (m_bSkipImages) + return; + assert(state); + printf( "updateLineWidth %f\n", normalize(state->getLineWidth()) ); +} + +void PDFOutDev::updateFillColor(GfxState *state) +{ + if (m_bSkipImages) + return; + assert(state); + + GfxRGB aRGB; + state->getFillRGB( &aRGB ); + + printf( "updateFillColor %f %f %f %f\n", + normalize(colToDbl(aRGB.r)), + normalize(colToDbl(aRGB.g)), + normalize(colToDbl(aRGB.b)), + normalize(state->getFillOpacity()) ); +} + +void PDFOutDev::updateStrokeColor(GfxState *state) +{ + if (m_bSkipImages) + return; + assert(state); + + GfxRGB aRGB; + state->getStrokeRGB( &aRGB ); + + printf( "updateStrokeColor %f %f %f %f\n", + normalize(colToDbl(aRGB.r)), + normalize(colToDbl(aRGB.g)), + normalize(colToDbl(aRGB.b)), + normalize(state->getFillOpacity()) ); +} + +void PDFOutDev::updateFillOpacity(GfxState *state) +{ + if (m_bSkipImages) + return; + updateFillColor(state); +} + +void PDFOutDev::updateStrokeOpacity(GfxState *state) +{ + if (m_bSkipImages) + return; + updateStrokeColor(state); +} + +void PDFOutDev::updateBlendMode(GfxState*) +{ +} + +void PDFOutDev::updateFont(GfxState *state) +{ + assert(state); + +#if POPPLER_CHECK_VERSION(22, 6, 0) + GfxFont *gfxFont = state->getFont().get(); +#else + GfxFont *gfxFont = state->getFont(); +#endif + if( !gfxFont ) + return; + + FontAttributes aFont; + int nEmbedSize=0; + +#if POPPLER_CHECK_VERSION(0, 64, 0) + const +#endif + Ref* pID = gfxFont->getID(); + // TODO(Q3): Portability problem + long long fontID = static_cast<long long>(pID->gen) << 32 | static_cast<long long>(pID->num); + std::unordered_map< long long, FontAttributes >::const_iterator it = + m_aFontMap.find( fontID ); + if( it == m_aFontMap.end() ) + { + nEmbedSize = parseFont( fontID, gfxFont, state ); + it = m_aFontMap.find( fontID ); + } + + printf( "updateFont" ); + if( it != m_aFontMap.end() ) + { + // conflating this with printf below crashes under Windoze + printf( " %lld", fontID ); + + aFont = it->second; + +#if POPPLER_CHECK_VERSION(0, 72, 0) + std::vector<char> aEsc( lcl_escapeLineFeeds(aFont.familyName.c_str()) ); +#else + std::vector<char> aEsc( lcl_escapeLineFeeds(aFont.familyName.getCString()) ); +#endif + printf( " %d %d %d %d %f %d %s", + aFont.isEmbedded, + aFont.maFontWeight, + aFont.isItalic, + aFont.isUnderline, + normalize(state->getTransformedFontSize()), + nEmbedSize, + aEsc.data() ); + } + printf( "\n" ); + + if (nEmbedSize) + { + writeFontFile(gfxFont); + } +} + +void PDFOutDev::updateRender(GfxState *state) +{ + assert(state); + + printf( "setTextRenderMode %d\n", state->getRender() ); +} + +void PDFOutDev::stroke(GfxState *state) +{ + if (m_bSkipImages) + return; + assert(state); + + printf( "strokePath" ); + printPath( state->getPath() ); + printf( "\n" ); +} + +void PDFOutDev::fill(GfxState *state) +{ + if (m_bSkipImages) + return; + assert(state); + + printf( "fillPath" ); + printPath( state->getPath() ); + printf( "\n" ); +} + +void PDFOutDev::eoFill(GfxState *state) +{ + if (m_bSkipImages) + return; + assert(state); + + printf( "eoFillPath" ); + printPath( state->getPath() ); + printf( "\n" ); +} + +void PDFOutDev::clip(GfxState *state) +{ + if (m_bSkipImages) + return; + assert(state); + + printf( "clipPath" ); + printPath( state->getPath() ); + printf( "\n" ); +} + +void PDFOutDev::eoClip(GfxState *state) +{ + if (m_bSkipImages) + return; + assert(state); + + printf( "eoClipPath" ); + printPath( state->getPath() ); + printf( "\n" ); +} + +/** Output one glyph + + + @param dx + horizontal skip for character (already scaled with font size) + + inter-char space: cursor is shifted by this amount for next char + + @param dy + vertical skip for character (zero for horizontal writing mode): + cursor is shifted by this amount for next char + + @param originX + local offset of character (zero for horizontal writing mode). not + taken into account for output pos updates. Used for vertical writing. + + @param originY + local offset of character (zero for horizontal writing mode). not + taken into account for output pos updates. Used for vertical writing. + */ + +#if POPPLER_CHECK_VERSION(0, 82, 0) +void PDFOutDev::drawChar(GfxState *state, double x, double y, + double dx, double dy, + double originX, double originY, + CharCode, int /*nBytes*/, const Unicode *u, int uLen) +{ +#else +void PDFOutDev::drawChar(GfxState *state, double x, double y, + double dx, double dy, + double originX, double originY, + CharCode, int /*nBytes*/, Unicode *u, int uLen) +{ +#endif + assert(state); + + if( u == nullptr ) + return; + + // Fix for tdf#96080 + if (uLen == 4 && u[0] == '\t' && u[1] == '\r' && u[2] == ' ' && u[3] == 0xA0) + { + u += 2; + uLen = 1; + } + + double csdx = 0.0; + double csdy = 0.0; + if (state->getFont()->getWMode()) + { + csdy = state->getCharSpace(); + if (*u == ' ') + csdy += state->getWordSpace(); + } + else + { + csdx = state->getCharSpace(); + if (*u == ' ') + csdx += state->getWordSpace(); + csdx *= state->getHorizScaling(); + } + + double cstdx = 0.0; + double cstdy = 0.0; + state->textTransformDelta(csdx, csdy, &cstdx, &cstdy); + + const double fontSize = state->getFontSize(); + + const double aPositionX(x-originX); + const double aPositionY(y-originY); + + const double* pTextMat=state->getTextMat(); + printf( "drawChar %f %f %f %f %f %f %f %f %f ", + normalize(aPositionX), + normalize(aPositionY), + normalize(aPositionX + dx - cstdx), + normalize(aPositionY + dy - cstdy), + normalize(pTextMat[0]), + normalize(pTextMat[2]), + normalize(pTextMat[1]), + normalize(pTextMat[3]), + normalize(fontSize) + ); + + // silence spurious warning +#if POPPLER_CHECK_VERSION(0, 62, 0) + (void)&mapUTF16; +#else + (void)&mapUCS2; +#endif + + char buf[9]; + for( int i=0; i<uLen; ++i ) + { + buf[ m_pUtf8Map->mapUnicode(u[i], buf, sizeof(buf)-1) ] = 0; + std::vector<char> aEsc( lcl_escapeLineFeeds(buf) ); + printf( "%s", aEsc.data() ); + } + + printf( "\n" ); +} + +#if POPPLER_CHECK_VERSION(0, 64, 0) +void PDFOutDev::drawString(GfxState*, const GooString* /*s*/) +#else +void PDFOutDev::drawString(GfxState*, GooString* /*s*/) +#endif +{ + // TODO(F3): NYI +} + +void PDFOutDev::endTextObject(GfxState*) +{ + printf( "endTextObject\n" ); +} + +void PDFOutDev::drawImageMask(GfxState* pState, Object*, Stream* str, + int width, int height, poppler_bool invert, + poppler_bool /*interpolate*/, + poppler_bool /*inlineImg*/ ) +{ + if (m_bSkipImages) + return; + OutputBuffer aBuf; initBuf(aBuf); + + printf( "drawMask %d %d %d", width, height, invert ); + + int bitsPerComponent = 1; + StreamColorSpaceMode csMode = streamCSNone; + str->getImageParams( &bitsPerComponent, &csMode ); + if( bitsPerComponent == 1 && (csMode == streamCSNone || csMode == streamCSDeviceGray) ) + { + GfxRGB oneColor = { dblToCol( 1.0 ), dblToCol( 1.0 ), dblToCol( 1.0 ) }; + GfxRGB zeroColor = { dblToCol( 0.0 ), dblToCol( 0.0 ), dblToCol( 0.0 ) }; + pState->getFillColorSpace()->getRGB( pState->getFillColor(), &zeroColor ); + if( invert ) + writePng_( aBuf, str, width, height, oneColor, zeroColor, true ); + else + writePng_( aBuf, str, width, height, zeroColor, oneColor, true ); + } + else + writeMaskLF(aBuf, str, width, height, invert); + writeBinaryBuffer(aBuf); +} + +#if POPPLER_CHECK_VERSION(0, 82, 0) +void PDFOutDev::drawImage(GfxState*, Object*, Stream* str, + int width, int height, GfxImageColorMap* colorMap, + poppler_bool /*interpolate*/, + const int* maskColors, poppler_bool /*inlineImg*/ ) +{ +#else +void PDFOutDev::drawImage(GfxState*, Object*, Stream* str, + int width, int height, GfxImageColorMap* colorMap, + poppler_bool /*interpolate*/, + int* maskColors, poppler_bool /*inlineImg*/ ) +{ +#endif + if (m_bSkipImages) + return; + OutputBuffer aBuf; initBuf(aBuf); + OutputBuffer aMaskBuf; + + printf( "drawImage %d %d", width, height ); + + if( maskColors ) + { + // write mask colors. nBytes must be even - first half is + // lower bound values, second half upper bound values + if( colorMap->getColorSpace()->getMode() == csIndexed ) + { + aMaskBuf.push_back( static_cast<char>(maskColors[0]) ); + aMaskBuf.push_back( static_cast<char>(maskColors[gfxColorMaxComps]) ); + } + else + { + GfxRGB aMinRGB; + colorMap->getColorSpace()->getRGB( +#if POPPLER_CHECK_VERSION(0, 82, 0) + reinterpret_cast<const GfxColor*>(maskColors), +#else + reinterpret_cast<GfxColor*>(maskColors), +#endif + &aMinRGB ); + + GfxRGB aMaxRGB; + colorMap->getColorSpace()->getRGB( +#if POPPLER_CHECK_VERSION(0, 82, 0) + reinterpret_cast<const GfxColor*>(maskColors)+gfxColorMaxComps, +#else + reinterpret_cast<GfxColor*>(maskColors)+gfxColorMaxComps, +#endif + &aMaxRGB ); + + aMaskBuf.push_back( colToByte(aMinRGB.r) ); + aMaskBuf.push_back( colToByte(aMinRGB.g) ); + aMaskBuf.push_back( colToByte(aMinRGB.b) ); + aMaskBuf.push_back( colToByte(aMaxRGB.r) ); + aMaskBuf.push_back( colToByte(aMaxRGB.g) ); + aMaskBuf.push_back( colToByte(aMaxRGB.b) ); + } + } + + printf( " %d", static_cast<int>(aMaskBuf.size()) ); + writeImageLF( aBuf, str, width, height, colorMap ); + writeBinaryBuffer(aBuf); + writeBinaryBuffer(aMaskBuf); +} + +void PDFOutDev::drawMaskedImage(GfxState*, Object*, Stream* str, + int width, int height, + GfxImageColorMap* colorMap, + poppler_bool /*interpolate*/, + Stream* maskStr, + int maskWidth, int maskHeight, + poppler_bool maskInvert, poppler_bool /*maskInterpolate*/ + ) +{ + if (m_bSkipImages) + return; + OutputBuffer aBuf; initBuf(aBuf); + printf( "drawImage %d %d 0", width, height ); + writePng_( aBuf, str, width, height, colorMap, maskStr, maskWidth, maskHeight, maskInvert ); + writeBinaryBuffer( aBuf ); +} + +void PDFOutDev::drawSoftMaskedImage(GfxState*, Object*, Stream* str, + int width, int height, + GfxImageColorMap* colorMap, + poppler_bool /*interpolate*/, + Stream* maskStr, + int maskWidth, int maskHeight, + GfxImageColorMap* maskColorMap + , poppler_bool /*maskInterpolate*/ + ) +{ + if (m_bSkipImages) + return; + OutputBuffer aBuf; initBuf(aBuf); + printf( "drawImage %d %d 0", width, height ); + writePng_( aBuf, str, width, height, colorMap, maskStr, maskWidth, maskHeight, maskColorMap ); + writeBinaryBuffer( aBuf ); +} + +void PDFOutDev::setPageNum( int nNumPages ) +{ + // TODO(F3): printf might format int locale-dependent! + printf("setPageNum %d\n", nNumPages); +} + +void PDFOutDev::setSkipImages( bool bSkipImages ) +{ + m_bSkipImages = bSkipImages; +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sdext/source/pdfimport/xpdfwrapper/pdfioutdev_gpl.hxx b/sdext/source/pdfimport/xpdfwrapper/pdfioutdev_gpl.hxx new file mode 100644 index 000000000..b34fad1ba --- /dev/null +++ b/sdext/source/pdfimport/xpdfwrapper/pdfioutdev_gpl.hxx @@ -0,0 +1,302 @@ +/* -*- 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 . + */ + +#ifndef INCLUDED_SDEXT_SOURCE_PDFIMPORT_XPDFWRAPPER_PDFIOUTDEV_GPL_HXX +#define INCLUDED_SDEXT_SOURCE_PDFIMPORT_XPDFWRAPPER_PDFIOUTDEV_GPL_HXX + +#include <sal/types.h> +#include <config_poppler.h> + +#if defined __GNUC__ || defined __clang__ +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wundef" +# pragma GCC diagnostic ignored "-Wunused-parameter" +#elif defined _MSC_VER +#pragma warning(push) +#pragma warning(disable : 4100) // unreferenced formal parameter +#pragma warning(disable : 4310) // cast truncates constant value +#endif + +#include <GfxState.h> +#include <GfxFont.h> +#include <UnicodeMap.h> +#include <Link.h> +#include <Object.h> +#include <OutputDev.h> +#include <GlobalParams.h> +#include <PDFDoc.h> + +#if defined __GNUC__ || defined __clang__ +# pragma GCC diagnostic pop +#elif defined _MSC_VER +#pragma warning(pop) +#endif + +#include <unordered_map> +#include <vector> +#include <memory> + +class GfxPath; +class GfxFont; +class PDFDoc; +#include <cpp/poppler-version.h> +#define POPPLER_CHECK_VERSION(major,minor,micro) \ + (POPPLER_VERSION_MAJOR > (major) || \ + (POPPLER_VERSION_MAJOR == (major) && POPPLER_VERSION_MINOR > (minor)) || \ + (POPPLER_VERSION_MAJOR == (major) && POPPLER_VERSION_MINOR == (minor) && POPPLER_VERSION_MICRO >= (micro))) + +namespace pdfi +{ + struct FontAttributes + { + FontAttributes() : + familyName(), + isEmbedded(false), + maFontWeight(GfxFont::W400), + isItalic(false), + isUnderline(false), + size(0.0) + {} + + // xpdf goo stuff is so totally borked... + // ...need to hand-code assignment + FontAttributes( const FontAttributes& rSrc ) : + familyName(), + isEmbedded(rSrc.isEmbedded), + maFontWeight(rSrc.maFontWeight), + isItalic(rSrc.isItalic), + isUnderline(rSrc.isUnderline), + size(rSrc.size) + { + familyName.append(&rSrc.getFamilyName()); + } + + FontAttributes& operator=( const FontAttributes& rSrc ) + { + familyName.clear(); + familyName.append(&rSrc.getFamilyName()); + + isEmbedded = rSrc.isEmbedded; + maFontWeight= rSrc.maFontWeight; + isItalic = rSrc.isItalic; + isUnderline = rSrc.isUnderline; + size = rSrc.size; + + return *this; + } + + bool operator==(const FontAttributes& rFont) const + { + return getFamilyName().cmp(&rFont.getFamilyName())==0 && + isEmbedded == rFont.isEmbedded && + maFontWeight == rFont.maFontWeight && + isItalic == rFont.isItalic && + isUnderline == rFont.isUnderline && + size == rFont.size; + } + + GooString familyName; + bool isEmbedded; + GfxFont::Weight maFontWeight; + bool isItalic; + bool isUnderline; + double size; + + private: + // Work around const-ness issues in the GooString API: + GooString & getFamilyName() const + { return const_cast<GooString &>(familyName); } + }; + + // Versions before 0.15 defined GBool as int; 0.15 redefined it as bool; 0.71 dropped GBool +#if POPPLER_VERSION_MAJOR == 0 && POPPLER_VERSION_MINOR < 71 + typedef GBool poppler_bool; +#else + typedef bool poppler_bool; +#endif + + class PDFOutDev : public OutputDev + { + // not owned by this class + PDFDoc* m_pDoc; + mutable std::unordered_map< long long, + FontAttributes > m_aFontMap; + std::unique_ptr<UnicodeMap> m_pUtf8Map; + bool m_bSkipImages; + + int parseFont( long long nNewId, GfxFont* pFont, const GfxState* state ) const; + void writeFontFile( GfxFont* gfxFont ) const; +#if POPPLER_CHECK_VERSION(0, 83, 0) + static void printPath( const GfxPath* pPath ); +#else + static void printPath( GfxPath* pPath ); +#endif + + public: + explicit PDFOutDev( PDFDoc* pDoc ); + virtual ~PDFOutDev() override; + + //----- get info about output device + + // Does this device use upside-down coordinates? + // (Upside-down means (0,0) is the top left corner of the page.) + virtual poppler_bool upsideDown() override { return true; } + + // Does this device use drawChar() or drawString()? + virtual poppler_bool useDrawChar() override { return true; } + + // Does this device use beginType3Char/endType3Char? Otherwise, + // text in Type 3 fonts will be drawn with drawChar/drawString. + virtual poppler_bool interpretType3Chars() override { return false; } + + // Does this device need non-text content? + virtual poppler_bool needNonText() override { return true; } + + //----- initialization and control + + // Set default transform matrix. +#if POPPLER_CHECK_VERSION(0, 71, 0) + virtual void setDefaultCTM(const double *ctm) override; +#else + virtual void setDefaultCTM(double *ctm) override; +#endif + + // Start a page. + virtual void startPage(int pageNum, GfxState *state +#if POPPLER_CHECK_VERSION(0, 23, 0) || POPPLER_CHECK_VERSION(0, 24, 0) + , XRef *xref +#endif + ) override; + + // End a page. + virtual void endPage() override; + + //----- link borders + #if POPPLER_CHECK_VERSION(0, 19, 0) + virtual void processLink(AnnotLink *link) override; + #elif POPPLER_CHECK_VERSION(0, 17, 0) + virtual void processLink(AnnotLink *link, Catalog *catalog) override; + #else + virtual void processLink(Link *link, Catalog *catalog) override; + #endif + + //----- save/restore graphics state + virtual void saveState(GfxState *state) override; + virtual void restoreState(GfxState *state) override; + + //----- update graphics state + virtual void updateCTM(GfxState *state, double m11, double m12, + double m21, double m22, double m31, double m32) override; + virtual void updateLineDash(GfxState *state) override; + virtual void updateFlatness(GfxState *state) override; + virtual void updateLineJoin(GfxState *state) override; + virtual void updateLineCap(GfxState *state) override; + virtual void updateMiterLimit(GfxState *state) override; + virtual void updateLineWidth(GfxState *state) override; + virtual void updateFillColor(GfxState *state) override; + virtual void updateStrokeColor(GfxState *state) override; + virtual void updateFillOpacity(GfxState *state) override; + virtual void updateStrokeOpacity(GfxState *state) override; + virtual void updateBlendMode(GfxState *state) override; + + //----- update text state + virtual void updateFont(GfxState *state) override; + virtual void updateRender(GfxState *state) override; + + //----- path painting + virtual void stroke(GfxState *state) override; + virtual void fill(GfxState *state) override; + virtual void eoFill(GfxState *state) override; + + //----- path clipping + virtual void clip(GfxState *state) override; + virtual void eoClip(GfxState *state) override; + + //----- text drawing +#if POPPLER_CHECK_VERSION(0, 82, 0) + virtual void drawChar(GfxState *state, double x, double y, + double dx, double dy, + double originX, double originY, + CharCode code, int nBytes, const Unicode *u, int uLen) override; +#else + virtual void drawChar(GfxState *state, double x, double y, + double dx, double dy, + double originX, double originY, + CharCode code, int nBytes, Unicode *u, int uLen) override; +#endif +#if POPPLER_CHECK_VERSION(0, 64, 0) + virtual void drawString(GfxState *state, const GooString *s) override; +#else + virtual void drawString(GfxState *state, GooString *s) override; +#endif + virtual void endTextObject(GfxState *state) override; + + //----- image drawing + virtual void drawImageMask(GfxState *state, Object *ref, Stream *str, + int width, int height, poppler_bool invert, + poppler_bool interpolate, + poppler_bool inlineImg) override; +#if POPPLER_CHECK_VERSION(0, 82, 0) + virtual void drawImage(GfxState *state, Object *ref, Stream *str, + int width, int height, GfxImageColorMap *colorMap, + poppler_bool interpolate, + const int* maskColors, poppler_bool inlineImg) override; +#else + virtual void drawImage(GfxState *state, Object *ref, Stream *str, + int width, int height, GfxImageColorMap *colorMap, + poppler_bool interpolate, + int* maskColors, poppler_bool inlineImg) override; +#endif + virtual void drawMaskedImage(GfxState *state, Object *ref, Stream *str, + int width, int height, + GfxImageColorMap *colorMap, + poppler_bool interpolate, + Stream *maskStr, int maskWidth, int maskHeight, + poppler_bool maskInvert, + poppler_bool maskInterpolate + ) override; + virtual void drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str, + int width, int height, + GfxImageColorMap *colorMap, + poppler_bool interpolate, + Stream *maskStr, + int maskWidth, int maskHeight, + GfxImageColorMap *maskColorMap + , poppler_bool maskInterpolate + ) override; + + static void setPageNum( int nNumPages ); + void setSkipImages ( bool bSkipImages ); + }; +} + +extern FILE* g_binary_out; + +// note: if you ever change Output_t, please keep in mind that the current code +// relies on it being of 8 bit size +typedef unsigned char Output_t; +typedef std::vector< Output_t > OutputBuffer; + +#if !POPPLER_CHECK_VERSION(0, 73, 0) +static_assert(std::is_same_v<Guchar, unsigned char>, "unexpected typedef"); +#endif + +#endif // INCLUDED_SDEXT_SOURCE_PDFIMPORT_XPDFWRAPPER_PDFIOUTDEV_GPL_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sdext/source/pdfimport/xpdfwrapper/pnghelper.cxx b/sdext/source/pdfimport/xpdfwrapper/pnghelper.cxx new file mode 100644 index 000000000..798555fe2 --- /dev/null +++ b/sdext/source/pdfimport/xpdfwrapper/pnghelper.cxx @@ -0,0 +1,404 @@ +/* -*- 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 "pnghelper.hxx" +#include <sal/macros.h> + +#include <zlib.h> + +using namespace pdfi; + +// checksum helpers, courtesy of libpng.org + +/* Table of CRCs of all 8-bit messages. */ +sal_uInt32 PngHelper::crc_table[256]; + +/* Flag: has the table been computed? Initially false. */ +bool PngHelper::bCRCTableInit = true; + +/* Make the table for a fast CRC. */ +void PngHelper::initCRCTable() +{ + for (sal_uInt32 n = 0; n < 256; n++) + { + sal_uInt32 c = n; + for (int k = 0; k < 8; k++) + { + if (c & 1) + c = 0xedb88320L ^ (c >> 1); + else + c = c >> 1; + } + crc_table[n] = c; + } + bCRCTableInit = false; +} + +/* Update a running CRC with the bytes buf[0..len-1]--the CRC + should be initialized to all 1's, and the transmitted value + is the 1's complement of the final running CRC (see the + crc() routine below)). */ + +void PngHelper::updateCRC( sal_uInt32& io_rCRC, const sal_uInt8* i_pBuf, size_t i_nLen ) +{ + if( bCRCTableInit ) + initCRCTable(); + + sal_uInt32 nCRC = io_rCRC; + for( size_t n = 0; n < i_nLen; n++ ) + nCRC = crc_table[(nCRC ^ i_pBuf[n]) & 0xff] ^ (nCRC >> 8); + io_rCRC = nCRC; +} + +sal_uInt32 PngHelper::getCRC( const sal_uInt8* i_pBuf, size_t i_nLen ) +{ + sal_uInt32 nCRC = 0xffffffff; + updateCRC( nCRC, i_pBuf, i_nLen ); + return nCRC ^ 0xffffffff; +} + +sal_uInt32 PngHelper::deflateBuffer( const Output_t* i_pBuf, size_t i_nLen, OutputBuffer& o_rOut ) +{ + size_t nOrigSize = o_rOut.size(); + + // prepare z stream + z_stream aStream; + aStream.zalloc = Z_NULL; + aStream.zfree = Z_NULL; + aStream.opaque = Z_NULL; + aStream.total_out = aStream.total_in = 0; + if (Z_OK != deflateInit(&aStream, Z_BEST_COMPRESSION)) + return 0; + aStream.avail_in = uInt(i_nLen); + aStream.next_in = i_pBuf; + + sal_uInt8 aOutBuf[ 32768 ]; + do + { + aStream.avail_out = sizeof( aOutBuf ); + aStream.next_out = aOutBuf; + + if( deflate( &aStream, Z_FINISH ) == Z_STREAM_ERROR ) + { + deflateEnd( &aStream ); + // scrap the data of this broken stream + o_rOut.resize( nOrigSize ); + return 0; + } + + // append compressed bytes + sal_uInt32 nCompressedBytes = sizeof( aOutBuf ) - aStream.avail_out; + if( nCompressedBytes ) + o_rOut.insert( o_rOut.end(), aOutBuf, aOutBuf+nCompressedBytes ); + + } while( aStream.avail_out == 0 ); + + // cleanup + deflateEnd( &aStream ); + + return sal_uInt32( o_rOut.size() - nOrigSize ); +} + +void PngHelper::appendFileHeader( OutputBuffer& o_rOutputBuf ) +{ + static const unsigned char aHeader[] = { 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a }; + + o_rOutputBuf.insert( o_rOutputBuf.end(), aHeader, aHeader + SAL_N_ELEMENTS(aHeader) ); +} + +size_t PngHelper::startChunk( const char* pChunkName, OutputBuffer& o_rOutputBuf ) +{ + size_t nIndex = sal_uInt32( o_rOutputBuf.size() ); + o_rOutputBuf.insert( o_rOutputBuf.end(), 4, Output_t(0) ); + o_rOutputBuf.push_back( pChunkName[0] ); + o_rOutputBuf.push_back( pChunkName[1] ); + o_rOutputBuf.push_back( pChunkName[2] ); + o_rOutputBuf.push_back( pChunkName[3] ); + return nIndex; +} + +void PngHelper::set( sal_uInt32 i_nValue, OutputBuffer& o_rOutputBuf, size_t i_nIndex ) +{ + o_rOutputBuf[ i_nIndex ] = (i_nValue & 0xff000000) >> 24; + o_rOutputBuf[ i_nIndex+1 ] = (i_nValue & 0x00ff0000) >> 16; + o_rOutputBuf[ i_nIndex+2 ] = (i_nValue & 0x0000ff00) >> 8; + o_rOutputBuf[ i_nIndex+3 ] = (i_nValue & 0x000000ff); +} + +void PngHelper::endChunk( size_t nStart, OutputBuffer& o_rOutputBuf ) +{ + if( nStart+8 > o_rOutputBuf.size() ) + return; // something broken is going on + + // update chunk length + size_t nLen = o_rOutputBuf.size() - nStart; + sal_uInt32 nDataLen = sal_uInt32(nLen)-8; + set( nDataLen, o_rOutputBuf, nStart ); + + // append chunk crc + sal_uInt32 nChunkCRC = getCRC( reinterpret_cast<sal_uInt8*>(&o_rOutputBuf[nStart+4]), nLen-4 ); + append( nChunkCRC, o_rOutputBuf ); +} + +void PngHelper::appendIHDR( OutputBuffer& o_rOutputBuf, int width, int height, int depth, int colortype ) +{ + size_t nStart = startChunk( "IHDR", o_rOutputBuf ); + append( width, o_rOutputBuf ); + append( height, o_rOutputBuf ); + o_rOutputBuf.push_back( Output_t(depth) ); + o_rOutputBuf.push_back( Output_t(colortype) ); + o_rOutputBuf.push_back( 0 ); // compression method deflate + o_rOutputBuf.push_back( 0 ); // filtering method 0 (default) + o_rOutputBuf.push_back( 0 ); // no interlacing + endChunk( nStart, o_rOutputBuf ); +} + +void PngHelper::appendIEND( OutputBuffer& o_rOutputBuf ) +{ + size_t nStart = startChunk( "IEND", o_rOutputBuf ); + endChunk( nStart, o_rOutputBuf ); +} + +void PngHelper::createPng( OutputBuffer& o_rOutputBuf, + Stream* str, + int width, + int height, + GfxRGB const & zeroColor, + GfxRGB const & oneColor, + bool bIsMask + ) +{ + appendFileHeader( o_rOutputBuf ); + appendIHDR( o_rOutputBuf, width, height, 1, 3 ); + + // write palette + size_t nIdx = startChunk( "PLTE", o_rOutputBuf ); + // write colors 0 and 1 + o_rOutputBuf.push_back(colToByte(zeroColor.r)); + o_rOutputBuf.push_back(colToByte(zeroColor.g)); + o_rOutputBuf.push_back(colToByte(zeroColor.b)); + o_rOutputBuf.push_back(colToByte(oneColor.r)); + o_rOutputBuf.push_back(colToByte(oneColor.g)); + o_rOutputBuf.push_back(colToByte(oneColor.b)); + // end PLTE chunk + endChunk( nIdx, o_rOutputBuf ); + + if( bIsMask ) + { + // write tRNS chunk + nIdx = startChunk( "tRNS", o_rOutputBuf ); + o_rOutputBuf.push_back( 0xff ); + o_rOutputBuf.push_back( 0 ); + // end tRNS chunk + endChunk( nIdx, o_rOutputBuf ); + } + + // create scan line data buffer + OutputBuffer aScanlines; + int nLineSize = (width + 7)/8; + aScanlines.reserve( nLineSize * height + height ); + + str->reset(); + for( int y = 0; y < height; y++ ) + { + // determine filter type (none) for this scanline + aScanlines.push_back( 0 ); + for( int x = 0; x < nLineSize; x++ ) + aScanlines.push_back( str->getChar() ); + } + + // begin IDAT chunk for scanline data + nIdx = startChunk( "IDAT", o_rOutputBuf ); + // compress scanlines + deflateBuffer( aScanlines.data(), aScanlines.size(), o_rOutputBuf ); + // end IDAT chunk + endChunk( nIdx, o_rOutputBuf ); + + // output IEND + appendIEND( o_rOutputBuf ); +} + +void PngHelper::createPng( OutputBuffer& o_rOutputBuf, + Stream* str, + int width, int height, GfxImageColorMap* colorMap, + Stream* maskStr, + int maskWidth, int maskHeight, GfxImageColorMap* maskColorMap ) +{ + appendFileHeader( o_rOutputBuf ); + appendIHDR( o_rOutputBuf, width, height, 8, 6 ); // RGBA image + + // initialize stream + unsigned char *p, *pm; + GfxRGB rgb; + GfxGray alpha; + std::unique_ptr<ImageStream> imgStr( + new ImageStream(str, + width, + colorMap->getNumPixelComps(), + colorMap->getBits())); + imgStr->reset(); + + // create scan line data buffer + OutputBuffer aScanlines; + aScanlines.reserve( width*height*4 + height ); + + for( int y=0; y<height; ++y) + { + aScanlines.push_back( 0 ); + p = imgStr->getLine(); + for( int x=0; x<width; ++x) + { + colorMap->getRGB(p, &rgb); + aScanlines.push_back(colToByte(rgb.r)); + aScanlines.push_back(colToByte(rgb.g)); + aScanlines.push_back(colToByte(rgb.b)); + aScanlines.push_back( 0xff ); + + p +=colorMap->getNumPixelComps(); + } + } + + + // now fill in the mask data + + // CAUTION: originally this was done in one single loop + // it caused merry chaos; the reason is that maskStr and str are + // not independent streams, it happens that reading one advances + // the other, too. Hence the two passes are imperative ! + + // initialize mask stream + std::unique_ptr<ImageStream> imgStrMask( + new ImageStream(maskStr, + maskWidth, + maskColorMap->getNumPixelComps(), + maskColorMap->getBits())); + + imgStrMask->reset(); + for( int y = 0; y < maskHeight; ++y ) + { + pm = imgStrMask->getLine(); + for( int x = 0; x < maskWidth; ++x ) + { + maskColorMap->getGray(pm,&alpha); + pm += maskColorMap->getNumPixelComps(); + int nIndex = (y*height/maskHeight) * (width*4+1) + // mapped line + (x*width/maskWidth)*4 + 1 + 3 // mapped column + ; + aScanlines[ nIndex ] = colToByte(alpha); + } + } + + imgStr.reset(); + imgStrMask.reset(); + + // begind IDAT chunk for scanline data + size_t nIdx = startChunk( "IDAT", o_rOutputBuf ); + // compress scanlines + deflateBuffer( aScanlines.data(), aScanlines.size(), o_rOutputBuf ); + // end IDAT chunk + endChunk( nIdx, o_rOutputBuf ); + // output IEND + appendIEND( o_rOutputBuf ); +} + +// one bit mask; 0 bits opaque +void PngHelper::createPng( OutputBuffer& o_rOutputBuf, + Stream* str, + int width, int height, GfxImageColorMap* colorMap, + Stream* maskStr, + int maskWidth, int maskHeight, + bool maskInvert + ) +{ + appendFileHeader( o_rOutputBuf ); + appendIHDR( o_rOutputBuf, width, height, 8, 6 ); // RGBA image + + // initialize stream + unsigned char *p; + GfxRGB rgb; + std::unique_ptr<ImageStream> imgStr( + new ImageStream(str, + width, + colorMap->getNumPixelComps(), + colorMap->getBits())); + imgStr->reset(); + + // create scan line data buffer + OutputBuffer aScanlines; + aScanlines.reserve( width*height*4 + height ); + + for( int y=0; y<height; ++y) + { + aScanlines.push_back( 0 ); + p = imgStr->getLine(); + for( int x=0; x<width; ++x) + { + colorMap->getRGB(p, &rgb); + aScanlines.push_back(colToByte(rgb.r)); + aScanlines.push_back(colToByte(rgb.g)); + aScanlines.push_back(colToByte(rgb.b)); + aScanlines.push_back( 0xff ); + + p +=colorMap->getNumPixelComps(); + } + } + + + // now fill in the mask data + + // CAUTION: originally this was done in one single loop + // it caused merry chaos; the reason is that maskStr and str are + // not independent streams, it happens that reading one advances + // the other, too. Hence the two passes are imperative ! + + // initialize mask stream + std::unique_ptr<ImageStream> imgStrMask( + new ImageStream(maskStr, maskWidth, 1, 1)); + + imgStrMask->reset(); + for( int y = 0; y < maskHeight; ++y ) + { + for( int x = 0; x < maskWidth; ++x ) + { + unsigned char aPixel = 0; + imgStrMask->getPixel( &aPixel ); + int nIndex = (y*height/maskHeight) * (width*4+1) + // mapped line + (x*width/maskWidth)*4 + 1 + 3 // mapped column + ; + if( maskInvert ) + aScanlines[ nIndex ] = aPixel ? 0xff : 0x00; + else + aScanlines[ nIndex ] = aPixel ? 0x00 : 0xff; + } + } + + imgStr.reset(); + imgStrMask.reset(); + + // begind IDAT chunk for scanline data + size_t nIdx = startChunk( "IDAT", o_rOutputBuf ); + // compress scanlines + deflateBuffer( aScanlines.data(), aScanlines.size(), o_rOutputBuf ); + // end IDAT chunk + endChunk( nIdx, o_rOutputBuf ); + // output IEND + appendIEND( o_rOutputBuf ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sdext/source/pdfimport/xpdfwrapper/pnghelper.hxx b/sdext/source/pdfimport/xpdfwrapper/pnghelper.hxx new file mode 100644 index 000000000..e0f2ea98b --- /dev/null +++ b/sdext/source/pdfimport/xpdfwrapper/pnghelper.hxx @@ -0,0 +1,84 @@ +/* -*- 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 . + */ + +#ifndef INCLUDED_SDEXT_SOURCE_PDFIMPORT_XPDFWRAPPER_PNGHELPER_HXX +#define INCLUDED_SDEXT_SOURCE_PDFIMPORT_XPDFWRAPPER_PNGHELPER_HXX + +#include <sal/types.h> +#include "pdfioutdev_gpl.hxx" + + +namespace pdfi +{ + class PngHelper + { + static sal_uInt32 crc_table[ 256 ]; + static bool bCRCTableInit; + + static void initCRCTable(); + static void appendFileHeader( OutputBuffer& o_rOutputBuf ); + static size_t startChunk( const char* pChunkName, OutputBuffer& o_rOut ); + static void endChunk( size_t nStart, OutputBuffer& o_rOut ); + + static void set( sal_uInt32 i_nValue, OutputBuffer& o_rOutputBuf, size_t i_nIndex ); + static void append( sal_uInt32 i_nValue, OutputBuffer& o_rOutputBuf ) + { + size_t nCur = o_rOutputBuf.size(); + o_rOutputBuf.insert( o_rOutputBuf.end(), 4, Output_t(0) ); + set( i_nValue, o_rOutputBuf, nCur ); + } + + static void appendIHDR( OutputBuffer& o_rOutputBuf, int width, int height, int depth, int colortype ); + static void appendIEND( OutputBuffer& o_rOutputBuf ); + + public: + static void updateCRC( sal_uInt32& io_rCRC, const sal_uInt8* i_pBuf, size_t i_nLen ); + static sal_uInt32 getCRC( const sal_uInt8* i_pBuf, size_t i_nLen ); + + // deflates the passed buff i_pBuf and appends it to the output vector + // returns the number of bytes added to the output + static sal_uInt32 deflateBuffer( const Output_t* i_pBuf, size_t i_nLen, OutputBuffer& o_rOut ); + + static void createPng( OutputBuffer& o_rOutputBuf, + Stream* str, + int width, + int height, + GfxRGB const & zeroColor, + GfxRGB const & oneColor, + bool bIsMask + ); + static void createPng( OutputBuffer& o_rOutputBuf, + Stream* str, + int width, int height, GfxImageColorMap* colorMap, + Stream* maskStr, + int maskWidth, int maskHeight, GfxImageColorMap* maskColorMap ); + + // for one bit masks + static void createPng( OutputBuffer& o_rOutputBuf, + Stream* str, + int width, int height, GfxImageColorMap* colorMap, + Stream* maskStr, + int maskWidth, int maskHeight, bool maskInvert ); + + }; +} + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sdext/source/pdfimport/xpdfwrapper/wrapper_gpl.cxx b/sdext/source/pdfimport/xpdfwrapper/wrapper_gpl.cxx new file mode 100644 index 000000000..e924547e9 --- /dev/null +++ b/sdext/source/pdfimport/xpdfwrapper/wrapper_gpl.cxx @@ -0,0 +1,188 @@ +/* -*- 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 "pdfioutdev_gpl.hxx" +#ifdef _WIN32 +# include <io.h> +# include <fcntl.h> /*_O_BINARY*/ +#endif +#ifndef SYSTEM_POPPLER +#include <string> // std::string +#include <cstddef> // std::size_t +#include <config_folders.h> //LIBO_SHARE_FOLDER +#endif + +FILE* g_binary_out=stderr; + +static const char *ownerPassword = "\001"; +static const char *userPassword = "\001"; +static const char *outputFile = "\001"; +static const char *options = "\001"; + +int main(int argc, char **argv) +{ + int k = 0; + while (k < argc) + { + if (!strcmp(argv[k], "-f")) + { + outputFile = argv[k+1]; + argc -= 2; + for (int j = k; j < argc; ++j) + argv[j] = argv[j+2]; + } + else if (!strcmp(argv[k], "-o")) + { + options = argv[k+1]; + argc -= 2; + for (int j = k; j < argc; ++j) + argv[j] = argv[j+2]; + } + + else if (!strcmp(argv[k], "-opw")) + { + ownerPassword = argv[k+1]; + argc -= 2; + for (int j = k; j < argc; ++j) + argv[j] = argv[j+2]; + } + else if (!strcmp(argv[k], "-upw")) + { + userPassword = argv[k+1]; + argc -= 2; + for (int j = k; j < argc; ++j) + argv[j] = argv[j+2]; + } + ++k; + } + + /* Get data directory location */ +#ifdef SYSTEM_POPPLER + const char* datadir = nullptr; +#else + /* Creates an absolute path to the poppler_data directory, by taking the path + * to the xpdfimport executable (provided in argv[0], and concatenating a + * relative path to the poppler_data directory from the program directory. */ + const std::string execPath = argv[0]; + const std::size_t filenameStartPos = execPath.find_last_of("/\\")+1; + const std::string programPath = execPath.substr(0,filenameStartPos); + const std::string popplerDataPath = programPath + "../" LIBO_SHARE_FOLDER "/xpdfimport/poppler_data"; + const char* datadir = popplerDataPath.c_str(); +#endif + + // read config file +#if POPPLER_CHECK_VERSION(0, 83, 0) + globalParams = std::make_unique<GlobalParams>(datadir); +#else + globalParams = new GlobalParams(datadir); +#endif + globalParams->setErrQuiet(true); +#if defined(_MSC_VER) + globalParams->setupBaseFonts(nullptr); +#endif + + // try to read a possible open password from stdin + char aPwBuf[129]; + aPwBuf[128] = 0; + if( ! fgets( aPwBuf, sizeof(aPwBuf)-1, stdin ) ) + aPwBuf[0] = 0; // mark as empty + else + { + for( size_t i = 0; i < sizeof(aPwBuf); i++ ) + { + if( aPwBuf[i] == '\n' ) + { + aPwBuf[i] = 0; + break; + } + } + } + + // PDFDoc takes over ownership for all strings below + GooString* pFileName = new GooString(argv[1]); + GooString* pErrFileName = new GooString(argv[2]); + + // check for password string(s) + GooString* pOwnerPasswordStr( aPwBuf[0] != 0 + ? new GooString( aPwBuf ) + : (ownerPassword[0] != '\001' + ? new GooString(ownerPassword) + : nullptr ) ); + GooString* pUserPasswordStr( aPwBuf[0] != 0 + ? new GooString( aPwBuf ) + : (userPassword[0] != '\001' + ? new GooString(userPassword) + : nullptr ) ); + if( outputFile[0] != '\001' ) + g_binary_out = fopen(outputFile,"wb"); + +#ifdef _WIN32 + // Win actually modifies output for O_TEXT file mode, so need to + // revert to binary here + _setmode( _fileno( g_binary_out ), _O_BINARY ); +#endif + +#if POPPLER_CHECK_VERSION(22, 6, 0) + PDFDoc aDoc( std::make_unique<GooString>(pFileName), + std::optional<GooString>(pOwnerPasswordStr), + std::optional<GooString>(pUserPasswordStr) ); + + PDFDoc aErrDoc( std::make_unique<GooString>(pErrFileName), + std::optional<GooString>(pOwnerPasswordStr), + std::optional<GooString>(pUserPasswordStr) ); +#else + PDFDoc aDoc( pFileName, + pOwnerPasswordStr, + pUserPasswordStr ); + + PDFDoc aErrDoc( pErrFileName, + pOwnerPasswordStr, + pUserPasswordStr ); +#endif + + // Check various permissions for aDoc. + PDFDoc &rDoc = aDoc.isOk()? aDoc: aErrDoc; + + pdfi::PDFOutDev aOutDev(&rDoc); + if (!strcmp(options, "SkipImages")) { + aOutDev.setSkipImages(true); + } + + // tell the receiver early - needed for proper progress calculation + const int nPages = rDoc.isOk()? rDoc.getNumPages(): 0; + pdfi::PDFOutDev::setPageNum(nPages); + + // virtual resolution of the PDF OutputDev in dpi + static const int PDFI_OUTDEV_RESOLUTION = 7200; + + // do the conversion + for (int i = 1; i <= nPages; ++i) + { + rDoc.displayPage(&aOutDev, + i, + PDFI_OUTDEV_RESOLUTION, + PDFI_OUTDEV_RESOLUTION, + 0, true, true, true); + rDoc.processLinks(&aOutDev, i); + } + + return 0; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |