From 835e373b3eeaabcd0621ed6798ab500f37982fae Mon Sep 17 00:00:00 2001 From: Calvin Morrison Date: Wed, 5 Apr 2023 14:13:39 -0400 Subject: xpdf-no-select-disable --- xpdf/SplashOutputDev.cc | 4550 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 4550 insertions(+) create mode 100644 xpdf/SplashOutputDev.cc (limited to 'xpdf/SplashOutputDev.cc') diff --git a/xpdf/SplashOutputDev.cc b/xpdf/SplashOutputDev.cc new file mode 100644 index 0000000..d758e25 --- /dev/null +++ b/xpdf/SplashOutputDev.cc @@ -0,0 +1,4550 @@ +//======================================================================== +// +// SplashOutputDev.cc +// +// Copyright 2003-2013 Glyph & Cog, LLC +// +//======================================================================== + +#include + +#ifdef USE_GCC_PRAGMAS +#pragma implementation +#endif + +#include +#include +#include +#include "gmempp.h" +#include "gfile.h" +#include "Trace.h" +#include "GlobalParams.h" +#include "Error.h" +#include "Object.h" +#include "Gfx.h" +#include "GfxFont.h" +#include "ShadingImage.h" +#include "Link.h" +#include "CharCodeToUnicode.h" +#include "FontEncodingTables.h" +#include "BuiltinFont.h" +#include "BuiltinFontTables.h" +#include "FoFiTrueType.h" +#include "FoFiType1C.h" +#include "JPXStream.h" +#include "SplashBitmap.h" +#include "SplashGlyphBitmap.h" +#include "SplashPattern.h" +#include "SplashScreen.h" +#include "SplashPath.h" +#include "SplashState.h" +#include "SplashErrorCodes.h" +#include "SplashFontEngine.h" +#include "SplashFont.h" +#include "SplashFontFile.h" +#include "SplashFontFileID.h" +#include "Splash.h" +#include "SplashOutputDev.h" + +#ifdef VMS +#if (__VMS_VER < 70000000) +extern "C" int unlink(char *filename); +#endif +#endif + +//------------------------------------------------------------------------ + +// max tile size (used in tilingPatternFill()) +// - Adobe uses a resolution-independent threshold here, of 6M sq pts +// - xpdf uses a resolution-dependent max, but with different values +// on 32-bit and 64-bit systems +#if SplashBitmapRowSizeMax == INT_MAX +# define maxTileSize 200000000 +#else +# define maxTileSize 2000000000 +#endif + +//------------------------------------------------------------------------ + +// Type 3 font cache size parameters +#define type3FontCacheAssoc 8 +#define type3FontCacheMaxSets 8 +#define type3FontCacheSize (128*1024) + +// Map StrokeAdjustMode (from GlobalParams) to SplashStrokeAdjustMode +// (for Splash). +static SplashStrokeAdjustMode mapStrokeAdjustMode[3] = { + splashStrokeAdjustOff, + splashStrokeAdjustNormal, + splashStrokeAdjustCAD +}; + +//------------------------------------------------------------------------ + +// Divide a 16-bit value (in [0, 255*255]) by 255, returning an 8-bit result. +static inline Guchar div255(int x) { + return (Guchar)((x + (x >> 8) + 0x80) >> 8); +} + +// Clip x to lie in [0, 255]. +static inline Guchar clip255(int x) { + return x < 0 ? 0 : x > 255 ? 255 : (Guchar)x; +} + + +//------------------------------------------------------------------------ +// Blend functions +//------------------------------------------------------------------------ + +static void splashOutBlendMultiply(SplashColorPtr src, SplashColorPtr dest, + SplashColorPtr blend, SplashColorMode cm) { + int i; + + for (i = 0; i < splashColorModeNComps[cm]; ++i) { + blend[i] = (Guchar)((dest[i] * src[i]) / 255); + } +} + +static void splashOutBlendScreen(SplashColorPtr src, SplashColorPtr dest, + SplashColorPtr blend, SplashColorMode cm) { + int i; + + for (i = 0; i < splashColorModeNComps[cm]; ++i) { + blend[i] = (Guchar)(dest[i] + src[i] - (dest[i] * src[i]) / 255); + } +} + +// note: this is the same as HardLight, with src/dest reversed +static void splashOutBlendOverlay(SplashColorPtr src, SplashColorPtr dest, + SplashColorPtr blend, SplashColorMode cm) { + int i; + + for (i = 0; i < splashColorModeNComps[cm]; ++i) { + // the spec says "if Cs <= 0.5" -- note that 0x80 is 128/255=0.5020 + blend[i] = dest[i] < 0x80 + ? (Guchar)((src[i] * 2 * dest[i]) / 255) + : (Guchar)(255 - 2 * ((255 - src[i]) * (255 - dest[i])) / 255); + } +} + +static void splashOutBlendDarken(SplashColorPtr src, SplashColorPtr dest, + SplashColorPtr blend, SplashColorMode cm) { + int i; + + for (i = 0; i < splashColorModeNComps[cm]; ++i) { + blend[i] = dest[i] < src[i] ? dest[i] : src[i]; + } +} + +static void splashOutBlendLighten(SplashColorPtr src, SplashColorPtr dest, + SplashColorPtr blend, SplashColorMode cm) { + int i; + + for (i = 0; i < splashColorModeNComps[cm]; ++i) { + blend[i] = dest[i] > src[i] ? dest[i] : src[i]; + } +} + +static void splashOutBlendColorDodge(SplashColorPtr src, SplashColorPtr dest, + SplashColorPtr blend, + SplashColorMode cm) { + int i; + + for (i = 0; i < splashColorModeNComps[cm]; ++i) { + if (dest[i] == 0) { + blend[i] = 0; + } else if (dest[i] >= 255 - src[i]) { + blend[i] = 255; + } else { + blend[i] = (Guchar)((dest[i] * 255) / (255 - src[i])); + } + } +} + +static void splashOutBlendColorBurn(SplashColorPtr src, SplashColorPtr dest, + SplashColorPtr blend, SplashColorMode cm) { + int i; + + for (i = 0; i < splashColorModeNComps[cm]; ++i) { + if (dest[i] == 255) { + blend[i] = 255; + } else if (255 - dest[i] >= src[i]) { + blend[i] = 0; + } else { + blend[i] = (Guchar)(255 - (((255 - dest[i]) * 255) / src[i])); + } + } +} + +static void splashOutBlendHardLight(SplashColorPtr src, SplashColorPtr dest, + SplashColorPtr blend, SplashColorMode cm) { + int i; + + for (i = 0; i < splashColorModeNComps[cm]; ++i) { + // the spec says "if Cs <= 0.5" -- note that 0x80 is 128/255=0.5020 + blend[i] = src[i] < 0x80 + ? (Guchar)((dest[i] * 2 * src[i]) / 255) + : (Guchar)(255 - 2 * ((255 - dest[i]) * (255 - src[i])) / 255); + } +} + +static void splashOutBlendSoftLight(SplashColorPtr src, SplashColorPtr dest, + SplashColorPtr blend, SplashColorMode cm) { + int i, x; + + for (i = 0; i < splashColorModeNComps[cm]; ++i) { + // the spec says "if Cs <= 0.5" -- note that 0x80 is 128/255=0.5020 + if (src[i] < 0x80) { + blend[i] = (Guchar)(dest[i] - + (255 - 2 * src[i]) * dest[i] * (255 - dest[i]) / + (255 * 255)); + } else { + // the spec says "if Cb <= 0.25" -- note that 0x40 is 64/255=0.2510 + if (dest[i] < 0x40) { + x = (((((16 * dest[i] - 12 * 255) * dest[i]) / 255) + + 4 * 255) * dest[i]) / 255; + } else { + x = (int)sqrt(255.0 * dest[i]); + } + blend[i] = (Guchar)(dest[i] + (2 * src[i] - 255) * (x - dest[i]) / 255); + } + } +} + +static void splashOutBlendDifference(SplashColorPtr src, SplashColorPtr dest, + SplashColorPtr blend, + SplashColorMode cm) { + int i; + + for (i = 0; i < splashColorModeNComps[cm]; ++i) { + blend[i] = dest[i] < src[i] ? (Guchar)(src[i] - dest[i]) + : (Guchar)(dest[i] - src[i]); + } +} + +static void splashOutBlendExclusion(SplashColorPtr src, SplashColorPtr dest, + SplashColorPtr blend, SplashColorMode cm) { + int i; + + for (i = 0; i < splashColorModeNComps[cm]; ++i) { + blend[i] = (Guchar)(dest[i] + src[i] - (2 * dest[i] * src[i]) / 255); + } +} + +static int getLum(int r, int g, int b) { + return (int)(0.3 * r + 0.59 * g + 0.11 * b); +} + +static int getSat(int r, int g, int b) { + int rgbMin, rgbMax; + + rgbMin = rgbMax = r; + if (g < rgbMin) { + rgbMin = g; + } else if (g > rgbMax) { + rgbMax = g; + } + if (b < rgbMin) { + rgbMin = b; + } else if (b > rgbMax) { + rgbMax = b; + } + return rgbMax - rgbMin; +} + +static void clipColor(int rIn, int gIn, int bIn, + Guchar *rOut, Guchar *gOut, Guchar *bOut) { + int lum, rgbMin, rgbMax, r, g, b; + + lum = getLum(rIn, gIn, bIn); + rgbMin = rgbMax = rIn; + if (gIn < rgbMin) { + rgbMin = gIn; + } else if (gIn > rgbMax) { + rgbMax = gIn; + } + if (bIn < rgbMin) { + rgbMin = bIn; + } else if (bIn > rgbMax) { + rgbMax = bIn; + } + r = rIn; + g = gIn; + b = bIn; + if (rgbMin < 0) { + r = lum + ((r - lum) * lum) / (lum - rgbMin); + g = lum + ((g - lum) * lum) / (lum - rgbMin); + b = lum + ((b - lum) * lum) / (lum - rgbMin); + } + if (rgbMax > 255) { + r = lum + ((r - lum) * (255 - lum)) / (rgbMax - lum); + g = lum + ((g - lum) * (255 - lum)) / (rgbMax - lum); + b = lum + ((b - lum) * (255 - lum)) / (rgbMax - lum); + } + *rOut = (Guchar)r; + *gOut = (Guchar)g; + *bOut = (Guchar)b; +} + +static void setLum(Guchar rIn, Guchar gIn, Guchar bIn, int lum, + Guchar *rOut, Guchar *gOut, Guchar *bOut) { + int d; + + d = lum - getLum(rIn, gIn, bIn); + clipColor(rIn + d, gIn + d, bIn + d, rOut, gOut, bOut); +} + +static void setSat(Guchar rIn, Guchar gIn, Guchar bIn, int sat, + Guchar *rOut, Guchar *gOut, Guchar *bOut) { + int rgbMin, rgbMid, rgbMax; + Guchar *minOut, *midOut, *maxOut; + + if (rIn < gIn) { + rgbMin = rIn; minOut = rOut; + rgbMid = gIn; midOut = gOut; + } else { + rgbMin = gIn; minOut = gOut; + rgbMid = rIn; midOut = rOut; + } + if (bIn > rgbMid) { + rgbMax = bIn; maxOut = bOut; + } else if (bIn > rgbMin) { + rgbMax = rgbMid; maxOut = midOut; + rgbMid = bIn; midOut = bOut; + } else { + rgbMax = rgbMid; maxOut = midOut; + rgbMid = rgbMin; midOut = minOut; + rgbMin = bIn; minOut = bOut; + } + if (rgbMax > rgbMin) { + *midOut = (Guchar)(((rgbMid - rgbMin) * sat) / (rgbMax - rgbMin)); + *maxOut = (Guchar)sat; + } else { + *midOut = *maxOut = 0; + } + *minOut = 0; +} + +static void splashOutBlendHue(SplashColorPtr src, SplashColorPtr dest, + SplashColorPtr blend, SplashColorMode cm) { + Guchar r0, g0, b0; + + switch (cm) { + case splashModeMono1: + case splashModeMono8: + blend[0] = dest[0]; + break; + case splashModeRGB8: + case splashModeBGR8: + setSat(src[0], src[1], src[2], getSat(dest[0], dest[1], dest[2]), + &r0, &g0, &b0); + setLum(r0, g0, b0, getLum(dest[0], dest[1], dest[2]), + &blend[0], &blend[1], &blend[2]); + break; +#if SPLASH_CMYK + case splashModeCMYK8: + // NB: inputs have already been converted to additive mode + setSat(src[0], src[1], src[2], getSat(dest[0], dest[1], dest[2]), + &r0, &g0, &b0); + setLum(r0, g0, b0, getLum(dest[0], dest[1], dest[2]), + &blend[0], &blend[1], &blend[2]); + blend[3] = dest[3]; + break; +#endif + } +} + +static void splashOutBlendSaturation(SplashColorPtr src, SplashColorPtr dest, + SplashColorPtr blend, + SplashColorMode cm) { + Guchar r0, g0, b0; + + switch (cm) { + case splashModeMono1: + case splashModeMono8: + blend[0] = dest[0]; + break; + case splashModeRGB8: + case splashModeBGR8: + setSat(dest[0], dest[1], dest[2], getSat(src[0], src[1], src[2]), + &r0, &g0, &b0); + setLum(r0, g0, b0, getLum(dest[0], dest[1], dest[2]), + &blend[0], &blend[1], &blend[2]); + break; +#if SPLASH_CMYK + case splashModeCMYK8: + // NB: inputs have already been converted to additive mode + setSat(dest[0], dest[1], dest[2], getSat(src[0], src[1], src[2]), + &r0, &g0, &b0); + setLum(r0, g0, b0, getLum(dest[0], dest[1], dest[2]), + &blend[0], &blend[1], &blend[2]); + blend[3] = dest[3]; + break; +#endif + } +} + +static void splashOutBlendColor(SplashColorPtr src, SplashColorPtr dest, + SplashColorPtr blend, SplashColorMode cm) { + + switch (cm) { + case splashModeMono1: + case splashModeMono8: + blend[0] = dest[0]; + break; + case splashModeRGB8: + case splashModeBGR8: + setLum(src[0], src[1], src[2], getLum(dest[0], dest[1], dest[2]), + &blend[0], &blend[1], &blend[2]); + break; +#if SPLASH_CMYK + case splashModeCMYK8: + // NB: inputs have already been converted to additive mode + setLum(src[0], src[1], src[2], getLum(dest[0], dest[1], dest[2]), + &blend[0], &blend[1], &blend[2]); + blend[3] = dest[3]; + break; +#endif + } +} + +static void splashOutBlendLuminosity(SplashColorPtr src, SplashColorPtr dest, + SplashColorPtr blend, + SplashColorMode cm) { + + switch (cm) { + case splashModeMono1: + case splashModeMono8: + blend[0] = dest[0]; + break; + case splashModeRGB8: + case splashModeBGR8: + setLum(dest[0], dest[1], dest[2], getLum(src[0], src[1], src[2]), + &blend[0], &blend[1], &blend[2]); + break; +#if SPLASH_CMYK + case splashModeCMYK8: + // NB: inputs have already been converted to additive mode + setLum(dest[0], dest[1], dest[2], getLum(src[0], src[1], src[2]), + &blend[0], &blend[1], &blend[2]); + blend[3] = src[3]; + break; +#endif + } +} + +// NB: This must match the GfxBlendMode enum defined in GfxState.h. +SplashBlendFunc splashOutBlendFuncs[] = { + NULL, + &splashOutBlendMultiply, + &splashOutBlendScreen, + &splashOutBlendOverlay, + &splashOutBlendDarken, + &splashOutBlendLighten, + &splashOutBlendColorDodge, + &splashOutBlendColorBurn, + &splashOutBlendHardLight, + &splashOutBlendSoftLight, + &splashOutBlendDifference, + &splashOutBlendExclusion, + &splashOutBlendHue, + &splashOutBlendSaturation, + &splashOutBlendColor, + &splashOutBlendLuminosity +}; + + +//------------------------------------------------------------------------ +// SplashOutFontFileID +//------------------------------------------------------------------------ + +class SplashOutFontFileID: public SplashFontFileID { +public: + + SplashOutFontFileID(Ref *rA) { + r = *rA; + substIdx = -1; + oblique = 0; + } + + ~SplashOutFontFileID() {} + + GBool matches(SplashFontFileID *id) { + return ((SplashOutFontFileID *)id)->r.num == r.num && + ((SplashOutFontFileID *)id)->r.gen == r.gen; + } + + void setOblique(double obliqueA) { oblique = obliqueA; } + double getOblique() { return oblique; } + void setSubstIdx(int substIdxA) { substIdx = substIdxA; } + int getSubstIdx() { return substIdx; } + +private: + + Ref r; + double oblique; + int substIdx; +}; + +//------------------------------------------------------------------------ +// T3FontCache +//------------------------------------------------------------------------ + +struct T3FontCacheTag { + Gushort code; + Gushort mru; // valid bit (0x8000) and MRU index +}; + +class T3FontCache { +public: + + T3FontCache(Ref *fontID, double m11A, double m12A, + double m21A, double m22A, + int glyphXA, int glyphYA, int glyphWA, int glyphHA, + GBool validBBoxA, GBool aa); + ~T3FontCache(); + GBool matches(Ref *idA, double m11A, double m12A, + double m21A, double m22A) + { return fontID.num == idA->num && fontID.gen == idA->gen && + m11 == m11A && m12 == m12A && m21 == m21A && m22 == m22A; } + + Ref fontID; // PDF font ID + double m11, m12, m21, m22; // transform matrix + int glyphX, glyphY; // pixel offset of glyph bitmaps + int glyphW, glyphH; // size of glyph bitmaps, in pixels + GBool validBBox; // false if the bbox was [0 0 0 0] + int glyphSize; // size of glyph bitmaps, in bytes + int cacheSets; // number of sets in cache + int cacheAssoc; // cache associativity (glyphs per set) + Guchar *cacheData; // glyph pixmap cache + T3FontCacheTag *cacheTags; // cache tags, i.e., char codes + int refCount; // active reference count for this T3 font +}; + +T3FontCache::T3FontCache(Ref *fontIDA, double m11A, double m12A, + double m21A, double m22A, + int glyphXA, int glyphYA, int glyphWA, int glyphHA, + GBool validBBoxA, GBool aa) { + int i; + + fontID = *fontIDA; + m11 = m11A; + m12 = m12A; + m21 = m21A; + m22 = m22A; + glyphX = glyphXA; + glyphY = glyphYA; + glyphW = glyphWA; + glyphH = glyphHA; + validBBox = validBBoxA; + // sanity check for excessively large glyphs (which most likely + // indicate an incorrect BBox) + i = glyphW * glyphH; + if (i > 100000 || glyphW > INT_MAX / glyphH || glyphW <= 0 || glyphH <= 0) { + glyphW = glyphH = 100; + validBBox = gFalse; + } + if (aa) { + glyphSize = glyphW * glyphH; + } else { + glyphSize = ((glyphW + 7) >> 3) * glyphH; + } + cacheAssoc = type3FontCacheAssoc; + for (cacheSets = type3FontCacheMaxSets; + cacheSets > 1 && + cacheSets * cacheAssoc * glyphSize > type3FontCacheSize; + cacheSets >>= 1) ; + cacheData = (Guchar *)gmallocn(cacheSets * cacheAssoc, glyphSize); + cacheTags = (T3FontCacheTag *)gmallocn(cacheSets * cacheAssoc, + sizeof(T3FontCacheTag)); + for (i = 0; i < cacheSets * cacheAssoc; ++i) { + cacheTags[i].mru = (Gushort)(i & (cacheAssoc - 1)); + } + refCount = 0; +} + +T3FontCache::~T3FontCache() { + gfree(cacheData); + gfree(cacheTags); +} + +struct T3GlyphStack { + Gushort code; // character code + + GBool haveDx; // set after seeing a d0/d1 operator + GBool doNotCache; // set if we see a gsave/grestore before + // the d0/d1 + + //----- cache info + T3FontCache *cache; // font cache for the current font + T3FontCacheTag *cacheTag; // pointer to cache tag for the glyph + Guchar *cacheData; // pointer to cache data for the glyph + + //----- saved state + SplashBitmap *origBitmap; + Splash *origSplash; + double origCTM4, origCTM5; + SplashStrokeAdjustMode savedStrokeAdjust; + + T3GlyphStack *next; // next object on stack +}; + +//------------------------------------------------------------------------ +// SplashTransparencyGroup +//------------------------------------------------------------------------ + +struct SplashTransparencyGroup { + int tx, ty; // translation coordinates + SplashBitmap *tBitmap; // bitmap for transparency group + GfxColorSpace *blendingColorSpace; + GBool isolated; + + //----- modified region in tBitmap + int modXMin, modYMin, modXMax, modYMax; + + //----- saved state + SplashBitmap *origBitmap; + Splash *origSplash; + SplashBitmap *backdropBitmap; + + SplashTransparencyGroup *next; +}; + +//------------------------------------------------------------------------ +// SplashOutputDev +//------------------------------------------------------------------------ + +SplashOutputDev::SplashOutputDev(SplashColorMode colorModeA, + int bitmapRowPadA, + GBool reverseVideoA, + SplashColorPtr paperColorA, + GBool bitmapTopDownA, + GBool allowAntialiasA) { + colorMode = colorModeA; + bitmapRowPad = bitmapRowPadA; + bitmapTopDown = bitmapTopDownA; + bitmapUpsideDown = gFalse; + noComposite = gFalse; + allowAntialias = allowAntialiasA; + vectorAntialias = allowAntialias && + globalParams->getVectorAntialias() && + colorMode != splashModeMono1; + setupScreenParams(72.0, 72.0); + reverseVideo = reverseVideoA; + splashColorCopy(paperColor, paperColorA); + skipHorizText = gFalse; + skipRotatedText = gFalse; + + xref = NULL; + + bitmap = new SplashBitmap(1, 1, bitmapRowPad, colorMode, + colorMode != splashModeMono1, bitmapTopDown, NULL); + splash = new Splash(bitmap, vectorAntialias, NULL, &screenParams); + splash->setMinLineWidth(globalParams->getMinLineWidth()); + splash->setStrokeAdjust( + mapStrokeAdjustMode[globalParams->getStrokeAdjust()]); + splash->setEnablePathSimplification( + globalParams->getEnablePathSimplification()); + splash->clear(paperColor, 0); + + fontEngine = NULL; + + nT3Fonts = 0; + t3GlyphStack = NULL; + + font = NULL; + needFontUpdate = gFalse; + textClipPath = NULL; + + transpGroupStack = NULL; + + nestCount = 0; + + startPageCbk = NULL; + startPageCbkData = NULL; +} + + +void SplashOutputDev::setupScreenParams(double hDPI, double vDPI) { + screenParams.size = globalParams->getScreenSize(); + screenParams.dotRadius = globalParams->getScreenDotRadius(); + screenParams.gamma = (SplashCoord)globalParams->getScreenGamma(); + screenParams.blackThreshold = + (SplashCoord)globalParams->getScreenBlackThreshold(); + screenParams.whiteThreshold = + (SplashCoord)globalParams->getScreenWhiteThreshold(); + switch (globalParams->getScreenType()) { + case screenDispersed: + screenParams.type = splashScreenDispersed; + if (screenParams.size < 0) { + screenParams.size = 4; + } + break; + case screenClustered: + screenParams.type = splashScreenClustered; + if (screenParams.size < 0) { + screenParams.size = 10; + } + break; + case screenStochasticClustered: + screenParams.type = splashScreenStochasticClustered; + if (screenParams.size < 0) { + screenParams.size = 64; + } + if (screenParams.dotRadius < 0) { + screenParams.dotRadius = 2; + } + break; + case screenUnset: + default: + // use clustered dithering for resolution >= 300 dpi + // (compare to 299.9 to avoid floating point issues) + if (hDPI > 299.9 && vDPI > 299.9) { + screenParams.type = splashScreenStochasticClustered; + if (screenParams.size < 0) { + screenParams.size = 64; + } + if (screenParams.dotRadius < 0) { + screenParams.dotRadius = 2; + } + } else { + screenParams.type = splashScreenDispersed; + if (screenParams.size < 0) { + screenParams.size = 4; + } + } + } +} + +SplashOutputDev::~SplashOutputDev() { + int i; + + for (i = 0; i < nT3Fonts; ++i) { + delete t3FontCache[i]; + } + if (fontEngine) { + delete fontEngine; + } + if (splash) { + delete splash; + } + if (bitmap) { + delete bitmap; + } + if (textClipPath) { + delete textClipPath; + } +} + +void SplashOutputDev::startDoc(XRef *xrefA) { + int i; + + xref = xrefA; + if (fontEngine) { + delete fontEngine; + } + fontEngine = new SplashFontEngine( +#if HAVE_FREETYPE_H + globalParams->getEnableFreeType(), + globalParams->getDisableFreeTypeHinting() + ? splashFTNoHinting : 0, +#endif + allowAntialias && + globalParams->getAntialias() && + colorMode != splashModeMono1); + for (i = 0; i < nT3Fonts; ++i) { + delete t3FontCache[i]; + } + nT3Fonts = 0; +} + +void SplashOutputDev::startPage(int pageNum, GfxState *state) { + int w, h; + double *ctm; + SplashCoord mat[6]; + SplashColor color; + + if (state) { + setupScreenParams(state->getHDPI(), state->getVDPI()); + w = (int)(state->getPageWidth() + 0.5); + if (w <= 0) { + w = 1; + } + h = (int)(state->getPageHeight() + 0.5); + if (h <= 0) { + h = 1; + } + } else { + w = h = 1; + } + if (splash) { + delete splash; + splash = NULL; + } + if (!bitmap || w != bitmap->getWidth() || h != bitmap->getHeight()) { + if (bitmap) { + delete bitmap; + bitmap = NULL; + } + traceMessage("page bitmap"); + bitmap = new SplashBitmap(w, h, bitmapRowPad, colorMode, + colorMode != splashModeMono1, bitmapTopDown, + NULL); + } + splash = new Splash(bitmap, vectorAntialias, NULL, &screenParams); + splash->setMinLineWidth(globalParams->getMinLineWidth()); + splash->setEnablePathSimplification( + globalParams->getEnablePathSimplification()); + if (state) { + ctm = state->getCTM(); + mat[0] = (SplashCoord)ctm[0]; + mat[1] = (SplashCoord)ctm[1]; + mat[2] = (SplashCoord)ctm[2]; + mat[3] = (SplashCoord)ctm[3]; + mat[4] = (SplashCoord)ctm[4]; + mat[5] = (SplashCoord)ctm[5]; + splash->setMatrix(mat); + } + switch (colorMode) { + case splashModeMono1: + case splashModeMono8: + color[0] = 0; + break; + case splashModeRGB8: + case splashModeBGR8: + color[0] = color[1] = color[2] = 0; + break; +#if SPLASH_CMYK + case splashModeCMYK8: + color[0] = color[1] = color[2] = color[3] = 0; + break; +#endif + } + splash->setStrokePattern(new SplashSolidColor(color)); + splash->setFillPattern(new SplashSolidColor(color)); + splash->setLineCap(splashLineCapButt); + splash->setLineJoin(splashLineJoinMiter); + splash->setLineDash(NULL, 0, 0); + splash->setMiterLimit(10); + splash->setFlatness(1); + // the SA parameter supposedly defaults to false, but Acrobat + // apparently hardwires it to true + splash->setStrokeAdjust( + mapStrokeAdjustMode[globalParams->getStrokeAdjust()]); + splash->clear(paperColor, 0); + reverseVideoInvertImages = globalParams->getReverseVideoInvertImages(); + if (startPageCbk) { + (*startPageCbk)(startPageCbkData); + } +} + +void SplashOutputDev::endPage() { + if (colorMode != splashModeMono1 && !noComposite) { + splash->compositeBackground(paperColor); + } +} + +void SplashOutputDev::saveState(GfxState *state) { + splash->saveState(); + if (t3GlyphStack && !t3GlyphStack->haveDx) { + t3GlyphStack->doNotCache = gTrue; + error(errSyntaxWarning, -1, + "Save (q) operator before d0/d1 in Type 3 glyph"); + } +} + +void SplashOutputDev::restoreState(GfxState *state) { + splash->restoreState(); + needFontUpdate = gTrue; + if (t3GlyphStack && !t3GlyphStack->haveDx) { + t3GlyphStack->doNotCache = gTrue; + error(errSyntaxWarning, -1, + "Restore (Q) operator before d0/d1 in Type 3 glyph"); + } +} + +void SplashOutputDev::updateAll(GfxState *state) { + updateLineDash(state); + updateLineJoin(state); + updateLineCap(state); + updateLineWidth(state); + updateFlatness(state); + updateMiterLimit(state); + updateStrokeAdjust(state); + updateFillColor(state); + updateStrokeColor(state); + needFontUpdate = gTrue; +} + +void SplashOutputDev::updateCTM(GfxState *state, double m11, double m12, + double m21, double m22, + double m31, double m32) { + double *ctm; + SplashCoord mat[6]; + + ctm = state->getCTM(); + mat[0] = (SplashCoord)ctm[0]; + mat[1] = (SplashCoord)ctm[1]; + mat[2] = (SplashCoord)ctm[2]; + mat[3] = (SplashCoord)ctm[3]; + mat[4] = (SplashCoord)ctm[4]; + mat[5] = (SplashCoord)ctm[5]; + splash->setMatrix(mat); +} + +void SplashOutputDev::updateLineDash(GfxState *state) { + double *dashPattern; + int dashLength; + double dashStart; + SplashCoord dash[20]; + int i; + + state->getLineDash(&dashPattern, &dashLength, &dashStart); + if (dashLength > 20) { + dashLength = 20; + } + for (i = 0; i < dashLength; ++i) { + dash[i] = (SplashCoord)dashPattern[i]; + if (dash[i] < 0) { + dash[i] = 0; + } + } + splash->setLineDash(dash, dashLength, (SplashCoord)dashStart); +} + +void SplashOutputDev::updateFlatness(GfxState *state) { +#if 0 // Acrobat ignores the flatness setting, and always renders curves + // with a fairly small flatness value + splash->setFlatness(state->getFlatness()); +#endif +} + +void SplashOutputDev::updateLineJoin(GfxState *state) { + splash->setLineJoin(state->getLineJoin()); +} + +void SplashOutputDev::updateLineCap(GfxState *state) { + splash->setLineCap(state->getLineCap()); +} + +void SplashOutputDev::updateMiterLimit(GfxState *state) { + splash->setMiterLimit(state->getMiterLimit()); +} + +void SplashOutputDev::updateLineWidth(GfxState *state) { + splash->setLineWidth(state->getLineWidth()); +} + +void SplashOutputDev::updateStrokeAdjust(GfxState *state) { +#if 0 // the SA parameter supposedly defaults to false, but Acrobat + // apparently hardwires it to true + if (state->getStrokeAdjust()) { + if (globalParams->getStrokeAdjustMode() == strokeAdjustCAD) { + splash->setStrokeAdjust(splashStrokeAdjustCAD); + } else { + splash->setStrokeAdjust(splashStrokeAdjustNormal); + } + } else { + splash->setStrokeAdjust(splashStrokeAdjustOff); + } +#endif +} + + +void SplashOutputDev::updateFillColor(GfxState *state) { + GfxGray gray; + GfxRGB rgb; +#if SPLASH_CMYK + GfxCMYK cmyk; +#endif + + switch (colorMode) { + case splashModeMono1: + case splashModeMono8: + state->getFillGray(&gray); + splash->setFillPattern(getColor(gray)); + break; + case splashModeRGB8: + case splashModeBGR8: + state->getFillRGB(&rgb); + splash->setFillPattern(getColor(&rgb)); + break; +#if SPLASH_CMYK + case splashModeCMYK8: + state->getFillCMYK(&cmyk); + splash->setFillPattern(getColor(&cmyk)); + break; +#endif + } +} + +void SplashOutputDev::updateStrokeColor(GfxState *state) { + GfxGray gray; + GfxRGB rgb; +#if SPLASH_CMYK + GfxCMYK cmyk; +#endif + + switch (colorMode) { + case splashModeMono1: + case splashModeMono8: + state->getStrokeGray(&gray); + splash->setStrokePattern(getColor(gray)); + break; + case splashModeRGB8: + case splashModeBGR8: + state->getStrokeRGB(&rgb); + splash->setStrokePattern(getColor(&rgb)); + break; +#if SPLASH_CMYK + case splashModeCMYK8: + state->getStrokeCMYK(&cmyk); + splash->setStrokePattern(getColor(&cmyk)); + break; +#endif + } +} + + +SplashPattern *SplashOutputDev::getColor(GfxGray gray) { + SplashColor color; + + getColor(gray, color); + return new SplashSolidColor(color); +} + +SplashPattern *SplashOutputDev::getColor(GfxRGB *rgb) { + SplashColor color; + + getColor(rgb, color); + return new SplashSolidColor(color); +} + +#if SPLASH_CMYK +SplashPattern *SplashOutputDev::getColor(GfxCMYK *cmyk) { + SplashColor color; + + getColor(cmyk, color); + return new SplashSolidColor(color); +} +#endif + + +void SplashOutputDev::getColor(GfxGray gray, SplashColorPtr color) { + if (reverseVideo) { + gray = gfxColorComp1 - gray; + } + color[0] = colToByte(gray); +} + +void SplashOutputDev::getColor(GfxRGB *rgb, SplashColorPtr color) { + GfxColorComp r, g, b; + + if (reverseVideo) { + r = gfxColorComp1 - rgb->r; + g = gfxColorComp1 - rgb->g; + b = gfxColorComp1 - rgb->b; + } else { + r = rgb->r; + g = rgb->g; + b = rgb->b; + } + color[0] = colToByte(r); + color[1] = colToByte(g); + color[2] = colToByte(b); +} + +#if SPLASH_CMYK +void SplashOutputDev::getColor(GfxCMYK *cmyk, SplashColorPtr color) { + color[0] = colToByte(cmyk->c); + color[1] = colToByte(cmyk->m); + color[2] = colToByte(cmyk->y); + color[3] = colToByte(cmyk->k); +} +#endif + + + +void SplashOutputDev::setOverprintMask(GfxState *state, + GfxColorSpace *colorSpace, + GBool overprintFlag, + int overprintMode, + GfxColor *singleColor) { +#if SPLASH_CMYK + Guint mask; + GfxCMYK cmyk; + + if (overprintFlag && globalParams->getOverprintPreview()) { + mask = colorSpace->getOverprintMask(); + // The OPM (overprintMode) setting is only relevant when the color + // space is DeviceCMYK or is "implicitly converted to DeviceCMYK". + // Per the PDF spec, this happens with ICCBased color spaces only + // if the profile matches the output device. + if (singleColor && overprintMode && + colorSpace->getMode() == csDeviceCMYK) { + colorSpace->getCMYK(singleColor, &cmyk, state->getRenderingIntent()); + if (cmyk.c == 0) { + mask &= ~1; + } + if (cmyk.m == 0) { + mask &= ~2; + } + if (cmyk.y == 0) { + mask &= ~4; + } + if (cmyk.k == 0) { + mask &= ~8; + } + } + } else { + mask = 0xffffffff; + } + splash->setOverprintMask(mask); +#endif + +} + +void SplashOutputDev::updateBlendMode(GfxState *state) { + splash->setBlendFunc(splashOutBlendFuncs[state->getBlendMode()]); +} + +void SplashOutputDev::updateFillOpacity(GfxState *state) { + splash->setFillAlpha((SplashCoord)state->getFillOpacity()); +} + +void SplashOutputDev::updateStrokeOpacity(GfxState *state) { + splash->setStrokeAlpha((SplashCoord)state->getStrokeOpacity()); +} + +void SplashOutputDev::updateRenderingIntent(GfxState *state) { + updateFillColor(state); + updateStrokeColor(state); +} + +void SplashOutputDev::updateTransfer(GfxState *state) { + Function **transfer; + Guchar red[256], green[256], blue[256], gray[256]; + double x, y; + int i; + + transfer = state->getTransfer(); + if (transfer[0] && + transfer[0]->getInputSize() == 1 && + transfer[0]->getOutputSize() == 1) { + if (transfer[1] && + transfer[1]->getInputSize() == 1 && + transfer[1]->getOutputSize() == 1 && + transfer[2] && + transfer[2]->getInputSize() == 1 && + transfer[2]->getOutputSize() == 1 && + transfer[3] && + transfer[3]->getInputSize() == 1 && + transfer[3]->getOutputSize() == 1) { + for (i = 0; i < 256; ++i) { + x = i / 255.0; + transfer[0]->transform(&x, &y); + red[i] = (Guchar)(y * 255.0 + 0.5); + transfer[1]->transform(&x, &y); + green[i] = (Guchar)(y * 255.0 + 0.5); + transfer[2]->transform(&x, &y); + blue[i] = (Guchar)(y * 255.0 + 0.5); + transfer[3]->transform(&x, &y); + gray[i] = (Guchar)(y * 255.0 + 0.5); + } + } else { + for (i = 0; i < 256; ++i) { + x = i / 255.0; + transfer[0]->transform(&x, &y); + red[i] = green[i] = blue[i] = gray[i] = (Guchar)(y * 255.0 + 0.5); + } + } + } else { + for (i = 0; i < 256; ++i) { + red[i] = green[i] = blue[i] = gray[i] = (Guchar)i; + } + } + splash->setTransfer(red, green, blue, gray); +} + +void SplashOutputDev::updateFont(GfxState *state) { + needFontUpdate = gTrue; +} + +void SplashOutputDev::doUpdateFont(GfxState *state) { + GfxFont *gfxFont; + GfxFontLoc *fontLoc; + GfxFontType fontType; + SplashOutFontFileID *id; + SplashFontFile *fontFile; + int fontNum; + FoFiTrueType *ff; + FoFiType1C *ffT1C; + Ref embRef; + Object refObj, strObj; +#if LOAD_FONTS_FROM_MEM + GString *fontBuf; + FILE *extFontFile; +#else + GString *tmpFileName, *fileName; + FILE *tmpFile; +#endif + char blk[4096]; + int *codeToGID; + CharCodeToUnicode *ctu; + double *textMat; + double m11, m12, m21, m22, fontSize, oblique; + double fsx, fsy, w, fontScaleMin, fontScaleAvg, fontScale; + Gushort ww; + SplashCoord mat[4]; + char *name, *start; + Unicode uBuf[8]; + int substIdx, n, code, cmap, cmapPlatform, cmapEncoding, length, i; + + needFontUpdate = gFalse; + font = NULL; +#if LOAD_FONTS_FROM_MEM + fontBuf = NULL; +#else + tmpFileName = NULL; + fileName = NULL; +#endif + substIdx = -1; + + if (!(gfxFont = state->getFont())) { + goto err1; + } + fontType = gfxFont->getType(); + if (fontType == fontType3) { + goto err1; + } + + // sanity-check the font size: skip anything larger than 10k x 10k, + // to avoid problems allocating a bitmap (note that code in + // SplashFont disables caching at a smaller size than this) + state->textTransformDelta(state->getFontSize(), state->getFontSize(), + &fsx, &fsy); + state->transformDelta(fsx, fsy, &fsx, &fsy); + if (fabs(fsx) > 20000 || fabs(fsy) > 20000) { + goto err1; + } + + // check the font file cache + id = new SplashOutFontFileID(gfxFont->getID()); + if (fontEngine->checkForBadFontFile(id)) { + goto err2; + } + if ((fontFile = fontEngine->getFontFile(id))) { + delete id; + + } else { + + fontNum = 0; + + if (!(fontLoc = gfxFont->locateFont(xref, gFalse))) { + error(errSyntaxError, -1, "Couldn't find a font for '{0:s}'", + gfxFont->getName() ? gfxFont->getName()->getCString() + : "(unnamed)"); + goto err2; + } + + // embedded font + if (fontLoc->locType == gfxFontLocEmbedded) { + gfxFont->getEmbeddedFontID(&embRef); +#if LOAD_FONTS_FROM_MEM + fontBuf = new GString(); + refObj.initRef(embRef.num, embRef.gen); + refObj.fetch(xref, &strObj); + refObj.free(); + if (!strObj.isStream()) { + error(errSyntaxError, -1, "Embedded font object is wrong type"); + strObj.free(); + delete fontLoc; + goto err2; + } + strObj.streamReset(); + while ((n = strObj.streamGetBlock(blk, sizeof(blk))) > 0) { + fontBuf->append(blk, n); + } + strObj.streamClose(); + strObj.free(); +#else + if (!openTempFile(&tmpFileName, &tmpFile, "wb", NULL)) { + error(errIO, -1, "Couldn't create temporary font file"); + delete fontLoc; + goto err2; + } + refObj.initRef(embRef.num, embRef.gen); + refObj.fetch(xref, &strObj); + refObj.free(); + if (!strObj.isStream()) { + error(errSyntaxError, -1, "Embedded font object is wrong type"); + strObj.free(); + fclose(tmpFile); + delete fontLoc; + goto err2; + } + strObj.streamReset(); + while ((n = strObj.streamGetBlock(blk, sizeof(blk))) > 0) { + fwrite(blk, 1, n, tmpFile); + } + strObj.streamClose(); + strObj.free(); + fclose(tmpFile); + fileName = tmpFileName; +#endif + + // external font + } else { // gfxFontLocExternal +#if LOAD_FONTS_FROM_MEM + if (!(extFontFile = fopen(fontLoc->path->getCString(), "rb"))) { + error(errSyntaxError, -1, "Couldn't open external font file '{0:t}'", + fontLoc->path); + delete fontLoc; + goto err2; + } + fontBuf = new GString(); + while ((n = fread(blk, 1, sizeof(blk), extFontFile)) > 0) { + fontBuf->append(blk, n); + } + fclose(extFontFile); +#else + fileName = fontLoc->path; +#endif + fontNum = fontLoc->fontNum; + if (fontLoc->substIdx >= 0) { + id->setSubstIdx(fontLoc->substIdx); + } + if (fontLoc->oblique != 0) { + id->setOblique(fontLoc->oblique); + } + } + + // load the font file + switch (fontLoc->fontType) { + case fontType1: + if (!(fontFile = fontEngine->loadType1Font( + id, +#if LOAD_FONTS_FROM_MEM + fontBuf, +#else + fileName->getCString(), + fileName == tmpFileName, +#endif + (const char **)((Gfx8BitFont *)gfxFont)->getEncoding()))) { + error(errSyntaxError, -1, "Couldn't create a font for '{0:s}'", + gfxFont->getName() ? gfxFont->getName()->getCString() + : "(unnamed)"); + delete fontLoc; + goto err1; + } + break; + case fontType1C: +#if LOAD_FONTS_FROM_MEM + if ((ffT1C = FoFiType1C::make(fontBuf->getCString(), + fontBuf->getLength()))) { +#else + if ((ffT1C = FoFiType1C::load(fileName->getCString()))) { +#endif + codeToGID = ((Gfx8BitFont *)gfxFont)->getCodeToGIDMap(ffT1C); + delete ffT1C; + } else { + codeToGID = NULL; + } + if (!(fontFile = fontEngine->loadType1CFont( + id, +#if LOAD_FONTS_FROM_MEM + fontBuf, +#else + fileName->getCString(), + fileName == tmpFileName, +#endif + codeToGID, + (const char **)((Gfx8BitFont *)gfxFont)->getEncoding()))) { + error(errSyntaxError, -1, "Couldn't create a font for '{0:s}'", + gfxFont->getName() ? gfxFont->getName()->getCString() + : "(unnamed)"); + delete fontLoc; + goto err1; + } + break; + case fontType1COT: + codeToGID = NULL; +#if LOAD_FONTS_FROM_MEM + if ((ff = FoFiTrueType::make(fontBuf->getCString(), fontBuf->getLength(), + fontNum, gTrue))) { +#else + if ((ff = FoFiTrueType::load(fileName->getCString(), + fontNum, gTrue))) { +#endif + if (ff->getCFFBlock(&start, &length) && + (ffT1C = FoFiType1C::make(start, length))) { + codeToGID = ((Gfx8BitFont *)gfxFont)->getCodeToGIDMap(ffT1C); + delete ffT1C; + } + delete ff; + } + if (!(fontFile = fontEngine->loadOpenTypeT1CFont( + id, +#if LOAD_FONTS_FROM_MEM + fontBuf, +#else + fileName->getCString(), + fileName == tmpFileName, +#endif + codeToGID, + (const char **)((Gfx8BitFont *)gfxFont)->getEncoding()))) { + error(errSyntaxError, -1, "Couldn't create a font for '{0:s}'", + gfxFont->getName() ? gfxFont->getName()->getCString() + : "(unnamed)"); + delete fontLoc; + goto err1; + } + break; + case fontTrueType: + case fontTrueTypeOT: +#if LOAD_FONTS_FROM_MEM + if ((ff = FoFiTrueType::make(fontBuf->getCString(), fontBuf->getLength(), + fontNum))) { +#else + if ((ff = FoFiTrueType::load(fileName->getCString(), fontNum))) { +#endif + codeToGID = ((Gfx8BitFont *)gfxFont)->getCodeToGIDMap(ff); + n = 256; + delete ff; + // if we're substituting for a non-TrueType font, we need to mark + // all notdef codes as "do not draw" (rather than drawing TrueType + // notdef glyphs) + if (gfxFont->getType() != fontTrueType && + gfxFont->getType() != fontTrueTypeOT) { + for (i = 0; i < 256; ++i) { + if (codeToGID[i] == 0) { + codeToGID[i] = -1; + } + } + } + } else { + codeToGID = NULL; + n = 0; + } + if (!(fontFile = fontEngine->loadTrueTypeFont( + id, +#if LOAD_FONTS_FROM_MEM + fontBuf, +#else + fileName->getCString(), + fileName == tmpFileName, +#endif + fontNum, codeToGID, n, + gfxFont->getEmbeddedFontName() + ? gfxFont->getEmbeddedFontName()->getCString() + : (char *)NULL))) { + error(errSyntaxError, -1, "Couldn't create a font for '{0:s}'", + gfxFont->getName() ? gfxFont->getName()->getCString() + : "(unnamed)"); + delete fontLoc; + goto err1; + } + break; + case fontCIDType0: + case fontCIDType0C: + if (((GfxCIDFont *)gfxFont)->getCIDToGID()) { + n = ((GfxCIDFont *)gfxFont)->getCIDToGIDLen(); + codeToGID = (int *)gmallocn(n, sizeof(int)); + memcpy(codeToGID, ((GfxCIDFont *)gfxFont)->getCIDToGID(), + n * sizeof(int)); + } else { + codeToGID = NULL; + n = 0; + } + if (!(fontFile = fontEngine->loadCIDFont( + id, +#if LOAD_FONTS_FROM_MEM + fontBuf, +#else + fileName->getCString(), + fileName == tmpFileName, +#endif + codeToGID, n))) { + + error(errSyntaxError, -1, "Couldn't create a font for '{0:s}'", + gfxFont->getName() ? gfxFont->getName()->getCString() + : "(unnamed)"); + delete fontLoc; + goto err1; + } + break; + case fontCIDType0COT: + codeToGID = NULL; + n = 0; + if (fontLoc->locType == gfxFontLocEmbedded) { + if (((GfxCIDFont *)gfxFont)->getCIDToGID()) { + n = ((GfxCIDFont *)gfxFont)->getCIDToGIDLen(); + codeToGID = (int *)gmallocn(n, sizeof(int)); + memcpy(codeToGID, ((GfxCIDFont *)gfxFont)->getCIDToGID(), + n * sizeof(int)); + } + } else if (globalParams->getMapExtTrueTypeFontsViaUnicode()) { + // create a CID-to-GID mapping, via Unicode + if ((ctu = ((GfxCIDFont *)gfxFont)->getToUnicode())) { +#if LOAD_FONTS_FROM_MEM + if ((ff = FoFiTrueType::make(fontBuf->getCString(), + fontBuf->getLength(), fontNum))) { +#else + if ((ff = FoFiTrueType::load(fileName->getCString(), fontNum))) { +#endif + // look for a Unicode cmap + for (cmap = 0; cmap < ff->getNumCmaps(); ++cmap) { + cmapPlatform = ff->getCmapPlatform(cmap); + cmapEncoding = ff->getCmapEncoding(cmap); + if ((cmapPlatform == 3 && cmapEncoding == 1) || + (cmapPlatform == 0 && cmapEncoding <= 4)) { + break; + } + } + if (cmap < ff->getNumCmaps()) { + // map CID -> Unicode -> GID + if (ctu->isIdentity()) { + n = 65536; + } else { + n = ctu->getLength(); + } + codeToGID = (int *)gmallocn(n, sizeof(int)); + for (code = 0; code < n; ++code) { + if (ctu->mapToUnicode(code, uBuf, 8) > 0) { + codeToGID[code] = ff->mapCodeToGID(cmap, uBuf[0]); + } else { + codeToGID[code] = -1; + } + } + } + delete ff; + } + ctu->decRefCnt(); + } else { + error(errSyntaxError, -1, + "Couldn't find a mapping to Unicode for font '{0:s}'", + gfxFont->getName() ? gfxFont->getName()->getCString() + : "(unnamed)"); + } + } + if (!(fontFile = fontEngine->loadOpenTypeCFFFont( + id, +#if LOAD_FONTS_FROM_MEM + fontBuf, +#else + fileName->getCString(), + fileName == tmpFileName, +#endif + codeToGID, n))) { + error(errSyntaxError, -1, "Couldn't create a font for '{0:s}'", + gfxFont->getName() ? gfxFont->getName()->getCString() + : "(unnamed)"); + delete fontLoc; + goto err1; + } + break; + case fontCIDType2: + case fontCIDType2OT: + codeToGID = NULL; + n = 0; + if (fontLoc->locType == gfxFontLocEmbedded) { + if (((GfxCIDFont *)gfxFont)->getCIDToGID()) { + n = ((GfxCIDFont *)gfxFont)->getCIDToGIDLen(); + codeToGID = (int *)gmallocn(n, sizeof(int)); + memcpy(codeToGID, ((GfxCIDFont *)gfxFont)->getCIDToGID(), + n * sizeof(int)); + } + } else if (globalParams->getMapExtTrueTypeFontsViaUnicode() && + !((GfxCIDFont *)gfxFont)->usesIdentityEncoding()) { + // create a CID-to-GID mapping, via Unicode + if ((ctu = ((GfxCIDFont *)gfxFont)->getToUnicode())) { +#if LOAD_FONTS_FROM_MEM + if ((ff = FoFiTrueType::make(fontBuf->getCString(), + fontBuf->getLength(), fontNum))) { +#else + if ((ff = FoFiTrueType::load(fileName->getCString(), fontNum))) { +#endif + // look for a Unicode cmap + for (cmap = 0; cmap < ff->getNumCmaps(); ++cmap) { + cmapPlatform = ff->getCmapPlatform(cmap); + cmapEncoding = ff->getCmapEncoding(cmap); + if ((cmapPlatform == 3 && cmapEncoding == 1) || + (cmapPlatform == 0 && cmapEncoding <= 4)) { + break; + } + } + if (cmap < ff->getNumCmaps()) { + // map CID -> Unicode -> GID + if (ctu->isIdentity()) { + n = 65536; + } else { + n = ctu->getLength(); + } + codeToGID = (int *)gmallocn(n, sizeof(int)); + for (code = 0; code < n; ++code) { + if (ctu->mapToUnicode(code, uBuf, 8) > 0) { + codeToGID[code] = ff->mapCodeToGID(cmap, uBuf[0]); + } else { + codeToGID[code] = -1; + } + } + } + delete ff; + } + ctu->decRefCnt(); + } else { + error(errSyntaxError, -1, + "Couldn't find a mapping to Unicode for font '{0:s}'", + gfxFont->getName() ? gfxFont->getName()->getCString() + : "(unnamed)"); + } + } + if (!(fontFile = fontEngine->loadTrueTypeFont( + id, +#if LOAD_FONTS_FROM_MEM + fontBuf, +#else + fileName->getCString(), + fileName == tmpFileName, +#endif + fontNum, codeToGID, n, + gfxFont->getEmbeddedFontName() + ? gfxFont->getEmbeddedFontName()->getCString() + : (char *)NULL))) { + error(errSyntaxError, -1, "Couldn't create a font for '{0:s}'", + gfxFont->getName() ? gfxFont->getName()->getCString() + : "(unnamed)"); + delete fontLoc; + goto err1; + } + break; + default: + // this shouldn't happen + delete fontLoc; + goto err2; + } + + delete fontLoc; + } + + // get the font matrix + textMat = state->getTextMat(); + fontSize = state->getFontSize(); + oblique = ((SplashOutFontFileID *)fontFile->getID())->getOblique(); + m11 = state->getHorizScaling() * textMat[0]; + m12 = state->getHorizScaling() * textMat[1]; + m21 = oblique * m11 + textMat[2]; + m22 = oblique * m12 + textMat[3]; + m11 *= fontSize; + m12 *= fontSize; + m21 *= fontSize; + m22 *= fontSize; + + // for substituted fonts: adjust the font matrix -- compare the + // widths of letters and digits (A-Z, a-z, 0-9) in the original font + // and the substituted font + substIdx = ((SplashOutFontFileID *)fontFile->getID())->getSubstIdx(); + if (substIdx >= 0 && substIdx < 12) { + fontScaleMin = 1; + fontScaleAvg = 0; + n = 0; + for (code = 0; code < 256; ++code) { + if ((name = ((Gfx8BitFont *)gfxFont)->getCharName(code)) && + name[0] && !name[1] && + ((name[0] >= 'A' && name[0] <= 'Z') || + (name[0] >= 'a' && name[0] <= 'z') || + (name[0] >= '0' && name[0] <= '9'))) { + w = ((Gfx8BitFont *)gfxFont)->getWidth((Guchar)code); + if (builtinFontSubst[substIdx]->widths->getWidth(name, &ww) && + w > 0.01 && ww > 10) { + w /= ww * 0.001; + if (w < fontScaleMin) { + fontScaleMin = w; + } + fontScaleAvg += w; + ++n; + } + } + } + // if real font is narrower than substituted font, reduce the font + // size accordingly -- this currently uses a scale factor halfway + // between the minimum and average computed scale factors, which + // is a bit of a kludge, but seems to produce mostly decent + // results + if (n) { + fontScaleAvg /= n; + if (fontScaleAvg < 1) { + fontScale = 0.5 * (fontScaleMin + fontScaleAvg); + m11 *= fontScale; + m12 *= fontScale; + } + } + } + + // create the scaled font + mat[0] = m11; mat[1] = m12; + mat[2] = m21; mat[3] = m22; + font = fontEngine->getFont(fontFile, mat, splash->getMatrix()); + +#if !LOAD_FONTS_FROM_MEM + if (tmpFileName) { + delete tmpFileName; + } +#endif + return; + + err2: + delete id; + err1: +#if LOAD_FONTS_FROM_MEM + if (fontBuf) { + delete fontBuf; + } +#else + if (tmpFileName) { + unlink(tmpFileName->getCString()); + delete tmpFileName; + } +#endif + return; +} + +void SplashOutputDev::stroke(GfxState *state) { + SplashPath *path; + + if (state->getStrokeColorSpace()->isNonMarking()) { + return; + } + setOverprintMask(state, state->getStrokeColorSpace(), + state->getStrokeOverprint(), state->getOverprintMode(), + state->getStrokeColor()); + path = convertPath(state, state->getPath(), gFalse); + splash->stroke(path); + delete path; +} + +void SplashOutputDev::fill(GfxState *state) { + SplashPath *path; + + if (state->getFillColorSpace()->isNonMarking()) { + return; + } + setOverprintMask(state, state->getFillColorSpace(), + state->getFillOverprint(), state->getOverprintMode(), + state->getFillColor()); + path = convertPath(state, state->getPath(), gTrue); + splash->fill(path, gFalse); + delete path; +} + +void SplashOutputDev::eoFill(GfxState *state) { + SplashPath *path; + + if (state->getFillColorSpace()->isNonMarking()) { + return; + } + setOverprintMask(state, state->getFillColorSpace(), + state->getFillOverprint(), state->getOverprintMode(), + state->getFillColor()); + path = convertPath(state, state->getPath(), gTrue); + splash->fill(path, gTrue); + delete path; +} + +void SplashOutputDev::tilingPatternFill(GfxState *state, Gfx *gfx, + Object *strRef, + int paintType, int tilingType, + Dict *resDict, + double *mat, double *bbox, + int x0, int y0, int x1, int y1, + double xStep, double yStep) { + SplashBitmap *origBitmap, *tileBitmap; + Splash *origSplash; + SplashColor color; + Guint *overprintMaskBitmap; + double *ctm; + double ictm[6], tileMat[6], mat1[6], mat2[6]; + double tileXMin, tileYMin, tileXMax, tileYMax; + double xStepX, xStepY, yStepX, yStepY; + double adjXMin, adjYMin; + double sx, sy; + double clipXMin, clipYMin, clipXMax, clipYMax, clipXC, clipYC; + double tx, ty, idet, txMin, tyMin, txMax, tyMax; + int tileW, tileH, tileSize; + int ixMin, ixMax, iyMin, iyMax, ix, iy, x, y; + int i; + + // Notes: + // - PTM = pattern matrix = transform from pattern space to default + // user space (default for most recent page or form) + // - BTM = transform from default user space to device space + // + // This function is called with: + // - mat = PTM * BTM * iCTM = transform from pattern space to + // current user space + + // transform the four corners of the pattern bbox from pattern space + // to device space and compute the device space bbox + state->transform(bbox[0] * mat[0] + bbox[1] * mat[2] + mat[4], + bbox[0] * mat[1] + bbox[1] * mat[3] + mat[5], + &tx, &ty); + tileXMin = tileXMax = tx; + tileYMin = tileYMax = ty; + state->transform(bbox[2] * mat[0] + bbox[1] * mat[2] + mat[4], + bbox[2] * mat[1] + bbox[1] * mat[3] + mat[5], + &tx, &ty); + if (tx < tileXMin) { + tileXMin = tx; + } else if (tx > tileXMax) { + tileXMax = tx; + } + if (ty < tileYMin) { + tileYMin = ty; + } else if (ty > tileYMax) { + tileYMax = ty; + } + state->transform(bbox[2] * mat[0] + bbox[3] * mat[2] + mat[4], + bbox[2] * mat[1] + bbox[3] * mat[3] + mat[5], + &tx, &ty); + if (tx < tileXMin) { + tileXMin = tx; + } else if (tx > tileXMax) { + tileXMax = tx; + } + if (ty < tileYMin) { + tileYMin = ty; + } else if (ty > tileYMax) { + tileYMax = ty; + } + state->transform(bbox[0] * mat[0] + bbox[3] * mat[2] + mat[4], + bbox[0] * mat[1] + bbox[3] * mat[3] + mat[5], + &tx, &ty); + if (tx < tileXMin) { + tileXMin = tx; + } else if (tx > tileXMax) { + tileXMax = tx; + } + if (ty < tileYMin) { + tileYMin = ty; + } else if (ty > tileYMax) { + tileYMax = ty; + } + if (tileXMin == tileXMax || tileYMin == tileYMax) { + return; + } + tileW = (int)(tileXMax - tileXMin + 0.5); + tileH = (int)(tileYMax - tileYMin + 0.5); + if (tileW < 1) { + tileW = 1; + } + if (tileH < 1) { + tileH = 1; + } + + // check for an excessively large tile size + tileSize = tileW * tileH; + if (tileXMax - tileXMin + 0.5 > (double)INT_MAX || + tileYMax - tileYMin + 0.5 > (double)INT_MAX || + tileW > INT_MAX / tileH || + tileSize > maxTileSize) { + mat1[0] = mat[0]; + mat1[1] = mat[1]; + mat1[2] = mat[2]; + mat1[3] = mat[3]; + for (iy = y0; iy < y1; ++iy) { + for (ix = x0; ix < x1; ++ix) { + tx = ix * xStep; + ty = iy * yStep; + mat1[4] = tx * mat[0] + ty * mat[2] + mat[4]; + mat1[5] = tx * mat[1] + ty * mat[3] + mat[5]; + gfx->drawForm(strRef, resDict, mat1, bbox); + } + } + return; + } + + // transform XStep and YStep to device space + state->transformDelta(xStep * mat[0], xStep * mat[1], &xStepX, &xStepY); + state->transformDelta(yStep * mat[2], yStep * mat[3], &yStepX, &yStepY); + + // get the clipping bbox (in device space) + state->getClipBBox(&clipXMin, &clipYMin, &clipXMax, &clipYMax); + + // compute tiling parameters + idet = xStepX * yStepY - yStepX * xStepY; + if (tilingType == 2 || idet == 0) { + adjXMin = tileXMin; + adjYMin = tileYMin; + sx = 1; + sy = 1; + } else { + // reposition the pattern origin to the center of the clipping bbox + idet = 1 / idet; + clipXC = 0.5 * (clipXMin + clipXMax); + clipYC = 0.5 * (clipYMin + clipYMax); + ix = (int)floor((yStepX * (tileYMin - clipYC) + - (tileXMin - clipXC) * yStepY) * idet + 0.5); + iy = (int)floor((xStepX * (clipYC - tileYMin) + - (clipXC - tileXMin) * xStepY) * idet + 0.5); + adjXMin = (int)floor(tileXMin + ix * xStepX + iy * yStepX + 0.5); + adjYMin = (int)floor(tileYMin + ix * xStepY + iy * yStepY + 0.5); + sx = tileW / (tileXMax - tileXMin); + sy = tileH / (tileYMax - tileYMin); + xStepX = (int)floor(sx * xStepX + 0.5); + xStepY = (int)floor(sy * xStepY + 0.5); + yStepX = (int)floor(sx * yStepX + 0.5); + yStepY = (int)floor(sy * yStepY + 0.5); + } + + // compute tiling range: + // - look at the four corners of the clipping bbox + // - solve for the (ix,iy) tile position at each corner + // - take the min and max values for ix, iy + idet = xStepX * yStepY - xStepY * yStepX; + if (idet == 0) { + return; + } + idet = 1 / idet; + // LL corner + tx = idet * (yStepY * (clipXMin - tileW - 1 - adjXMin) + - yStepX * (clipYMax + 1 - adjYMin)); + ty = idet * (xStepX * (clipYMax + 1 - adjYMin) + - xStepY * (clipXMin - tileW - 1 - adjXMin)); + txMin = txMax = tx; + tyMin = tyMax = ty; + // LR corner + tx = idet * (yStepY * (clipXMax + 1 - adjXMin) + - yStepX * (clipYMax + 1 - adjYMin)); + ty = idet * (xStepX * (clipYMax + 1 - adjYMin) + - xStepY * (clipXMax + 1 - adjXMin)); + if (tx < txMin) { + txMin = tx; + } else if (tx > txMax) { + txMax = tx; + } + if (ty < tyMin) { + tyMin = ty; + } else if (ty > tyMax) { + tyMax = ty; + } + // UL corner + tx = idet * (yStepY * (clipXMin - tileW - 1 - adjXMin) + - yStepX * (clipYMin - tileH - 1 - adjYMin)); + ty = idet * (xStepX * (clipYMin - tileH - 1 - adjYMin) + - xStepY * (clipXMin - tileW - 1 - adjXMin)); + if (tx < txMin) { + txMin = tx; + } else if (tx > txMax) { + txMax = tx; + } + if (ty < tyMin) { + tyMin = ty; + } else if (ty > tyMax) { + tyMax = ty; + } + // UR corner + tx = idet * (yStepY * (clipXMax + 1 - adjXMin) + - yStepX * (clipYMin - tileH - 1 - adjYMin)); + ty = idet * (xStepX * (clipYMin - tileH - 1 - adjYMin) + - xStepY * (clipXMax + 1 - adjXMin)); + if (tx < txMin) { + txMin = tx; + } else if (tx > txMax) { + txMax = tx; + } + if (ty < tyMin) { + tyMin = ty; + } else if (ty > tyMax) { + tyMax = ty; + } + ixMin = (int)ceil(txMin); + ixMax = (int)floor(txMax) + 1; + iyMin = (int)ceil(tyMin); + iyMax = (int)floor(tyMax) + 1; + + // special case: pattern tile is larger than clipping bbox + if (ixMax - ixMin == 1 && iyMax - iyMin == 1) { + // reduce the tile size to just the clipping bbox -- this improves + // performance in cases where just a small portion of one tile is + // needed + tileW = (int)(clipXMax - clipXMin + 0.5); + tileH = (int)(clipYMax - clipYMin + 0.5); + if (tileW < 1) { + tileW = 1; + } + if (tileH < 1) { + tileH = 1; + } + tileXMin += clipXMin - (adjXMin + ixMin * xStepX + iyMin * yStepX); + tileYMin += clipYMin - (adjYMin + ixMin * xStepY + iyMin * yStepY); + ixMin = 0; + iyMin = 0; + ixMax = 1; + iyMax = 1; + adjXMin = clipXMin; + adjYMin = clipYMin; + } + + // compute tile matrix = PTM * BTM * Mtranslate * Mscale * iCTM + // = mat * CTM * Mtranslate * Mscale * iCTM + ctm = state->getCTM(); + idet = 1 / (ctm[0] * ctm[3] - ctm[1] * ctm[2]); + ictm[0] = ctm[3] * idet; + ictm[1] = -ctm[1] * idet; + ictm[2] = -ctm[2] * idet; + ictm[3] = ctm[0] * idet; + ictm[4] = (ctm[2] * ctm[5] - ctm[3] * ctm[4]) * idet; + ictm[5] = (ctm[1] * ctm[4] - ctm[0] * ctm[5]) * idet; + // mat * CTM + mat1[0] = mat[0] * ctm[0] + mat[1] * ctm[2]; + mat1[1] = mat[0] * ctm[1] + mat[1] * ctm[3]; + mat1[2] = mat[2] * ctm[0] + mat[3] * ctm[2]; + mat1[3] = mat[2] * ctm[1] + mat[3] * ctm[3]; + mat1[4] = mat[4] * ctm[0] + mat[5] * ctm[2] + ctm[4]; + mat1[5] = mat[4] * ctm[1] + mat[5] * ctm[3] + ctm[5]; + // mat * CTM * (Mtranslate * Mscale) + mat2[0] = mat1[0] * sx; + mat2[1] = mat1[1] * sy; + mat2[2] = mat1[2] * sx; + mat2[3] = mat1[3] * sy; + mat2[4] = mat1[4] * sx - sx * tileXMin; + mat2[5] = mat1[5] * sy - sy * tileYMin; + // mat * CTM * (Mtranslate * Mscale) * iCTM + tileMat[0] = mat2[0] * ictm[0] + mat2[1] * ictm[2]; + tileMat[1] = mat2[0] * ictm[1] + mat2[1] * ictm[3]; + tileMat[2] = mat2[2] * ictm[0] + mat2[3] * ictm[2]; + tileMat[3] = mat2[2] * ictm[1] + mat2[3] * ictm[3]; + tileMat[4] = mat2[4] * ictm[0] + mat2[5] * ictm[2] + ictm[4]; + tileMat[5] = mat2[4] * ictm[1] + mat2[5] * ictm[3] + ictm[5]; + + // create a temporary bitmap + origBitmap = bitmap; + origSplash = splash; + traceMessage("tiling pattern bitmap"); + bitmap = tileBitmap = new SplashBitmap(tileW, tileH, bitmapRowPad, + colorMode, gTrue, bitmapTopDown, + origBitmap); + splash = new Splash(bitmap, vectorAntialias, + origSplash->getImageCache(), origSplash->getScreen()); + for (i = 0; i < splashMaxColorComps; ++i) { + color[i] = 0; + } + splash->clear(color); +#if SPLASH_CMYK + // if we're doing overprint preview, we need to track the overprint + // mask at each pixel in the tile bitmap + if (globalParams->getOverprintPreview() && + colorMode == splashModeCMYK8) { + overprintMaskBitmap = (Guint *)gmallocn(tileH, tileW * (int)sizeof(Guint)); + memset(overprintMaskBitmap, 0, tileH * tileW * sizeof(Guint)); + splash->setOverprintMaskBitmap(overprintMaskBitmap); + } else { + overprintMaskBitmap = NULL; + } +#else // SPLASH_CMYK + overprintMaskBitmap = NULL; +#endif // SPLASH_CMYK + splash->setMinLineWidth(globalParams->getMinLineWidth()); + splash->setStrokeAdjust( + mapStrokeAdjustMode[globalParams->getStrokeAdjust()]); + splash->setEnablePathSimplification( + globalParams->getEnablePathSimplification()); + ++nestCount; + + // copy the fill color (for uncolored tiling patterns) + // (and stroke color, to handle buggy PDF files) + // -- Acrobat apparently doesn't copy the full state here + splash->setFillPattern(origSplash->getFillPattern()->copy()); + splash->setStrokePattern(origSplash->getStrokePattern()->copy()); + + // reset the clip rectangle + state->resetDevClipRect(0, 0, tileW, tileH); + + // render the tile + gfx->drawForm(strRef, resDict, tileMat, bbox); + + // restore the original bitmap + --nestCount; + delete splash; + bitmap = origBitmap; + splash = origSplash; + splash->setOverprintMask(0xffffffff); + + // draw the tiles + if (tileW == 1 && tileH == 1 && + fabs(xStepX * yStepY - xStepY * yStepX) < 0.9) { + // if the tile is 1x1 pixel, and the stepping completely fills the + // area, just composite the 1x1 image across the clip region + // (this avoids performance problems in cases where the step size + // is very small) (we compare to 0.9 instead of 1.0 to avoid fp + // jitter issues) + ixMin = (int)floor(clipXMin); + ixMax = (int)floor(clipXMax) + 1; + iyMin = (int)floor(clipYMin); + iyMax = (int)floor(clipYMax) + 1; + for (iy = iyMin; iy < iyMax; ++iy) { + for (ix = ixMin; ix < ixMax; ++ix) { + splash->composite(tileBitmap, 0, 0, ix, iy, tileW, tileH, + gFalse, gFalse); + } + } + } else { + for (iy = iyMin; iy < iyMax; ++iy) { + for (ix = ixMin; ix < ixMax; ++ix) { + x = (int)floor(adjXMin + ix * xStepX + iy * yStepX + 0.5); + y = (int)floor(adjYMin + ix * xStepY + iy * yStepY + 0.5); + if (overprintMaskBitmap) { + splash->compositeWithOverprint(tileBitmap, overprintMaskBitmap, + 0, 0, x, y, tileW, tileH, + gFalse, gFalse); + } else { + splash->composite(tileBitmap, 0, 0, x, y, tileW, tileH, + gFalse, gFalse); + } + } + } + } + + gfree(overprintMaskBitmap); + delete tileBitmap; +} + +GBool SplashOutputDev::shadedFill(GfxState *state, GfxShading *shading) { + + // generate the bitmap + SplashColorMode srcMode; + if (colorMode == splashModeMono1) { + srcMode = splashModeMono8; + } else if (colorMode == splashModeBGR8) { + srcMode = splashModeRGB8; + } else { + srcMode = colorMode; + } + int x, y; + SplashBitmap *tBitmap = ShadingImage::generateBitmap(state, shading, srcMode, + reverseVideo, + splash, bitmap, &x, &y); + if (!tBitmap) { + // clip region is empty - nothing to draw + return gTrue; + } + + // check clipping and composite the bitmap + int xMin = x; + int yMin = y; + int xMax = x + tBitmap->getWidth(); + int yMax = y + tBitmap->getHeight(); + SplashClipResult clipRes = splash->limitRectToClipRect(&xMin, &yMin, + &xMax, &yMax); + if (clipRes != splashClipAllOutside) { + setOverprintMask(state, state->getFillColorSpace(), + state->getFillOverprint(), state->getOverprintMode(), + NULL); + splash->composite(tBitmap, xMin - x, yMin - y, xMin, yMin, + xMax - xMin, yMax - yMin, + clipRes == splashClipAllInside, gFalse); + } + + delete tBitmap; + + return gTrue; +} + +void SplashOutputDev::clip(GfxState *state) { + SplashPath *path; + + path = convertPath(state, state->getPath(), gTrue); + splash->clipToPath(path, gFalse); + delete path; +} + +void SplashOutputDev::eoClip(GfxState *state) { + SplashPath *path; + + path = convertPath(state, state->getPath(), gTrue); + splash->clipToPath(path, gTrue); + delete path; +} + +void SplashOutputDev::clipToStrokePath(GfxState *state) { + SplashPath *path, *path2; + + path = convertPath(state, state->getPath(), gFalse); + path2 = splash->makeStrokePath(path, state->getLineWidth(), + state->getLineCap(), state->getLineJoin()); + delete path; + splash->clipToPath(path2, gFalse); + delete path2; +} + +SplashPath *SplashOutputDev::convertPath(GfxState *state, GfxPath *path, + GBool dropEmptySubpaths) { + SplashPath *sPath; + GfxSubpath *subpath; + int n, i, j; + + n = dropEmptySubpaths ? 1 : 0; + sPath = new SplashPath(); + for (i = 0; i < path->getNumSubpaths(); ++i) { + subpath = path->getSubpath(i); + if (subpath->getNumPoints() > n) { + sPath->moveTo((SplashCoord)subpath->getX(0), + (SplashCoord)subpath->getY(0)); + j = 1; + while (j < subpath->getNumPoints()) { + if (subpath->getCurve(j)) { + sPath->curveTo((SplashCoord)subpath->getX(j), + (SplashCoord)subpath->getY(j), + (SplashCoord)subpath->getX(j+1), + (SplashCoord)subpath->getY(j+1), + (SplashCoord)subpath->getX(j+2), + (SplashCoord)subpath->getY(j+2)); + j += 3; + } else { + sPath->lineTo((SplashCoord)subpath->getX(j), + (SplashCoord)subpath->getY(j)); + ++j; + } + } + if (subpath->isClosed()) { + sPath->close(); + } + } + } + return sPath; +} + +void SplashOutputDev::drawChar(GfxState *state, double x, double y, + double dx, double dy, + double originX, double originY, + CharCode code, int nBytes, + Unicode *u, int uLen) { + SplashPath *path; + int render; + GBool doFill, doStroke, doClip; + SplashStrokeAdjustMode strokeAdjust; + double m[4]; + GBool horiz; + + if (skipHorizText || skipRotatedText) { + state->getFontTransMat(&m[0], &m[1], &m[2], &m[3]); + // this matches the 'diagonal' test in TextPage::updateFont() + horiz = m[0] > 0 && fabs(m[1]) < 0.001 && + fabs(m[2]) < 0.001 && m[3] < 0; + if ((skipHorizText && horiz) || (skipRotatedText && !horiz)) { + return; + } + } + + // check for invisible text -- this is used by Acrobat Capture + render = state->getRender(); + if (render == 3) { + return; + } + + if (needFontUpdate) { + doUpdateFont(state); + } + if (!font) { + return; + } + + x -= originX; + y -= originY; + + doFill = !(render & 1) && !state->getFillColorSpace()->isNonMarking(); + doStroke = ((render & 3) == 1 || (render & 3) == 2) && + !state->getStrokeColorSpace()->isNonMarking(); + doClip = render & 4; + + path = NULL; + if (doStroke || doClip) { + if ((path = font->getGlyphPath(code))) { + path->offset((SplashCoord)x, (SplashCoord)y); + } + } + + // don't use stroke adjustment when stroking text -- the results + // tend to be ugly (because characters with horizontal upper or + // lower edges get misaligned relative to the other characters) + strokeAdjust = splashStrokeAdjustOff; // make gcc happy + if (doStroke) { + strokeAdjust = splash->getStrokeAdjust(); + splash->setStrokeAdjust(splashStrokeAdjustOff); + } + + // fill and stroke + if (doFill && doStroke) { + if (path) { + setOverprintMask(state, state->getFillColorSpace(), + state->getFillOverprint(), state->getOverprintMode(), + state->getFillColor()); + splash->fill(path, gFalse); + setOverprintMask(state, state->getStrokeColorSpace(), + state->getStrokeOverprint(), state->getOverprintMode(), + state->getStrokeColor()); + splash->stroke(path); + } + + // fill + } else if (doFill) { + setOverprintMask(state, state->getFillColorSpace(), + state->getFillOverprint(), state->getOverprintMode(), + state->getFillColor()); + splash->fillChar((SplashCoord)x, (SplashCoord)y, code, font); + + // stroke + } else if (doStroke) { + if (path) { + setOverprintMask(state, state->getStrokeColorSpace(), + state->getStrokeOverprint(), state->getOverprintMode(), + state->getStrokeColor()); + splash->stroke(path); + } + } + + // clip + if (doClip) { + if (path) { + if (textClipPath) { + textClipPath->append(path); + } else { + textClipPath = path; + path = NULL; + } + } + } + + if (doStroke) { + splash->setStrokeAdjust(strokeAdjust); + } + + if (path) { + delete path; + } +} + +GBool SplashOutputDev::beginType3Char(GfxState *state, double x, double y, + double dx, double dy, + CharCode code, Unicode *u, int uLen) { + GfxFont *gfxFont; + Ref *fontID; + double *ctm, *bbox; + T3FontCache *t3Font; + T3GlyphStack *t3gs; + GBool validBBox; + double m[4]; + GBool horiz; + double x1, y1, xMin, yMin, xMax, yMax, xt, yt; + int render, i, j; + + if (skipHorizText || skipRotatedText) { + state->getFontTransMat(&m[0], &m[1], &m[2], &m[3]); + horiz = m[0] > 0 && fabs(m[1]) < 0.001 && + fabs(m[2]) < 0.001 && m[3] < 0; + if ((skipHorizText && horiz) || (skipRotatedText && !horiz)) { + return gTrue; + } + } + + // check for invisible text + render = state->getRender(); + if (render == 3 || render == 7) { + return gTrue; + } + + if (!(gfxFont = state->getFont())) { + return gTrue; + } + fontID = gfxFont->getID(); + ctm = state->getCTM(); + state->transform(0, 0, &xt, &yt); + + // is it the first (MRU) font in the cache? + if (!(nT3Fonts > 0 && + t3FontCache[0]->matches(fontID, ctm[0], ctm[1], ctm[2], ctm[3]))) { + + // is the font elsewhere in the cache? + for (i = 1; i < nT3Fonts; ++i) { + if (t3FontCache[i]->matches(fontID, ctm[0], ctm[1], ctm[2], ctm[3])) { + t3Font = t3FontCache[i]; + for (j = i; j > 0; --j) { + t3FontCache[j] = t3FontCache[j - 1]; + } + t3FontCache[0] = t3Font; + break; + } + } + if (i >= nT3Fonts) { + + // create new entry in the font cache + if (nT3Fonts < splashOutT3FontCacheSize) { + for (j = nT3Fonts; j > 0; --j) { + t3FontCache[j] = t3FontCache[j - 1]; + } + } else { + for (j = nT3Fonts - 1; j >= 0; --j) { + if (t3FontCache[j]->refCount == 0) { + break; + } + } + if (j < 0) { + error(errSyntaxError, -1, "Type 3 fonts nested too deeply"); + return gTrue; + } + delete t3FontCache[j]; + --nT3Fonts; + for (; j > 0; --j) { + t3FontCache[j] = t3FontCache[j - 1]; + } + } + ++nT3Fonts; + bbox = gfxFont->getFontBBox(); + if (bbox[0] == 0 && bbox[1] == 0 && bbox[2] == 0 && bbox[3] == 0) { + // unspecified bounding box -- just take a guess + xMin = xt - 5; + xMax = xMin + 30; + yMax = yt + 15; + yMin = yMax - 45; + validBBox = gFalse; + } else { + state->transform(bbox[0], bbox[1], &x1, &y1); + xMin = xMax = x1; + yMin = yMax = y1; + state->transform(bbox[0], bbox[3], &x1, &y1); + if (x1 < xMin) { + xMin = x1; + } else if (x1 > xMax) { + xMax = x1; + } + if (y1 < yMin) { + yMin = y1; + } else if (y1 > yMax) { + yMax = y1; + } + state->transform(bbox[2], bbox[1], &x1, &y1); + if (x1 < xMin) { + xMin = x1; + } else if (x1 > xMax) { + xMax = x1; + } + if (y1 < yMin) { + yMin = y1; + } else if (y1 > yMax) { + yMax = y1; + } + state->transform(bbox[2], bbox[3], &x1, &y1); + if (x1 < xMin) { + xMin = x1; + } else if (x1 > xMax) { + xMax = x1; + } + if (y1 < yMin) { + yMin = y1; + } else if (y1 > yMax) { + yMax = y1; + } + validBBox = gTrue; + } + t3FontCache[0] = new T3FontCache(fontID, ctm[0], ctm[1], ctm[2], ctm[3], + (int)floor(xMin - xt) - 2, + (int)floor(yMin - yt) - 2, + (int)ceil(xMax) - (int)floor(xMin) + 4, + (int)ceil(yMax) - (int)floor(yMin) + 4, + validBBox, + colorMode != splashModeMono1); + } + } + t3Font = t3FontCache[0]; + + // is the glyph in the cache? + i = (code & (t3Font->cacheSets - 1)) * t3Font->cacheAssoc; + for (j = 0; j < t3Font->cacheAssoc; ++j) { + if ((t3Font->cacheTags[i+j].mru & 0x8000) && + t3Font->cacheTags[i+j].code == code) { + drawType3Glyph(state, t3Font, &t3Font->cacheTags[i+j], + t3Font->cacheData + (i+j) * t3Font->glyphSize); + return gTrue; + } + } + + if (t3Font->refCount > 1000) { + error(errSyntaxError, -1, "Type 3 CharProcs nested too deeply"); + return gTrue; + } + ++t3Font->refCount; + + // push a new Type 3 glyph record + t3gs = new T3GlyphStack(); + t3gs->next = t3GlyphStack; + t3GlyphStack = t3gs; + t3GlyphStack->code = (Gushort)code; + t3GlyphStack->cache = t3Font; + t3GlyphStack->cacheTag = NULL; + t3GlyphStack->cacheData = NULL; + t3GlyphStack->haveDx = gFalse; + t3GlyphStack->doNotCache = gFalse; +#if 1 //~t3-sa + t3GlyphStack->savedStrokeAdjust = splash->getStrokeAdjust(); + splash->setStrokeAdjust(splashStrokeAdjustOff); +#endif + + return gFalse; +} + +void SplashOutputDev::endType3Char(GfxState *state) { + T3GlyphStack *t3gs; + double *ctm; + + if (t3GlyphStack->cacheTag) { + --nestCount; + memcpy(t3GlyphStack->cacheData, bitmap->getDataPtr(), + t3GlyphStack->cache->glyphSize); + delete bitmap; + delete splash; + bitmap = t3GlyphStack->origBitmap; + colorMode = bitmap->getMode(); + splash = t3GlyphStack->origSplash; + ctm = state->getCTM(); + state->setCTM(ctm[0], ctm[1], ctm[2], ctm[3], + t3GlyphStack->origCTM4, t3GlyphStack->origCTM5); + updateCTM(state, 0, 0, 0, 0, 0, 0); + drawType3Glyph(state, t3GlyphStack->cache, + t3GlyphStack->cacheTag, t3GlyphStack->cacheData); + } +#if 1 //~t3-sa + splash->setStrokeAdjust(t3GlyphStack->savedStrokeAdjust); +#endif + t3gs = t3GlyphStack; + t3GlyphStack = t3gs->next; + --t3gs->cache->refCount; + delete t3gs; +} + +void SplashOutputDev::type3D0(GfxState *state, double wx, double wy) { + if (!t3GlyphStack) { + error(errSyntaxError, -1, + "Encountered d0 operator outside of Type 3 CharProc"); + return; + } + t3GlyphStack->haveDx = gTrue; +} + +void SplashOutputDev::type3D1(GfxState *state, double wx, double wy, + double llx, double lly, double urx, double ury) { + double *ctm; + T3FontCache *t3Font; + SplashColor color; + double xt, yt, xMin, xMax, yMin, yMax, x1, y1; + int i, j; + + if (!t3GlyphStack) { + error(errSyntaxError, -1, + "Encountered d1 operator outside of Type 3 CharProc"); + return; + } + + // ignore multiple d0/d1 operators + if (t3GlyphStack->haveDx) { + return; + } + t3GlyphStack->haveDx = gTrue; + // don't cache if we got a gsave/grestore before the d1 + if (t3GlyphStack->doNotCache) { + return; + } + + t3Font = t3GlyphStack->cache; + + // check for a valid bbox + state->transform(0, 0, &xt, &yt); + state->transform(llx, lly, &x1, &y1); + xMin = xMax = x1; + yMin = yMax = y1; + state->transform(llx, ury, &x1, &y1); + if (x1 < xMin) { + xMin = x1; + } else if (x1 > xMax) { + xMax = x1; + } + if (y1 < yMin) { + yMin = y1; + } else if (y1 > yMax) { + yMax = y1; + } + state->transform(urx, lly, &x1, &y1); + if (x1 < xMin) { + xMin = x1; + } else if (x1 > xMax) { + xMax = x1; + } + if (y1 < yMin) { + yMin = y1; + } else if (y1 > yMax) { + yMax = y1; + } + state->transform(urx, ury, &x1, &y1); + if (x1 < xMin) { + xMin = x1; + } else if (x1 > xMax) { + xMax = x1; + } + if (y1 < yMin) { + yMin = y1; + } else if (y1 > yMax) { + yMax = y1; + } + if (xMin - xt < t3Font->glyphX || + yMin - yt < t3Font->glyphY || + xMax - xt > t3Font->glyphX + t3Font->glyphW || + yMax - yt > t3Font->glyphY + t3Font->glyphH) { + if (t3Font->validBBox) { + error(errSyntaxWarning, -1, "Bad bounding box in Type 3 glyph"); + } + return; + } + + // allocate a cache entry + i = (t3GlyphStack->code & (t3Font->cacheSets - 1)) * t3Font->cacheAssoc; + for (j = 0; j < t3Font->cacheAssoc; ++j) { + if ((t3Font->cacheTags[i+j].mru & 0x7fff) == t3Font->cacheAssoc - 1) { + t3Font->cacheTags[i+j].mru = 0x8000; + t3Font->cacheTags[i+j].code = t3GlyphStack->code; + t3GlyphStack->cacheTag = &t3Font->cacheTags[i+j]; + t3GlyphStack->cacheData = t3Font->cacheData + (i+j) * t3Font->glyphSize; + } else { + ++t3Font->cacheTags[i+j].mru; + } + } + + // save state + t3GlyphStack->origBitmap = bitmap; + t3GlyphStack->origSplash = splash; + ctm = state->getCTM(); + t3GlyphStack->origCTM4 = ctm[4]; + t3GlyphStack->origCTM5 = ctm[5]; + + // create the temporary bitmap + if (colorMode == splashModeMono1) { + colorMode = splashModeMono1; + traceMessage("T3 glyph bitmap"); + bitmap = new SplashBitmap(t3Font->glyphW, t3Font->glyphH, 1, + splashModeMono1, gFalse, gTrue, bitmap); + splash = new Splash(bitmap, gFalse, + t3GlyphStack->origSplash->getImageCache(), + t3GlyphStack->origSplash->getScreen()); + color[0] = 0; + splash->clear(color); + color[0] = 0xff; + } else { + colorMode = splashModeMono8; + traceMessage("T3 glyph bitmap"); + bitmap = new SplashBitmap(t3Font->glyphW, t3Font->glyphH, 1, + splashModeMono8, gFalse, gTrue, bitmap); + splash = new Splash(bitmap, vectorAntialias, + t3GlyphStack->origSplash->getImageCache(), + t3GlyphStack->origSplash->getScreen()); + color[0] = 0x00; + splash->clear(color); + color[0] = 0xff; + } + splash->setMinLineWidth(globalParams->getMinLineWidth()); + splash->setStrokeAdjust(t3GlyphStack->origSplash->getStrokeAdjust()); + splash->setEnablePathSimplification( + globalParams->getEnablePathSimplification()); + copyState(t3GlyphStack->origSplash, gFalse); + splash->setFillPattern(new SplashSolidColor(color)); + splash->setStrokePattern(new SplashSolidColor(color)); + state->setCTM(ctm[0], ctm[1], ctm[2], ctm[3], + -t3Font->glyphX, -t3Font->glyphY); + updateCTM(state, 0, 0, 0, 0, 0, 0); + ++nestCount; +} + +void SplashOutputDev::drawType3Glyph(GfxState *state, T3FontCache *t3Font, + T3FontCacheTag *tag, Guchar *data) { + SplashGlyphBitmap glyph; + + setOverprintMask(state, state->getFillColorSpace(), + state->getFillOverprint(), state->getOverprintMode(), + state->getFillColor()); + glyph.x = -t3Font->glyphX; + glyph.y = -t3Font->glyphY; + glyph.w = t3Font->glyphW; + glyph.h = t3Font->glyphH; + glyph.aa = colorMode != splashModeMono1; + glyph.data = data; + glyph.freeData = gFalse; + splash->fillGlyph(0, 0, &glyph); +} + +void SplashOutputDev::endTextObject(GfxState *state) { + if (textClipPath) { + splash->clipToPath(textClipPath, gFalse); + delete textClipPath; + textClipPath = NULL; + } +} + +struct SplashOutImageMaskData { + ImageStream *imgStr; + Guchar invert; + int width, height, y; +}; + +GBool SplashOutputDev::imageMaskSrc(void *data, Guchar *line) { + SplashOutImageMaskData *imgMaskData = (SplashOutImageMaskData *)data; + Guchar *p; + SplashColorPtr q; + int x; + + if (imgMaskData->y == imgMaskData->height || + !(p = imgMaskData->imgStr->getLine())) { + memset(line, 0, imgMaskData->width); + return gFalse; + } + for (x = 0, q = line; x < imgMaskData->width; ++x) { + *q++ = *p++ ^ imgMaskData->invert; + } + ++imgMaskData->y; + return gTrue; +} + +void SplashOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str, + int width, int height, GBool invert, + GBool inlineImg, GBool interpolate) { + double *ctm; + SplashCoord mat[6]; + SplashOutImageMaskData imgMaskData; + GString *imgTag; + + if (state->getFillColorSpace()->isNonMarking()) { + return; + } + setOverprintMask(state, state->getFillColorSpace(), + state->getFillOverprint(), state->getOverprintMode(), + state->getFillColor()); + + ctm = state->getCTM(); + mat[0] = ctm[0]; + mat[1] = ctm[1]; + mat[2] = -ctm[2]; + mat[3] = -ctm[3]; + mat[4] = ctm[2] + ctm[4]; + mat[5] = ctm[3] + ctm[5]; + + reduceImageResolution(str, ctm, &width, &height); + + imgMaskData.imgStr = new ImageStream(str, width, 1, 1); + imgMaskData.imgStr->reset(); + imgMaskData.invert = invert ? 0 : 1; + imgMaskData.width = width; + imgMaskData.height = height; + imgMaskData.y = 0; + + imgTag = makeImageTag(ref, gfxRenderingIntentRelativeColorimetric, NULL); + splash->fillImageMask(imgTag, + &imageMaskSrc, &imgMaskData, width, height, mat, + t3GlyphStack != NULL, interpolate, + globalParams->getImageMaskAntialias()); + + if (inlineImg) { + while (imgMaskData.y < height) { + imgMaskData.imgStr->getLine(); + ++imgMaskData.y; + } + } + + delete imgTag; + delete imgMaskData.imgStr; + str->close(); +} + +void SplashOutputDev::setSoftMaskFromImageMask(GfxState *state, + Object *ref, Stream *str, + int width, int height, + GBool invert, + GBool inlineImg, + GBool interpolate) { + double *ctm; + SplashCoord mat[6]; + SplashOutImageMaskData imgMaskData; + SplashBitmap *maskBitmap; + Splash *maskSplash; + SplashColor maskColor; + GString *imgTag; + + ctm = state->getCTM(); + mat[0] = ctm[0]; + mat[1] = ctm[1]; + mat[2] = -ctm[2]; + mat[3] = -ctm[3]; + mat[4] = ctm[2] + ctm[4]; + mat[5] = ctm[3] + ctm[5]; + reduceImageResolution(str, ctm, &width, &height); + imgMaskData.imgStr = new ImageStream(str, width, 1, 1); + imgMaskData.imgStr->reset(); + imgMaskData.invert = invert ? 0 : 1; + imgMaskData.width = width; + imgMaskData.height = height; + imgMaskData.y = 0; + traceMessage("image mask soft mask bitmap"); + maskBitmap = new SplashBitmap(bitmap->getWidth(), bitmap->getHeight(), + 1, splashModeMono8, gFalse, gTrue, bitmap); + maskSplash = new Splash(maskBitmap, gTrue, splash->getImageCache()); + maskSplash->setStrokeAdjust( + mapStrokeAdjustMode[globalParams->getStrokeAdjust()]); + maskSplash->setEnablePathSimplification( + globalParams->getEnablePathSimplification()); + if (splash->getSoftMask()) { + maskSplash->setSoftMask(splash->getSoftMask(), gFalse); + } + clearMaskRegion(state, maskSplash, 0, 0, 1, 1); + maskColor[0] = 0xff; + maskSplash->setFillPattern(new SplashSolidColor(maskColor)); + imgTag = makeImageTag(ref, gfxRenderingIntentRelativeColorimetric, NULL); + maskSplash->fillImageMask(imgTag, &imageMaskSrc, &imgMaskData, + width, height, mat, gFalse, interpolate, + globalParams->getImageMaskAntialias()); + delete imgTag; + delete imgMaskData.imgStr; + str->close(); + delete maskSplash; + splash->setSoftMask(maskBitmap); +} + +struct SplashOutImageData { + ImageStream *imgStr; + GfxImageColorMap *colorMap; + GfxRenderingIntent ri; + SplashColorPtr lookup; + int *maskColors; + SplashColorMode colorMode; + GBool invert; + int width, height, y; +}; + +GBool SplashOutputDev::imageSrc(void *data, SplashColorPtr colorLine, + Guchar *alphaLine) { + SplashOutImageData *imgData = (SplashOutImageData *)data; + Guchar *p; + SplashColorPtr q, col; + int n, x; + + if (imgData->y == imgData->height || + !(p = imgData->imgStr->getLine())) { + memset(colorLine, 0, + imgData->width * splashColorModeNComps[imgData->colorMode]); + return gFalse; + } + + if (imgData->lookup) { + switch (imgData->colorMode) { + case splashModeMono1: + case splashModeMono8: + for (x = 0, q = colorLine; x < imgData->width; ++x, ++p) { + *q++ = imgData->lookup[*p]; + } + break; + case splashModeRGB8: + case splashModeBGR8: + for (x = 0, q = colorLine; x < imgData->width; ++x, ++p) { + col = &imgData->lookup[3 * *p]; + *q++ = col[0]; + *q++ = col[1]; + *q++ = col[2]; + } + break; +#if SPLASH_CMYK + case splashModeCMYK8: + for (x = 0, q = colorLine; x < imgData->width; ++x, ++p) { + col = &imgData->lookup[4 * *p]; + *q++ = col[0]; + *q++ = col[1]; + *q++ = col[2]; + *q++ = col[3]; + } + break; +#endif + } + } else { + switch (imgData->colorMode) { + case splashModeMono1: + case splashModeMono8: + imgData->colorMap->getGrayByteLine(p, colorLine, imgData->width, + imgData->ri); + break; + case splashModeRGB8: + case splashModeBGR8: + imgData->colorMap->getRGBByteLine(p, colorLine, imgData->width, + imgData->ri); + break; +#if SPLASH_CMYK + case splashModeCMYK8: + imgData->colorMap->getCMYKByteLine(p, colorLine, imgData->width, + imgData->ri); + break; +#endif + } + } + + if (imgData->invert) { + n = imgData->width * splashColorModeNComps[imgData->colorMode]; + for (x = 0, p = colorLine; x < n; ++x, ++p) { + *p ^= 0xff; + } + } + + ++imgData->y; + return gTrue; +} + +GBool SplashOutputDev::alphaImageSrc(void *data, SplashColorPtr colorLine, + Guchar *alphaLine) { + SplashOutImageData *imgData = (SplashOutImageData *)data; + Guchar *p0, *p, *aq; + SplashColorPtr q, col; + Guchar alpha; + int nComps, x, n, i; + + if (imgData->y == imgData->height || + !(p0 = imgData->imgStr->getLine())) { + memset(colorLine, 0, + imgData->width * splashColorModeNComps[imgData->colorMode]); + memset(alphaLine, 0, imgData->width); + return gFalse; + } + + nComps = imgData->colorMap->getNumPixelComps(); + + if (imgData->lookup) { + switch (imgData->colorMode) { + case splashModeMono1: + case splashModeMono8: + for (x = 0, p = p0, q = colorLine; x < imgData->width; ++x, ++p) { + *q++ = imgData->lookup[*p]; + } + break; + case splashModeRGB8: + case splashModeBGR8: + for (x = 0, p = p0, q = colorLine; x < imgData->width; ++x, ++p) { + col = &imgData->lookup[3 * *p]; + *q++ = col[0]; + *q++ = col[1]; + *q++ = col[2]; + } + break; +#if SPLASH_CMYK + case splashModeCMYK8: + for (x = 0, p = p0, q = colorLine; x < imgData->width; ++x, ++p) { + col = &imgData->lookup[4 * *p]; + *q++ = col[0]; + *q++ = col[1]; + *q++ = col[2]; + *q++ = col[3]; + } + break; +#endif + } + } else { + switch (imgData->colorMode) { + case splashModeMono1: + case splashModeMono8: + imgData->colorMap->getGrayByteLine(p0, colorLine, imgData->width, + imgData->ri); + break; + case splashModeRGB8: + case splashModeBGR8: + imgData->colorMap->getRGBByteLine(p0, colorLine, imgData->width, + imgData->ri); + break; +#if SPLASH_CMYK + case splashModeCMYK8: + imgData->colorMap->getCMYKByteLine(p0, colorLine, imgData->width, + imgData->ri); + break; +#endif + } + } + + for (x = 0, p = p0, aq = alphaLine; x < imgData->width; ++x, p += nComps) { + alpha = 0; + for (i = 0; i < nComps; ++i) { + if (p[i] < imgData->maskColors[2*i] || + p[i] > imgData->maskColors[2*i+1]) { + alpha = 0xff; + break; + } + } + *aq++ = alpha; + } + + if (imgData->invert) { + n = imgData->width * splashColorModeNComps[imgData->colorMode]; + for (x = 0, p = colorLine; x < n; ++x, ++p) { + *p ^= 0xff; + } + } + + ++imgData->y; + return gTrue; +} + + +void SplashOutputDev::drawImage(GfxState *state, Object *ref, Stream *str, + int width, int height, + GfxImageColorMap *colorMap, + int *maskColors, GBool inlineImg, + GBool interpolate) { + double *ctm; + SplashCoord mat[6]; + SplashOutImageData imgData; + SplashColorMode srcMode; + SplashImageSource src; + GString *imgTag; + GfxGray gray; + GfxRGB rgb; +#if SPLASH_CMYK + GfxCMYK cmyk; +#endif + Guchar pix; + int n, i; + + setOverprintMask(state, colorMap->getColorSpace(), + state->getFillOverprint(), state->getOverprintMode(), + NULL); + + ctm = state->getCTM(); + mat[0] = ctm[0]; + mat[1] = ctm[1]; + mat[2] = -ctm[2]; + mat[3] = -ctm[3]; + mat[4] = ctm[2] + ctm[4]; + mat[5] = ctm[3] + ctm[5]; + + reduceImageResolution(str, ctm, &width, &height); + + imgData.imgStr = new ImageStream(str, width, + colorMap->getNumPixelComps(), + colorMap->getBits()); + imgData.imgStr->reset(); + imgData.colorMap = colorMap; + imgData.ri = state->getRenderingIntent(); + imgData.maskColors = maskColors; + imgData.colorMode = colorMode; + imgData.invert = reverseVideo && reverseVideoInvertImages; + imgData.width = width; + imgData.height = height; + imgData.y = 0; + + // special case for one-channel (monochrome/gray/separation) images: + // build a lookup table here + imgData.lookup = NULL; + if (colorMap->getNumPixelComps() == 1) { + if (colorMap->getBits() <= 8) { + n = 1 << colorMap->getBits(); + } else { + // GfxImageColorMap and ImageStream compress 16-bit samples to 8-bit + n = 1 << 8; + } + switch (colorMode) { + case splashModeMono1: + case splashModeMono8: + imgData.lookup = (SplashColorPtr)gmalloc(n); + for (i = 0; i < n; ++i) { + pix = (Guchar)i; + colorMap->getGray(&pix, &gray, state->getRenderingIntent()); + imgData.lookup[i] = colToByte(gray); + } + break; + case splashModeRGB8: + case splashModeBGR8: + imgData.lookup = (SplashColorPtr)gmallocn(n, 3); + for (i = 0; i < n; ++i) { + pix = (Guchar)i; + colorMap->getRGB(&pix, &rgb, state->getRenderingIntent()); + imgData.lookup[3*i] = colToByte(rgb.r); + imgData.lookup[3*i+1] = colToByte(rgb.g); + imgData.lookup[3*i+2] = colToByte(rgb.b); + } + break; +#if SPLASH_CMYK + case splashModeCMYK8: + imgData.lookup = (SplashColorPtr)gmallocn(n, 4); + for (i = 0; i < n; ++i) { + pix = (Guchar)i; + colorMap->getCMYK(&pix, &cmyk, state->getRenderingIntent()); + imgData.lookup[4*i] = colToByte(cmyk.c); + imgData.lookup[4*i+1] = colToByte(cmyk.m); + imgData.lookup[4*i+2] = colToByte(cmyk.y); + imgData.lookup[4*i+3] = colToByte(cmyk.k); + } + break; +#endif + } + } + + if (colorMode == splashModeMono1) { + srcMode = splashModeMono8; + } else if (colorMode == splashModeBGR8) { + srcMode = splashModeRGB8; + } else { + srcMode = colorMode; + } + src = maskColors ? &alphaImageSrc : &imageSrc; + imgTag = makeImageTag(ref, state->getRenderingIntent(), + colorMap->getColorSpace()); + splash->drawImage(imgTag, + src, &imgData, srcMode, maskColors ? gTrue : gFalse, + width, height, mat, interpolate); + if (inlineImg) { + while (imgData.y < height) { + imgData.imgStr->getLine(); + ++imgData.y; + } + } + + delete imgTag; + gfree(imgData.lookup); + delete imgData.imgStr; + str->close(); +} + +struct SplashOutMaskedImageData { + ImageStream *imgStr; + GfxImageColorMap *colorMap; + GfxRenderingIntent ri; + SplashBitmap *mask; + SplashColorPtr lookup; + SplashColorMode colorMode; + GBool invert; + int width, height, y; +}; + +GBool SplashOutputDev::maskedImageSrc(void *data, SplashColorPtr colorLine, + Guchar *alphaLine) { + SplashOutMaskedImageData *imgData = (SplashOutMaskedImageData *)data; + Guchar *p, *aq; + SplashColorPtr q, col; + static Guchar bitToByte[2] = {0x00, 0xff}; + Guchar *maskPtr; + int maskShift; + int n, x; + + if (imgData->y == imgData->height || + !(p = imgData->imgStr->getLine())) { + memset(colorLine, 0, + imgData->width * splashColorModeNComps[imgData->colorMode]); + memset(alphaLine, 0, imgData->width); + return gFalse; + } + + maskPtr = imgData->mask->getDataPtr() + + imgData->y * imgData->mask->getRowSize(); + aq = alphaLine; + for (x = 0; x <= imgData->width - 8; x += 8) { + aq[0] = bitToByte[(*maskPtr >> 7) & 1]; + aq[1] = bitToByte[(*maskPtr >> 6) & 1]; + aq[2] = bitToByte[(*maskPtr >> 5) & 1]; + aq[3] = bitToByte[(*maskPtr >> 4) & 1]; + aq[4] = bitToByte[(*maskPtr >> 3) & 1]; + aq[5] = bitToByte[(*maskPtr >> 2) & 1]; + aq[6] = bitToByte[(*maskPtr >> 1) & 1]; + aq[7] = bitToByte[*maskPtr & 1]; + aq += 8; + ++maskPtr; + } + maskShift = 7; + for (; x < imgData->width; ++x) { + *aq++ = bitToByte[(*maskPtr >> maskShift) & 1]; + --maskShift; + } + + if (imgData->lookup) { + switch (imgData->colorMode) { + case splashModeMono1: + case splashModeMono8: + for (x = 0, q = colorLine; x < imgData->width; ++x, ++p) { + *q++ = imgData->lookup[*p]; + } + break; + case splashModeRGB8: + case splashModeBGR8: + for (x = 0, q = colorLine; x < imgData->width; ++x, ++p) { + col = &imgData->lookup[3 * *p]; + *q++ = col[0]; + *q++ = col[1]; + *q++ = col[2]; + } + break; +#if SPLASH_CMYK + case splashModeCMYK8: + for (x = 0, q = colorLine; x < imgData->width; ++x, ++p) { + col = &imgData->lookup[4 * *p]; + *q++ = col[0]; + *q++ = col[1]; + *q++ = col[2]; + *q++ = col[3]; + } + break; +#endif + } + } else { + switch (imgData->colorMode) { + case splashModeMono1: + case splashModeMono8: + imgData->colorMap->getGrayByteLine(p, colorLine, imgData->width, + imgData->ri); + break; + case splashModeRGB8: + case splashModeBGR8: + imgData->colorMap->getRGBByteLine(p, colorLine, imgData->width, + imgData->ri); + break; +#if SPLASH_CMYK + case splashModeCMYK8: + imgData->colorMap->getCMYKByteLine(p, colorLine, imgData->width, + imgData->ri); + break; +#endif + } + } + + if (imgData->invert) { + n = imgData->width * splashColorModeNComps[imgData->colorMode]; + for (x = 0, p = colorLine; x < n; ++x, ++p) { + *p ^= 0xff; + } + } + + ++imgData->y; + return gTrue; +} + + +void SplashOutputDev::drawMaskedImage(GfxState *state, Object *ref, + Stream *str, int width, int height, + GfxImageColorMap *colorMap, + Object *maskRef, Stream *maskStr, + int maskWidth, int maskHeight, + GBool maskInvert, GBool interpolate) { + GfxImageColorMap *maskColorMap; + Object maskDecode, decodeLow, decodeHigh; + double *ctm; + SplashCoord mat[6]; + SplashOutMaskedImageData imgData; + SplashOutImageMaskData imgMaskData; + SplashColorMode srcMode; + SplashBitmap *maskBitmap; + Splash *maskSplash; + GString *imgTag; + SplashColor maskColor; + GfxGray gray; + GfxRGB rgb; +#if SPLASH_CMYK + GfxCMYK cmyk; +#endif + Guchar pix; + int n, i; + + setOverprintMask(state, colorMap->getColorSpace(), + state->getFillOverprint(), state->getOverprintMode(), + NULL); + + ctm = state->getCTM(); + reduceImageResolution(str, ctm, &width, &height); + reduceImageResolution(maskStr, ctm, &maskWidth, &maskHeight); + + // If the mask is higher resolution than the image, use + // drawSoftMaskedImage() instead. + if (maskWidth > width || maskHeight > height) { + decodeLow.initInt(maskInvert ? 0 : 1); + decodeHigh.initInt(maskInvert ? 1 : 0); + maskDecode.initArray(xref); + maskDecode.arrayAdd(&decodeLow); + maskDecode.arrayAdd(&decodeHigh); + maskColorMap = new GfxImageColorMap(1, &maskDecode, + new GfxDeviceGrayColorSpace()); + maskDecode.free(); + drawSoftMaskedImage(state, ref, str, width, height, colorMap, + maskRef, maskStr, maskWidth, maskHeight, maskColorMap, + NULL, interpolate); + delete maskColorMap; + + } else { + + //----- scale the mask image to the same size as the source image + + mat[0] = (SplashCoord)width; + mat[1] = 0; + mat[2] = 0; + mat[3] = (SplashCoord)height; + mat[4] = 0; + mat[5] = 0; + imgMaskData.imgStr = new ImageStream(maskStr, maskWidth, 1, 1); + imgMaskData.imgStr->reset(); + imgMaskData.invert = maskInvert ? 0 : 1; + imgMaskData.width = maskWidth; + imgMaskData.height = maskHeight; + imgMaskData.y = 0; + traceMessage("masked image bitmap"); + maskBitmap = new SplashBitmap(width, height, 1, splashModeMono1, + gFalse, gTrue, bitmap); + maskSplash = new Splash(maskBitmap, gFalse, splash->getImageCache()); + maskSplash->setStrokeAdjust( + mapStrokeAdjustMode[globalParams->getStrokeAdjust()]); + maskSplash->setEnablePathSimplification( + globalParams->getEnablePathSimplification()); + maskColor[0] = 0; + maskSplash->clear(maskColor); + maskColor[0] = 0xff; + maskSplash->setFillPattern(new SplashSolidColor(maskColor)); + // use "glyph mode" here to get the correct scaled size + maskSplash->fillImageMask(NULL, &imageMaskSrc, &imgMaskData, + maskWidth, maskHeight, mat, gTrue, interpolate, + globalParams->getImageMaskAntialias()); + delete imgMaskData.imgStr; + maskStr->close(); + delete maskSplash; + + //----- draw the source image + + mat[0] = ctm[0]; + mat[1] = ctm[1]; + mat[2] = -ctm[2]; + mat[3] = -ctm[3]; + mat[4] = ctm[2] + ctm[4]; + mat[5] = ctm[3] + ctm[5]; + + imgData.imgStr = new ImageStream(str, width, + colorMap->getNumPixelComps(), + colorMap->getBits()); + imgData.imgStr->reset(); + imgData.colorMap = colorMap; + imgData.ri = state->getRenderingIntent(); + imgData.mask = maskBitmap; + imgData.colorMode = colorMode; + imgData.invert = reverseVideo && reverseVideoInvertImages; + imgData.width = width; + imgData.height = height; + imgData.y = 0; + + // special case for one-channel (monochrome/gray/separation) images: + // build a lookup table here + imgData.lookup = NULL; + if (colorMap->getNumPixelComps() == 1) { + if (colorMap->getBits() <= 8) { + n = 1 << colorMap->getBits(); + } else { + // GfxImageColorMap and ImageStream compress 16-bit samples to 8-bit + n = 1 << 8; + } + switch (colorMode) { + case splashModeMono1: + case splashModeMono8: + imgData.lookup = (SplashColorPtr)gmalloc(n); + for (i = 0; i < n; ++i) { + pix = (Guchar)i; + colorMap->getGray(&pix, &gray, state->getRenderingIntent()); + imgData.lookup[i] = colToByte(gray); + } + break; + case splashModeRGB8: + case splashModeBGR8: + imgData.lookup = (SplashColorPtr)gmallocn(n, 3); + for (i = 0; i < n; ++i) { + pix = (Guchar)i; + colorMap->getRGB(&pix, &rgb, state->getRenderingIntent()); + imgData.lookup[3*i] = colToByte(rgb.r); + imgData.lookup[3*i+1] = colToByte(rgb.g); + imgData.lookup[3*i+2] = colToByte(rgb.b); + } + break; +#if SPLASH_CMYK + case splashModeCMYK8: + imgData.lookup = (SplashColorPtr)gmallocn(n, 4); + for (i = 0; i < n; ++i) { + pix = (Guchar)i; + colorMap->getCMYK(&pix, &cmyk, state->getRenderingIntent()); + imgData.lookup[4*i] = colToByte(cmyk.c); + imgData.lookup[4*i+1] = colToByte(cmyk.m); + imgData.lookup[4*i+2] = colToByte(cmyk.y); + imgData.lookup[4*i+3] = colToByte(cmyk.k); + } + break; +#endif + } + } + + if (colorMode == splashModeMono1) { + srcMode = splashModeMono8; + } else if (colorMode == splashModeBGR8) { + srcMode = splashModeRGB8; + } else { + srcMode = colorMode; + } + imgTag = makeImageTag(ref, state->getRenderingIntent(), + colorMap->getColorSpace()); + splash->drawImage(imgTag, + &maskedImageSrc, &imgData, srcMode, gTrue, + width, height, mat, interpolate); + + delete imgTag; + delete maskBitmap; + gfree(imgData.lookup); + delete imgData.imgStr; + str->close(); + } +} + +struct SplashOutSoftMaskMatteImageData { + ImageStream *imgStr; + ImageStream *maskStr; + GfxImageColorMap *colorMap; + GfxRenderingIntent ri; + Guchar matte[gfxColorMaxComps]; + SplashColorMode colorMode; + GBool invert; + int width, height, y; +}; + +GBool SplashOutputDev::softMaskMatteImageSrc(void *data, + SplashColorPtr colorLine, + Guchar *alphaLine) { + SplashOutSoftMaskMatteImageData *imgData = + (SplashOutSoftMaskMatteImageData *)data; + Guchar *p, *ap, *aq; + SplashColorPtr q; + GfxRGB rgb; + GfxGray gray; +#if SPLASH_CMYK + GfxCMYK cmyk; +#endif + Guchar alpha; + int nComps, n, x; + + if (imgData->y == imgData->height || + !(p = imgData->imgStr->getLine()) || + !(ap = imgData->maskStr->getLine())) { + memset(colorLine, 0, + imgData->width * splashColorModeNComps[imgData->colorMode]); + memset(alphaLine, 0, imgData->width); + return gFalse; + } + + nComps = imgData->colorMap->getNumPixelComps(); + + for (x = 0, q = colorLine, aq = alphaLine; + x < imgData->width; + ++x, p += nComps, ++ap) { + alpha = *ap; + switch (imgData->colorMode) { + case splashModeMono1: + case splashModeMono8: + if (alpha) { + imgData->colorMap->getGray(p, &gray, imgData->ri); + *q++ = clip255(imgData->matte[0] + + (255 * (colToByte(gray) - imgData->matte[0])) / alpha); + } else { + *q++ = 0; + } + break; + case splashModeRGB8: + case splashModeBGR8: + if (alpha) { + imgData->colorMap->getRGB(p, &rgb, imgData->ri); + *q++ = clip255(imgData->matte[0] + + (255 * (colToByte(rgb.r) - imgData->matte[0])) / alpha); + *q++ = clip255(imgData->matte[1] + + (255 * (colToByte(rgb.g) - imgData->matte[1])) / alpha); + *q++ = clip255(imgData->matte[2] + + (255 * (colToByte(rgb.b) - imgData->matte[2])) / alpha); + } else { + *q++ = 0; + *q++ = 0; + *q++ = 0; + } + break; +#if SPLASH_CMYK + case splashModeCMYK8: + if (alpha) { + imgData->colorMap->getCMYK(p, &cmyk, imgData->ri); + *q++ = clip255(imgData->matte[0] + + (255 * (colToByte(cmyk.c) - imgData->matte[0])) + / alpha); + *q++ = clip255(imgData->matte[1] + + (255 * (colToByte(cmyk.m) - imgData->matte[1])) + / alpha); + *q++ = clip255(imgData->matte[2] + + (255 * (colToByte(cmyk.y) - imgData->matte[2])) + / alpha); + *q++ = clip255(imgData->matte[3] + + (255 * (colToByte(cmyk.k) - imgData->matte[3])) + / alpha); + } else { + *q++ = 0; + *q++ = 0; + *q++ = 0; + *q++ = 0; + } + break; +#endif + } + *aq++ = alpha; + } + + if (imgData->invert) { + n = imgData->width * splashColorModeNComps[imgData->colorMode]; + for (x = 0, p = colorLine; x < n; ++x, ++p) { + *p ^= 0xff; + } + } + + ++imgData->y; + return gTrue; +} + + +void SplashOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, + Stream *str, int width, int height, + GfxImageColorMap *colorMap, + Object *maskRef, Stream *maskStr, + int maskWidth, int maskHeight, + GfxImageColorMap *maskColorMap, + double *matte, GBool interpolate) { + double *ctm; + SplashCoord mat[6]; + SplashOutImageData imgData; + SplashOutImageData imgMaskData; + SplashOutSoftMaskMatteImageData matteImgData; + GString *imgTag; + SplashColorMode srcMode; + SplashBitmap *maskBitmap; + Splash *maskSplash; + GfxColor matteColor; + GfxGray gray; + GfxRGB rgb; +#if SPLASH_CMYK + GfxCMYK cmyk; +#endif + Guchar pix; + int n, i; + + setOverprintMask(state, colorMap->getColorSpace(), + state->getFillOverprint(), state->getOverprintMode(), + NULL); + + ctm = state->getCTM(); + mat[0] = ctm[0]; + mat[1] = ctm[1]; + mat[2] = -ctm[2]; + mat[3] = -ctm[3]; + mat[4] = ctm[2] + ctm[4]; + mat[5] = ctm[3] + ctm[5]; + + if (colorMode == splashModeMono1) { + srcMode = splashModeMono8; + } else if (colorMode == splashModeBGR8) { + srcMode = splashModeRGB8; + } else { + srcMode = colorMode; + } + + //----- handle a preblended image + + if (matte && width == maskWidth && height == maskHeight) { + + // the image and mask must be the same size, so don't call + // reduceImageResolution(), which might result in different + // reductions (e.g., if the image filter supports resolution + // reduction but the mask filter doesn't) + + matteImgData.imgStr = new ImageStream(str, width, + colorMap->getNumPixelComps(), + colorMap->getBits()); + matteImgData.imgStr->reset(); + matteImgData.maskStr = new ImageStream(maskStr, maskWidth, + maskColorMap->getNumPixelComps(), + maskColorMap->getBits()); + matteImgData.maskStr->reset(); + matteImgData.colorMap = colorMap; + matteImgData.ri = state->getRenderingIntent(); + n = colorMap->getNumPixelComps(); + for (i = 0; i < n; ++i) { + matteColor.c[i] = dblToCol(matte[i]); + } + switch (colorMode) { + case splashModeMono1: + case splashModeMono8: + colorMap->getColorSpace()->getGray(&matteColor, &gray, + state->getRenderingIntent()); + matteImgData.matte[0] = colToByte(gray); + break; + case splashModeRGB8: + case splashModeBGR8: + colorMap->getColorSpace()->getRGB(&matteColor, &rgb, + state->getRenderingIntent()); + matteImgData.matte[0] = colToByte(rgb.r); + matteImgData.matte[1] = colToByte(rgb.g); + matteImgData.matte[2] = colToByte(rgb.b); + break; +#if SPLASH_CMYK + case splashModeCMYK8: + colorMap->getColorSpace()->getCMYK(&matteColor, &cmyk, + state->getRenderingIntent()); + matteImgData.matte[0] = colToByte(cmyk.c); + matteImgData.matte[1] = colToByte(cmyk.m); + matteImgData.matte[2] = colToByte(cmyk.y); + matteImgData.matte[3] = colToByte(cmyk.k); + break; +#endif + } + //~ could add the matteImgData.lookup special case + matteImgData.colorMode = colorMode; + matteImgData.invert = reverseVideo && reverseVideoInvertImages; + matteImgData.width = width; + matteImgData.height = height; + matteImgData.y = 0; + imgTag = makeImageTag(ref, state->getRenderingIntent(), + colorMap->getColorSpace()); + splash->drawImage(imgTag, &softMaskMatteImageSrc, &matteImgData, + srcMode, gTrue, width, height, mat, interpolate); + delete imgTag; + delete matteImgData.maskStr; + delete matteImgData.imgStr; + maskStr->close(); + str->close(); + + } else { + + reduceImageResolution(str, ctm, &width, &height); + reduceImageResolution(maskStr, ctm, &maskWidth, &maskHeight); + + //----- set up the soft mask + + imgMaskData.imgStr = new ImageStream(maskStr, maskWidth, + maskColorMap->getNumPixelComps(), + maskColorMap->getBits()); + imgMaskData.imgStr->reset(); + imgMaskData.colorMap = maskColorMap; + imgMaskData.ri = state->getRenderingIntent(); + imgMaskData.maskColors = NULL; + imgMaskData.colorMode = splashModeMono8; + imgMaskData.invert = gFalse; + imgMaskData.width = maskWidth; + imgMaskData.height = maskHeight; + imgMaskData.y = 0; + if (maskColorMap->getBits() <= 8) { + n = 1 << maskColorMap->getBits(); + } else { + // GfxImageColorMap and ImageStream compress 16-bit samples to 8-bit + n = 1 << 8; + } + imgMaskData.lookup = (SplashColorPtr)gmalloc(n); + for (i = 0; i < n; ++i) { + pix = (Guchar)i; + maskColorMap->getGray(&pix, &gray, state->getRenderingIntent()); + imgMaskData.lookup[i] = colToByte(gray); + } + traceMessage("soft masked image bitmap"); + maskBitmap = new SplashBitmap(bitmap->getWidth(), bitmap->getHeight(), + 1, splashModeMono8, gFalse, gTrue, bitmap); + maskSplash = new Splash(maskBitmap, vectorAntialias, + splash->getImageCache()); + maskSplash->setStrokeAdjust( + mapStrokeAdjustMode[globalParams->getStrokeAdjust()]); + maskSplash->setEnablePathSimplification( + globalParams->getEnablePathSimplification()); + clearMaskRegion(state, maskSplash, 0, 0, 1, 1); + maskSplash->drawImage(NULL, + &imageSrc, &imgMaskData, splashModeMono8, gFalse, + maskWidth, maskHeight, mat, interpolate); + delete imgMaskData.imgStr; + maskStr->close(); + gfree(imgMaskData.lookup); + delete maskSplash; + splash->setSoftMask(maskBitmap); + + //----- draw the source image + + imgData.imgStr = new ImageStream(str, width, + colorMap->getNumPixelComps(), + colorMap->getBits()); + imgData.imgStr->reset(); + imgData.colorMap = colorMap; + imgData.ri = state->getRenderingIntent(); + imgData.maskColors = NULL; + imgData.colorMode = colorMode; + imgData.invert = reverseVideo && reverseVideoInvertImages; + imgData.width = width; + imgData.height = height; + imgData.y = 0; + + // special case for one-channel (monochrome/gray/separation) images: + // build a lookup table here + imgData.lookup = NULL; + if (colorMap->getNumPixelComps() == 1) { + if (colorMap->getBits() <= 8) { + n = 1 << colorMap->getBits(); + } else { + // GfxImageColorMap and ImageStream compress 16-bit samples to 8-bit + n = 1 << 8; + } + switch (colorMode) { + case splashModeMono1: + case splashModeMono8: + imgData.lookup = (SplashColorPtr)gmalloc(n); + for (i = 0; i < n; ++i) { + pix = (Guchar)i; + colorMap->getGray(&pix, &gray, state->getRenderingIntent()); + imgData.lookup[i] = colToByte(gray); + } + break; + case splashModeRGB8: + case splashModeBGR8: + imgData.lookup = (SplashColorPtr)gmallocn(n, 3); + for (i = 0; i < n; ++i) { + pix = (Guchar)i; + colorMap->getRGB(&pix, &rgb, state->getRenderingIntent()); + imgData.lookup[3*i] = colToByte(rgb.r); + imgData.lookup[3*i+1] = colToByte(rgb.g); + imgData.lookup[3*i+2] = colToByte(rgb.b); + } + break; +#if SPLASH_CMYK + case splashModeCMYK8: + imgData.lookup = (SplashColorPtr)gmallocn(n, 4); + for (i = 0; i < n; ++i) { + pix = (Guchar)i; + colorMap->getCMYK(&pix, &cmyk, state->getRenderingIntent()); + imgData.lookup[4*i] = colToByte(cmyk.c); + imgData.lookup[4*i+1] = colToByte(cmyk.m); + imgData.lookup[4*i+2] = colToByte(cmyk.y); + imgData.lookup[4*i+3] = colToByte(cmyk.k); + } + break; +#endif + } + } + + imgTag = makeImageTag(ref, state->getRenderingIntent(), + colorMap->getColorSpace()); + splash->drawImage(imgTag, + &imageSrc, &imgData, srcMode, gFalse, width, height, mat, + interpolate); + + splash->setSoftMask(NULL); + delete imgTag; + gfree(imgData.lookup); + delete imgData.imgStr; + + + str->close(); + } +} + +GString *SplashOutputDev::makeImageTag(Object *ref, GfxRenderingIntent ri, + GfxColorSpace *colorSpace) { + if (!ref || !ref->isRef() || + (colorSpace && colorSpace->isDefaultColorSpace())) { + return NULL; + } + return GString::format("{0:d}_{1:d}_{2:d}", + ref->getRefNum(), ref->getRefGen(), (int)ri); +} + +void SplashOutputDev::reduceImageResolution(Stream *str, double *ctm, + int *width, int *height) { + double sw, sh; + int reduction; + + if (str->getKind() == strJPX && + *width >= 256 && + *height >= 256 && + *width * *height > 10000000) { + sw = (double)*width / (fabs(ctm[0]) + fabs(ctm[1])); + sh = (double)*height / (fabs(ctm[2]) + fabs(ctm[3])); + if (sw > 8 && sh > 8) { + reduction = 3; + } else if (sw > 4 && sh > 4) { + reduction = 2; + } else if (sw > 2 && sh > 2) { + reduction = 1; + } else { + reduction = 0; + } + if (reduction > 0) { + ((JPXStream *)str)->reduceResolution(reduction); + *width >>= reduction; + *height >>= reduction; + } + } +} + +void SplashOutputDev::clearMaskRegion(GfxState *state, + Splash *maskSplash, + double xMin, double yMin, + double xMax, double yMax) { + SplashBitmap *maskBitmap; + double xxMin, yyMin, xxMax, yyMax, xx, yy; + int xxMinI, yyMinI, xxMaxI, yyMaxI, y, n; + Guchar *p; + + maskBitmap = maskSplash->getBitmap(); + xxMin = maskBitmap->getWidth(); + xxMax = 0; + yyMin = maskBitmap->getHeight(); + yyMax = 0; + state->transform(xMin, yMin, &xx, &yy); + if (xx < xxMin) { xxMin = xx; } + if (xx > xxMax) { xxMax = xx; } + if (yy < yyMin) { yyMin = yy; } + if (yy > yyMax) { yyMax = yy; } + state->transform(xMin, yMax, &xx, &yy); + if (xx < xxMin) { xxMin = xx; } + if (xx > xxMax) { xxMax = xx; } + if (yy < yyMin) { yyMin = yy; } + if (yy > yyMax) { yyMax = yy; } + state->transform(xMax, yMin, &xx, &yy); + if (xx < xxMin) { xxMin = xx; } + if (xx > xxMax) { xxMax = xx; } + if (yy < yyMin) { yyMin = yy; } + if (yy > yyMax) { yyMax = yy; } + state->transform(xMax, yMax, &xx, &yy); + if (xx < xxMin) { xxMin = xx; } + if (xx > xxMax) { xxMax = xx; } + if (yy < yyMin) { yyMin = yy; } + if (yy > yyMax) { yyMax = yy; } + xxMinI = (int)floor(xxMin); + if (xxMinI < 0) { + xxMinI = 0; + } + xxMaxI = (int)ceil(xxMax); + if (xxMaxI > maskBitmap->getWidth()) { + xxMaxI = maskBitmap->getWidth(); + } + yyMinI = (int)floor(yyMin); + if (yyMinI < 0) { + yyMinI = 0; + } + yyMaxI = (int)ceil(yyMax); + if (yyMaxI > maskBitmap->getHeight()) { + yyMaxI = maskBitmap->getHeight(); + } + p = maskBitmap->getDataPtr() + yyMinI * maskBitmap->getRowSize(); + if (maskBitmap->getMode() == splashModeMono1) { + n = (xxMaxI + 7) / 8 - xxMinI / 8; + p += xxMinI / 8; + } else { + n = xxMaxI - xxMinI; + p += xxMinI; + } + if (xxMaxI > xxMinI) { + for (y = yyMinI; y < yyMaxI; ++y) { + memset(p, 0, n); + p += maskBitmap->getRowSize(); + } + } +} + +GBool SplashOutputDev::beginTransparencyGroup(GfxState *state, double *bbox, + GfxColorSpace *blendingColorSpace, + GBool isolated, GBool knockout, + GBool forSoftMask) { + SplashTransparencyGroup *transpGroup; + SplashBitmap *backdropBitmap; + SplashColor color; + double xMin, yMin, xMax, yMax, x, y; + int bw, bh, tx, ty, w, h, i; + + // transform the bbox + state->transform(bbox[0], bbox[1], &x, &y); + xMin = xMax = x; + yMin = yMax = y; + state->transform(bbox[0], bbox[3], &x, &y); + if (x < xMin) { + xMin = x; + } else if (x > xMax) { + xMax = x; + } + if (y < yMin) { + yMin = y; + } else if (y > yMax) { + yMax = y; + } + state->transform(bbox[2], bbox[1], &x, &y); + if (x < xMin) { + xMin = x; + } else if (x > xMax) { + xMax = x; + } + if (y < yMin) { + yMin = y; + } else if (y > yMax) { + yMax = y; + } + state->transform(bbox[2], bbox[3], &x, &y); + if (x < xMin) { + xMin = x; + } else if (x > xMax) { + xMax = x; + } + if (y < yMin) { + yMin = y; + } else if (y > yMax) { + yMax = y; + } + + // clip the box + x = splash->getClip()->getXMin(); + if (x > xMin) { + xMin = x; + } + x = splash->getClip()->getXMax(); + if (x < xMax) { + xMax = x; + } + y = splash->getClip()->getYMin(); + if (y > yMin) { + yMin = y; + } + y = splash->getClip()->getYMax(); + if (y < yMax) { + yMax = y; + } + + // convert box coords to integers + bw = bitmap->getWidth(); + bh = bitmap->getHeight(); + tx = (int)floor(xMin); + if (tx < 0) { + tx = 0; + } else if (tx >= bw) { + tx = bw - 1; + } + ty = (int)floor(yMin); + if (ty < 0) { + ty = 0; + } else if (ty >= bh) { + ty = bh - 1; + } + w = (int)ceil(xMax) - tx + 1; + // NB bw and tx are both non-negative, so 'bw - tx' can't overflow + if (bw - tx < w) { + w = bw - tx; + } + if (w < 1) { + w = 1; + } + h = (int)ceil(yMax) - ty + 1; + // NB bh and ty are both non-negative, so 'bh - ty' can't overflow + if (bh - ty < h) { + h = bh - ty; + } + if (h < 1) { + h = 1; + } + + // optimization: a non-isolated group drawn with alpha=1 and + // Blend=Normal and backdrop alpha=0 is equivalent to drawing + // directly onto the backdrop (i.e., a regular non-t-group Form) + // notes: + // - if we are already in a non-isolated group, it means the + // backdrop alpha is non-zero (otherwise the parent non-isolated + // group would have been optimized away) + // - if there is a soft mask in place, then source alpha is not 1 + // (i.e., source alpha = fillOpacity * softMask) + // - both the parent and child groups must be non-knockout + if (!isolated && + !splash->getInNonIsolatedGroup() && + !knockout && + !splash->getInKnockoutGroup() && + !forSoftMask && + !splash->getSoftMask() && + state->getFillOpacity() == 1 && + state->getBlendMode() == gfxBlendNormal && + splash->checkTransparentRect(tx, ty, w, h)) { + return gFalse; + } + + // push a new stack entry + transpGroup = new SplashTransparencyGroup(); + transpGroup->tx = tx; + transpGroup->ty = ty; + transpGroup->blendingColorSpace = blendingColorSpace; + transpGroup->isolated = isolated; + transpGroup->next = transpGroupStack; + transpGroupStack = transpGroup; + + // save state + transpGroup->origBitmap = bitmap; + transpGroup->origSplash = splash; + + //~ this handles the blendingColorSpace arg for soft masks, but + //~ not yet for transparency groups + + // switch to the blending color space + if (forSoftMask && isolated && !knockout && blendingColorSpace) { + if (blendingColorSpace->getMode() == csDeviceGray || + blendingColorSpace->getMode() == csCalGray || + (blendingColorSpace->getMode() == csICCBased && + blendingColorSpace->getNComps() == 1)) { + colorMode = splashModeMono8; + } else if (blendingColorSpace->getMode() == csDeviceRGB || + blendingColorSpace->getMode() == csCalRGB || + (blendingColorSpace->getMode() == csICCBased && + blendingColorSpace->getNComps() == 3)) { + //~ does this need to use BGR8? + colorMode = splashModeRGB8; +#if SPLASH_CMYK + } else if (blendingColorSpace->getMode() == csDeviceCMYK || + (blendingColorSpace->getMode() == csICCBased && + blendingColorSpace->getNComps() == 4)) { + colorMode = splashModeCMYK8; +#endif + } + } + + // create the temporary bitmap + traceMessage("t-group bitmap"); + bitmap = new SplashBitmap(w, h, bitmapRowPad, colorMode, gTrue, + bitmapTopDown, transpGroup->origBitmap); + splash = new Splash(bitmap, vectorAntialias, + transpGroup->origSplash->getImageCache(), + transpGroup->origSplash->getScreen()); + splash->setMinLineWidth(globalParams->getMinLineWidth()); + splash->setStrokeAdjust( + mapStrokeAdjustMode[globalParams->getStrokeAdjust()]); + splash->setEnablePathSimplification( + globalParams->getEnablePathSimplification()); + copyState(transpGroup->origSplash, gTrue); + if (!isolated || knockout) { + // non-isolated and knockout groups nested in another group will + // read the parent group bitmap, so we need to force any deferred + // initialization on the parent + transpGroup->origSplash->forceDeferredInit(ty, h); + } + if (isolated) { + // isolated group + backdropBitmap = transpGroup->origBitmap; + transpGroup->backdropBitmap = NULL; + if (forSoftMask) { + // setSoftMask uses the whole bitmap, not just the mod region, + // so we can't use the deferred initialization optimization + for (i = 0; i < splashMaxColorComps; ++i) { + color[i] = 0; + } + splash->clear(color, 0); + splash->setInTransparencyGroup(backdropBitmap, tx, ty, + splashGroupDestPreInit, + gFalse, knockout); + } else { + splash->setInTransparencyGroup(backdropBitmap, tx, ty, + splashGroupDestInitZero, + gFalse, knockout); + } + } else if (transpGroup->origBitmap->getAlphaPtr() && + transpGroup->origSplash->getInNonIsolatedGroup() && + colorMode != splashModeMono1) { + // non-isolated group drawn in another non-isolated group: + // compute a backdrop bitmap with corrected alpha values + traceMessage("t-group backdrop bitmap"); + backdropBitmap = new SplashBitmap(w, h, bitmapRowPad, colorMode, gTrue, + bitmapTopDown, transpGroup->origBitmap); + transpGroup->origSplash->blitCorrectedAlpha(backdropBitmap, + tx, ty, 0, 0, w, h); + transpGroup->backdropBitmap = backdropBitmap; + if (forSoftMask) { + // setSoftMask uses the whole bitmap, not just the mod region, + // so we can't use the deferred initialization optimization + splash->blitTransparent(transpGroup->origBitmap, tx, ty, 0, 0, w, h); + splash->setInTransparencyGroup(backdropBitmap, 0, 0, + splashGroupDestPreInit, + gTrue, knockout); + } else { + splash->setInTransparencyGroup(backdropBitmap, 0, 0, + splashGroupDestInitCopy, + gTrue, knockout); + } + } else { + // other non-isolated group + backdropBitmap = transpGroup->origBitmap; + transpGroup->backdropBitmap = NULL; + if (forSoftMask) { + // setSoftMask uses the whole bitmap, not just the mod region, + // so we can't use the deferred initialization optimization + splash->blitTransparent(transpGroup->origBitmap, tx, ty, 0, 0, w, h); + splash->setInTransparencyGroup(backdropBitmap, tx, ty, + splashGroupDestPreInit, + gTrue, knockout); + } else { + splash->setInTransparencyGroup(backdropBitmap, tx, ty, + splashGroupDestInitCopy, + gTrue, knockout); + } + } + splash->clearModRegion(); + transpGroup->tBitmap = bitmap; +#if 1 //~tmp + if (knockout) { + splash->setInShading(gTrue); + } +#endif + state->shiftCTM(-tx, -ty); + updateCTM(state, 0, 0, 0, 0, 0, 0); + ++nestCount; + + return gTrue; +} + +void SplashOutputDev::endTransparencyGroup(GfxState *state) { + splash->getModRegion(&transpGroupStack->modXMin, &transpGroupStack->modYMin, + &transpGroupStack->modXMax, &transpGroupStack->modYMax); + + // restore state + --nestCount; + delete splash; + bitmap = transpGroupStack->origBitmap; + colorMode = bitmap->getMode(); + splash = transpGroupStack->origSplash; + state->shiftCTM(transpGroupStack->tx, transpGroupStack->ty); + updateCTM(state, 0, 0, 0, 0, 0, 0); +} + +void SplashOutputDev::paintTransparencyGroup(GfxState *state, double *bbox) { + SplashBitmap *tBitmap; + SplashTransparencyGroup *transpGroup; + GBool isolated; + int xSrc, ySrc, xDest, yDest, w, h; + + xSrc = transpGroupStack->modXMin; + ySrc = transpGroupStack->modYMin; + xDest = transpGroupStack->tx + transpGroupStack->modXMin; + yDest = transpGroupStack->ty + transpGroupStack->modYMin; + w = transpGroupStack->modXMax - transpGroupStack->modXMin + 1; + h = transpGroupStack->modYMax - transpGroupStack->modYMin + 1; + tBitmap = transpGroupStack->tBitmap; + isolated = transpGroupStack->isolated; + + // paint the transparency group onto the parent bitmap + // - the clip path was set in the parent's state) + if (xDest < bitmap->getWidth() && yDest < bitmap->getHeight() && + w > 0 && h > 0) { + splash->setOverprintMask(0xffffffff); + splash->composite(tBitmap, xSrc, ySrc, xDest, yDest, w, h, + gFalse, !isolated); + } + + // free the temporary backdrop bitmap + if (transpGroupStack->backdropBitmap) { + delete transpGroupStack->backdropBitmap; + } + + // pop the stack + transpGroup = transpGroupStack; + transpGroupStack = transpGroup->next; + delete transpGroup; + + delete tBitmap; +} + +void SplashOutputDev::setSoftMask(GfxState *state, double *bbox, + GBool alpha, Function *transferFunc, + GfxColor *backdropColor) { + SplashBitmap *softMask, *tBitmap; + Splash *tSplash; + SplashTransparencyGroup *transpGroup; + SplashColor color; + SplashColorPtr p, colorPtr, colorPtr2; + Guchar *alphaPtr; + GfxGray gray; + GfxRGB rgb; +#if SPLASH_CMYK + GfxCMYK cmyk; +#endif + double backdrop, backdrop2, lum, lum2; + Guchar lum8; + SplashBitmapRowSize rowSize; + int tw, th, tNComps, tx, ty, x, y; + + tx = transpGroupStack->tx; + ty = transpGroupStack->ty; + tBitmap = transpGroupStack->tBitmap; + + // composite with backdrop color + backdrop = 0; + if (!alpha && tBitmap->getMode() != splashModeMono1) { + //~ need to correctly handle the case where no blending color + //~ space is given + if (transpGroupStack->blendingColorSpace) { + tSplash = new Splash(tBitmap, vectorAntialias, + transpGroupStack->origSplash->getImageCache(), + transpGroupStack->origSplash->getScreen()); + tSplash->setStrokeAdjust( + mapStrokeAdjustMode[globalParams->getStrokeAdjust()]); + tSplash->setEnablePathSimplification( + globalParams->getEnablePathSimplification()); + switch (tBitmap->getMode()) { + case splashModeMono1: + // transparency is not supported in mono1 mode + break; + case splashModeMono8: + transpGroupStack->blendingColorSpace->getGray( + backdropColor, &gray, state->getRenderingIntent()); + backdrop = colToDbl(gray); + color[0] = colToByte(gray); + tSplash->compositeBackground(color); + break; + case splashModeRGB8: + case splashModeBGR8: + transpGroupStack->blendingColorSpace->getRGB( + backdropColor, &rgb, state->getRenderingIntent()); + backdrop = 0.3 * colToDbl(rgb.r) + + 0.59 * colToDbl(rgb.g) + + 0.11 * colToDbl(rgb.b); + color[0] = colToByte(rgb.r); + color[1] = colToByte(rgb.g); + color[2] = colToByte(rgb.b); + tSplash->compositeBackground(color); + break; +#if SPLASH_CMYK + case splashModeCMYK8: + transpGroupStack->blendingColorSpace->getCMYK( + backdropColor, &cmyk, state->getRenderingIntent()); + backdrop = (1 - colToDbl(cmyk.k)) + - 0.3 * colToDbl(cmyk.c) + - 0.59 * colToDbl(cmyk.m) + - 0.11 * colToDbl(cmyk.y); + if (backdrop < 0) { + backdrop = 0; + } + color[0] = colToByte(cmyk.c); + color[1] = colToByte(cmyk.m); + color[2] = colToByte(cmyk.y); + color[3] = colToByte(cmyk.k); + tSplash->compositeBackground(color); + break; +#endif + } + delete tSplash; + } + } + if (transferFunc) { + transferFunc->transform(&backdrop, &backdrop2); + } else { + backdrop2 = backdrop; + } + + traceMessage("soft mask bitmap"); + softMask = new SplashBitmap(bitmap->getWidth(), bitmap->getHeight(), + 1, splashModeMono8, gFalse, gTrue, bitmap); + memset(softMask->getDataPtr(), (int)(backdrop2 * 255.0 + 0.5), + softMask->getRowSize() * softMask->getHeight()); + if (tx < softMask->getWidth() && ty < softMask->getHeight()) { + p = softMask->getDataPtr() + ty * softMask->getRowSize() + tx; + tw = tBitmap->getWidth(); + th = tBitmap->getHeight(); + rowSize = softMask->getRowSize(); + if (alpha) { + alphaPtr = tBitmap->getAlphaPtr(); + for (y = 0; y < th; ++y) { + for (x = 0; x < tw; ++x) { + lum = *alphaPtr++ / 255.0; + if (transferFunc) { + transferFunc->transform(&lum, &lum2); + } else { + lum2 = lum; + } + p[x] = (Guchar)(lum2 * 255.0 + 0.5); + } + p += rowSize; + } + } else { + colorPtr = tBitmap->getDataPtr(); + tNComps = splashColorModeNComps[tBitmap->getMode()]; + lum8 = 0; // make gcc happy + for (y = 0; y < th; ++y) { + colorPtr2 = colorPtr; + for (x = 0; x < tw; ++x) { + // convert to luminosity + switch (tBitmap->getMode()) { + case splashModeMono1: + lum8 = 0; + break; + case splashModeMono8: + lum8 = colorPtr2[0]; + break; + case splashModeRGB8: + case splashModeBGR8: + // [0.3, 0.59, 0.11] * 255 = [77, 150, 28] + lum8 = div255(77 * colorPtr2[0] + + 150 * colorPtr2[1] + + 28 * colorPtr2[1]); + break; +#if SPLASH_CMYK + case splashModeCMYK8: + lum8 = clip255(255 - colorPtr2[3] + - div255(77 * colorPtr2[0] + + 150 * colorPtr2[1] + + 28 * colorPtr2[2])); + break; +#endif + } + if (transferFunc) { + lum = lum8 / 255.0; + transferFunc->transform(&lum, &lum2); + lum8 = (Guchar)(lum2 * 255.0 + 0.5); + } + p[x] = lum8; + colorPtr2 += tNComps; + } + p += rowSize; + colorPtr += tBitmap->getRowSize(); + } + } + } + splash->setSoftMask(softMask); + + // free the temporary backdrop bitmap + if (transpGroupStack->backdropBitmap) { + delete transpGroupStack->backdropBitmap; + } + + // pop the stack + transpGroup = transpGroupStack; + transpGroupStack = transpGroup->next; + delete transpGroup; + + delete tBitmap; +} + +void SplashOutputDev::clearSoftMask(GfxState *state) { + splash->setSoftMask(NULL); +} + +void SplashOutputDev::setPaperColor(SplashColorPtr paperColorA) { + splashColorCopy(paperColor, paperColorA); +} + +int SplashOutputDev::getBitmapWidth() { + return bitmap->getWidth(); +} + +int SplashOutputDev::getBitmapHeight() { + return bitmap->getHeight(); +} + +SplashBitmap *SplashOutputDev::takeBitmap() { + SplashBitmap *ret; + + ret = bitmap; + bitmap = new SplashBitmap(1, 1, bitmapRowPad, colorMode, + colorMode != splashModeMono1, bitmapTopDown, NULL); + return ret; +} + +void SplashOutputDev::getModRegion(int *xMin, int *yMin, + int *xMax, int *yMax) { + splash->getModRegion(xMin, yMin, xMax, yMax); +} + +void SplashOutputDev::clearModRegion() { + splash->clearModRegion(); +} + +void SplashOutputDev::setFillColor(int r, int g, int b) { + GfxRGB rgb; + GfxGray gray; +#if SPLASH_CMYK + GfxCMYK cmyk; +#endif + + rgb.r = byteToCol((Guchar)r); + rgb.g = byteToCol((Guchar)g); + rgb.b = byteToCol((Guchar)b); + switch (colorMode) { + case splashModeMono1: + case splashModeMono8: + gray = (GfxColorComp)(0.299 * rgb.r + 0.587 * rgb.g + 0.114 * rgb.g + 0.5); + if (gray > gfxColorComp1) { + gray = gfxColorComp1; + } + splash->setFillPattern(getColor(gray)); + break; + case splashModeRGB8: + case splashModeBGR8: + splash->setFillPattern(getColor(&rgb)); + break; +#if SPLASH_CMYK + case splashModeCMYK8: + cmyk.c = gfxColorComp1 - rgb.r; + cmyk.m = gfxColorComp1 - rgb.g; + cmyk.y = gfxColorComp1 - rgb.b; + cmyk.k = 0; + splash->setFillPattern(getColor(&cmyk)); + break; +#endif + } +} + +SplashFont *SplashOutputDev::getFont(GString *name, SplashCoord *textMatA) { + Ref ref; + SplashOutFontFileID *id; + GfxFontLoc *fontLoc; +#if LOAD_FONTS_FROM_MEM + GString *fontBuf; + FILE *extFontFile; + char blk[4096]; + int n; +#endif + SplashFontFile *fontFile; + SplashFont *fontObj; + FoFiTrueType *ff; + int *codeToGID; + Unicode u; + SplashCoord textMat[4]; + SplashCoord oblique; + int cmap, cmapPlatform, cmapEncoding, i; + + for (i = 0; i < nBuiltinFonts; ++i) { + if (!name->cmp(builtinFonts[i].name)) { + break; + } + } + if (i == nBuiltinFonts) { + return NULL; + } + ref.num = i; + ref.gen = -1; + id = new SplashOutFontFileID(&ref); + + // check the font file cache + if ((fontFile = fontEngine->getFontFile(id))) { + delete id; + + // load the font file + } else { + if (!(fontLoc = GfxFont::locateBase14Font(name))) { + return NULL; + } +#if LOAD_FONTS_FROM_MEM + fontBuf = NULL; + if (fontLoc->fontType == fontType1 || + fontLoc->fontType == fontTrueType) { + if (!(extFontFile = fopen(fontLoc->path->getCString(), "rb"))) { + delete fontLoc; + delete id; + return NULL; + } + fontBuf = new GString(); + while ((n = fread(blk, 1, sizeof(blk), extFontFile)) > 0) { + fontBuf->append(blk, n); + } + fclose(extFontFile); + } +#endif + if (fontLoc->fontType == fontType1) { + fontFile = fontEngine->loadType1Font(id, +#if LOAD_FONTS_FROM_MEM + fontBuf, +#else + fontLoc->path->getCString(), + gFalse, +#endif + winAnsiEncoding); + } else if (fontLoc->fontType == fontTrueType) { +#if LOAD_FONTS_FROM_MEM + if (!(ff = FoFiTrueType::make(fontBuf->getCString(), + fontBuf->getLength(), + fontLoc->fontNum))) { +#else + if (!(ff = FoFiTrueType::load(fontLoc->path->getCString(), + fontLoc->fontNum))) { +#endif + delete fontLoc; + delete id; + return NULL; + } + for (cmap = 0; cmap < ff->getNumCmaps(); ++cmap) { + cmapPlatform = ff->getCmapPlatform(cmap); + cmapEncoding = ff->getCmapEncoding(cmap); + if ((cmapPlatform == 3 && cmapEncoding == 1) || + (cmapPlatform == 0 && cmapEncoding <= 4)) { + break; + } + } + if (cmap == ff->getNumCmaps()) { + delete ff; + delete fontLoc; + delete id; + return NULL; + } + codeToGID = (int *)gmallocn(256, sizeof(int)); + for (i = 0; i < 256; ++i) { + codeToGID[i] = 0; + if (winAnsiEncoding[i] && + (u = globalParams->mapNameToUnicode(winAnsiEncoding[i]))) { + codeToGID[i] = ff->mapCodeToGID(cmap, u); + } + } + delete ff; + fontFile = fontEngine->loadTrueTypeFont(id, +#if LOAD_FONTS_FROM_MEM + fontBuf, +#else + fontLoc->path->getCString(), + gFalse, +#endif + fontLoc->fontNum, + codeToGID, 256, NULL); + } else { + delete fontLoc; + delete id; + return NULL; + } + delete fontLoc; + } + if (!fontFile) { + return NULL; + } + + // create the scaled font + oblique = (SplashCoord) + ((SplashOutFontFileID *)fontFile->getID())->getOblique(); + textMat[0] = (SplashCoord)textMatA[0]; + textMat[1] = (SplashCoord)textMatA[1]; + textMat[2] = oblique * textMatA[0] + textMatA[2]; + textMat[3] = oblique * textMatA[1] + textMatA[3]; + fontObj = fontEngine->getFont(fontFile, textMat, splash->getMatrix()); + + return fontObj; +} + +// This is called when initializing a temporary Splash object for Type +// 3 characters and transparency groups. Acrobat apparently copies at +// least the fill and stroke colors, and the line parameters. +//~ not sure what else should be copied -- the PDF spec is unclear +//~ - fill and stroke alpha? +void SplashOutputDev::copyState(Splash *oldSplash, GBool copyColors) { + // cached Type 3 chars set a color, so no need to copy the color here + if (copyColors) { + splash->setFillPattern(oldSplash->getFillPattern()->copy()); + splash->setStrokePattern(oldSplash->getStrokePattern()->copy()); + } + splash->setLineDash(oldSplash->getLineDash(), + oldSplash->getLineDashLength(), + oldSplash->getLineDashPhase()); + splash->setLineCap(oldSplash->getLineCap()); + splash->setLineJoin(oldSplash->getLineJoin()); + splash->setLineWidth(oldSplash->getLineWidth()); +} + +#if 1 //~tmp: turn off anti-aliasing temporarily +// This was originally used with gradient shadings -- that's no longer +// necessary, now that shadings are all converted to device space +// images. It's still used with knockout groups, however, because the +// rasterizer doesn't properly separate opacity and shape. +void SplashOutputDev::setInShading(GBool sh) { + splash->setInShading(sh); +} +#endif -- cgit v1.2.3