diff options
Diffstat (limited to 'filters/kword/pdf/xpdf/xpdf/XOutputDev.cpp')
-rw-r--r-- | filters/kword/pdf/xpdf/xpdf/XOutputDev.cpp | 3690 |
1 files changed, 3690 insertions, 0 deletions
diff --git a/filters/kword/pdf/xpdf/xpdf/XOutputDev.cpp b/filters/kword/pdf/xpdf/xpdf/XOutputDev.cpp new file mode 100644 index 000000000..e8acbf66a --- /dev/null +++ b/filters/kword/pdf/xpdf/xpdf/XOutputDev.cpp @@ -0,0 +1,3690 @@ +//======================================================================== +// +// XOutputDev.cpp +// +// Copyright 1996-2002 Glyph & Cog, LLC +// +//======================================================================== + +#include <aconf.h> + +#ifdef USE_GCC_PRAGMAS +#pragma implementation +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <stddef.h> +#include <unistd.h> +#include <string.h> +#include <ctype.h> +#include <math.h> +#include "gmem.h" +#include "gfile.h" +#include "GString.h" +#include "GList.h" +#include "Object.h" +#include "Stream.h" +#include "Link.h" +#include "GfxState.h" +#include "GfxFont.h" +#include "UnicodeMap.h" +#include "CharCodeToUnicode.h" +#include "FontFile.h" +#include "Error.h" +#include "TextOutputDev.h" +#include "XOutputDev.h" +#if HAVE_T1LIB_H +#include "T1Font.h" +#endif +#if FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H) +#include "FTFont.h" +#endif +#if !FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H) +#include "TTFont.h" +#endif + +#ifdef VMS +#if (__VMS_VER < 70000000) +extern "C" int unlink(char *filename); +#endif +#endif + +#ifdef XlibSpecificationRelease +#if XlibSpecificationRelease < 5 +typedef char *XPointer; +#endif +#else +typedef char *XPointer; +#endif + +//------------------------------------------------------------------------ +// Constants and macros +//------------------------------------------------------------------------ + +#define xoutRound(x) ((int)(x + 0.5)) + +#define maxCurveSplits 6 // max number of splits when recursively + // drawing Bezier curves + +//------------------------------------------------------------------------ +// Font substitutions +//------------------------------------------------------------------------ + +struct XOutFontSubst { + char *name; + double mWidth; +}; + +// index: {symbolic:12, fixed:8, serif:4, sans-serif:0} + bold*2 + italic +static XOutFontSubst xOutSubstFonts[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} +}; + +//------------------------------------------------------------------------ + +static void outputToFile(void *stream, char *data, int len) { + fwrite(data, 1, len, (FILE *)stream); +} + +//------------------------------------------------------------------------ +// XOutputFont +//------------------------------------------------------------------------ + +XOutputFont::XOutputFont(Ref *idA, double m11OrigA, double m12OrigA, + double m21OrigA, double m22OrigA, + double m11A, double m12A, double m21A, double m22A, + Display *displayA, XOutputDev *xOutA) { + id = *idA; + display = displayA; + xOut = xOutA; + m11Orig = m11OrigA; + m12Orig = m12OrigA; + m21Orig = m21OrigA; + m22Orig = m22OrigA; + m11 = m11A; + m12 = m12A; + m21 = m21A; + m22 = m22A; +} + +XOutputFont::~XOutputFont() { +} + +void XOutputFont::getCharPath(GfxState *state, + CharCode c, Unicode *u, int ulen) { +} + +#if HAVE_T1LIB_H +//------------------------------------------------------------------------ +// XOutputT1Font +//------------------------------------------------------------------------ + +XOutputT1Font::XOutputT1Font(Ref *idA, T1FontFile *fontFileA, + double m11OrigA, double m12OrigA, + double m21OrigA, double m22OrigA, + double m11A, double m12A, + double m21A, double m22A, + Display *displayA, XOutputDev *xOutA): + XOutputFont(idA, m11OrigA, m12OrigA, m21OrigA, m22OrigA, + m11A, m12A, m21A, m22A, displayA, xOutA) +{ + double matrix[4]; + + fontFile = fontFileA; + + // create the transformed instance + matrix[0] = m11; + matrix[1] = -m12; + matrix[2] = m21; + matrix[3] = -m22; + font = new T1Font(fontFile, matrix); +} + +XOutputT1Font::~XOutputT1Font() { + if (font) { + delete font; + } +} + +GBool XOutputT1Font::isOk() { + return font != NULL; +} + +void XOutputT1Font::updateGC(GC gc) { +} + +void XOutputT1Font::drawChar(GfxState *state, Pixmap pixmap, int w, int h, + GC gc, GfxRGB *rgb, + double x, double y, double dx, double dy, + CharCode c, Unicode *u, int uLen) { + font->drawChar(pixmap, w, h, gc, xoutRound(x), xoutRound(y), + (int)(rgb->r * 65535), (int)(rgb->g * 65535), + (int)(rgb->b * 65535), c, u[0]); +} + +void XOutputT1Font::getCharPath(GfxState *state, + CharCode c, Unicode *u, int uLen) { + font->getCharPath(c, u[0], state); +} +#endif // HAVE_T1LIB_H + +#if FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H) +//------------------------------------------------------------------------ +// XOutputFTFont +//------------------------------------------------------------------------ + +XOutputFTFont::XOutputFTFont(Ref *idA, FTFontFile *fontFileA, + double m11OrigA, double m12OrigA, + double m21OrigA, double m22OrigA, + double m11A, double m12A, + double m21A, double m22A, + Display *displayA, XOutputDev *xOutA): + XOutputFont(idA, m11OrigA, m12OrigA, m21OrigA, m22OrigA, + m11A, m12A, m21A, m22A, displayA, xOutA) +{ + double matrix[4]; + + fontFile = fontFileA; + + // create the transformed instance + matrix[0] = m11; + matrix[1] = -m12; + matrix[2] = m21; + matrix[3] = -m22; + font = new FTFont(fontFile, matrix); +} + +XOutputFTFont::~XOutputFTFont() { + if (font) { + delete font; + } +} + +GBool XOutputFTFont::isOk() { + return font != NULL; +} + +void XOutputFTFont::updateGC(GC gc) { +} + +void XOutputFTFont::drawChar(GfxState *state, Pixmap pixmap, int w, int h, + GC gc, GfxRGB *rgb, + double x, double y, double dx, double dy, + CharCode c, Unicode *u, int uLen) { + font->drawChar(pixmap, w, h, gc, xoutRound(x), xoutRound(y), + (int)(rgb->r * 65535), (int)(rgb->g * 65535), + (int)(rgb->b * 65535), c, uLen > 0 ? u[0] : 0); +} + +void XOutputFTFont::getCharPath(GfxState *state, + CharCode c, Unicode *u, int uLen) { + font->getCharPath(c, u[0], state); +} +#endif // FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H) + +#if !FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H) +//------------------------------------------------------------------------ +// XOutputTTFont +//------------------------------------------------------------------------ + +XOutputTTFont::XOutputTTFont(Ref *idA, TTFontFile *fontFileA, + double m11OrigA, double m12OrigA, + double m21OrigA, double m22OrigA, + double m11A, double m12A, + double m21A, double m22A, + Display *displayA, XOutputDev *xOutA): + XOutputFont(idA, m11OrigA, m12OrigA, m21OrigA, m22OrigA, + m11A, m12A, m21A, m22A, displayA, xOutA) +{ + double matrix[4]; + + fontFile = fontFileA; + + // create the transformed instance + matrix[0] = m11; + matrix[1] = -m12; + matrix[2] = m21; + matrix[3] = -m22; + font = new TTFont(fontFile, matrix); +} + +XOutputTTFont::~XOutputTTFont() { + if (font) { + delete font; + } +} + +GBool XOutputTTFont::isOk() { + return font != NULL; +} + +void XOutputTTFont::updateGC(GC gc) { +} + +void XOutputTTFont::drawChar(GfxState *state, Pixmap pixmap, int w, int h, + GC gc, GfxRGB *rgb, + double x, double y, double dx, double dy, + CharCode c, Unicode *u, int uLen) { + font->drawChar(pixmap, w, h, gc, xoutRound(x), xoutRound(y), + (int)(rgb->r * 65535), (int)(rgb->g * 65535), + (int)(rgb->b * 65535), c, u[0]); +} +#endif // !FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H) + +//------------------------------------------------------------------------ +// XOutputServer8BitFont +//------------------------------------------------------------------------ + +// Copy <fmt>, substituting <val> for one occurrence of "%s", into +// <buf>. +static void stringSubst(char *buf, int bufSize, char *fmt, char *val) { + char *p, *q; + int i; + + i = 0; + p = fmt; + while (*p) { + if (p[0] == '%' && p[1] == 's') { + q = val; + while (*q && i < bufSize - 1) { + buf[i++] = *q++; + } + p += 2; + } else { + if (i < bufSize - 1) { + buf[i++] = *p; + } + ++p; + } + } + buf[i] = '\0'; +} + +XOutputServer8BitFont::XOutputServer8BitFont(Ref *idA, GString *xlfdFmt, + UnicodeMap *xUMapA, + CharCodeToUnicode *fontUMap, + double m11OrigA, double m12OrigA, + double m21OrigA, double m22OrigA, + double m11A, double m12A, + double m21A, double m22A, + Display *displayA, + XOutputDev *xOutA): + XOutputFont(idA, m11OrigA, m12OrigA, m21OrigA, m22OrigA, + m11A, m12A, m21A, m22A, displayA, xOutA) +{ + double size, ntm11, ntm12, ntm21, ntm22; + GBool rotated; + int startSize, sz; + char fontName[500], fontSize[100]; + Unicode u; + char buf; + int i; + + // compute size and normalized transform matrix + size = sqrt(m21*m21 + m22*m22); + ntm11 = m11 / size; + ntm12 = -m12 / size; + ntm21 = m21 / size; + ntm22 = -m22 / size; + + // try to get a rotated font? + rotated = !(ntm11 > 0 && ntm22 > 0 && + fabs(ntm11 / ntm22 - 1) < 0.2 && + fabs(ntm12) < 0.01 && + fabs(ntm21) < 0.01); + + // open X font -- if font is not found (which means the server can't + // scale fonts), try progressively smaller and then larger sizes + startSize = (int)size; + if (rotated) { + sprintf(fontSize, "[%s%0.2f %s%0.2f %s%0.2f %s%0.2f]", + ntm11<0 ? "~" : "", fabs(ntm11 * size), + ntm12<0 ? "~" : "", fabs(ntm12 * size), + ntm21<0 ? "~" : "", fabs(ntm21 * size), + ntm22<0 ? "~" : "", fabs(ntm22 * size)); + } else { + sprintf(fontSize, "%d", startSize); + } + stringSubst(fontName, sizeof(fontName), xlfdFmt->getCString(), fontSize); + xFont = XLoadQueryFont(display, fontName); + if (!xFont) { + for (sz = startSize; sz >= startSize/2 && sz >= 1; --sz) { + sprintf(fontSize, "%d", sz); + stringSubst(fontName, sizeof(fontName), xlfdFmt->getCString(), fontSize); + if ((xFont = XLoadQueryFont(display, fontName))) + break; + } + if (!xFont) { + for (sz = startSize + 1; sz < startSize + 10; ++sz) { + sprintf(fontSize, "%d", sz); + stringSubst(fontName, sizeof(fontName), xlfdFmt->getCString(), + fontSize); + if ((xFont = XLoadQueryFont(display, fontName))) { + break; + } + } + if (!xFont) { + sprintf(fontSize, "%d", startSize); + stringSubst(fontName, sizeof(fontName), xlfdFmt->getCString(), + fontSize); + error(-1, "Failed to open font: '%s'", fontName); + return; + } + } + } + + // Construct char code map. + xUMap = xUMapA; + for (i = 0; i < 256; ++i) { + if (fontUMap->mapToUnicode((CID)i, &u, 1) == 1 && + xUMap->mapUnicode(u, &buf, 1) == 1) { + map[i] = buf & 0xff; + } else { + map[i] = 0; + } + } +} + +XOutputServer8BitFont::~XOutputServer8BitFont() { + if (xFont) { + XFreeFont(display, xFont); + } +} + +GBool XOutputServer8BitFont::isOk() { + return xFont != NULL; +} + +void XOutputServer8BitFont::updateGC(GC gc) { + XSetFont(display, gc, xFont->fid); +} + +void XOutputServer8BitFont::drawChar(GfxState *state, Pixmap pixmap, + int w, int h, GC gc, GfxRGB *rgb, + double x, double y, double dx, double dy, + CharCode c, Unicode *u, int uLen) { + Gushort c1; + char buf[8]; + double dx1, dy1; + int m, n, i, j, k; + + c1 = map[c]; + if (c1 > 0) { + buf[0] = (char)c1; + XDrawString(display, pixmap, gc, xoutRound(x), xoutRound(y), buf, 1); + } else { + // substituted character, using more than one character + n = 0; + for (i = 0; i < uLen; ++i) { + n += xUMap->mapUnicode(u[i], buf, sizeof(buf)); + } + if (n > 0) { + dx1 = dx / n; + dy1 = dy / n; + k = 0; + for (i = 0; i < uLen; ++i) { + m = xUMap->mapUnicode(u[i], buf, sizeof(buf)); + for (j = 0; j < m; ++j) { + XDrawString(display, pixmap, gc, + xoutRound(x + k*dx1), xoutRound(y + k*dy1), + buf + j, 1); + ++k; + } + } + } + } +} + +//------------------------------------------------------------------------ +// XOutputServer16BitFont +//------------------------------------------------------------------------ + +XOutputServer16BitFont::XOutputServer16BitFont(Ref *idA, GString *xlfdFmt, + UnicodeMap *xUMapA, + CharCodeToUnicode *fontUMap, + double m11OrigA, + double m12OrigA, + double m21OrigA, + double m22OrigA, + double m11A, double m12A, + double m21A, double m22A, + Display *displayA, + XOutputDev *xOutA): + XOutputFont(idA, m11OrigA, m12OrigA, m21OrigA, m22OrigA, + m11A, m12A, m21A, m22A, displayA, xOutA) +{ + double size, ntm11, ntm12, ntm21, ntm22; + GBool rotated; + int startSize, sz; + char fontName[500], fontSize[100]; + + xUMap = xUMapA; + xUMap->incRefCnt(); + + // compute size and normalized transform matrix + size = sqrt(m21*m21 + m22*m22); + ntm11 = m11 / size; + ntm12 = -m12 / size; + ntm21 = m21 / size; + ntm22 = -m22 / size; + + // try to get a rotated font? + rotated = !(ntm11 > 0 && ntm22 > 0 && + fabs(ntm11 / ntm22 - 1) < 0.2 && + fabs(ntm12) < 0.01 && + fabs(ntm21) < 0.01); + + // open X font -- if font is not found (which means the server can't + // scale fonts), try progressively smaller and then larger sizes + startSize = (int)size; + if (rotated) { + sprintf(fontSize, "[%s%0.2f %s%0.2f %s%0.2f %s%0.2f]", + ntm11<0 ? "~" : "", fabs(ntm11 * size), + ntm12<0 ? "~" : "", fabs(ntm12 * size), + ntm21<0 ? "~" : "", fabs(ntm21 * size), + ntm22<0 ? "~" : "", fabs(ntm22 * size)); + } else { + sprintf(fontSize, "%d", startSize); + } + stringSubst(fontName, sizeof(fontName), xlfdFmt->getCString(), fontSize); + xFont = XLoadQueryFont(display, fontName); + if (!xFont) { + for (sz = startSize; sz >= startSize/2 && sz >= 1; --sz) { + sprintf(fontSize, "%d", sz); + stringSubst(fontName, sizeof(fontName), xlfdFmt->getCString(), fontSize); + if ((xFont = XLoadQueryFont(display, fontName))) + break; + } + if (!xFont) { + for (sz = startSize + 1; sz < startSize + 10; ++sz) { + sprintf(fontSize, "%d", sz); + stringSubst(fontName, sizeof(fontName), xlfdFmt->getCString(), + fontSize); + if ((xFont = XLoadQueryFont(display, fontName))) { + break; + } + } + if (!xFont) { + sprintf(fontSize, "%d", startSize); + stringSubst(fontName, sizeof(fontName), xlfdFmt->getCString(), + fontSize); + error(-1, "Failed to open font: '%s'", fontName); + return; + } + } + } +} + +XOutputServer16BitFont::~XOutputServer16BitFont() { + xUMap->decRefCnt(); + if (xFont) { + XFreeFont(display, xFont); + } +} + +GBool XOutputServer16BitFont::isOk() { + return xFont != NULL; +} + +void XOutputServer16BitFont::updateGC(GC gc) { + XSetFont(display, gc, xFont->fid); +} + +void XOutputServer16BitFont::drawChar(GfxState *state, Pixmap pixmap, + int w, int h, GC gc, GfxRGB *rgb, + double x, double y, double dx, double dy, + CharCode c, Unicode *u, int uLen) { + char buf[16]; + XChar2b c1; + double dx1, dy1; + int m, n, i, j, k; + + n = 0; + for (i = 0; i < uLen; ++i) { + n += xUMap->mapUnicode(u[i], buf, sizeof(buf)); + } + if (n > 0) { + dx1 = dx / n; + dy1 = dy / n; + k = 0; + for (i = 0; i < uLen; ++i) { + m = xUMap->mapUnicode(u[i], buf, sizeof(buf)); + for (j = 0; j+1 < m; j += 2) { + c1.byte1 = buf[j]; + c1.byte2 = buf[j+1]; + XDrawString16(display, pixmap, gc, + xoutRound(x + k*dx1), xoutRound(y + k*dy1), + &c1, 1); + ++k; + } + } + } else if (c != 0) { + // some PDF files use CID 0, which is .notdef, so just ignore it + error(-1, "Unknown character (CID=%d Unicode=%04x)", + c, uLen > 0 ? u[0] : (Unicode)0); + } +} + +//------------------------------------------------------------------------ +// XOutputFontCache +//------------------------------------------------------------------------ + +#if HAVE_T1LIB_H +XOutputT1FontFile::~XOutputT1FontFile() { + delete fontFile; + if (tmpFileName) { + unlink(tmpFileName->getCString()); + delete tmpFileName; + } +} +#endif + +#if FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H) +XOutputFTFontFile::~XOutputFTFontFile() { + delete fontFile; + if (tmpFileName) { + unlink(tmpFileName->getCString()); + delete tmpFileName; + } +} +#endif + +#if !FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H) +XOutputTTFontFile::~XOutputTTFontFile() { + delete fontFile; + if (tmpFileName) { + unlink(tmpFileName->getCString()); + delete tmpFileName; + } +} +#endif + +XOutputFontCache::XOutputFontCache(Display *displayA, Guint depthA, + XOutputDev *xOutA, + FontRastControl t1libControlA, + FontRastControl freetypeControlA) { + display = displayA; + depth = depthA; + xOut = xOutA; + +#if HAVE_T1LIB_H + t1Engine = NULL; + t1libControl = t1libControlA; +#endif + +#if FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H) + ftEngine = NULL; +#endif +#if !FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H) + ttEngine = NULL; +#endif +#if HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H + freetypeControl = freetypeControlA; +#endif + + clear(); +} + +XOutputFontCache::~XOutputFontCache() { + delFonts(); +} + +void XOutputFontCache::startDoc(int screenNum, Visual *visual, + Colormap colormap, GBool trueColor, + int rMul, int gMul, int bMul, + int rShift, int gShift, int bShift, + Gulong *colors, int numColors) { + delFonts(); + clear(); + +#if HAVE_T1LIB_H + if (t1libControl != fontRastNone) { + t1Engine = new T1FontEngine(display, visual, depth, colormap, + t1libControl == fontRastAALow || + t1libControl == fontRastAAHigh, + t1libControl == fontRastAAHigh); + if (t1Engine->isOk()) { + if (trueColor) { + t1Engine->useTrueColor(rMul, rShift, gMul, gShift, bMul, bShift); + } else { + t1Engine->useColorCube(colors, numColors); + } + } else { + delete t1Engine; + t1Engine = NULL; + } + } +#endif // HAVE_T1LIB_H + +#if FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H) + if (freetypeControl != fontRastNone) { + ftEngine = new FTFontEngine(display, visual, depth, colormap, + freetypeControl == fontRastAALow || + freetypeControl == fontRastAAHigh); + if (ftEngine->isOk()) { + if (trueColor) { + ftEngine->useTrueColor(rMul, rShift, gMul, gShift, bMul, bShift); + } else { + ftEngine->useColorCube(colors, numColors); + } + } else { + delete ftEngine; + ftEngine = NULL; + } + } +#endif // FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H) + +#if !FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H) + if (freetypeControl != fontRastNone) { + ttEngine = new TTFontEngine(display, visual, depth, colormap, + freetypeControl == fontRastAALow || + freetypeControl == fontRastAAHigh); + if (ttEngine->isOk()) { + if (trueColor) { + ttEngine->useTrueColor(rMul, rShift, gMul, gShift, bMul, bShift); + } else { + ttEngine->useColorCube(colors, numColors); + } + } else { + delete ttEngine; + ttEngine = NULL; + } + } +#endif // !FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H) +} + +void XOutputFontCache::delFonts() { + int i; + + for (i = 0; i < nFonts; ++i) { + delete fonts[i]; + } + +#if HAVE_T1LIB_H + // delete Type 1 font files + deleteGList(t1FontFiles, XOutputT1FontFile); + if (t1Engine) { + delete t1Engine; + } +#endif + +#if FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H) + // delete FreeType font files + deleteGList(ftFontFiles, XOutputFTFontFile); + if (ftEngine) { + delete ftEngine; + } +#endif + +#if !FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H) + // delete TrueType fonts + deleteGList(ttFontFiles, XOutputTTFontFile); + if (ttEngine) { + delete ttEngine; + } +#endif +} + +void XOutputFontCache::clear() { + int i; + + for (i = 0; i < xOutFontCacheSize; ++i) { + fonts[i] = NULL; + } + nFonts = 0; + +#if HAVE_T1LIB_H + // clear Type 1 font files + t1FontFiles = new GList(); +#endif + +#if FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H) + // clear FreeType font cache + ftFontFiles = new GList(); +#endif + +#if !FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H) + // clear TrueType font cache + ttFontFiles = new GList(); +#endif +} + +XOutputFont *XOutputFontCache::getFont(XRef *xref, GfxFont *gfxFont, + double m11, double m12, + double m21, double m22) { + XOutputFont *font; + DisplayFontParam *dfp; + GString *substName; + double m11New, m12New, m21New, m22New; + double w1, w2, v; + double *fm; + char *name; + int index; + int code; + int i, j; + + // is it the most recently used font? + if (nFonts > 0 && fonts[0]->matches(gfxFont->getID(), m11, m12, m21, m22)) { + return fonts[0]; + } + + // is it in the cache? + for (i = 1; i < nFonts; ++i) { + if (fonts[i]->matches(gfxFont->getID(), m11, m12, m21, m22)) { + font = fonts[i]; + for (j = i; j > 0; --j) { + fonts[j] = fonts[j-1]; + } + fonts[0] = font; + return font; + } + } + + // try for a cached FontFile, an embedded font, or an external font + // file + font = NULL; + switch (gfxFont->getType()) { + case fontType1: + case fontType1C: +#if HAVE_T1LIB_H + if (t1libControl != fontRastNone) { + font = tryGetT1Font(xref, gfxFont, m11, m12, m21, m22); + } +#endif +#if FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H) + if (!font) { + if (freetypeControl != fontRastNone) { + font = tryGetFTFont(xref, gfxFont, m11, m12, m21, m22); + } + } +#endif + break; + case fontTrueType: + case fontCIDType2: +#if FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H) + if (freetypeControl != fontRastNone) { + font = tryGetFTFont(xref, gfxFont, m11, m12, m21, m22); + } +#endif +#if !FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H) + if (freetypeControl != fontRastNone) { + font = tryGetTTFont(xref, gfxFont, m11, m12, m21, m22); + } +#endif + break; + case fontCIDType0C: +#if FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H) + if (freetypeControl != fontRastNone) { + font = tryGetFTFont(xref, gfxFont, m11, m12, m21, m22); + } +#endif + break; + default: + break; + } + + if (!font) { + + // search for a display font mapping + dfp = NULL; + if (gfxFont->isCIDFont()) { + if (((GfxCIDFont *)gfxFont)->getCollection()) { + dfp = globalParams-> + getDisplayCIDFont(gfxFont->getName(), + ((GfxCIDFont *)gfxFont)->getCollection()); + } else { + // this error (no CMap file) was already reported by GfxFont + return NULL; + } + } else { + if (gfxFont->getName()) { + dfp = globalParams->getDisplayFont(gfxFont->getName()); + } + } + if (dfp) { + font = tryGetFont(xref, dfp, gfxFont, m11, m12, m21, m22, + m11, m12, m21, m22, gFalse); + } + + // substitute a font (8-bit fonts only) + if (!font && !gfxFont->isCIDFont()) { + + // choose a substitute font + if (gfxFont->isFixedWidth()) { + index = 8; + } else if (gfxFont->isSerif()) { + index = 4; + } else { + index = 0; + } + if (gfxFont->isBold()) { + index += 2; + } + if (gfxFont->isItalic()) { + index += 1; + } + substName = new GString(xOutSubstFonts[index].name); + + // adjust the font matrix -- compare the width of 'm' in the + // original font and the substituted font + m11New = m11; + m12New = m12; + m21New = m21; + m22New = m22; + 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 = xOutSubstFonts[index].mWidth; + if (gfxFont->getType() == fontType3) { + // This is a hack which makes it possible to substitute for some + // Type 3 fonts. The problem is that it's impossible to know what + // the base coordinate system used in the font is without actually + // rendering the font. This code tries to guess by looking at the + // width of the character 'm' (which breaks if the font is a + // subset that doesn't contain 'm'). + if (w1 > 0 && (w1 > 1.1 * w2 || w1 < 0.9 * w2)) { + w1 /= w2; + m11New *= w1; + m12New *= w1; + m21New *= w1; + m22New *= w1; + } + fm = gfxFont->getFontMatrix(); + v = (fm[0] == 0) ? 1 : (fm[3] / fm[0]); + m21New *= v; + m22New *= v; + } else 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; + m11New *= w1; + m21New *= w1; + } + } + } + + // get the font + dfp = globalParams->getDisplayFont(substName); + delete substName; + if (!dfp) { + // this should never happen since GlobalParams sets up default + // mappings for the Base-14 fonts + error(-1, "Couldn't find a font for '%s'", + gfxFont->getName()->getCString()); + return NULL; + } + font = tryGetFont(xref, dfp, gfxFont, m11, m12, m21, m22, + m11New, m12New, m21New, m22New, gTrue); + } + } + + // check for error + if (!font) { + // This will happen if the user specifies a bogus font in the + // config file (a non-existent font file or a font that requires a + // rasterizer that is disabled or wasn't built in), or if a CID + // font has no associated font in the config file. + if (gfxFont->isCIDFont()) { + error(-1, "Couldn't find a font for the '%s' character collection", + ((GfxCIDFont *)gfxFont)->getCollection()->getCString()); + } else { + error(-1, "Couldn't find a font for '%s'", + gfxFont->getName() ? + gfxFont->getName()->getCString() : "[unnamed]"); + } + return NULL; + } + + // insert font in cache + if (nFonts == xOutFontCacheSize) { + --nFonts; + delete fonts[nFonts]; + } + for (j = nFonts; j > 0; --j) { + fonts[j] = fonts[j-1]; + } + fonts[0] = font; + ++nFonts; + + return font; +} + +XOutputFont *XOutputFontCache::tryGetFont(XRef *xref, DisplayFontParam *dfp, + GfxFont *gfxFont, + double m11Orig, double m12Orig, + double m21Orig, double m22Orig, + double m11, double m12, + double m21, double m22, + GBool subst) { + XOutputFont *font; + + font = NULL; + + // create the new font + switch (dfp->kind) { + + case displayFontX: + font = tryGetServerFont(dfp->x.xlfd, dfp->x.encoding, gfxFont, + m11Orig, m12Orig, m21Orig, m22Orig, + m11, m12, m21, m22); + break; + + case displayFontT1: +#if HAVE_T1LIB_H + if (t1libControl != fontRastNone) { + font = tryGetT1FontFromFile(xref, dfp->t1.fileName, gFalse, gfxFont, + m11Orig, m12Orig, m21Orig, m22Orig, + m11, m12, m21, m22, subst); + } +#endif +#if FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H) + if (!font) { + if (freetypeControl != fontRastNone) { + font = tryGetFTFontFromFile(xref, dfp->t1.fileName, gFalse, gfxFont, + m11Orig, m12Orig, m21Orig, m22Orig, + m11, m12, m21, m22, subst); + } + } +#endif +#if !((FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H)) || defined(HAVE_T1LIB_H)) + error(-1, "Config file specifies a Type 1 font,"); + error(-1, "but xpdf was not built with t1lib or FreeType2 support"); +#endif + break; + + case displayFontTT: +#if FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H) + if (freetypeControl != fontRastNone) { + font = tryGetFTFontFromFile(xref, dfp->tt.fileName, gFalse, gfxFont, + m11Orig, m12Orig, m21Orig, m22Orig, + m11, m12, m21, m22, subst); + } +#endif +#if !FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H) + if (freetypeControl != fontRastNone) { + font = tryGetTTFontFromFile(xref, dfp->tt.fileName, gFalse, gfxFont, + m11Orig, m12Orig, m21Orig, m22Orig, + m11, m12, m21, m22, subst); + } +#endif +#if !(HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H) + error(-1, "Config file specifies a TrueType font,"); + error(-1, "but xpdf was not built with FreeType support"); + dfp = NULL; +#endif + break; + } + + return font; +} + +#if HAVE_T1LIB_H +XOutputFont *XOutputFontCache::tryGetT1Font(XRef *xref, + GfxFont *gfxFont, + double m11, double m12, + double m21, double m22) { + Ref *id; + XOutputT1FontFile *xFontFile; + XOutputFont *font; + Ref embRef; + GString *fileName; + FILE *f; + char *fontBuf; + int fontLen; + Type1CFontFile *ff; + Object refObj, strObj; + int c; + int i; + + // check the already available font files + id = gfxFont->getID(); + for (i = 0; i < t1FontFiles->getLength(); ++i) { + xFontFile = (XOutputT1FontFile *)t1FontFiles->get(i); + if (xFontFile->num == id->num && xFontFile->gen == id->gen && + !xFontFile->subst) { + font = new XOutputT1Font(id, xFontFile->fontFile, + m11, m12, m21, m22, + m11, m12, m21, m22, display, xOut); + if (!font->isOk()) { + delete font; + return NULL; + } + return font; + } + } + + // check for an embedded font + if (gfxFont->getEmbeddedFontID(&embRef)) { + + // create the font file + fileName = NULL; + if (!openTempFile(&fileName, &f, "wb", NULL)) { + error(-1, "Couldn't create temporary Type 1 font file"); + return NULL; + } + if (gfxFont->getType() == fontType1C) { + if (!(fontBuf = gfxFont->readEmbFontFile(xref, &fontLen))) { + fclose(f); + return NULL; + } + ff = new Type1CFontFile(fontBuf, fontLen); + ff->convertToType1(outputToFile, f); + delete ff; + gfree(fontBuf); + } else { // fontType1 + refObj.initRef(embRef.num, embRef.gen); + refObj.fetch(xref, &strObj); + refObj.free(); + strObj.streamReset(); + while ((c = strObj.streamGetChar()) != EOF) { + fputc(c, f); + } + strObj.streamClose(); + strObj.free(); + } + fclose(f); + + // create the Font + font = tryGetT1FontFromFile(xref, fileName, gTrue, gfxFont, + m11, m12, m21, m22, + m11, m12, m21, m22, gFalse); + + // on systems with Unix hard link semantics, this will remove the + // last link to the temp file + unlink(fileName->getCString()); + + delete fileName; + + // check for an external font file + } else if ((fileName = gfxFont->getExtFontFile())) { + font = tryGetT1FontFromFile(xref, fileName, gFalse, gfxFont, + m11, m12, m21, m22, + m11, m12, m21, m22, gFalse); + + } else { + font = NULL; + } + + return font; +} + +XOutputFont *XOutputFontCache::tryGetT1FontFromFile(XRef *xref, + GString *fileName, + GBool deleteFile, + GfxFont *gfxFont, + double m11Orig, + double m12Orig, + double m21Orig, + double m22Orig, + double m11, double m12, + double m21, double m22, + GBool subst) { + Ref *id; + T1FontFile *fontFile; + XOutputFont *font; + + // create the t1lib font file + fontFile = new T1FontFile(t1Engine, fileName->getCString(), + ((Gfx8BitFont *)gfxFont)->getEncoding(), + gfxFont->getFontBBox()); + if (!fontFile->isOk()) { + error(-1, "Couldn't create t1lib font from '%s'", + fileName->getCString()); + delete fontFile; + if (deleteFile) { + unlink(fileName->getCString()); + } + return NULL; + } + + // add to list + id = gfxFont->getID(); + t1FontFiles->append(new XOutputT1FontFile(id->num, id->gen, + subst, fontFile, + deleteFile ? fileName->copy() + : (GString *)NULL)); + + // create the Font + font = new XOutputT1Font(gfxFont->getID(), fontFile, + m11Orig, m12Orig, m21Orig, m22Orig, + m11, m12, m21, m22, display, xOut); + if (!font->isOk()) { + delete font; + return NULL; + } + return font; +} +#endif // HAVE_T1LIB_H + +#if FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H) +XOutputFont *XOutputFontCache::tryGetFTFont(XRef *xref, + GfxFont *gfxFont, + double m11, double m12, + double m21, double m22) { + Ref *id; + XOutputFTFontFile *xFontFile; + XOutputFont *font; + Ref embRef; + GString *fileName; + FILE *f; +#if 1 //~ need this until FT can handle fonts with missing tables + char *fontBuf; + int fontLen; + TrueTypeFontFile *ff; +#endif + Object refObj, strObj; + int c; + int i; + + // check the already available font files + id = gfxFont->getID(); + for (i = 0; i < ftFontFiles->getLength(); ++i) { + xFontFile = (XOutputFTFontFile *)ftFontFiles->get(i); + if (xFontFile->num == id->num && xFontFile->gen == id->gen && + !xFontFile->subst) { + font = new XOutputFTFont(id, xFontFile->fontFile, + m11, m12, m21, m22, + m11, m12, m21, m22, display, xOut); + if (!font->isOk()) { + delete font; + return NULL; + } + return font; + } + } + + // check for an embedded font + if (gfxFont->getEmbeddedFontID(&embRef)) { + + // create the font file + fileName = NULL; + if (!openTempFile(&fileName, &f, "wb", NULL)) { + error(-1, "Couldn't create temporary TrueType font file"); + return NULL; + } +#if 1 //~ need this until FT can handle fonts with missing tables + if (gfxFont->getType() == fontTrueType || + gfxFont->getType() == fontCIDType2) { + if (!(fontBuf = gfxFont->readEmbFontFile(xref, &fontLen))) { + fclose(f); + return NULL; + } + ff = new TrueTypeFontFile(fontBuf, fontLen); + ff->writeTTF(f); + delete ff; + gfree(fontBuf); + } else { + refObj.initRef(embRef.num, embRef.gen); + refObj.fetch(xref, &strObj); + refObj.free(); + strObj.streamReset(); + while ((c = strObj.streamGetChar()) != EOF) { + fputc(c, f); + } + strObj.streamClose(); + strObj.free(); + } +#else + refObj.initRef(embRef.num, embRef.gen); + refObj.fetch(xref, &strObj); + refObj.free(); + strObj.streamReset(); + while ((c = strObj.streamGetChar()) != EOF) { + fputc(c, f); + } + strObj.streamClose(); + strObj.free(); +#endif + fclose(f); + + // create the Font + font = tryGetFTFontFromFile(xref, fileName, gTrue, gfxFont, + m11, m12, m21, m22, + m11, m12, m21, m22, gFalse); + + // on systems with Unix hard link semantics, this will remove the + // last link to the temp file + unlink(fileName->getCString()); + + delete fileName; + + // check for an external font file + } else if ((fileName = gfxFont->getExtFontFile())) { + font = tryGetFTFontFromFile(xref, fileName, gFalse, gfxFont, + m11, m12, m21, m22, + m11, m12, m21, m22, gFalse); + + } else { + font = NULL; + } + + return font; +} + +XOutputFont *XOutputFontCache::tryGetFTFontFromFile(XRef *xref, + GString *fileName, + GBool deleteFile, + GfxFont *gfxFont, + double m11Orig, + double m12Orig, + double m21Orig, + double m22Orig, + double m11, double m12, + double m21, double m22, + GBool subst) { + Ref *id; + FTFontFile *fontFile; + XOutputFont *font; + + // create the FreeType font file + if (gfxFont->isCIDFont()) { + if (gfxFont->getType() == fontCIDType2) { + fontFile = new FTFontFile(ftEngine, fileName->getCString(), + ((GfxCIDFont *)gfxFont)->getCIDToGID(), + ((GfxCIDFont *)gfxFont)->getCIDToGIDLen()); + } else { // fontCIDType0C + fontFile = new FTFontFile(ftEngine, fileName->getCString()); + } + } else { + fontFile = new FTFontFile(ftEngine, fileName->getCString(), + ((Gfx8BitFont *)gfxFont)->getEncoding(), + ((Gfx8BitFont *)gfxFont)->getHasEncoding()); + } + if (!fontFile->isOk()) { + error(-1, "Couldn't create FreeType font from '%s'", + fileName->getCString()); + delete fontFile; + if (deleteFile) { + unlink(fileName->getCString()); + } + return NULL; + } + + // add to list + id = gfxFont->getID(); + ftFontFiles->append(new XOutputFTFontFile(id->num, id->gen, + subst, fontFile, + deleteFile ? fileName->copy() + : (GString *)NULL)); + + // create the Font + font = new XOutputFTFont(gfxFont->getID(), fontFile, + m11Orig, m12Orig, m21Orig, m22Orig, + m11, m12, m21, m22, display, xOut); + if (!font->isOk()) { + delete font; + return NULL; + } + return font; +} +#endif // FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H) + +#if !FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H) +XOutputFont *XOutputFontCache::tryGetTTFont(XRef *xref, + GfxFont *gfxFont, + double m11, double m12, + double m21, double m22) { + Ref *id; + XOutputTTFontFile *xFontFile; + XOutputFont *font; + Ref embRef; + GString *fileName; + FILE *f; + Object refObj, strObj; + int c; + int i; + + // check the already available font files + id = gfxFont->getID(); + xFontFile = NULL; + for (i = 0; i < ttFontFiles->getLength(); ++i) { + xFontFile = (XOutputTTFontFile *)ttFontFiles->get(i); + if (xFontFile->num == id->num && xFontFile->gen == id->gen && + !xFontFile->subst) { + font = new XOutputTTFont(id, xFontFile->fontFile, + m11, m12, m21, m22, + m11, m12, m21, m22, display, xOut); + if (!font->isOk()) { + delete font; + return NULL; + } + return font; + } + } + + // check for an embedded font + if (gfxFont->getEmbeddedFontID(&embRef)) { + + // create the font file + fileName = NULL; + if (!openTempFile(&fileName, &f, "wb", NULL)) { + error(-1, "Couldn't create temporary TrueType font file"); + return NULL; + } + refObj.initRef(embRef.num, embRef.gen); + refObj.fetch(xref, &strObj); + refObj.free(); + strObj.streamReset(); + while ((c = strObj.streamGetChar()) != EOF) { + fputc(c, f); + } + strObj.streamClose(); + strObj.free(); + fclose(f); + + // create the Font + font = tryGetTTFontFromFile(xref, fileName, gTrue, gfxFont, + m11, m12, m21, m22, + m11, m12, m21, m22, gFalse); + + // on systems with Unix hard link semantics, this will remove the + // last link to the temp file + unlink(fileName->getCString()); + + delete fileName; + + } else if ((fileName = gfxFont->getExtFontFile())) { + font = tryGetTTFontFromFile(xref, fileName, gFalse, gfxFont, + m11, m12, m21, m22, + m11, m12, m21, m22, gFalse); + + } else { + font = NULL; + } + + return font; +} + +XOutputFont *XOutputFontCache::tryGetTTFontFromFile(XRef *xref, + GString *fileName, + GBool deleteFile, + GfxFont *gfxFont, + double m11Orig, + double m12Orig, + double m21Orig, + double m22Orig, + double m11, double m12, + double m21, double m22, + GBool subst) { + Ref *id; + TTFontFile *fontFile; + XOutputFont *font; + + // create the FreeType font file + if (gfxFont->isCIDFont()) { + // fontCIDType2 + fontFile = new TTFontFile(ttEngine, fileName->getCString(), + ((GfxCIDFont *)gfxFont)->getCIDToGID(), + ((GfxCIDFont *)gfxFont)->getCIDToGIDLen()); + } else { + fontFile = new TTFontFile(ttEngine, fileName->getCString(), + ((Gfx8BitFont *)gfxFont)->getEncoding(), + ((Gfx8BitFont *)gfxFont)->getHasEncoding()); + } + if (!fontFile->isOk()) { + error(-1, "Couldn't create FreeType font from '%s'", + fileName->getCString()); + delete fontFile; + if (deleteFile) { + unlink(fileName->getCString()); + } + return NULL; + } + + // add to list + id = gfxFont->getID(); + ttFontFiles->append(new XOutputTTFontFile(id->num, id->gen, + subst, fontFile, + deleteFile ? fileName->copy() + : (GString *)NULL)); + + // create the Font + font = new XOutputTTFont(gfxFont->getID(), fontFile, + m11Orig, m12Orig, m21Orig, m22Orig, + m11, m12, m21, m22, display, xOut); + if (!font->isOk()) { + delete font; + return NULL; + } + return font; +} +#endif // !FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H) + +XOutputFont *XOutputFontCache::tryGetServerFont(GString *xlfd, + GString *encodingName, + GfxFont *gfxFont, + double m11Orig, double m12Orig, + double m21Orig, double m22Orig, + double m11, double m12, + double m21, double m22) { + XOutputFont *font; + UnicodeMap *uMap; + CharCodeToUnicode *ctu; + + uMap = globalParams->getUnicodeMap(encodingName); + if (gfxFont->isCIDFont()) { + ctu = ((GfxCIDFont *)gfxFont)->getToUnicode(); + font = new XOutputServer16BitFont(gfxFont->getID(), xlfd, uMap, ctu, + m11Orig, m12Orig, m21Orig, m22Orig, + m11, m12, m21, m22, + display, xOut); + ctu->decRefCnt(); + } else { + ctu = ((Gfx8BitFont *)gfxFont)->getToUnicode(); + font = new XOutputServer8BitFont(gfxFont->getID(), xlfd, uMap, ctu, + m11Orig, m12Orig, m21Orig, m22Orig, + m11, m12, m21, m22, + display, xOut); + ctu->decRefCnt(); + } + uMap->decRefCnt(); + if (!font->isOk()) { + delete font; + return NULL; + } + return font; +} + +//------------------------------------------------------------------------ +// T3FontCache +//------------------------------------------------------------------------ + +struct T3FontCacheTag { + Gushort code; + Gushort mru; // valid bit (0x8000) and MRU index + double wx, wy; // untransformed glyph metrics +}; + +class T3FontCache { +public: + + T3FontCache(Ref *fontID, double m11A, double m12A, + double m21A, double m22A, + int glyphXA, int glyphYA, int glyphWA, int glyphHA, + Display *displayA, Visual *visual, Guint depth, + Pixmap origPixmap); + ~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 pixmaps + int glyphW, glyphH; // size of glyph pixmaps, in pixels + int glyphSize; // size of glyph pixmaps, 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 + Display *display; + Pixmap pixmap; + XImage *image; +}; + +T3FontCache::T3FontCache(Ref *fontIDA, double m11A, double m12A, + double m21A, double m22A, + int glyphXA, int glyphYA, int glyphWA, int glyphHA, + Display *displayA, Visual *visual, Guint depth, + Pixmap origPixmap) { + int i; + + fontID = *fontIDA; + m11 = m11A; + m12 = m12A; + m21 = m21A; + m22 = m22A; + glyphX = glyphXA; + glyphY = glyphYA; + glyphW = glyphWA; + glyphH = glyphHA; + glyphSize = glyphW * 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); + } + display = displayA; + pixmap = XCreatePixmap(display, origPixmap, glyphW, glyphH, depth); + image = XCreateImage(display, visual, depth, ZPixmap, 0, NULL, + glyphW, glyphH, 8, 0); + image->data = (char *)gmalloc(glyphH * image->bytes_per_line); +} + +T3FontCache::~T3FontCache() { + gfree(cacheData); + gfree(cacheTags); + XFreePixmap(display, pixmap); + gfree(image->data); + image->data = NULL; + XDestroyImage(image); +} + +struct T3GlyphStack { + GBool cacheable; + Gushort code; + T3FontCache *cache; + int cacheIdx; + T3FontCacheTag *cacheTag; + Guchar *cacheData; + double x, y; + Unicode *u; + int uLen; + GfxRGB color; + int origPixmapW, origPixmapH; + Pixmap origPixmap; + GC origStrokeGC; + GC origFillGC; + Region origClipRegion; + double origCTM4, origCTM5; + double wx, wy; // untransformed glyph metrics + T3GlyphStack *next; +}; + +//------------------------------------------------------------------------ +// XOutputDev +//------------------------------------------------------------------------ + +XOutputDev::XOutputDev(Display *displayA, int screenNumA, + Visual *visualA, Colormap colormapA, + GBool reverseVideoA, unsigned long paperColorA, + GBool installCmap, int rgbCubeSize, + int forceDepth) { + XVisualInfo visualTempl; + XVisualInfo *visualList; + int nVisuals; + Gulong mask; + XColor xcolor; + XColor *xcolors; + int r, g, b, n, m; + GBool ok; + + // no document yet + xref = NULL; + + // display / screen / visual / colormap + display = displayA; + screenNum = screenNumA; + visual = visualA; + colormap = colormapA; + + // no pixmap yet + pixmapW = pixmapH = 0; + + // check for TrueColor visual + if (forceDepth != 0) { + depth = forceDepth; + trueColor = depth >= 16; + } else { + visualTempl.visualid = XVisualIDFromVisual(visual); + visualList = XGetVisualInfo(display, VisualIDMask, + &visualTempl, &nVisuals); + if (nVisuals < 1) { + // this shouldn't happen + XFree((XPointer)visualList); + visualList = XGetVisualInfo(display, VisualNoMask, &visualTempl, + &nVisuals); + } + depth = visualList->depth; + if (visualList->c_class == TrueColor) { + trueColor = gTrue; + for (mask = visualList->red_mask, rShift = 0; + mask && !(mask & 1); + mask >>= 1, ++rShift) ; + rMul = (int)mask; + for (mask = visualList->green_mask, gShift = 0; + mask && !(mask & 1); + mask >>= 1, ++gShift) ; + gMul = (int)mask; + for (mask = visualList->blue_mask, bShift = 0; + mask && !(mask & 1); + mask >>= 1, ++bShift) ; + bMul = (int)mask; + } else { + trueColor = gFalse; + } + XFree((XPointer)visualList); + } + + // allocate a color cube + if (!trueColor) { + redMap[BlackPixel(display, screenNum) & 0xff] = 0; + redMap[WhitePixel(display, screenNum) & 0xff] = 1; + + // set colors in private colormap + if (installCmap) { + for (numColors = 6; numColors >= 2; --numColors) { + m = numColors * numColors * numColors; + if (XAllocColorCells(display, colormap, False, NULL, 0, colors, m)) { + break; + } + } + if (numColors >= 2) { + m = numColors * numColors * numColors; + xcolors = (XColor *)gmalloc(m * sizeof(XColor)); + n = 0; + for (r = 0; r < numColors; ++r) { + for (g = 0; g < numColors; ++g) { + for (b = 0; b < numColors; ++b) { + xcolors[n].pixel = colors[n]; + xcolors[n].red = (r * 65535) / (numColors - 1); + xcolors[n].green = (g * 65535) / (numColors - 1); + xcolors[n].blue = (b * 65535) / (numColors - 1); + xcolors[n].flags = DoRed | DoGreen | DoBlue; + redMap[xcolors[n].pixel & 0xff] = xcolors[n].red / 65535.0; + ++n; + } + } + } + XStoreColors(display, colormap, xcolors, m); + gfree(xcolors); + } else { + numColors = 1; + colors[0] = BlackPixel(display, screenNum); + colors[1] = WhitePixel(display, screenNum); + } + + // allocate colors in shared colormap + } else { + if (rgbCubeSize > maxRGBCube) { + rgbCubeSize = maxRGBCube; + } + ok = gFalse; + for (numColors = rgbCubeSize; numColors >= 2; --numColors) { + ok = gTrue; + n = 0; + for (r = 0; r < numColors && ok; ++r) { + for (g = 0; g < numColors && ok; ++g) { + for (b = 0; b < numColors && ok; ++b) { + if (n == 0) { + colors[n] = BlackPixel(display, screenNum); + redMap[colors[n] & 0xff] = 0; + ++n; + } else { + xcolor.red = (r * 65535) / (numColors - 1); + xcolor.green = (g * 65535) / (numColors - 1); + xcolor.blue = (b * 65535) / (numColors - 1); + if (XAllocColor(display, colormap, &xcolor)) { + colors[n++] = xcolor.pixel; + redMap[xcolor.pixel & 0xff] = xcolor.red / 65535.0; + } else { + ok = gFalse; + } + } + } + } + } + if (ok) { + break; + } + XFreeColors(display, colormap, &colors[1], n-1, 0); + } + if (!ok) { + numColors = 1; + colors[0] = BlackPixel(display, screenNum); + colors[1] = WhitePixel(display, screenNum); + } + } + } + + // misc parameters + reverseVideo = reverseVideoA; + paperColor = paperColorA; + + // set up the font cache and fonts + gfxFont = NULL; + font = NULL; + fontCache = new XOutputFontCache(display, depth, this, + globalParams->getT1libControl(), + globalParams->getFreeTypeControl()); + nT3Fonts = 0; + t3GlyphStack = NULL; + + // empty state stack + save = NULL; + + // create text object + text = new TextPage(gFalse); +} + +XOutputDev::~XOutputDev() { + int i; + + delete fontCache; + for (i = 0; i < nT3Fonts; ++i) { + delete t3FontCache[i]; + } + delete text; +} + +void XOutputDev::startDoc(XRef *xrefA) { + int i; + + xref = xrefA; + fontCache->startDoc(screenNum, visual, colormap, trueColor, rMul, gMul, bMul, + rShift, gShift, bShift, colors, numColors); + for (i = 0; i < nT3Fonts; ++i) { + delete t3FontCache[i]; + } + nT3Fonts = 0; +} + +void XOutputDev::startPage(int pageNum, GfxState *state) { + XGCValues gcValues; + XRectangle rect; + + // default line flatness + flatness = 0; + + // allocate GCs + gcValues.foreground = BlackPixel(display, screenNum); + gcValues.background = WhitePixel(display, screenNum); + gcValues.line_width = 0; + gcValues.line_style = LineSolid; + strokeGC = XCreateGC(display, pixmap, + GCForeground | GCBackground | GCLineWidth | GCLineStyle, + &gcValues); + fillGC = XCreateGC(display, pixmap, + GCForeground | GCBackground | GCLineWidth | GCLineStyle, + &gcValues); + gcValues.foreground = paperColor; + paperGC = XCreateGC(display, pixmap, + GCForeground | GCBackground | GCLineWidth | GCLineStyle, + &gcValues); + + // initialize clip region + clipRegion = XCreateRegion(); + rect.x = rect.y = 0; + rect.width = pixmapW; + rect.height = pixmapH; + XUnionRectWithRegion(&rect, clipRegion, clipRegion); + XSetRegion(display, strokeGC, clipRegion); + XSetRegion(display, fillGC, clipRegion); + + // clear font + gfxFont = NULL; + font = NULL; + + // clear window + XFillRectangle(display, pixmap, paperGC, 0, 0, pixmapW, pixmapH); + + // clear text object + text->clear(); +} + +void XOutputDev::endPage() { + XOutputState *s; + + text->coalesce(); + + // clear state stack, free all GCs, free the clip region + while (save) { + s = save; + save = save->next; + XFreeGC(display, s->strokeGC); + XFreeGC(display, s->fillGC); + XDestroyRegion(s->clipRegion); + delete s; + } + XFreeGC(display, strokeGC); + XFreeGC(display, fillGC); + XFreeGC(display, paperGC); + XDestroyRegion(clipRegion); +} + +void XOutputDev::drawLink(Link *link, Catalog *catalog) { + double x1, y1, x2, y2, w; + GfxRGB rgb; + XPoint points[5]; + int x, y; + + link->getBorder(&x1, &y1, &x2, &y2, &w); + if (w > 0) { + rgb.r = 0; + rgb.g = 0; + rgb.b = 1; + XSetForeground(display, strokeGC, findColor(&rgb)); + XSetLineAttributes(display, strokeGC, xoutRound(w), + LineSolid, CapRound, JoinRound); + cvtUserToDev(x1, y1, &x, &y); + points[0].x = points[4].x = x; + points[0].y = points[4].y = y; + cvtUserToDev(x2, y1, &x, &y); + points[1].x = x; + points[1].y = y; + cvtUserToDev(x2, y2, &x, &y); + points[2].x = x; + points[2].y = y; + cvtUserToDev(x1, y2, &x, &y); + points[3].x = x; + points[3].y = y; + XDrawLines(display, pixmap, strokeGC, points, 5, CoordModeOrigin); + } +} + +void XOutputDev::saveState(GfxState *state) { + XOutputState *s; + XGCValues values; + + // save current state + s = new XOutputState; + s->strokeGC = strokeGC; + s->fillGC = fillGC; + s->clipRegion = clipRegion; + + // push onto state stack + s->next = save; + save = s; + + // create a new current state by copying + strokeGC = XCreateGC(display, pixmap, 0, &values); + XCopyGC(display, s->strokeGC, 0xffffffff, strokeGC); + fillGC = XCreateGC(display, pixmap, 0, &values); + XCopyGC(display, s->fillGC, 0xffffffff, fillGC); + clipRegion = XCreateRegion(); + XUnionRegion(s->clipRegion, clipRegion, clipRegion); + XSetRegion(display, strokeGC, clipRegion); + XSetRegion(display, fillGC, clipRegion); +} + +void XOutputDev::restoreState(GfxState *state) { + XOutputState *s; + + if (save) { + // kill current state + XFreeGC(display, strokeGC); + XFreeGC(display, fillGC); + XDestroyRegion(clipRegion); + + // restore state + flatness = state->getFlatness(); + strokeGC = save->strokeGC; + fillGC = save->fillGC; + clipRegion = save->clipRegion; + XSetRegion(display, strokeGC, clipRegion); + XSetRegion(display, fillGC, clipRegion); + + // pop state stack + s = save; + save = save->next; + delete s; + } +} + +void XOutputDev::updateAll(GfxState *state) { + updateLineAttrs(state, gTrue); + updateFlatness(state); + updateMiterLimit(state); + updateFillColor(state); + updateStrokeColor(state); + updateFont(state); +} + +void XOutputDev::updateCTM(GfxState *state, double m11, double m12, + double m21, double m22, double m31, double m32) { + updateLineAttrs(state, gTrue); +} + +void XOutputDev::updateLineDash(GfxState *state) { + updateLineAttrs(state, gTrue); +} + +void XOutputDev::updateFlatness(GfxState *state) { + flatness = state->getFlatness(); +} + +void XOutputDev::updateLineJoin(GfxState *state) { + updateLineAttrs(state, gFalse); +} + +void XOutputDev::updateLineCap(GfxState *state) { + updateLineAttrs(state, gFalse); +} + +// unimplemented +void XOutputDev::updateMiterLimit(GfxState *state) { +} + +void XOutputDev::updateLineWidth(GfxState *state) { + updateLineAttrs(state, gFalse); +} + +void XOutputDev::updateLineAttrs(GfxState *state, GBool updateDash) { + double width; + int cap, join; + double *dashPattern; + int dashLength; + double dashStart; + char dashList[20]; + int i; + + width = state->getTransformedLineWidth(); + switch (state->getLineCap()) { + case 0: cap = CapButt; break; + case 1: cap = CapRound; break; + case 2: cap = CapProjecting; break; + default: + error(-1, "Bad line cap style (%d)", state->getLineCap()); + cap = CapButt; + break; + } + switch (state->getLineJoin()) { + case 0: join = JoinMiter; break; + case 1: join = JoinRound; break; + case 2: join = JoinBevel; break; + default: + error(-1, "Bad line join style (%d)", state->getLineJoin()); + join = JoinMiter; + break; + } + state->getLineDash(&dashPattern, &dashLength, &dashStart); +#if 1 //~ work around a bug in XFree86 (???) + if (dashLength > 0 && cap == CapProjecting) { + cap = CapButt; + } +#endif + XSetLineAttributes(display, strokeGC, xoutRound(width), + dashLength > 0 ? LineOnOffDash : LineSolid, + cap, join); + if (updateDash && dashLength > 0) { + if (dashLength > 20) + dashLength = 20; + for (i = 0; i < dashLength; ++i) { + dashList[i] = xoutRound(state->transformWidth(dashPattern[i])); + if (dashList[i] == 0) + dashList[i] = 1; + } + XSetDashes(display, strokeGC, xoutRound(dashStart), dashList, dashLength); + } +} + +void XOutputDev::updateFillColor(GfxState *state) { + GfxRGB rgb; + + state->getFillRGB(&rgb); + if (reverseVideo) { + rgb.r = 1 - rgb.r; + rgb.g = 1 - rgb.g; + rgb.b = 1 - rgb.b; + } + XSetForeground(display, fillGC, findColor(&rgb)); +} + +void XOutputDev::updateStrokeColor(GfxState *state) { + GfxRGB rgb; + + state->getStrokeRGB(&rgb); + if (reverseVideo) { + rgb.r = 1 - rgb.r; + rgb.g = 1 - rgb.g; + rgb.b = 1 - rgb.b; + } + XSetForeground(display, strokeGC, findColor(&rgb)); +} + +void XOutputDev::updateFont(GfxState *state) { + double m11, m12, m21, m22; + + text->updateFont(state); + + if (!(gfxFont = state->getFont())) { + font = NULL; + return; + } + if (gfxFont->getType() == fontType3) { + font = NULL; + return; + } + state->getFontTransMat(&m11, &m12, &m21, &m22); + m11 *= state->getHorizScaling(); + m12 *= state->getHorizScaling(); + font = fontCache->getFont(xref, gfxFont, m11, m12, m21, m22); + if (font) { + font->updateGC(fillGC); + font->updateGC(strokeGC); + } +} + +void XOutputDev::stroke(GfxState *state) { + XPoint *points; + int *lengths; + int n, size, numPoints, i, j; + + // transform points + n = convertPath(state, &points, &size, &numPoints, &lengths, gFalse); + + // draw each subpath + j = 0; + for (i = 0; i < n; ++i) { + XDrawLines(display, pixmap, strokeGC, points + j, lengths[i], + CoordModeOrigin); + j += lengths[i]; + } + + // free points and lengths arrays + if (points != tmpPoints) + gfree(points); + if (lengths != tmpLengths) + gfree(lengths); +} + +void XOutputDev::fill(GfxState *state) { + doFill(state, WindingRule); +} + +void XOutputDev::eoFill(GfxState *state) { + doFill(state, EvenOddRule); +} + +// +// X doesn't color the pixels on the right-most and bottom-most +// borders of a polygon. This means that one-pixel-thick polygons +// are not colored at all. I think this is supposed to be a +// feature, but I can't figure out why. So after it fills a +// polygon, it also draws lines around the border. This is done +// only for single-component polygons, since it's not very +// compatible with the compound polygon kludge (see convertPath()). +// +void XOutputDev::doFill(GfxState *state, int rule) { + XPoint *points; + int *lengths; + int n, size, numPoints, i, j; + + // set fill rule + XSetFillRule(display, fillGC, rule); + + // transform points, build separate polygons + n = convertPath(state, &points, &size, &numPoints, &lengths, gTrue); + + // fill them + j = 0; + for (i = 0; i < n; ++i) { + XFillPolygon(display, pixmap, fillGC, points + j, lengths[i], + Complex, CoordModeOrigin); + if (state->getPath()->getNumSubpaths() == 1) { + XDrawLines(display, pixmap, fillGC, points + j, lengths[i], + CoordModeOrigin); + } + j += lengths[i] + 1; + } + + // free points and lengths arrays + if (points != tmpPoints) + gfree(points); + if (lengths != tmpLengths) + gfree(lengths); +} + +void XOutputDev::clip(GfxState *state) { + doClip(state, WindingRule); +} + +void XOutputDev::eoClip(GfxState *state) { + doClip(state, EvenOddRule); +} + +void XOutputDev::doClip(GfxState *state, int rule) { + Region region, region2; + XPoint *points; + int *lengths; + int n, size, numPoints, i, j; + + // transform points, build separate polygons + n = convertPath(state, &points, &size, &numPoints, &lengths, gTrue); + + // construct union of subpath regions + // (XPolygonRegion chokes if there aren't at least three points -- + // this happens if the PDF file does moveto/closepath/clip, which + // sets an empty clipping region) + if (lengths[0] > 2) { + region = XPolygonRegion(points, lengths[0], rule); + } else { + region = XCreateRegion(); + } + j = lengths[0] + 1; + for (i = 1; i < n; ++i) { + if (lengths[i] > 2) { + region2 = XPolygonRegion(points + j, lengths[i], rule); + } else { + region2 = XCreateRegion(); + } + XUnionRegion(region2, region, region); + XDestroyRegion(region2); + j += lengths[i] + 1; + } + + // intersect region with clipping region + XIntersectRegion(region, clipRegion, clipRegion); + XDestroyRegion(region); + XSetRegion(display, strokeGC, clipRegion); + XSetRegion(display, fillGC, clipRegion); + + // free points and lengths arrays + if (points != tmpPoints) + gfree(points); + if (lengths != tmpLengths) + gfree(lengths); +} + +// +// Transform points in the path and convert curves to line segments. +// Builds a set of subpaths and returns the number of subpaths. +// If <fillHack> is set, close any unclosed subpaths and activate a +// kludge for polygon fills: First, it divides up the subpaths into +// non-overlapping polygons by simply comparing bounding rectangles. +// Then it connects subaths within a single compound polygon to a single +// point so that X can fill the polygon (sort of). +// +int XOutputDev::convertPath(GfxState *state, XPoint **points, int *size, + int *numPoints, int **lengths, GBool fillHack) { + GfxPath *path; + BoundingRect *rects; + BoundingRect rect; + int n, i, ii, j, k, k0; + + // get path and number of subpaths + path = state->getPath(); + n = path->getNumSubpaths(); + + // allocate lengths array + if (n < numTmpSubpaths) + *lengths = tmpLengths; + else + *lengths = (int *)gmalloc(n * sizeof(int)); + + // allocate bounding rectangles array + if (fillHack) { + if (n < numTmpSubpaths) + rects = tmpRects; + else + rects = (BoundingRect *)gmalloc(n * sizeof(BoundingRect)); + } else { + rects = NULL; + } + + // do each subpath + *points = tmpPoints; + *size = numTmpPoints; + *numPoints = 0; + for (i = 0; i < n; ++i) { + + // transform the points + j = *numPoints; + convertSubpath(state, path->getSubpath(i), points, size, numPoints); + + // construct bounding rectangle + if (fillHack) { + rects[i].xMin = rects[i].xMax = (*points)[j].x; + rects[i].yMin = rects[i].yMax = (*points)[j].y; + for (k = j + 1; k < *numPoints; ++k) { + if ((*points)[k].x < rects[i].xMin) + rects[i].xMin = (*points)[k].x; + else if ((*points)[k].x > rects[i].xMax) + rects[i].xMax = (*points)[k].x; + if ((*points)[k].y < rects[i].yMin) + rects[i].yMin = (*points)[k].y; + else if ((*points)[k].y > rects[i].yMax) + rects[i].yMax = (*points)[k].y; + } + } + + // close subpath if necessary + if (fillHack && ((*points)[*numPoints-1].x != (*points)[j].x || + (*points)[*numPoints-1].y != (*points)[j].y)) { + addPoint(points, size, numPoints, (*points)[j].x, (*points)[j].y); + } + + // length of this subpath + (*lengths)[i] = *numPoints - j; + + // leave an extra point for compound fill hack + if (fillHack) + addPoint(points, size, numPoints, 0, 0); + } + + // kludge: munge any points that are *way* out of bounds - these can + // crash certain (buggy) X servers + for (i = 0; i < *numPoints; ++i) { + if ((*points)[i].x < -pixmapW) { + (*points)[i].x = -pixmapW; + } else if ((*points)[i].x > 2 * pixmapW) { + (*points)[i].x = 2 * pixmapW; + } + if ((*points)[i].y < -pixmapH) { + (*points)[i].y = -pixmapH; + } else if ((*points)[i].y > 2 * pixmapH) { + (*points)[i].y = 2 * pixmapH; + } + } + + // combine compound polygons + if (fillHack) { + i = j = k = 0; + while (i < n) { + + // start with subpath i + rect = rects[i]; + (*lengths)[j] = (*lengths)[i]; + k0 = k; + (*points)[k + (*lengths)[i]] = (*points)[k0]; + k += (*lengths)[i] + 1; + ++i; + + // combine overlapping polygons + do { + + // look for the first subsequent subpath, if any, which overlaps + for (ii = i; ii < n; ++ii) { + if (rects[ii].xMax > rects[i].xMin && + rects[ii].xMin < rects[i].xMax && + rects[ii].yMax > rects[i].yMin && + rects[ii].yMin < rects[i].yMax) { + break; + } + } + + // if there is an overlap, combine the polygons + if (ii < n) { + for (; i <= ii; ++i) { + if (rects[i].xMin < rect.xMin) + rect.xMin = rects[j].xMin; + if (rects[i].xMax > rect.xMax) + rect.xMax = rects[j].xMax; + if (rects[i].yMin < rect.yMin) + rect.yMin = rects[j].yMin; + if (rects[i].yMax > rect.yMax) + rect.yMax = rects[j].yMax; + (*lengths)[j] += (*lengths)[i] + 1; + (*points)[k + (*lengths)[i]] = (*points)[k0]; + k += (*lengths)[i] + 1; + } + } + } while (ii < n && i < n); + + ++j; + } + + // free bounding rectangles + if (rects != tmpRects) + gfree(rects); + + n = j; + } + + return n; +} + +// +// Transform points in a single subpath and convert curves to line +// segments. +// +void XOutputDev::convertSubpath(GfxState *state, GfxSubpath *subpath, + XPoint **points, int *size, int *n) { + double x0, y0, x1, y1, x2, y2, x3, y3; + int m, i; + + m = subpath->getNumPoints(); + i = 0; + while (i < m) { + if (i >= 1 && subpath->getCurve(i)) { + state->transform(subpath->getX(i-1), subpath->getY(i-1), &x0, &y0); + state->transform(subpath->getX(i), subpath->getY(i), &x1, &y1); + state->transform(subpath->getX(i+1), subpath->getY(i+1), &x2, &y2); + state->transform(subpath->getX(i+2), subpath->getY(i+2), &x3, &y3); + doCurve(points, size, n, x0, y0, x1, y1, x2, y2, x3, y3); + i += 3; + } else { + state->transform(subpath->getX(i), subpath->getY(i), &x1, &y1); + addPoint(points, size, n, xoutRound(x1), xoutRound(y1)); + ++i; + } + } +} + +// +// Subdivide a Bezier curve. This uses floating point to avoid +// propagating rounding errors. (The curves look noticeably more +// jagged with integer arithmetic.) +// +void XOutputDev::doCurve(XPoint **points, int *size, int *n, + double x0, double y0, double x1, double y1, + double x2, double y2, double x3, double y3) { + double x[(1<<maxCurveSplits)+1][3]; + double y[(1<<maxCurveSplits)+1][3]; + int next[1<<maxCurveSplits]; + int p1, p2, p3; + double xx1, yy1, xx2, yy2; + double dx, dy, mx, my, d1, d2; + double xl0, yl0, xl1, yl1, xl2, yl2; + double xr0, yr0, xr1, yr1, xr2, yr2, xr3, yr3; + double xh, yh; + double flat; + + flat = (double)(flatness * flatness); + if (flat < 1) + flat = 1; + + // initial segment + p1 = 0; + p2 = 1<<maxCurveSplits; + x[p1][0] = x0; y[p1][0] = y0; + x[p1][1] = x1; y[p1][1] = y1; + x[p1][2] = x2; y[p1][2] = y2; + x[p2][0] = x3; y[p2][0] = y3; + next[p1] = p2; + + while (p1 < (1<<maxCurveSplits)) { + + // get next segment + xl0 = x[p1][0]; yl0 = y[p1][0]; + xx1 = x[p1][1]; yy1 = y[p1][1]; + xx2 = x[p1][2]; yy2 = y[p1][2]; + p2 = next[p1]; + xr3 = x[p2][0]; yr3 = y[p2][0]; + + // compute distances from control points to midpoint of the + // straight line (this is a bit of a hack, but it's much faster + // than computing the actual distances to the line) + mx = (xl0 + xr3) * 0.5; + my = (yl0 + yr3) * 0.5; + dx = xx1 - mx; + dy = yy1 - my; + d1 = dx*dx + dy*dy; + dx = xx2 - mx; + dy = yy2 - my; + d2 = dx*dx + dy*dy; + + // if curve is flat enough, or no more divisions allowed then + // add the straight line segment + if (p2 - p1 <= 1 || (d1 <= flat && d2 <= flat)) { + addPoint(points, size, n, xoutRound(xr3), xoutRound(yr3)); + p1 = p2; + + // otherwise, subdivide the curve + } else { + xl1 = (xl0 + xx1) * 0.5; + yl1 = (yl0 + yy1) * 0.5; + xh = (xx1 + xx2) * 0.5; + yh = (yy1 + yy2) * 0.5; + xl2 = (xl1 + xh) * 0.5; + yl2 = (yl1 + yh) * 0.5; + xr2 = (xx2 + xr3) * 0.5; + yr2 = (yy2 + yr3) * 0.5; + xr1 = (xh + xr2) * 0.5; + yr1 = (yh + yr2) * 0.5; + xr0 = (xl2 + xr1) * 0.5; + yr0 = (yl2 + yr1) * 0.5; + + // add the new subdivision points + p3 = (p1 + p2) / 2; + x[p1][1] = xl1; y[p1][1] = yl1; + x[p1][2] = xl2; y[p1][2] = yl2; + next[p1] = p3; + x[p3][0] = xr0; y[p3][0] = yr0; + x[p3][1] = xr1; y[p3][1] = yr1; + x[p3][2] = xr2; y[p3][2] = yr2; + next[p3] = p2; + } + } +} + +// +// Add a point to the points array. (This would use a generic resizable +// array type if C++ supported parameterized types in some reasonable +// way -- templates are a disgusting kludge.) +// +void XOutputDev::addPoint(XPoint **points, int *size, int *k, int x, int y) { + if (*k >= *size) { + *size += 32; + if (*points == tmpPoints) { + *points = (XPoint *)gmalloc(*size * sizeof(XPoint)); + memcpy(*points, tmpPoints, *k * sizeof(XPoint)); + } else { + *points = (XPoint *)grealloc(*points, *size * sizeof(XPoint)); + } + } + (*points)[*k].x = x; + (*points)[*k].y = y; + ++(*k); +} + +void XOutputDev::beginString(GfxState *state, GString *s) { + text->beginString(state, state->getCurX(), state->getCurY()); +} + +void XOutputDev::endString(GfxState *state) { + text->endString(); +} + +void XOutputDev::drawChar(GfxState *state, double x, double y, + double dx, double dy, + double originX, double originY, + CharCode code, Unicode *u, int uLen) { + int render; + double x1, y1, dx1, dy1; + GfxRGB rgb; + double saveCurX, saveCurY; + double *ctm; + double saveCTM[6]; + + text->addChar(state, x, y, dx, dy, u, uLen); + + if (!font) { + return; + } + + // check for invisible text -- this is used by Acrobat Capture + render = state->getRender(); + if ((render & 3) == 3) { + return; + } + + x -= originX; + y -= originY; + state->transform(x, y, &x1, &y1); + state->transformDelta(dx, dy, &dx1, &dy1); + + // fill + if (!(render & 1)) { + state->getFillRGB(&rgb); + if (reverseVideo) { + rgb.r = 1 - rgb.r; + rgb.g = 1 - rgb.g; + rgb.b = 1 - rgb.b; + } + font->drawChar(state, pixmap, pixmapW, pixmapH, fillGC, &rgb, + x1, y1, dx1, dy1, code, u, uLen); + } + + // stroke + if ((render & 3) == 1 || (render & 3) == 2) { + if (font->hasGetCharPath()) { + saveCurX = state->getCurX(); + saveCurY = state->getCurY(); + ctm = state->getCTM(); + memcpy(saveCTM, ctm, 6 * sizeof(double)); + state->setCTM(1, 0, 0, 1, x1, y1); + font->getCharPath(state, code, u, uLen); + stroke(state); + state->clearPath(); + state->setCTM(saveCTM[0], saveCTM[1], saveCTM[2], saveCTM[3], + saveCTM[4], saveCTM[5]); + state->moveTo(saveCurX, saveCurY); + } else { + // can't stroke the outline, so just fill it using the stroke + // color + state->getStrokeRGB(&rgb); + if (reverseVideo) { + rgb.r = 1 - rgb.r; + rgb.g = 1 - rgb.g; + rgb.b = 1 - rgb.b; + } + font->drawChar(state, pixmap, pixmapW, pixmapH, strokeGC, &rgb, + x1, y1, dx1, dy1, code, u, uLen); + } + } + +#if 0 //~ unimplemented: clipping to char path + // clip + if (render & 4) { + } +#endif +} + +GBool XOutputDev::beginType3Char(GfxState *state, + CharCode code, Unicode *u, int uLen) { + Ref *fontID; + double *ctm, *bbox; + GfxRGB color; + T3FontCache *t3Font; + T3GlyphStack *t3gs; + double x1, y1, xMin, yMin, xMax, yMax, xt, yt; + int i, j; + + if (!gfxFont) { + 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 == xOutT3FontCacheSize) { + 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, + display, visual, depth, pixmap); + } + } + 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) { + state->getFillRGB(&color); + if (reverseVideo) { + color.r = 1 - color.r; + color.g = 1 - color.g; + color.b = 1 - color.b; + } + text->addChar(state, 0, 0, + t3Font->cacheTags[i+j].wx, t3Font->cacheTags[i+j].wy, + u, uLen); + drawType3Glyph(t3Font, &t3Font->cacheTags[i+j], + t3Font->cacheData + (i+j) * t3Font->glyphSize, + xt, yt, &color); + return gTrue; + } + } + + // push a new Type 3 glyph record + t3gs = new T3GlyphStack(); + t3gs->next = t3GlyphStack; + t3GlyphStack = t3gs; + t3GlyphStack->cacheable = gFalse; + t3GlyphStack->code = code; + t3GlyphStack->cache = t3Font; + t3GlyphStack->cacheIdx = i; + t3GlyphStack->x = xt; + t3GlyphStack->y = yt; + t3GlyphStack->u = u; + t3GlyphStack->uLen = uLen; + + return gFalse; +} + +void XOutputDev::endType3Char(GfxState *state) { + XImage *image; + Guchar *p; + int x, y; + Gulong pixel; + double alpha; + T3GlyphStack *t3gs; + double *ctm; + + if (t3GlyphStack->cacheable) { + image = t3GlyphStack->cache->image; + XGetSubImage(display, pixmap, 0, 0, + t3GlyphStack->cache->glyphW, t3GlyphStack->cache->glyphH, + (1 << depth) - 1, ZPixmap, image, 0, 0); + p = t3GlyphStack->cacheData; + for (y = 0; y < t3GlyphStack->cache->glyphH; ++y) { + for (x = 0; x < t3GlyphStack->cache->glyphW; ++x) { + pixel = XGetPixel(image, x, y); + if (trueColor) { + alpha = (double)((pixel >> rShift) & rMul) / (double)rMul; + } else { + alpha = redMap[pixel & 0xff]; + } + if (alpha <= 0.2) { + *p++ = 4; + } else if (alpha <= 0.4) { + *p++ = 3; + } else if (alpha <= 0.6) { + *p++ = 2; + } else if (alpha <= 0.8) { + *p++ = 1; + } else { + *p++ = 0; + } + } + } + XDestroyRegion(clipRegion); + XFreeGC(display, strokeGC); + XFreeGC(display, fillGC); + pixmapW = t3GlyphStack->origPixmapW; + pixmapH = t3GlyphStack->origPixmapH; + pixmap = t3GlyphStack->origPixmap; + strokeGC = t3GlyphStack->origStrokeGC; + fillGC = t3GlyphStack->origFillGC; + clipRegion = t3GlyphStack->origClipRegion; + drawType3Glyph(t3GlyphStack->cache, + t3GlyphStack->cacheTag, t3GlyphStack->cacheData, + t3GlyphStack->x, t3GlyphStack->y, &t3GlyphStack->color); + // the CTM must be restored here in order for TextPage::addChar to + // work correctly + ctm = state->getCTM(); + state->setCTM(ctm[0], ctm[1], ctm[2], ctm[3], + t3GlyphStack->origCTM4, t3GlyphStack->origCTM5); + } + text->addChar(state, 0, 0, t3GlyphStack->wx, t3GlyphStack->wy, + t3GlyphStack->u, t3GlyphStack->uLen); + t3gs = t3GlyphStack; + t3GlyphStack = t3gs->next; + delete t3gs; +} + +void XOutputDev::drawType3Glyph(T3FontCache *t3Font, + T3FontCacheTag *tag, Guchar *data, + double x, double y, GfxRGB *color) { + XImage *image; + XColor xcolor; + GfxRGB bg, rgb; + Gulong map[5]; + Gulong pixel; + Guchar *p; + int x0, y0, w0, h0, x1, y1; + int ix, iy; + + // compute: (x0,y0) = position in destination pixmap + // (x1,y1) = position in the XImage + // (w0,h0) = size of XImage transfer + x0 = xoutRound(x + t3Font->glyphX); + y0 = xoutRound(y + t3Font->glyphY); + x1 = 0; + y1 = 0; + w0 = t3Font->glyphW; + h0 = t3Font->glyphH; + if (x0 < 0) { + x1 = -x0; + w0 += x0; + x0 = 0; + } + if (x0 + w0 > pixmapW) { + w0 = pixmapW - x0; + } + if (w0 <= 0) { + return; + } + if (y0 < 0) { + y1 = -y0; + h0 += y0; + y0 = 0; + } + if (y0 + h0 > pixmapH) { + h0 = pixmapH - y0; + } + if (h0 <= 0) { + return; + } + + image = t3Font->image; + XGetSubImage(display, pixmap, x0, y0, w0, h0, + (1 << depth) - 1, ZPixmap, image, x1, y1); + xcolor.pixel = XGetPixel(image, t3Font->glyphW / 2, t3Font->glyphH / 2); + XQueryColor(display, colormap, &xcolor); + bg.r = xcolor.red / 65535.0; + bg.g = xcolor.green / 65535.0; + bg.b = xcolor.blue / 65535.0; + rgb.r = 0.25 * (color->r + 3 * bg.r); + rgb.g = 0.25 * (color->g + 3 * bg.g); + rgb.b = 0.25 * (color->b + 3 * bg.b); + map[1] = findColor(&rgb); + rgb.r = 0.5 * (color->r + bg.r); + rgb.g = 0.5 * (color->g + bg.g); + rgb.b = 0.5 * (color->b + bg.b); + map[2] = findColor(&rgb); + rgb.r = 0.25 * (3 * color->r + bg.r); + rgb.g = 0.25 * (3 * color->g + bg.g); + rgb.b = 0.25 * (3 * color->b + bg.b); + map[3] = findColor(&rgb); + map[4] = findColor(color); + p = data; + for (iy = 0; iy < t3Font->glyphH; ++iy) { + for (ix = 0; ix < t3Font->glyphW; ++ix) { + pixel = *p++; + if (pixel > 0) { + XPutPixel(image, ix, iy, map[pixel]); + } + } + } + XPutImage(display, pixmap, fillGC, image, x1, y1, x0, y0, w0, h0); +} + +void XOutputDev::type3D0(GfxState *state, double wx, double wy) { + t3GlyphStack->wx = wx; + t3GlyphStack->wy = wy; +} + +void XOutputDev::type3D1(GfxState *state, double wx, double wy, + double llx, double lly, double urx, double ury) { + GfxColor fgColor; + XGCValues gcValues; + XRectangle rect; + double *ctm; + T3FontCache *t3Font; + int i, j; + + // allocate a cache entry + t3GlyphStack->cacheable = gTrue; + t3Font = t3GlyphStack->cache; + i = t3GlyphStack->cacheIdx; + 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; + } + } + t3GlyphStack->wx = wx; + t3GlyphStack->wy = wy; + t3GlyphStack->cacheTag->wx = wx; + t3GlyphStack->cacheTag->wy = wy; + + // prepare to rasterize the glyph + //~ do we need to handle both fill and stroke color? + state->getFillRGB(&t3GlyphStack->color); + if (reverseVideo) { + t3GlyphStack->color.r = 1 - t3GlyphStack->color.r; + t3GlyphStack->color.g = 1 - t3GlyphStack->color.g; + t3GlyphStack->color.b = 1 - t3GlyphStack->color.b; + } + fgColor.c[0] = reverseVideo ? 1 : 0; + state->setFillColorSpace(new GfxDeviceGrayColorSpace()); + state->setFillColor(&fgColor); + state->setStrokeColorSpace(new GfxDeviceGrayColorSpace()); + state->setStrokeColor(&fgColor); + t3GlyphStack->origPixmapW = pixmapW; + t3GlyphStack->origPixmapH = pixmapH; + t3GlyphStack->origPixmap = pixmap; + t3GlyphStack->origStrokeGC = strokeGC; + t3GlyphStack->origFillGC = fillGC; + t3GlyphStack->origClipRegion = clipRegion; + pixmapW = t3GlyphStack->cache->glyphW; + pixmapH = t3GlyphStack->cache->glyphH; + pixmap = t3GlyphStack->cache->pixmap; + gcValues.foreground = BlackPixel(display, screenNum); + gcValues.background = WhitePixel(display, screenNum); + gcValues.line_width = 0; + gcValues.line_style = LineSolid; + strokeGC = XCreateGC(display, pixmap, + GCForeground | GCBackground | GCLineWidth | GCLineStyle, + &gcValues); + updateLineAttrs(state, gTrue); + gcValues.foreground = WhitePixel(display, screenNum); + fillGC = XCreateGC(display, pixmap, + GCForeground | GCBackground | GCLineWidth | GCLineStyle, + &gcValues); + XFillRectangle(display, pixmap, fillGC, 0, 0, pixmapW, pixmapH); + XSetForeground(display, fillGC, BlackPixel(display, screenNum)); + clipRegion = XCreateRegion(); + rect.x = rect.y = 0; + rect.width = pixmapW; + rect.height = pixmapH; + XUnionRectWithRegion(&rect, clipRegion, clipRegion); + XSetRegion(display, strokeGC, clipRegion); + XSetRegion(display, fillGC, clipRegion); + ctm = state->getCTM(); + t3GlyphStack->origCTM4 = ctm[4]; + t3GlyphStack->origCTM5 = ctm[5]; + state->setCTM(ctm[0], ctm[1], ctm[2], ctm[3], + -t3GlyphStack->cache->glyphX, -t3GlyphStack->cache->glyphY); +} + +inline Gulong XOutputDev::findColor(GfxRGB *x, GfxRGB *err) { + double gray; + int r, g, b; + Gulong pixel; + + if (trueColor) { + r = xoutRound(x->r * rMul); + g = xoutRound(x->g * gMul); + b = xoutRound(x->b * bMul); + pixel = ((Gulong)r << rShift) + + ((Gulong)g << gShift) + + ((Gulong)b << bShift); + err->r = x->r - (double)r / rMul; + err->g = x->g - (double)g / gMul; + err->b = x->b - (double)b / bMul; + } else if (numColors == 1) { + gray = 0.299 * x->r + 0.587 * x->g + 0.114 * x->b; + if (gray < 0.5) { + pixel = colors[0]; + err->r = x->r; + err->g = x->g; + err->b = x->b; + } else { + pixel = colors[1]; + err->r = x->r - 1; + err->g = x->g - 1; + err->b = x->b - 1; + } + } else { + r = xoutRound(x->r * (numColors - 1)); + g = xoutRound(x->g * (numColors - 1)); + b = xoutRound(x->b * (numColors - 1)); + pixel = colors[(r * numColors + g) * numColors + b]; + err->r = x->r - (double)r / (numColors - 1); + err->g = x->g - (double)g / (numColors - 1); + err->b = x->b - (double)b / (numColors - 1); + } + return pixel; +} + +Gulong XOutputDev::findColor(GfxRGB *rgb) { + int r, g, b; + double gray; + Gulong pixel; + + if (trueColor) { + r = xoutRound(rgb->r * rMul); + g = xoutRound(rgb->g * gMul); + b = xoutRound(rgb->b * bMul); + pixel = ((Gulong)r << rShift) + + ((Gulong)g << gShift) + + ((Gulong)b << bShift); + } else if (numColors == 1) { + gray = 0.299 * rgb->r + 0.587 * rgb->g + 0.114 * rgb->b; + if (gray < 0.5) + pixel = colors[0]; + else + pixel = colors[1]; + } else { + r = xoutRound(rgb->r * (numColors - 1)); + g = xoutRound(rgb->g * (numColors - 1)); + b = xoutRound(rgb->b * (numColors - 1)); +#if 0 // this makes things worse as often as better + // even a very light color shouldn't map to white + if (r == numColors - 1 && g == numColors - 1 && b == numColors - 1) { + if (color->getR() < 0.95) + --r; + if (color->getG() < 0.95) + --g; + if (color->getB() < 0.95) + --b; + } +#endif + pixel = colors[(r * numColors + g) * numColors + b]; + } + return pixel; +} + +void XOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str, + int width, int height, GBool invert, + GBool inlineImg) { + ImageStream *imgStr; + XImage *image; + double *ctm; + GBool rot; + double xScale, yScale, xShear, yShear; + int tx, ty, scaledWidth, scaledHeight, xSign, ySign; + int ulx, uly, llx, lly, urx, ury, lrx, lry; + int ulx1, uly1, llx1, lly1, urx1, ury1, lrx1, lry1; + int bx0, by0, bx1, by1, bw, bh; + int cx0, cy0, cx1, cy1, cw, ch; + int yp, yq, yt, yStep, lastYStep; + int xp, xq, xt, xStep, xSrc; + GfxRGB rgb; + Guchar *pixBuf; + int imgPix; + double alpha; + XColor xcolor; + Gulong lastPixel; + GfxRGB rgb2; + double r0, g0, b0, r1, g1, b1; + Gulong pix; + Guchar *p; + int x, y, x1, y1, x2, y2; + int n, m, i, j; + + // get CTM, check for singular matrix + ctm = state->getCTM(); + if (fabs(ctm[0] * ctm[3] - ctm[1] * ctm[2]) < 0.000001) { + error(-1, "Singular CTM in drawImage"); + if (inlineImg) { + j = height * ((width + 7) / 8); + str->reset(); + for (i = 0; i < j; ++i) { + str->getChar(); + } + str->close(); + } + return; + } + + // compute scale, shear, rotation, translation parameters + rot = fabs(ctm[1]) > fabs(ctm[0]); + if (rot) { + xScale = -ctm[1]; + yScale = -ctm[2] + (ctm[0] * ctm[3]) / ctm[1]; + xShear = ctm[3] / yScale; + yShear = -ctm[0] / ctm[1]; + } else { + xScale = ctm[0]; + yScale = -ctm[3] + (ctm[1] * ctm[2]) / ctm[0]; + xShear = -ctm[2] / yScale; + yShear = ctm[1] / ctm[0]; + } + tx = xoutRound(ctm[2] + ctm[4]); + ty = xoutRound(ctm[3] + ctm[5]); + // use ceil() to avoid gaps between "striped" images + scaledWidth = (int)ceil(fabs(xScale)); + xSign = (xScale < 0) ? -1 : 1; + scaledHeight = (int)ceil(fabs(yScale)); + ySign = (yScale < 0) ? -1 : 1; + + // compute corners in device space + ulx1 = 0; + uly1 = 0; + urx1 = xSign * (scaledWidth - 1); + ury1 = xoutRound(yShear * urx1); + llx1 = xoutRound(xShear * ySign * (scaledHeight - 1)); + lly1 = ySign * (scaledHeight - 1) + xoutRound(yShear * llx1); + lrx1 = xSign * (scaledWidth - 1) + + xoutRound(xShear * ySign * (scaledHeight - 1)); + lry1 = ySign * (scaledHeight - 1) + xoutRound(yShear * lrx1); + if (rot) { + ulx = tx + uly1; uly = ty - ulx1; + urx = tx + ury1; ury = ty - urx1; + llx = tx + lly1; lly = ty - llx1; + lrx = tx + lry1; lry = ty - lrx1; + } else { + ulx = tx + ulx1; uly = ty + uly1; + urx = tx + urx1; ury = ty + ury1; + llx = tx + llx1; lly = ty + lly1; + lrx = tx + lrx1; lry = ty + lry1; + } + + // bounding box: + // (bx0, by0) = upper-left corner + // (bx1, by1) = lower-right corner + // (bw, bh) = size + bx0 = (ulx < urx) ? (ulx < llx) ? (ulx < lrx) ? ulx : lrx + : (llx < lrx) ? llx : lrx + : (urx < llx) ? (urx < lrx) ? urx : lrx + : (llx < lrx) ? llx : lrx; + bx1 = (ulx > urx) ? (ulx > llx) ? (ulx > lrx) ? ulx : lrx + : (llx > lrx) ? llx : lrx + : (urx > llx) ? (urx > lrx) ? urx : lrx + : (llx > lrx) ? llx : lrx; + by0 = (uly < ury) ? (uly < lly) ? (uly < lry) ? uly : lry + : (lly < lry) ? lly : lry + : (ury < lly) ? (ury < lry) ? ury : lry + : (lly < lry) ? lly : lry; + by1 = (uly > ury) ? (uly > lly) ? (uly > lry) ? uly : lry + : (lly > lry) ? lly : lry + : (ury > lly) ? (ury > lry) ? ury : lry + : (lly > lry) ? lly : lry; + bw = bx1 - bx0 + 1; + bh = by1 - by0 + 1; + + // Bounding box clipped to pixmap, i.e., "valid" rectangle: + // (cx0, cy0) = upper-left corner of valid rectangle in Pixmap + // (cx1, cy1) = upper-left corner of valid rectangle in XImage + // (cw, ch) = size of valid rectangle + // These values will be used to transfer the XImage from/to the + // Pixmap. + cw = (bx1 >= pixmapW) ? pixmapW - bx0 : bw; + if (bx0 < 0) { + cx0 = 0; + cx1 = -bx0; + cw += bx0; + } else { + cx0 = bx0; + cx1 = 0; + } + ch = (by1 >= pixmapH) ? pixmapH - by0 : bh; + if (by0 < 0) { + cy0 = 0; + cy1 = -by0; + ch += by0; + } else { + cy0 = by0; + cy1 = 0; + } + + // check for tiny (zero width or height) images + // and off-page images + if (scaledWidth <= 0 || scaledHeight <= 0 || cw <= 0 || ch <= 0) { + if (inlineImg) { + j = height * ((width + 7) / 8); + str->reset(); + for (i = 0; i < j; ++i) { + str->getChar(); + } + str->close(); + } + return; + } + + // compute Bresenham parameters for x and y scaling + yp = height / scaledHeight; + yq = height % scaledHeight; + xp = width / scaledWidth; + xq = width % scaledWidth; + + // allocate pixel buffer + pixBuf = (Guchar *)gmalloc((yp + 1) * width * sizeof(Guchar)); + + // allocate XImage and read from page pixmap + image = XCreateImage(display, visual, depth, ZPixmap, 0, NULL, bw, bh, 8, 0); + image->data = (char *)gmalloc(bh * image->bytes_per_line); + XGetSubImage(display, pixmap, cx0, cy0, cw, ch, (1 << depth) - 1, ZPixmap, + image, cx1, cy1); + + // get mask color + state->getFillRGB(&rgb); + if (reverseVideo) { + rgb.r = 1 - rgb.r; + rgb.g = 1 - rgb.g; + rgb.b = 1 - rgb.b; + } + r0 = rgb.r; + g0 = rgb.g; + b0 = rgb.b; + + // initialize background color + // (the specific pixel value doesn't matter here, as long as + // r1,g1,b1 correspond correctly to lastPixel) + xcolor.pixel = lastPixel = 0; + XQueryColor(display, colormap, &xcolor); + r1 = (double)xcolor.red / 65535.0; + g1 = (double)xcolor.green / 65535.0; + b1 = (double)xcolor.blue / 65535.0; + + // initialize the image stream + imgStr = new ImageStream(str, width, 1, 1); + imgStr->reset(); + + // init y scale Bresenham + yt = 0; + lastYStep = 1; + + for (y = 0; y < scaledHeight; ++y) { + + // y scale Bresenham + yStep = yp; + yt += yq; + if (yt >= scaledHeight) { + yt -= scaledHeight; + ++yStep; + } + + // read row(s) from image + n = (yp > 0) ? yStep : lastYStep; + if (n > 0) { + p = pixBuf; + for (i = 0; i < n; ++i) { + memcpy(p, imgStr->getLine(), width); + if (invert) { + for (j = 0; j < width; ++j) { + p[j] ^= 1; + } + } + p += width; + } + } + lastYStep = yStep; + + // init x scale Bresenham + xt = 0; + xSrc = 0; + + for (x = 0; x < scaledWidth; ++x) { + + // x scale Bresenham + xStep = xp; + xt += xq; + if (xt >= scaledWidth) { + xt -= scaledWidth; + ++xStep; + } + + // x shear + x1 = xSign * x + xoutRound(xShear * ySign * y); + + // y shear + y1 = ySign * y + xoutRound(yShear * x1); + + // rotation + if (rot) { + x2 = y1; + y2 = -x1; + } else { + x2 = x1; + y2 = y1; + } + + // compute the filtered pixel at (x,y) after the + // x and y scaling operations + n = yStep > 0 ? yStep : 1; + m = xStep > 0 ? xStep : 1; + p = pixBuf + xSrc; + imgPix = 0; + for (i = 0; i < n; ++i) { + for (j = 0; j < m; ++j) { + imgPix += *p++; + } + p += width - m; + } + + // x scale Bresenham + xSrc += xStep; + + // blend image pixel with background + alpha = (double)imgPix / (double)(n * m); + xcolor.pixel = XGetPixel(image, tx + x2 - bx0, ty + y2 - by0); + if (xcolor.pixel != lastPixel) { + XQueryColor(display, colormap, &xcolor); + r1 = (double)xcolor.red / 65535.0; + g1 = (double)xcolor.green / 65535.0; + b1 = (double)xcolor.blue / 65535.0; + lastPixel = xcolor.pixel; + } + rgb2.r = r0 * (1 - alpha) + r1 * alpha; + rgb2.g = g0 * (1 - alpha) + g1 * alpha; + rgb2.b = b0 * (1 - alpha) + b1 * alpha; + pix = findColor(&rgb2); + + // set pixel + XPutPixel(image, tx + x2 - bx0, ty + y2 - by0, pix); + } + } + + // blit the image into the pixmap + XPutImage(display, pixmap, fillGC, image, cx1, cy1, cx0, cy0, cw, ch); + + // free memory + delete imgStr; + gfree(pixBuf); + gfree(image->data); + image->data = NULL; + XDestroyImage(image); +} + +void XOutputDev::drawImage(GfxState *state, Object *ref, Stream *str, + int width, int height, GfxImageColorMap *colorMap, + int *maskColors, GBool inlineImg) { + ImageStream *imgStr; + XImage *image; + int nComps, nVals, nBits; + GBool dither; + double *ctm; + GBool rot; + double xScale, yScale, xShear, yShear; + int tx, ty, scaledWidth, scaledHeight, xSign, ySign; + int ulx, uly, llx, lly, urx, ury, lrx, lry; + int ulx1, uly1, llx1, lly1, urx1, ury1, lrx1, lry1; + int bx0, by0, bx1, by1, bw, bh; + int cx0, cy0, cx1, cy1, cw, ch; + int yp, yq, yt, yStep, lastYStep; + int xp, xq, xt, xStep, xSrc; + GfxRGB *pixBuf; + Guchar *alphaBuf; + Guchar pixBuf2[gfxColorMaxComps]; + GfxRGB color2, err, errRight; + GfxRGB *errDown; + double r0, g0, b0, alpha, mul; + Gulong pix; + GfxRGB *p; + Guchar *q, *p2; + GBool oneBitMode; + GfxRGB oneBitRGB[2]; + int x, y, x1, y1, x2, y2; + int n, m, i, j, k; + + // image parameters + nComps = colorMap->getNumPixelComps(); + nVals = width * nComps; + nBits = colorMap->getBits(); + dither = nComps > 1 || nBits > 1; + + // get CTM, check for singular matrix + ctm = state->getCTM(); + if (fabs(ctm[0] * ctm[3] - ctm[1] * ctm[2]) < 0.000001) { + error(-1, "Singular CTM in drawImage"); + if (inlineImg) { + str->reset(); + j = height * ((nVals * nBits + 7) / 8); + for (i = 0; i < j; ++i) { + str->getChar(); + } + str->close(); + } + return; + } + + // compute scale, shear, rotation, translation parameters + rot = fabs(ctm[1]) > fabs(ctm[0]); + if (rot) { + xScale = -ctm[1]; + yScale = -ctm[2] + (ctm[0] * ctm[3]) / ctm[1]; + xShear = ctm[3] / yScale; + yShear = -ctm[0] / ctm[1]; + } else { + xScale = ctm[0]; + yScale = -ctm[3] + (ctm[1] * ctm[2]) / ctm[0]; + xShear = -ctm[2] / yScale; + yShear = ctm[1] / ctm[0]; + } + tx = xoutRound(ctm[2] + ctm[4]); + ty = xoutRound(ctm[3] + ctm[5]); + if (xScale < 0) { + // this is the right edge which needs to be (left + width - 1) + --tx; + } + if (yScale < 0) { + // this is the bottom edge which needs to be (top + height - 1) + --ty; + } + // use ceil() to avoid gaps between "striped" images + scaledWidth = (int)ceil(fabs(xScale)); + xSign = (xScale < 0) ? -1 : 1; + scaledHeight = (int)ceil(fabs(yScale)); + ySign = (yScale < 0) ? -1 : 1; + + // compute corners in device space + ulx1 = 0; + uly1 = 0; + urx1 = xSign * (scaledWidth - 1); + ury1 = xoutRound(yShear * urx1); + llx1 = xoutRound(xShear * ySign * (scaledHeight - 1)); + lly1 = ySign * (scaledHeight - 1) + xoutRound(yShear * llx1); + lrx1 = xSign * (scaledWidth - 1) + + xoutRound(xShear * ySign * (scaledHeight - 1)); + lry1 = ySign * (scaledHeight - 1) + xoutRound(yShear * lrx1); + if (rot) { + ulx = tx + uly1; uly = ty - ulx1; + urx = tx + ury1; ury = ty - urx1; + llx = tx + lly1; lly = ty - llx1; + lrx = tx + lry1; lry = ty - lrx1; + } else { + ulx = tx + ulx1; uly = ty + uly1; + urx = tx + urx1; ury = ty + ury1; + llx = tx + llx1; lly = ty + lly1; + lrx = tx + lrx1; lry = ty + lry1; + } + + // bounding box: + // (bx0, by0) = upper-left corner + // (bx1, by1) = lower-right corner + // (bw, bh) = size + bx0 = (ulx < urx) ? (ulx < llx) ? (ulx < lrx) ? ulx : lrx + : (llx < lrx) ? llx : lrx + : (urx < llx) ? (urx < lrx) ? urx : lrx + : (llx < lrx) ? llx : lrx; + bx1 = (ulx > urx) ? (ulx > llx) ? (ulx > lrx) ? ulx : lrx + : (llx > lrx) ? llx : lrx + : (urx > llx) ? (urx > lrx) ? urx : lrx + : (llx > lrx) ? llx : lrx; + by0 = (uly < ury) ? (uly < lly) ? (uly < lry) ? uly : lry + : (lly < lry) ? lly : lry + : (ury < lly) ? (ury < lry) ? ury : lry + : (lly < lry) ? lly : lry; + by1 = (uly > ury) ? (uly > lly) ? (uly > lry) ? uly : lry + : (lly > lry) ? lly : lry + : (ury > lly) ? (ury > lry) ? ury : lry + : (lly > lry) ? lly : lry; + bw = bx1 - bx0 + 1; + bh = by1 - by0 + 1; + + // Bounding box clipped to pixmap, i.e., "valid" rectangle: + // (cx0, cy0) = upper-left corner of valid rectangle in Pixmap + // (cx1, cy1) = upper-left corner of valid rectangle in XImage + // (cw, ch) = size of valid rectangle + // These values will be used to transfer the XImage from/to the + // Pixmap. + cw = (bx1 >= pixmapW) ? pixmapW - bx0 : bw; + if (bx0 < 0) { + cx0 = 0; + cx1 = -bx0; + cw += bx0; + } else { + cx0 = bx0; + cx1 = 0; + } + ch = (by1 >= pixmapH) ? pixmapH - by0 : bh; + if (by0 < 0) { + cy0 = 0; + cy1 = -by0; + ch += by0; + } else { + cy0 = by0; + cy1 = 0; + } + + // check for tiny (zero width or height) images + // and off-page images + if (scaledWidth <= 0 || scaledHeight <= 0 || cw <= 0 || ch <= 0) { + if (inlineImg) { + str->reset(); + j = height * ((nVals * nBits + 7) / 8); + for (i = 0; i < j; ++i) + str->getChar(); + str->close(); + } + return; + } + + // compute Bresenham parameters for x and y scaling + yp = height / scaledHeight; + yq = height % scaledHeight; + xp = width / scaledWidth; + xq = width % scaledWidth; + + // allocate pixel buffer + pixBuf = (GfxRGB *)gmalloc((yp + 1) * width * sizeof(GfxRGB)); + if (maskColors) { + alphaBuf = (Guchar *)gmalloc((yp + 1) * width * sizeof(Guchar)); + } else { + alphaBuf = NULL; + } + + // allocate XImage + image = XCreateImage(display, visual, depth, ZPixmap, 0, NULL, bw, bh, 8, 0); + image->data = (char *)gmalloc(bh * image->bytes_per_line); + + // if the transform is anything other than a 0/90/180/270 degree + // rotation/flip, or if there is color key masking, read the + // backgound pixmap to fill in the corners + if (!((ulx == llx && uly == ury) || + (uly == lly && ulx == urx)) || + maskColors) { + XGetSubImage(display, pixmap, cx0, cy0, cw, ch, (1 << depth) - 1, ZPixmap, + image, cx1, cy1); + } + + // allocate error diffusion accumulators + if (dither) { + errDown = (GfxRGB *)gmalloc(bw * sizeof(GfxRGB)); + for (j = 0; j < bw; ++j) { + errDown[j].r = errDown[j].g = errDown[j].b = 0; + } + } else { + errDown = NULL; + } + + // optimize the one-bit-deep image case + if ((oneBitMode = nComps == 1 && nBits == 1)) { + pixBuf2[0] = 0; + colorMap->getRGB(pixBuf2, &oneBitRGB[0]); + pixBuf2[0] = 1; + colorMap->getRGB(pixBuf2, &oneBitRGB[1]); + } + + // initialize the image stream + imgStr = new ImageStream(str, width, nComps, nBits); + imgStr->reset(); + + // init y scale Bresenham + yt = 0; + lastYStep = 1; + + for (y = 0; y < scaledHeight; ++y) { + + // initialize error diffusion accumulator + errRight.r = errRight.g = errRight.b = 0; + + // y scale Bresenham + yStep = yp; + yt += yq; + if (yt >= scaledHeight) { + yt -= scaledHeight; + ++yStep; + } + + // read row(s) from image + n = (yp > 0) ? yStep : lastYStep; + if (n > 0) { + p = pixBuf; + q = alphaBuf; + for (i = 0; i < n; ++i) { + p2 = imgStr->getLine(); + for (j = 0; j < width; ++j) { + if (oneBitMode) { + *p = oneBitRGB[*p2]; + } else { + colorMap->getRGB(p2, p); + } + ++p; + if (q) { + *q = 1; + for (k = 0; k < nComps; ++k) { + if (p2[k] < maskColors[2*k] || + p2[k] > maskColors[2*k]) { + *q = 0; + break; + } + } + ++q; + } + p2 += nComps; + } + } + } + lastYStep = yStep; + + // init x scale Bresenham + xt = 0; + xSrc = 0; + + for (x = 0; x < scaledWidth; ++x) { + + // x scale Bresenham + xStep = xp; + xt += xq; + if (xt >= scaledWidth) { + xt -= scaledWidth; + ++xStep; + } + + // x shear + x1 = xSign * x + xoutRound(xShear * ySign * y); + + // y shear + y1 = ySign * y + xoutRound(yShear * x1); + + // rotation + if (rot) { + x2 = y1; + y2 = -x1; + } else { + x2 = x1; + y2 = y1; + } + + // compute the filtered pixel at (x,y) after the + // x and y scaling operations + n = yStep > 0 ? yStep : 1; + m = xStep > 0 ? xStep : 1; + p = pixBuf + xSrc; + r0 = g0 = b0 = 0; + q = alphaBuf ? alphaBuf + xSrc : (Guchar *)NULL; + alpha = 0; + for (i = 0; i < n; ++i) { + for (j = 0; j < m; ++j) { + r0 += p->r; + g0 += p->g; + b0 += p->b; + ++p; + if (q) { + alpha += *q++; + } + } + p += width - m; + } + mul = 1 / (double)(n * m); + r0 *= mul; + g0 *= mul; + b0 *= mul; + alpha *= mul; + + // x scale Bresenham + xSrc += xStep; + + // compute pixel + if (dither) { + color2.r = r0 + errRight.r + errDown[tx + x2 - bx0].r; + if (color2.r > 1) { + color2.r = 1; + } else if (color2.r < 0) { + color2.r = 0; + } + color2.g = g0 + errRight.g + errDown[tx + x2 - bx0].g; + if (color2.g > 1) { + color2.g = 1; + } else if (color2.g < 0) { + color2.g = 0; + } + color2.b = b0 + errRight.b + errDown[tx + x2 - bx0].b; + if (color2.b > 1) { + color2.b = 1; + } else if (color2.b < 0) { + color2.b = 0; + } + pix = findColor(&color2, &err); + errRight.r = errDown[tx + x2 - bx0].r = err.r / 2; + errRight.g = errDown[tx + x2 - bx0].g = err.g / 2; + errRight.b = errDown[tx + x2 - bx0].b = err.b / 2; + } else { + color2.r = r0; + color2.g = g0; + color2.b = b0; + pix = findColor(&color2, &err); + } + + // set pixel + //~ this should do a blend when 0 < alpha < 1 + if (alpha < 0.75) { + XPutPixel(image, tx + x2 - bx0, ty + y2 - by0, pix); + } + } + } + + // blit the image into the pixmap + XPutImage(display, pixmap, fillGC, image, cx1, cy1, cx0, cy0, cw, ch); + + // free memory + delete imgStr; + gfree(pixBuf); + if (maskColors) { + gfree(alphaBuf); + } + gfree(image->data); + image->data = NULL; + XDestroyImage(image); + gfree(errDown); +} + +GBool XOutputDev::findText(Unicode *s, int len, GBool top, GBool bottom, + int *xMin, int *yMin, int *xMax, int *yMax) { + double xMin1, yMin1, xMax1, yMax1; + + xMin1 = (double)*xMin; + yMin1 = (double)*yMin; + xMax1 = (double)*xMax; + yMax1 = (double)*yMax; + if (text->findText(s, len, top, bottom, &xMin1, &yMin1, &xMax1, &yMax1)) { + *xMin = xoutRound(xMin1); + *xMax = xoutRound(xMax1); + *yMin = xoutRound(yMin1); + *yMax = xoutRound(yMax1); + return gTrue; + } + return gFalse; +} + +GString *XOutputDev::getText(int xMin, int yMin, int xMax, int yMax) { + return text->getText((double)xMin, (double)yMin, + (double)xMax, (double)yMax); +} |