| //======================================================================== |
| // |
| // PreScanOutputDev.cc |
| // |
| // Copyright 2005 Glyph & Cog, LLC |
| // |
| //======================================================================== |
| |
| //======================================================================== |
| // |
| // Modified under the Poppler project - http://poppler.freedesktop.org |
| // |
| // All changes made under the Poppler project to this file are licensed |
| // under GPL version 2 or later |
| // |
| // Copyright (C) 2009 Carlos Garcia Campos <carlosgc@gnome.org> |
| // Copyright (C) 2010, 2011, 2018 Albert Astals Cid <aacid@kde.org> |
| // Copyright (C) 2011, 2014 William Bader <williambader@hotmail.com> |
| // Copyright (C) 2011, 2013 Thomas Freitag <Thomas.Freitag@alfa.de> |
| // Copyright (C) 2011 Adrian Johnson <ajohnson@redneon.com> |
| // |
| // To see a description of the changes please see the Changelog file that |
| // came with your tarball or type make ChangeLog if you are building from git |
| // |
| //======================================================================== |
| |
| #include <config.h> |
| |
| #include <math.h> |
| #include "GlobalParams.h" |
| #include "Gfx.h" |
| #include "GfxFont.h" |
| #include "Link.h" |
| #include "Catalog.h" |
| #include "Page.h" |
| #include "PreScanOutputDev.h" |
| |
| //------------------------------------------------------------------------ |
| // PreScanOutputDev |
| //------------------------------------------------------------------------ |
| |
| PreScanOutputDev::PreScanOutputDev(PDFDoc *docA) { |
| level = globalParams->getPSLevel(); |
| doc = docA; |
| clearStats(); |
| } |
| |
| PreScanOutputDev::~PreScanOutputDev() { |
| } |
| |
| void PreScanOutputDev::startPage(int /*pageNum*/, GfxState * /*state*/, XRef * /*xref*/) { |
| } |
| |
| void PreScanOutputDev::endPage() { |
| } |
| |
| void PreScanOutputDev::stroke(GfxState *state) { |
| double *dash; |
| int dashLen; |
| double dashStart; |
| |
| check(state->getStrokeColorSpace(), state->getStrokeColor(), |
| state->getStrokeOpacity(), state->getBlendMode()); |
| state->getLineDash(&dash, &dashLen, &dashStart); |
| if (dashLen != 0) { |
| gdi = false; |
| } |
| } |
| |
| void PreScanOutputDev::fill(GfxState *state) { |
| check(state->getFillColorSpace(), state->getFillColor(), |
| state->getFillOpacity(), state->getBlendMode()); |
| } |
| |
| void PreScanOutputDev::eoFill(GfxState *state) { |
| check(state->getFillColorSpace(), state->getFillColor(), |
| state->getFillOpacity(), state->getBlendMode()); |
| } |
| |
| bool PreScanOutputDev::tilingPatternFill(GfxState *state, Gfx *gfx, Catalog *catalog, Object *str, |
| const double *pmat, int paintType, int /*tilingType*/, Dict *resDict, |
| const double *mat, const double *bbox, |
| int x0, int y0, int x1, int y1, |
| double xStep, double yStep) { |
| if (paintType == 1) { |
| bool tilingNeeded = (x1 - x0 != 1 || y1 - y0 != 1); |
| if (tilingNeeded) { |
| inTilingPatternFill++; |
| } |
| gfx->drawForm(str, resDict, mat, bbox); |
| if (tilingNeeded) { |
| inTilingPatternFill--; |
| } |
| } else { |
| check(state->getFillColorSpace(), state->getFillColor(), |
| state->getFillOpacity(), state->getBlendMode()); |
| } |
| return true; |
| } |
| |
| bool PreScanOutputDev::functionShadedFill(GfxState *state, |
| GfxFunctionShading *shading) { |
| if (shading->getColorSpace()->getMode() != csDeviceGray && |
| shading->getColorSpace()->getMode() != csCalGray) { |
| gray = false; |
| } |
| mono = false; |
| if (state->getFillOpacity() != 1 || |
| state->getBlendMode() != gfxBlendNormal) { |
| transparency = true; |
| } |
| return true; |
| } |
| |
| bool PreScanOutputDev::axialShadedFill(GfxState *state, GfxAxialShading *shading, double /*tMin*/, double /*tMax*/) { |
| if (shading->getColorSpace()->getMode() != csDeviceGray && |
| shading->getColorSpace()->getMode() != csCalGray) { |
| gray = false; |
| } |
| mono = false; |
| if (state->getFillOpacity() != 1 || |
| state->getBlendMode() != gfxBlendNormal) { |
| transparency = true; |
| } |
| return true; |
| } |
| |
| bool PreScanOutputDev::radialShadedFill(GfxState *state, GfxRadialShading *shading, double /*sMin*/, double /*sMax*/) { |
| if (shading->getColorSpace()->getMode() != csDeviceGray && |
| shading->getColorSpace()->getMode() != csCalGray) { |
| gray = false; |
| } |
| mono = false; |
| if (state->getFillOpacity() != 1 || |
| state->getBlendMode() != gfxBlendNormal) { |
| transparency = true; |
| } |
| return true; |
| } |
| |
| void PreScanOutputDev::clip(GfxState * /*state*/) { |
| //~ check for a rectangle "near" the edge of the page; |
| //~ else set gdi to false |
| } |
| |
| void PreScanOutputDev::eoClip(GfxState * /*state*/) { |
| //~ see clip() |
| } |
| |
| void PreScanOutputDev::beginStringOp(GfxState *state) { |
| int render; |
| double m11, m12, m21, m22; |
| bool simpleTTF; |
| |
| render = state->getRender(); |
| if (!(render & 1)) { |
| check(state->getFillColorSpace(), state->getFillColor(), |
| state->getFillOpacity(), state->getBlendMode()); |
| } |
| if ((render & 3) == 1 || (render & 3) == 2) { |
| check(state->getStrokeColorSpace(), state->getStrokeColor(), |
| state->getStrokeOpacity(), state->getBlendMode()); |
| } |
| |
| const GfxFont *font = state->getFont(); |
| state->getFontTransMat(&m11, &m12, &m21, &m22); |
| //~ this should check for external fonts that are non-TrueType |
| simpleTTF = fabs(m11 + m22) < 0.01 && |
| m11 > 0 && |
| fabs(m12) < 0.01 && |
| fabs(m21) < 0.01 && |
| fabs(state->getHorizScaling() - 1) < 0.001 && |
| (font->getType() == fontTrueType || |
| font->getType() == fontTrueTypeOT); |
| if (simpleTTF) { |
| //~ need to create a FoFiTrueType object, and check for a Unicode cmap |
| } |
| if (state->getRender() != 0 || !simpleTTF) { |
| gdi = false; |
| } |
| } |
| |
| void PreScanOutputDev::endStringOp(GfxState * /*state*/) { |
| } |
| |
| bool PreScanOutputDev::beginType3Char(GfxState * /*state*/, double /*x*/, double /*y*/, |
| double /*dx*/, double /*dy*/, |
| CharCode /*code*/, Unicode * /*u*/, int /*uLen*/) { |
| // return false so all Type 3 chars get rendered (no caching) |
| return false; |
| } |
| |
| void PreScanOutputDev::endType3Char(GfxState * /*state*/) { |
| } |
| |
| void PreScanOutputDev::drawImageMask(GfxState *state, Object * /*ref*/, Stream *str, |
| int width, int height, bool /*invert*/, |
| bool /*interpolate*/, bool inlineImg) { |
| int i, j; |
| |
| check(state->getFillColorSpace(), state->getFillColor(), |
| state->getFillOpacity(), state->getBlendMode()); |
| gdi = false; |
| if ((level == psLevel1 || level == psLevel1Sep) && |
| (state->getFillColorSpace()->getMode() == csPattern || inTilingPatternFill > 0)) { |
| patternImgMask = true; |
| } |
| |
| if (inlineImg) { |
| str->reset(); |
| j = height * ((width + 7) / 8); |
| for (i = 0; i < j; ++i) |
| str->getChar(); |
| str->close(); |
| } |
| } |
| |
| void PreScanOutputDev::drawImage(GfxState *state, Object * /*ref*/, Stream *str, |
| int width, int height, |
| GfxImageColorMap *colorMap, |
| bool /*interpolate*/, int * /*maskColors*/, bool inlineImg) { |
| GfxColorSpace *colorSpace; |
| int i, j; |
| |
| colorSpace = colorMap->getColorSpace(); |
| if (colorSpace->getMode() == csIndexed) { |
| colorSpace = ((GfxIndexedColorSpace *)colorSpace)->getBase(); |
| } |
| if (colorSpace->getMode() == csDeviceGray || |
| colorSpace->getMode() == csCalGray) { |
| if (colorMap->getBits() > 1) { |
| mono = false; |
| } |
| } else { |
| gray = false; |
| mono = false; |
| } |
| if (state->getFillOpacity() != 1 || |
| state->getBlendMode() != gfxBlendNormal) { |
| transparency = true; |
| } |
| gdi = false; |
| if ((level == psLevel1 || level == psLevel1Sep) && |
| inTilingPatternFill > 0) { |
| patternImgMask = true; |
| } |
| |
| if (inlineImg) { |
| str->reset(); |
| j = height * ((width * colorMap->getNumPixelComps() * |
| colorMap->getBits() + 7) / 8); |
| for (i = 0; i < j; ++i) |
| str->getChar(); |
| str->close(); |
| } |
| } |
| |
| void PreScanOutputDev::drawMaskedImage(GfxState *state, Object * /*ref*/, |
| Stream * /*str*/, |
| int /*width*/, int /*height*/, |
| GfxImageColorMap *colorMap, |
| bool /*interpolate*/, |
| Stream * /*maskStr*/, |
| int /*maskWidth*/, int /*maskHeight*/, |
| bool /*maskInvert*/, bool /*maskInterpolate*/) { |
| GfxColorSpace *colorSpace; |
| |
| colorSpace = colorMap->getColorSpace(); |
| if (colorSpace->getMode() == csIndexed) { |
| colorSpace = ((GfxIndexedColorSpace *)colorSpace)->getBase(); |
| } |
| if (colorSpace->getMode() == csDeviceGray || |
| colorSpace->getMode() == csCalGray) { |
| if (colorMap->getBits() > 1) { |
| mono = false; |
| } |
| } else { |
| gray = false; |
| mono = false; |
| } |
| if (state->getFillOpacity() != 1 || |
| state->getBlendMode() != gfxBlendNormal) { |
| transparency = true; |
| } |
| gdi = false; |
| } |
| |
| void PreScanOutputDev::drawSoftMaskedImage(GfxState * /*state*/, Object * /*ref*/, |
| Stream * /*str*/, |
| int /*width*/, int /*height*/, |
| GfxImageColorMap *colorMap, |
| bool /*interpolate*/, |
| Stream * /*maskStr*/, |
| int /*maskWidth*/, int /*maskHeight*/, |
| GfxImageColorMap * /*maskColorMap*/, |
| bool /*maskInterpolate*/) { |
| GfxColorSpace *colorSpace; |
| |
| colorSpace = colorMap->getColorSpace(); |
| if (colorSpace->getMode() == csIndexed) { |
| colorSpace = ((GfxIndexedColorSpace *)colorSpace)->getBase(); |
| } |
| if (colorSpace->getMode() != csDeviceGray && |
| colorSpace->getMode() != csCalGray) { |
| gray = false; |
| } |
| mono = false; |
| transparency = true; |
| gdi = false; |
| } |
| |
| void PreScanOutputDev::beginTransparencyGroup( |
| GfxState * /*state*/, const double * /*bbox*/, |
| GfxColorSpace * /*blendingColorSpace*/, |
| bool /*isolated*/, bool /*knockout*/, |
| bool /*forSoftMask*/) { |
| gdi = false; |
| } |
| |
| void PreScanOutputDev::paintTransparencyGroup(GfxState *state, const double * /*bbox*/) |
| { |
| check(state->getFillColorSpace(), state->getFillColor(), |
| state->getFillOpacity(), state->getBlendMode()); |
| } |
| |
| void PreScanOutputDev::setSoftMask(GfxState * /*state*/, const double * /*bbox*/, bool /*alpha*/, |
| Function * /*transferFunc*/, GfxColor * /*backdropColor*/) |
| { |
| transparency = true; |
| } |
| |
| void PreScanOutputDev::check(GfxColorSpace *colorSpace, const GfxColor *color, |
| double opacity, GfxBlendMode blendMode) { |
| GfxRGB rgb; |
| |
| if (colorSpace->getMode() == csPattern) { |
| mono = false; |
| gray = false; |
| gdi = false; |
| } else { |
| colorSpace->getRGB(color, &rgb); |
| if (rgb.r != rgb.g || rgb.g != rgb.b || rgb.b != rgb.r) { |
| mono = false; |
| gray = false; |
| } else if (!((rgb.r == 0 && rgb.g == 0 && rgb.b == 0) || |
| (rgb.r == gfxColorComp1 && |
| rgb.g == gfxColorComp1 && |
| rgb.b == gfxColorComp1))) { |
| mono = false; |
| } |
| } |
| if (opacity != 1 || blendMode != gfxBlendNormal) { |
| transparency = true; |
| } |
| } |
| |
| void PreScanOutputDev::clearStats() { |
| mono = true; |
| gray = true; |
| transparency = false; |
| gdi = true; |
| patternImgMask = false; |
| inTilingPatternFill = 0; |
| } |