Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/pdf/xpdf/SplashOutputDev.cc
diff options
context:
space:
mode:
Diffstat (limited to 'pdf/xpdf/SplashOutputDev.cc')
-rw-r--r--pdf/xpdf/SplashOutputDev.cc1345
1 files changed, 1345 insertions, 0 deletions
diff --git a/pdf/xpdf/SplashOutputDev.cc b/pdf/xpdf/SplashOutputDev.cc
new file mode 100644
index 0000000..0284d82
--- /dev/null
+++ b/pdf/xpdf/SplashOutputDev.cc
@@ -0,0 +1,1345 @@
+//========================================================================
+//
+// SplashOutputDev.cc
+//
+// Copyright 2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <string.h>
+#include <math.h>
+#include "gfile.h"
+#include "GlobalParams.h"
+#include "Error.h"
+#include "Object.h"
+#include "GfxState.h"
+#include "GfxFont.h"
+#include "Link.h"
+#include "CharCodeToUnicode.h"
+#include "FontEncodingTables.h"
+#include "FoFiTrueType.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"
+
+//------------------------------------------------------------------------
+// Font substitutions
+//------------------------------------------------------------------------
+
+struct SplashOutFontSubst {
+ char *name;
+ double mWidth;
+};
+
+// index: {symbolic:12, fixed:8, serif:4, sans-serif:0} + bold*2 + italic
+static SplashOutFontSubst splashOutSubstFonts[16] = {
+ {"Helvetica", 0.833},
+ {"Helvetica-Oblique", 0.833},
+ {"Helvetica-Bold", 0.889},
+ {"Helvetica-BoldOblique", 0.889},
+ {"Times-Roman", 0.788},
+ {"Times-Italic", 0.722},
+ {"Times-Bold", 0.833},
+ {"Times-BoldItalic", 0.778},
+ {"Courier", 0.600},
+ {"Courier-Oblique", 0.600},
+ {"Courier-Bold", 0.600},
+ {"Courier-BoldOblique", 0.600},
+ {"Symbol", 0.576},
+ {"Symbol", 0.576},
+ {"Symbol", 0.576},
+ {"Symbol", 0.576}
+};
+
+//------------------------------------------------------------------------
+
+#define soutRound(x) ((int)(x + 0.5))
+
+//------------------------------------------------------------------------
+// SplashOutFontFileID
+//------------------------------------------------------------------------
+
+class SplashOutFontFileID: public SplashFontFileID {
+public:
+
+ SplashOutFontFileID(Ref *rA) { r = *rA; substIdx = -1; }
+
+ ~SplashOutFontFileID() {}
+
+ GBool matches(SplashFontFileID *id) {
+ return ((SplashOutFontFileID *)id)->r.num == r.num &&
+ ((SplashOutFontFileID *)id)->r.gen == r.gen;
+ }
+
+ void setSubstIdx(int substIdxA) { substIdx = substIdxA; }
+ int getSubstIdx() { return substIdx; }
+
+private:
+
+ Ref r;
+ 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 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
+ 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
+};
+
+T3FontCache::T3FontCache(Ref *fontIDA, double m11A, double m12A,
+ double m21A, double m22A,
+ int glyphXA, int glyphYA, int glyphWA, int glyphHA,
+ GBool aa) {
+ int i;
+
+ fontID = *fontIDA;
+ m11 = m11A;
+ m12 = m12A;
+ m21 = m21A;
+ m22 = m22A;
+ glyphX = glyphXA;
+ glyphY = glyphYA;
+ glyphW = glyphWA;
+ glyphH = glyphHA;
+ if (aa) {
+ glyphSize = glyphW * glyphH;
+ } else {
+ glyphSize = ((glyphW + 7) >> 3) * glyphH;
+ }
+ cacheAssoc = 8;
+ if (glyphSize <= 256) {
+ cacheSets = 8;
+ } else if (glyphSize <= 512) {
+ cacheSets = 4;
+ } else if (glyphSize <= 1024) {
+ cacheSets = 2;
+ } else {
+ cacheSets = 1;
+ }
+ cacheData = (Guchar *)gmalloc(cacheSets * cacheAssoc * glyphSize);
+ cacheTags = (T3FontCacheTag *)gmalloc(cacheSets * cacheAssoc *
+ sizeof(T3FontCacheTag));
+ for (i = 0; i < cacheSets * cacheAssoc; ++i) {
+ cacheTags[i].mru = i & (cacheAssoc - 1);
+ }
+}
+
+T3FontCache::~T3FontCache() {
+ gfree(cacheData);
+ gfree(cacheTags);
+}
+
+struct T3GlyphStack {
+ Gushort code; // character code
+ double x, y; // position to draw the glyph
+
+ //----- 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;
+
+ T3GlyphStack *next; // next object on stack
+};
+
+//------------------------------------------------------------------------
+// SplashOutputDev
+//------------------------------------------------------------------------
+
+SplashOutputDev::SplashOutputDev(SplashColorMode colorModeA,
+ GBool reverseVideoA,
+ SplashColor paperColorA) {
+ colorMode = colorModeA;
+ reverseVideo = reverseVideoA;
+ paperColor = paperColorA;
+
+ xref = NULL;
+
+ bitmap = new SplashBitmap(1, 1, colorMode);
+ splash = new Splash(bitmap);
+ splash->clear(paperColor);
+
+ fontEngine = NULL;
+
+ nT3Fonts = 0;
+ t3GlyphStack = NULL;
+
+ font = NULL;
+ needFontUpdate = gFalse;
+ textClipPath = NULL;
+
+ underlayCbk = NULL;
+ underlayCbkData = NULL;
+}
+
+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;
+ }
+}
+
+void SplashOutputDev::startDoc(XRef *xrefA) {
+ int i;
+
+ xref = xrefA;
+ if (fontEngine) {
+ delete fontEngine;
+ }
+ fontEngine = new SplashFontEngine(
+#if HAVE_T1LIB_H
+ globalParams->getEnableT1lib(),
+#endif
+#if HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H
+ globalParams->getEnableFreeType(),
+#endif
+ globalParams->getAntialias());
+ for (i = 0; i < nT3Fonts; ++i) {
+ delete t3FontCache[i];
+ }
+ nT3Fonts = 0;
+}
+
+void SplashOutputDev::startPage(int pageNum, GfxState *state) {
+ int w, h;
+ SplashColor color;
+
+ w = state ? (int)(state->getPageWidth() + 0.5) : 1;
+ h = state ? (int)(state->getPageHeight() + 0.5) : 1;
+ if (splash) {
+ delete splash;
+ }
+ if (!bitmap || w != bitmap->getWidth() || h != bitmap->getHeight()) {
+ if (bitmap) {
+ delete bitmap;
+ }
+ bitmap = new SplashBitmap(w, h, colorMode);
+ }
+ splash = new Splash(bitmap);
+ switch (colorMode) {
+ case splashModeMono1: color.mono1 = 0; break;
+ case splashModeMono8: color.mono8 = 0; break;
+ case splashModeRGB8: color.rgb8 = splashMakeRGB8(0, 0, 0); break;
+ case splashModeBGR8Packed: color.bgr8 = splashMakeBGR8(0, 0, 0); break;
+ }
+ 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);
+ splash->clear(paperColor);
+
+ if (underlayCbk) {
+ (*underlayCbk)(underlayCbkData);
+ }
+}
+
+void SplashOutputDev::endPage() {
+}
+
+void SplashOutputDev::drawLink(Link *link, Catalog *catalog) {
+ double x1, y1, x2, y2;
+ LinkBorderStyle *borderStyle;
+ GfxRGB rgb;
+ double gray;
+ double *dash;
+ int dashLength;
+ SplashCoord dashList[20];
+ SplashPath *path;
+ int x, y, i;
+
+ link->getRect(&x1, &y1, &x2, &y2);
+ borderStyle = link->getBorderStyle();
+ if (borderStyle->getWidth() > 0) {
+ borderStyle->getColor(&rgb.r, &rgb.g, &rgb.b);
+ gray = 0.299 * rgb.r + 0.587 * rgb.g + 0.114 * rgb.b;
+ if (gray > 1) {
+ gray = 1;
+ }
+ splash->setStrokePattern(getColor(gray, &rgb));
+ splash->setLineWidth((SplashCoord)borderStyle->getWidth());
+ borderStyle->getDash(&dash, &dashLength);
+ if (borderStyle->getType() == linkBorderDashed && dashLength > 0) {
+ if (dashLength > 20) {
+ dashLength = 20;
+ }
+ for (i = 0; i < dashLength; ++i) {
+ dashList[i] = (SplashCoord)dash[i];
+ }
+ splash->setLineDash(dashList, dashLength, 0);
+ }
+ path = new SplashPath();
+ if (borderStyle->getType() == linkBorderUnderlined) {
+ cvtUserToDev(x1, y1, &x, &y);
+ path->moveTo((SplashCoord)x, (SplashCoord)y);
+ cvtUserToDev(x2, y1, &x, &y);
+ path->lineTo((SplashCoord)x, (SplashCoord)y);
+ } else {
+ cvtUserToDev(x1, y1, &x, &y);
+ path->moveTo((SplashCoord)x, (SplashCoord)y);
+ cvtUserToDev(x2, y1, &x, &y);
+ path->lineTo((SplashCoord)x, (SplashCoord)y);
+ cvtUserToDev(x2, y2, &x, &y);
+ path->lineTo((SplashCoord)x, (SplashCoord)y);
+ cvtUserToDev(x1, y2, &x, &y);
+ path->lineTo((SplashCoord)x, (SplashCoord)y);
+ path->close();
+ }
+ splash->stroke(path);
+ delete path;
+ }
+}
+
+void SplashOutputDev::saveState(GfxState *state) {
+ splash->saveState();
+}
+
+void SplashOutputDev::restoreState(GfxState *state) {
+ splash->restoreState();
+ needFontUpdate = gTrue;
+}
+
+void SplashOutputDev::updateAll(GfxState *state) {
+ updateLineDash(state);
+ updateLineJoin(state);
+ updateLineCap(state);
+ updateLineWidth(state);
+ updateFlatness(state);
+ updateMiterLimit(state);
+ updateFillColor(state);
+ updateStrokeColor(state);
+ needFontUpdate = gTrue;
+}
+
+void SplashOutputDev::updateCTM(GfxState *state, double m11, double m12,
+ double m21, double m22,
+ double m31, double m32) {
+ updateLineDash(state);
+ updateLineJoin(state);
+ updateLineCap(state);
+ updateLineWidth(state);
+}
+
+void SplashOutputDev::updateLineDash(GfxState *state) {
+ double *dashPattern;
+ int dashLength;
+ double dashStart;
+ SplashCoord dash[20];
+ SplashCoord phase;
+ int i;
+
+ state->getLineDash(&dashPattern, &dashLength, &dashStart);
+ if (dashLength > 20) {
+ dashLength = 20;
+ }
+ for (i = 0; i < dashLength; ++i) {
+ dash[i] = (SplashCoord)state->transformWidth(dashPattern[i]);
+ if (dash[i] < 1) {
+ dash[i] = 1;
+ }
+ }
+ phase = (SplashCoord)state->transformWidth(dashStart);
+ splash->setLineDash(dash, dashLength, phase);
+}
+
+void SplashOutputDev::updateFlatness(GfxState *state) {
+ splash->setFlatness(state->getFlatness());
+}
+
+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->getTransformedLineWidth());
+}
+
+void SplashOutputDev::updateFillColor(GfxState *state) {
+ double gray;
+ GfxRGB rgb;
+
+ state->getFillGray(&gray);
+ state->getFillRGB(&rgb);
+ splash->setFillPattern(getColor(gray, &rgb));
+}
+
+void SplashOutputDev::updateStrokeColor(GfxState *state) {
+ double gray;
+ GfxRGB rgb;
+
+ state->getStrokeGray(&gray);
+ state->getStrokeRGB(&rgb);
+ splash->setStrokePattern(getColor(gray, &rgb));
+}
+
+SplashPattern *SplashOutputDev::getColor(double gray, GfxRGB *rgb) {
+ SplashPattern *pattern;
+ SplashColor color0, color1;
+ double r, g, b;
+
+ if (reverseVideo) {
+ gray = 1 - gray;
+ r = 1 - rgb->r;
+ g = 1 - rgb->g;
+ b = 1 - rgb->b;
+ } else {
+ r = rgb->r;
+ g = rgb->g;
+ b = rgb->b;
+ }
+
+ pattern = NULL; // make gcc happy
+ switch (colorMode) {
+ case splashModeMono1:
+ color0.mono1 = 0;
+ color1.mono1 = 1;
+ pattern = new SplashHalftone(color0, color1,
+ splash->getScreen()->copy(),
+ (SplashCoord)gray);
+ break;
+ case splashModeMono8:
+ color1.mono8 = soutRound(255 * gray);
+ pattern = new SplashSolidColor(color1);
+ break;
+ case splashModeRGB8:
+ color1.rgb8 = splashMakeRGB8(soutRound(255 * r),
+ soutRound(255 * g),
+ soutRound(255 * b));
+ pattern = new SplashSolidColor(color1);
+ break;
+ case splashModeBGR8Packed:
+ color1.bgr8 = splashMakeBGR8(soutRound(255 * r),
+ soutRound(255 * g),
+ soutRound(255 * b));
+ pattern = new SplashSolidColor(color1);
+ break;
+ }
+
+ return pattern;
+}
+
+void SplashOutputDev::updateFont(GfxState *state) {
+ GfxFont *gfxFont;
+ GfxFontType fontType;
+ SplashOutFontFileID *id;
+ SplashFontFile *fontFile;
+ FoFiTrueType *ff;
+ Ref embRef;
+ Object refObj, strObj;
+ GString *tmpFileName, *fileName, *substName;
+ FILE *tmpFile;
+ Gushort *codeToGID;
+ DisplayFontParam *dfp;
+ double m11, m12, m21, m22, w1, w2;
+ SplashCoord mat[4];
+ char *name;
+ int c, substIdx, n, code;
+
+ needFontUpdate = gFalse;
+ font = NULL;
+ tmpFileName = NULL;
+ substIdx = -1;
+
+ if (!(gfxFont = state->getFont())) {
+ goto err1;
+ }
+ fontType = gfxFont->getType();
+ if (fontType == fontType3) {
+ goto err1;
+ }
+
+ // check the font file cache
+ id = new SplashOutFontFileID(gfxFont->getID());
+ if ((fontFile = fontEngine->getFontFile(id))) {
+ delete id;
+
+ } else {
+
+ // if there is an embedded font, write it to disk
+ if (gfxFont->getEmbeddedFontID(&embRef)) {
+ if (!openTempFile(&tmpFileName, &tmpFile, "wb", NULL)) {
+ error(-1, "Couldn't create temporary font file");
+ goto err2;
+ }
+ refObj.initRef(embRef.num, embRef.gen);
+ refObj.fetch(xref, &strObj);
+ refObj.free();
+ strObj.streamReset();
+ while ((c = strObj.streamGetChar()) != EOF) {
+ fputc(c, tmpFile);
+ }
+ strObj.streamClose();
+ strObj.free();
+ fclose(tmpFile);
+ fileName = tmpFileName;
+
+ // if there is an external font file, use it
+ } else if (!(fileName = gfxFont->getExtFontFile())) {
+
+ // look for a display font mapping or a substitute font
+ dfp = NULL;
+ if (gfxFont->isCIDFont()) {
+ if (((GfxCIDFont *)gfxFont)->getCollection()) {
+ dfp = globalParams->
+ getDisplayCIDFont(gfxFont->getName(),
+ ((GfxCIDFont *)gfxFont)->getCollection());
+ }
+ } else {
+ if (gfxFont->getName()) {
+ dfp = globalParams->getDisplayFont(gfxFont->getName());
+ }
+ if (!dfp) {
+ // 8-bit font substitution
+ if (gfxFont->isFixedWidth()) {
+ substIdx = 8;
+ } else if (gfxFont->isSerif()) {
+ substIdx = 4;
+ } else {
+ substIdx = 0;
+ }
+ if (gfxFont->isBold()) {
+ substIdx += 2;
+ }
+ if (gfxFont->isItalic()) {
+ substIdx += 1;
+ }
+ substName = new GString(splashOutSubstFonts[substIdx].name);
+ dfp = globalParams->getDisplayFont(substName);
+ delete substName;
+ id->setSubstIdx(substIdx);
+ }
+ }
+ if (!dfp) {
+ error(-1, "Couldn't find a font for '%s'",
+ gfxFont->getName() ? gfxFont->getName()->getCString()
+ : "(unnamed)");
+ goto err2;
+ }
+ switch (dfp->kind) {
+ case displayFontT1:
+ fileName = dfp->t1.fileName;
+ fontType = gfxFont->isCIDFont() ? fontCIDType0 : fontType1;
+ break;
+ case displayFontTT:
+ fileName = dfp->tt.fileName;
+ fontType = gfxFont->isCIDFont() ? fontCIDType2 : fontTrueType;
+ break;
+ }
+ }
+
+ // load the font file
+ switch (fontType) {
+ case fontType1:
+ if (!(fontFile = fontEngine->loadType1Font(
+ id,
+ fileName->getCString(),
+ fileName == tmpFileName,
+ ((Gfx8BitFont *)gfxFont)->getEncoding()))) {
+ error(-1, "Couldn't create a font for '%s'",
+ gfxFont->getName() ? gfxFont->getName()->getCString()
+ : "(unnamed)");
+ goto err2;
+ }
+ break;
+ case fontType1C:
+ if (!(fontFile = fontEngine->loadType1CFont(
+ id,
+ fileName->getCString(),
+ fileName == tmpFileName,
+ ((Gfx8BitFont *)gfxFont)->getEncoding()))) {
+ error(-1, "Couldn't create a font for '%s'",
+ gfxFont->getName() ? gfxFont->getName()->getCString()
+ : "(unnamed)");
+ goto err2;
+ }
+ break;
+ case fontTrueType:
+ if (!(ff = FoFiTrueType::load(fileName->getCString()))) {
+ goto err2;
+ }
+ codeToGID = ((Gfx8BitFont *)gfxFont)->getCodeToGIDMap(ff);
+ delete ff;
+ if (!(fontFile = fontEngine->loadTrueTypeFont(
+ id,
+ fileName->getCString(),
+ fileName == tmpFileName,
+ codeToGID, 256))) {
+ error(-1, "Couldn't create a font for '%s'",
+ gfxFont->getName() ? gfxFont->getName()->getCString()
+ : "(unnamed)");
+ goto err2;
+ }
+ break;
+ case fontCIDType0:
+ case fontCIDType0C:
+ if (!(fontFile = fontEngine->loadCIDFont(
+ id,
+ fileName->getCString(),
+ fileName == tmpFileName))) {
+ error(-1, "Couldn't create a font for '%s'",
+ gfxFont->getName() ? gfxFont->getName()->getCString()
+ : "(unnamed)");
+ goto err2;
+ }
+ break;
+ case fontCIDType2:
+ n = ((GfxCIDFont *)gfxFont)->getCIDToGIDLen();
+ codeToGID = (Gushort *)gmalloc(n * sizeof(Gushort));
+ memcpy(codeToGID, ((GfxCIDFont *)gfxFont)->getCIDToGID(),
+ n * sizeof(Gushort));
+ if (!(fontFile = fontEngine->loadTrueTypeFont(
+ id,
+ fileName->getCString(),
+ fileName == tmpFileName,
+ codeToGID, n))) {
+ error(-1, "Couldn't create a font for '%s'",
+ gfxFont->getName() ? gfxFont->getName()->getCString()
+ : "(unnamed)");
+ goto err2;
+ }
+ break;
+ default:
+ // this shouldn't happen
+ goto err2;
+ }
+ }
+
+ // get the font matrix
+ state->getFontTransMat(&m11, &m12, &m21, &m22);
+ m11 *= state->getHorizScaling();
+ m12 *= state->getHorizScaling();
+
+ // for substituted fonts: adjust the font matrix -- compare the
+ // width of 'm' in the original font and the substituted font
+ substIdx = ((SplashOutFontFileID *)fontFile->getID())->getSubstIdx();
+ if (substIdx >= 0) {
+ for (code = 0; code < 256; ++code) {
+ if ((name = ((Gfx8BitFont *)gfxFont)->getCharName(code)) &&
+ name[0] == 'm' && name[1] == '\0') {
+ break;
+ }
+ }
+ if (code < 256) {
+ w1 = ((Gfx8BitFont *)gfxFont)->getWidth(code);
+ w2 = splashOutSubstFonts[substIdx].mWidth;
+ if (!gfxFont->isSymbolic()) {
+ // if real font is substantially narrower than substituted
+ // font, reduce the font size accordingly
+ if (w1 > 0.01 && w1 < 0.9 * w2) {
+ w1 /= w2;
+ m11 *= w1;
+ m21 *= w1;
+ }
+ }
+ }
+ }
+
+ // create the scaled font
+ mat[0] = m11; mat[1] = -m12;
+ mat[2] = m21; mat[3] = -m22;
+ font = fontEngine->getFont(fontFile, mat);
+
+ if (tmpFileName) {
+ delete tmpFileName;
+ }
+ return;
+
+ err2:
+ delete id;
+ err1:
+ if (tmpFileName) {
+ delete tmpFileName;
+ }
+ return;
+}
+
+void SplashOutputDev::stroke(GfxState *state) {
+ SplashPath *path;
+
+ path = convertPath(state, state->getPath());
+ splash->stroke(path);
+ delete path;
+}
+
+void SplashOutputDev::fill(GfxState *state) {
+ SplashPath *path;
+
+ path = convertPath(state, state->getPath());
+ splash->fill(path, gFalse);
+ delete path;
+}
+
+void SplashOutputDev::eoFill(GfxState *state) {
+ SplashPath *path;
+
+ path = convertPath(state, state->getPath());
+ splash->fill(path, gTrue);
+ delete path;
+}
+
+void SplashOutputDev::clip(GfxState *state) {
+ SplashPath *path;
+
+ path = convertPath(state, state->getPath());
+ splash->clipToPath(path, gFalse);
+ delete path;
+}
+
+void SplashOutputDev::eoClip(GfxState *state) {
+ SplashPath *path;
+
+ path = convertPath(state, state->getPath());
+ splash->clipToPath(path, gTrue);
+ delete path;
+}
+
+SplashPath *SplashOutputDev::convertPath(GfxState *state, GfxPath *path) {
+ SplashPath *sPath;
+ GfxSubpath *subpath;
+ double x1, y1, x2, y2, x3, y3;
+ int i, j;
+
+ sPath = new SplashPath();
+ for (i = 0; i < path->getNumSubpaths(); ++i) {
+ subpath = path->getSubpath(i);
+ if (subpath->getNumPoints() > 0) {
+ state->transform(subpath->getX(0), subpath->getY(0), &x1, &y1);
+ sPath->moveTo((SplashCoord)x1, (SplashCoord)y1);
+ j = 1;
+ while (j < subpath->getNumPoints()) {
+ if (subpath->getCurve(j)) {
+ state->transform(subpath->getX(j), subpath->getY(j), &x1, &y1);
+ state->transform(subpath->getX(j+1), subpath->getY(j+1), &x2, &y2);
+ state->transform(subpath->getX(j+2), subpath->getY(j+2), &x3, &y3);
+ sPath->curveTo((SplashCoord)x1, (SplashCoord)y1,
+ (SplashCoord)x2, (SplashCoord)y2,
+ (SplashCoord)x3, (SplashCoord)y3);
+ j += 3;
+ } else {
+ state->transform(subpath->getX(j), subpath->getY(j), &x1, &y1);
+ sPath->lineTo((SplashCoord)x1, (SplashCoord)y1);
+ ++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, Unicode *u, int uLen) {
+ double x1, y1;
+ SplashPath *path;
+ int render;
+
+ if (needFontUpdate) {
+ updateFont(state);
+ }
+ if (!font) {
+ return;
+ }
+
+ // check for invisible text -- this is used by Acrobat Capture
+ render = state->getRender();
+ if (render == 3) {
+ return;
+ }
+
+ x -= originX;
+ y -= originY;
+ state->transform(x, y, &x1, &y1);
+
+ // fill
+ if (!(render & 1)) {
+ splash->fillChar((SplashCoord)x1, (SplashCoord)y1, code, font);
+ }
+
+ // stroke
+ if ((render & 3) == 1 || (render & 3) == 2) {
+ if ((path = font->getGlyphPath(code))) {
+ path->offset((SplashCoord)x1, (SplashCoord)y1);
+ splash->stroke(path);
+ delete path;
+ }
+ }
+
+ // clip
+ if (render & 4) {
+ path = font->getGlyphPath(code);
+ path->offset((SplashCoord)x1, (SplashCoord)y1);
+ if (textClipPath) {
+ textClipPath->append(path);
+ delete path;
+ } else {
+ textClipPath = 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;
+ double x1, y1, xMin, yMin, xMax, yMax, xt, yt;
+ int i, j;
+
+ if (!(gfxFont = state->getFont())) {
+ return gFalse;
+ }
+ 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) {
+ delete t3FontCache[nT3Fonts - 1];
+ --nT3Fonts;
+ }
+ for (j = nT3Fonts; 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) {
+ // broken bounding box -- just take a guess
+ xMin = xt - 5;
+ xMax = xMin + 30;
+ yMax = yt + 15;
+ yMin = yMax - 45;
+ } 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;
+ }
+ }
+ t3FontCache[0] = new T3FontCache(fontID, ctm[0], ctm[1], ctm[2], ctm[3],
+ (int)floor(xMin - xt),
+ (int)floor(yMin - yt),
+ (int)ceil(xMax) - (int)floor(xMin) + 3,
+ (int)ceil(yMax) - (int)floor(yMin) + 3,
+ 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(t3Font, &t3Font->cacheTags[i+j],
+ t3Font->cacheData + (i+j) * t3Font->glyphSize,
+ xt, yt);
+ return gTrue;
+ }
+ }
+
+ // push a new Type 3 glyph record
+ t3gs = new T3GlyphStack();
+ t3gs->next = t3GlyphStack;
+ t3GlyphStack = t3gs;
+ t3GlyphStack->code = code;
+ t3GlyphStack->x = xt;
+ t3GlyphStack->y = yt;
+ t3GlyphStack->cache = t3Font;
+ t3GlyphStack->cacheTag = NULL;
+ t3GlyphStack->cacheData = NULL;
+
+ return gFalse;
+}
+
+void SplashOutputDev::endType3Char(GfxState *state) {
+ T3GlyphStack *t3gs;
+ double *ctm;
+
+ if (t3GlyphStack->cacheTag) {
+ memcpy(t3GlyphStack->cacheData, bitmap->getDataPtr().mono8,
+ t3GlyphStack->cache->glyphSize);
+ delete bitmap;
+ delete splash;
+ bitmap = t3GlyphStack->origBitmap;
+ splash = t3GlyphStack->origSplash;
+ ctm = state->getCTM();
+ state->setCTM(ctm[0], ctm[1], ctm[2], ctm[3],
+ t3GlyphStack->origCTM4, t3GlyphStack->origCTM5);
+ drawType3Glyph(t3GlyphStack->cache,
+ t3GlyphStack->cacheTag, t3GlyphStack->cacheData,
+ t3GlyphStack->x, t3GlyphStack->y);
+ }
+ t3gs = t3GlyphStack;
+ t3GlyphStack = t3gs->next;
+ delete t3gs;
+}
+
+void SplashOutputDev::type3D0(GfxState *state, double wx, double wy) {
+}
+
+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;
+
+ 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) {
+ error(-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) {
+ bitmap = new SplashBitmap(t3Font->glyphW, t3Font->glyphH, splashModeMono1);
+ splash = new Splash(bitmap);
+ color.mono1 = 0;
+ splash->clear(color);
+ color.mono1 = 1;
+ } else {
+ bitmap = new SplashBitmap(t3Font->glyphW, t3Font->glyphH, splashModeMono8);
+ splash = new Splash(bitmap);
+ color.mono8 = 0x00;
+ splash->clear(color);
+ color.mono8 = 0xff;
+ }
+ splash->setFillPattern(new SplashSolidColor(color));
+ splash->setStrokePattern(new SplashSolidColor(color));
+ //~ this should copy other state from t3GlyphStack->origSplash?
+ state->setCTM(ctm[0], ctm[1], ctm[2], ctm[3],
+ -t3Font->glyphX, -t3Font->glyphY);
+}
+
+void SplashOutputDev::drawType3Glyph(T3FontCache *t3Font,
+ T3FontCacheTag *tag, Guchar *data,
+ double x, double y) {
+ SplashGlyphBitmap glyph;
+
+ 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((SplashCoord)x, (SplashCoord)y, &glyph);
+}
+
+void SplashOutputDev::endTextObject(GfxState *state) {
+ if (textClipPath) {
+ splash->clipToPath(textClipPath, gFalse);
+ delete textClipPath;
+ textClipPath = NULL;
+ }
+}
+
+struct SplashOutImageMaskData {
+ ImageStream *imgStr;
+ int nPixels, idx;
+ GBool invert;
+};
+
+GBool SplashOutputDev::imageMaskSrc(void *data, SplashMono1 *pixel) {
+ SplashOutImageMaskData *imgMaskData = (SplashOutImageMaskData *)data;
+ Guchar pix;
+
+ if (imgMaskData->idx >= imgMaskData->nPixels) {
+ return gFalse;
+ }
+ //~ use getLine
+ imgMaskData->imgStr->getPixel(&pix);
+ if (!imgMaskData->invert) {
+ pix ^= 1;
+ }
+ *pixel = pix;
+ ++imgMaskData->idx;
+ return gTrue;
+}
+
+void SplashOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
+ int width, int height, GBool invert,
+ GBool inlineImg) {
+ double *ctm;
+ SplashCoord mat[6];
+ SplashOutImageMaskData imgMaskData;
+ Guchar pix;
+
+ 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];
+
+ imgMaskData.imgStr = new ImageStream(str, width, 1, 1);
+ imgMaskData.imgStr->reset();
+ imgMaskData.nPixels = width * height;
+ imgMaskData.idx = 0;
+ imgMaskData.invert = invert;
+
+ splash->fillImageMask(&imageMaskSrc, &imgMaskData, width, height, mat);
+ if (inlineImg) {
+ while (imageMaskSrc(&imgMaskData, &pix)) ;
+ }
+
+ delete imgMaskData.imgStr;
+}
+
+struct SplashOutImageData {
+ ImageStream *imgStr;
+ GfxImageColorMap *colorMap;
+ int *maskColors;
+ SplashOutputDev *out;
+ int nPixels, idx;
+};
+
+GBool SplashOutputDev::imageSrc(void *data, SplashColor *pixel,
+ Guchar *alpha) {
+ SplashOutImageData *imgData = (SplashOutImageData *)data;
+ Guchar pix[gfxColorMaxComps];
+ GfxRGB rgb;
+ double gray;
+ int i;
+
+ if (imgData->idx >= imgData->nPixels) {
+ return gFalse;
+ }
+
+ //~ use getLine
+ imgData->imgStr->getPixel(pix);
+ switch (imgData->out->colorMode) {
+ case splashModeMono1:
+ case splashModeMono8:
+ imgData->colorMap->getGray(pix, &gray);
+ pixel->mono8 = soutRound(255 * gray);
+ break;
+ case splashModeRGB8:
+ imgData->colorMap->getRGB(pix, &rgb);
+ pixel->rgb8 = splashMakeRGB8(soutRound(255 * rgb.r),
+ soutRound(255 * rgb.g),
+ soutRound(255 * rgb.b));
+ break;
+ case splashModeBGR8Packed:
+ imgData->colorMap->getRGB(pix, &rgb);
+ pixel->bgr8 = splashMakeBGR8(soutRound(255 * rgb.r),
+ soutRound(255 * rgb.g),
+ soutRound(255 * rgb.b));
+ break;
+ }
+
+ if (imgData->maskColors) {
+ *alpha = 0;
+ for (i = 0; i < imgData->colorMap->getNumPixelComps(); ++i) {
+ if (pix[i] < imgData->maskColors[2*i] ||
+ pix[i] > imgData->maskColors[2*i+1]) {
+ *alpha = 1;
+ break;
+ }
+ }
+ } else {
+ *alpha = 1;
+ }
+
+ ++imgData->idx;
+ return gTrue;
+}
+
+void SplashOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
+ int width, int height,
+ GfxImageColorMap *colorMap,
+ int *maskColors, GBool inlineImg) {
+ double *ctm;
+ SplashCoord mat[6];
+ SplashOutImageData imgData;
+ SplashColor pix;
+ Guchar alpha;
+
+ 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];
+
+ imgData.imgStr = new ImageStream(str, width,
+ colorMap->getNumPixelComps(),
+ colorMap->getBits());
+ imgData.imgStr->reset();
+ imgData.colorMap = colorMap;
+ imgData.maskColors = maskColors;
+ imgData.out = this;
+ imgData.nPixels = width * height;
+ imgData.idx = 0;
+
+ splash->drawImage(&imageSrc, &imgData,
+ (colorMode == splashModeMono1) ? splashModeMono8
+ : colorMode,
+ width, height, mat);
+ if (inlineImg) {
+ while (imageSrc(&imgData, &pix, &alpha)) ;
+ }
+
+ delete imgData.imgStr;
+}
+
+int SplashOutputDev::getBitmapWidth() {
+ return bitmap->getWidth();
+}
+
+int SplashOutputDev::getBitmapHeight() {
+ return bitmap->getHeight();
+}
+
+void SplashOutputDev::xorRectangle(int x0, int y0, int x1, int y1,
+ SplashPattern *pattern) {
+ SplashPath *path;
+
+ path = new SplashPath();
+ path->moveTo((SplashCoord)x0, (SplashCoord)y0);
+ path->lineTo((SplashCoord)x1, (SplashCoord)y0);
+ path->lineTo((SplashCoord)x1, (SplashCoord)y1);
+ path->lineTo((SplashCoord)x0, (SplashCoord)y1);
+ path->close();
+ splash->setFillPattern(pattern);
+ splash->xorFill(path, gTrue);
+ delete path;
+}
+
+void SplashOutputDev::setFillColor(int r, int g, int b) {
+ GfxRGB rgb;
+ double gray;
+
+ rgb.r = r / 255.0;
+ rgb.g = g / 255.0;
+ rgb.b = b / 255.0;
+ gray = 0.299 * rgb.r + 0.587 * rgb.g + 0.114 * rgb.g;
+ splash->setFillPattern(getColor(gray, &rgb));
+}
+
+SplashFont *SplashOutputDev::getFont(GString *name, double *mat) {
+ DisplayFontParam *dfp;
+ Ref ref;
+ SplashOutFontFileID *id;
+ SplashFontFile *fontFile;
+ SplashFont *fontObj;
+ int i;
+
+ for (i = 0; i < 16; ++i) {
+ if (!name->cmp(splashOutSubstFonts[i].name)) {
+ break;
+ }
+ }
+ if (i == 16) {
+ 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 {
+ dfp = globalParams->getDisplayFont(name);
+ if (dfp->kind != displayFontT1) {
+ return NULL;
+ }
+ fontFile = fontEngine->loadType1Font(id, dfp->t1.fileName->getCString(),
+ gFalse, winAnsiEncoding);
+ }
+
+ // create the scaled font
+ fontObj = fontEngine->getFont(fontFile, (SplashCoord *)mat);
+
+ return fontObj;
+}