Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/pdf/xpdf/XOutputDev.cc
diff options
context:
space:
mode:
authorArturo Espinosa <unammx@src.gnome.org>1999-04-17 02:59:58 (GMT)
committer Arturo Espinosa <unammx@src.gnome.org>1999-04-17 02:59:58 (GMT)
commitd9f9a6449f377b4c933b75d57541b19c6d088994 (patch)
tree04f7f0c54447ef792fbf83bc5039174f4681b3bb /pdf/xpdf/XOutputDev.cc
Initial revision
Diffstat (limited to 'pdf/xpdf/XOutputDev.cc')
-rw-r--r--pdf/xpdf/XOutputDev.cc2183
1 files changed, 2183 insertions, 0 deletions
diff --git a/pdf/xpdf/XOutputDev.cc b/pdf/xpdf/XOutputDev.cc
new file mode 100644
index 0000000..3e762f6
--- /dev/null
+++ b/pdf/xpdf/XOutputDev.cc
@@ -0,0 +1,2183 @@
+//========================================================================
+//
+// XOutputDev.cc
+//
+// Copyright 1996 Derek B. Noonburg
+//
+//========================================================================
+
+#ifdef __GNUC__
+#pragma implementation
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <string.h>
+#include <ctype.h>
+#include <math.h>
+#include "gmem.h"
+#include "GString.h"
+#include "Object.h"
+#include "Stream.h"
+#include "GfxState.h"
+#include "GfxFont.h"
+#include "Error.h"
+#include "Params.h"
+#include "TextOutputDev.h"
+#include "XOutputDev.h"
+
+#include "XOutputFontInfo.h"
+
+#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
+
+//------------------------------------------------------------------------
+// Parameters
+//------------------------------------------------------------------------
+
+GBool installCmap;
+
+int rgbCubeSize;
+
+//------------------------------------------------------------------------
+// Font map
+//------------------------------------------------------------------------
+
+struct FontMapEntry {
+ char *pdfFont;
+ char *xFont;
+ GfxFontEncoding *encoding;
+};
+
+static FontMapEntry fontMap[] = {
+ {"Courier",
+ "-*-courier-medium-r-normal-*-%s-*-*-*-*-*-iso8859-1",
+ &isoLatin1Encoding},
+ {"Courier-Bold",
+ "-*-courier-bold-r-normal-*-%s-*-*-*-*-*-iso8859-1",
+ &isoLatin1Encoding},
+ {"Courier-BoldOblique",
+ "-*-courier-bold-o-normal-*-%s-*-*-*-*-*-iso8859-1",
+ &isoLatin1Encoding},
+ {"Courier-Oblique",
+ "-*-courier-medium-o-normal-*-%s-*-*-*-*-*-iso8859-1",
+ &isoLatin1Encoding},
+ {"Helvetica",
+ "-*-helvetica-medium-r-normal-*-%s-*-*-*-*-*-iso8859-1",
+ &isoLatin1Encoding},
+ {"Helvetica-Bold",
+ "-*-helvetica-bold-r-normal-*-%s-*-*-*-*-*-iso8859-1",
+ &isoLatin1Encoding},
+ {"Helvetica-BoldOblique",
+ "-*-helvetica-bold-o-normal-*-%s-*-*-*-*-*-iso8859-1",
+ &isoLatin1Encoding},
+ {"Helvetica-Oblique",
+ "-*-helvetica-medium-o-normal-*-%s-*-*-*-*-*-iso8859-1",
+ &isoLatin1Encoding},
+ {"Symbol",
+ "-*-symbol-medium-r-normal-*-%s-*-*-*-*-*-adobe-fontspecific",
+ &symbolEncoding},
+ {"Times-Bold",
+ "-*-times-bold-r-normal-*-%s-*-*-*-*-*-iso8859-1",
+ &isoLatin1Encoding},
+ {"Times-BoldItalic",
+ "-*-times-bold-i-normal-*-%s-*-*-*-*-*-iso8859-1",
+ &isoLatin1Encoding},
+ {"Times-Italic",
+ "-*-times-medium-i-normal-*-%s-*-*-*-*-*-iso8859-1",
+ &isoLatin1Encoding},
+ {"Times-Roman",
+ "-*-times-medium-r-normal-*-%s-*-*-*-*-*-iso8859-1",
+ &isoLatin1Encoding},
+ {"ZapfDingbats",
+ "-*-zapfdingbats-medium-r-normal-*-%s-*-*-*-*-*-*-*",
+ &zapfDingbatsEncoding},
+ {NULL}
+};
+
+static FontMapEntry *userFontMap;
+
+//------------------------------------------------------------------------
+// Font substitutions
+//------------------------------------------------------------------------
+
+struct FontSubst {
+ char *xFont;
+ double mWidth;
+};
+
+// index: {symbolic:12, fixed:8, serif:4, sans-serif:0} + bold*2 + italic
+static FontSubst fontSubst[16] = {
+ {"-*-helvetica-medium-r-normal-*-%s-*-*-*-*-*-iso8859-1", 0.833},
+ {"-*-helvetica-medium-o-normal-*-%s-*-*-*-*-*-iso8859-1", 0.833},
+ {"-*-helvetica-bold-r-normal-*-%s-*-*-*-*-*-iso8859-1", 0.889},
+ {"-*-helvetica-bold-o-normal-*-%s-*-*-*-*-*-iso8859-1", 0.889},
+ {"-*-times-medium-r-normal-*-%s-*-*-*-*-*-iso8859-1", 0.788},
+ {"-*-times-medium-i-normal-*-%s-*-*-*-*-*-iso8859-1", 0.722},
+ {"-*-times-bold-r-normal-*-%s-*-*-*-*-*-iso8859-1", 0.833},
+ {"-*-times-bold-i-normal-*-%s-*-*-*-*-*-iso8859-1", 0.778},
+ {"-*-courier-medium-r-normal-*-%s-*-*-*-*-*-iso8859-1", 0.600},
+ {"-*-courier-medium-o-normal-*-%s-*-*-*-*-*-iso8859-1", 0.600},
+ {"-*-courier-bold-r-normal-*-%s-*-*-*-*-*-iso8859-1", 0.600},
+ {"-*-courier-bold-o-normal-*-%s-*-*-*-*-*-iso8859-1", 0.600},
+ {"-*-symbol-medium-r-normal-*-%s-*-*-*-*-*-adobe-fontspecific", 0.576},
+ {"-*-symbol-medium-r-normal-*-%s-*-*-*-*-*-adobe-fontspecific", 0.576},
+ {"-*-symbol-medium-r-normal-*-%s-*-*-*-*-*-adobe-fontspecific", 0.576},
+ {"-*-symbol-medium-r-normal-*-%s-*-*-*-*-*-adobe-fontspecific", 0.576}
+};
+
+//------------------------------------------------------------------------
+// 16-bit fonts
+//------------------------------------------------------------------------
+
+#if JAPANESE_SUPPORT
+
+static char *japan12Font = "-*-fixed-medium-r-normal-*-%s-*-*-*-*-*-jisx0208.1983-0";
+
+// CID 0 .. 96
+static Gushort japan12Map[96] = {
+ 0x2120, 0x2120, 0x212a, 0x2149, 0x2174, 0x2170, 0x2173, 0x2175, // 00 .. 07
+ 0x2147, 0x214a, 0x214b, 0x2176, 0x215c, 0x2124, 0x213e, 0x2123, // 08 .. 0f
+ 0x213f, 0x2330, 0x2331, 0x2332, 0x2333, 0x2334, 0x2335, 0x2336, // 10 .. 17
+ 0x2337, 0x2338, 0x2339, 0x2127, 0x2128, 0x2163, 0x2161, 0x2164, // 18 .. 1f
+ 0x2129, 0x2177, 0x2341, 0x2342, 0x2343, 0x2344, 0x2345, 0x2346, // 20 .. 27
+ 0x2347, 0x2348, 0x2349, 0x234a, 0x234b, 0x234c, 0x234d, 0x234e, // 28 .. 2f
+ 0x234f, 0x2350, 0x2351, 0x2352, 0x2353, 0x2354, 0x2355, 0x2356, // 30 .. 37
+ 0x2357, 0x2358, 0x2359, 0x235a, 0x214e, 0x216f, 0x214f, 0x2130, // 38 .. 3f
+ 0x2132, 0x2146, 0x2361, 0x2362, 0x2363, 0x2364, 0x2365, 0x2366, // 40 .. 47
+ 0x2367, 0x2368, 0x2369, 0x236a, 0x236b, 0x236c, 0x236d, 0x236e, // 48 .. 4f
+ 0x236f, 0x2370, 0x2371, 0x2372, 0x2373, 0x2374, 0x2375, 0x2376, // 50 .. 57
+ 0x2377, 0x2378, 0x2379, 0x237a, 0x2150, 0x2143, 0x2151, 0x2141 // 58 .. 5f
+};
+
+// CID 325 .. 421
+static Gushort japan12KanaMap1[97] = {
+ 0x2131, 0x2121, 0x2123, 0x2156, 0x2157, 0x2122, 0x2126, 0x2572,
+ 0x2521, 0x2523, 0x2525, 0x2527, 0x2529, 0x2563, 0x2565, 0x2567,
+ 0x2543, 0x213c, 0x2522, 0x2524, 0x2526, 0x2528, 0x252a, 0x252b,
+ 0x252d, 0x252f, 0x2531, 0x2533, 0x2535, 0x2537, 0x2539, 0x253b,
+ 0x253d, 0x253f, 0x2541, 0x2544, 0x2546, 0x2548, 0x254a, 0x254b,
+ 0x254c, 0x254d, 0x254e, 0x254f, 0x2552, 0x2555, 0x2558, 0x255b,
+ 0x255e, 0x255f, 0x2560, 0x2561, 0x2562, 0x2564, 0x2566, 0x2568,
+ 0x2569, 0x256a, 0x256b, 0x256c, 0x256d, 0x256f, 0x2573, 0x212b,
+ 0x212c, 0x212e, 0x2570, 0x2571, 0x256e, 0x2575, 0x2576, 0x2574,
+ 0x252c, 0x252e, 0x2530, 0x2532, 0x2534, 0x2536, 0x2538, 0x253a,
+ 0x253c, 0x253e, 0x2540, 0x2542, 0x2545, 0x2547, 0x2549, 0x2550,
+ 0x2551, 0x2553, 0x2554, 0x2556, 0x2557, 0x2559, 0x255a, 0x255c,
+ 0x255d
+};
+
+// CID 501 .. 598
+static Gushort japan12KanaMap2[98] = {
+ 0x212d, 0x212f, 0x216d, 0x214c, 0x214d, 0x2152, 0x2153, 0x2154,
+ 0x2155, 0x2158, 0x2159, 0x215a, 0x215b, 0x213d, 0x2121, 0x2472,
+ 0x2421, 0x2423, 0x2425, 0x2427, 0x2429, 0x2463, 0x2465, 0x2467,
+ 0x2443, 0x2422, 0x2424, 0x2426, 0x2428, 0x242a, 0x242b, 0x242d,
+ 0x242f, 0x2431, 0x2433, 0x2435, 0x2437, 0x2439, 0x243b, 0x243d,
+ 0x243f, 0x2441, 0x2444, 0x2446, 0x2448, 0x244a, 0x244b, 0x244c,
+ 0x244d, 0x244e, 0x244f, 0x2452, 0x2455, 0x2458, 0x245b, 0x245e,
+ 0x245f, 0x2460, 0x2461, 0x2462, 0x2464, 0x2466, 0x2468, 0x2469,
+ 0x246a, 0x246b, 0x246c, 0x246d, 0x246f, 0x2473, 0x2470, 0x2471,
+ 0x246e, 0x242c, 0x242e, 0x2430, 0x2432, 0x2434, 0x2436, 0x2438,
+ 0x243a, 0x243c, 0x243e, 0x2440, 0x2442, 0x2445, 0x2447, 0x2449,
+ 0x2450, 0x2451, 0x2453, 0x2454, 0x2456, 0x2457, 0x2459, 0x245a,
+ 0x245c, 0x245d
+};
+
+static char *japan12Roman[10] = {
+ "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX", "X"
+};
+
+static char *japan12Abbrev1[6] = {
+ "mm", "cm", "km", "mg", "kg", "cc"
+};
+
+#endif
+
+//------------------------------------------------------------------------
+// Constructed characters
+//------------------------------------------------------------------------
+
+#define lastRegularChar 0x0ff
+#define firstSubstChar 0x100
+#define lastSubstChar 0x104
+#define firstConstrChar 0x105
+#define lastConstrChar 0x106
+#define firstMultiChar 0x107
+#define lastMultiChar 0x10d
+
+// substituted chars
+static Guchar substChars[] = {
+ 0x27, // 100: quotesingle --> quoteright
+ 0x2d, // 101: emdash --> hyphen
+ 0xad, // 102: hyphen --> endash
+ 0x2f, // 103: fraction --> slash
+ 0xb0, // 104: ring --> degree
+};
+
+// constructed chars
+// 105: bullet
+// 106: trademark
+
+// built-up chars
+static char *multiChars[] = {
+ "fi", // 107: fi
+ "fl", // 108: fl
+ "OE", // 109: OE
+ "oe", // 10a: oe
+ "...", // 10b: ellipsis
+ "``", // 10c: quotedblleft
+ "''" // 10d: quotedblright
+};
+
+// ignored chars
+// 10c: Lslash
+// 10d: Scaron
+// 10e: Zcaron
+// 10f: Ydieresis
+// 110: breve
+// 111: caron
+// 112: circumflex
+// 113: dagger
+// 114: daggerdbl
+// 115: dotaccent
+// 116: dotlessi
+// 117: florin
+// 118: grave
+// 119: guilsinglleft
+// 11a: guilsinglright
+// 11b: hungarumlaut
+// 11c: lslash
+// 11d: ogonek
+// 11e: perthousand
+// 11f: quotedblbase
+// 120: quotesinglbase
+// 121: scaron
+// 122: tilde
+// 123: zcaron
+
+//------------------------------------------------------------------------
+// XOutputFont
+//------------------------------------------------------------------------
+
+// Note: if real font is substantially narrower than substituted
+// font, the size is reduced accordingly.
+XOutputFont::XOutputFont(GfxFont *gfxFont, double m11, double m12,
+ double m21, double m22, Display *display1) {
+ GString *pdfFont;
+ FontMapEntry *p;
+ GfxFontEncoding *encoding;
+ char *fontNameFmt;
+ char fontName[200], fontSize[100];
+ GBool rotated;
+ double size;
+ int startSize, sz;
+ int index;
+ int code, code2;
+ double w1, w2, v;
+ double *fm;
+ char *charName;
+ int n;
+
+ // init
+ id = gfxFont->getID();
+ mat11 = m11;
+ mat12 = m12;
+ mat21 = m21;
+ mat22 = m22;
+ display = display1;
+ xFont = NULL;
+ hex = gFalse;
+
+ // construct X font name
+ if (gfxFont->is16Bit()) {
+ fontNameFmt = fontSubst[0].xFont;
+ switch (gfxFont->getCharSet16()) {
+ case font16AdobeJapan12:
+#if JAPANESE_SUPPORT
+ fontNameFmt = japan12Font;
+#endif
+ break;
+ }
+ } else {
+ pdfFont = gfxFont->getName();
+ if (pdfFont) {
+ for (p = userFontMap; p->pdfFont; ++p) {
+ if (!pdfFont->cmp(p->pdfFont))
+ break;
+ }
+ if (!p->pdfFont) {
+ for (p = fontMap; p->pdfFont; ++p) {
+ if (!pdfFont->cmp(p->pdfFont))
+ break;
+ }
+ }
+ } else {
+ p = NULL;
+ }
+ if (p && p->pdfFont) {
+ fontNameFmt = p->xFont;
+ encoding = p->encoding;
+ } else {
+ encoding = &isoLatin1Encoding;
+//~ Some non-symbolic fonts are tagged as symbolic.
+// if (gfxFont->isSymbolic()) {
+// index = 12;
+// encoding = symbolEncoding;
+// } else
+ if (gfxFont->isFixedWidth()) {
+ index = 8;
+ } else if (gfxFont->isSerif()) {
+ index = 4;
+ } else {
+ index = 0;
+ }
+ if (gfxFont->isBold())
+ index += 2;
+ if (gfxFont->isItalic())
+ index += 1;
+ if ((code = gfxFont->getCharCode("m")) >= 0)
+ w1 = gfxFont->getWidth(code);
+ else
+ w1 = 0;
+ w2 = fontSubst[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;
+ mat11 *= w1;
+ mat12 *= w1;
+ mat21 *= w1;
+ mat22 *= w1;
+ }
+ fm = gfxFont->getFontMatrix();
+ v = (fm[0] == 0) ? 1 : (fm[3] / fm[0]);
+ mat12 *= v;
+ mat22 *= v;
+ } else if (!gfxFont->isSymbolic()) {
+ if (w1 > 0.01 && w1 < 0.9 * w2) {
+ w1 /= w2;
+ if (w1 < 0.8)
+ w1 = 0.8;
+ mat11 *= w1;
+ mat12 *= w1;
+ mat21 *= w1;
+ mat22 *= w1;
+ }
+ }
+ fontNameFmt = fontSubst[index].xFont;
+ }
+
+ // Construct forward and reverse map.
+ // This tries to deal with font subset character names of the
+ // form 'Bxx', 'Cxx', 'Gxx', with decimal or hex numbering.
+ for (code = 0; code < 256; ++code)
+ revMap[code] = 0;
+ if (encoding) {
+ for (code = 0; code < 256; ++code) {
+ if ((charName = gfxFont->getCharName(code))) {
+ if ((charName[0] == 'B' || charName[0] == 'C' ||
+ charName[0] == 'G') &&
+ strlen(charName) == 3 &&
+ ((charName[1] >= 'a' && charName[1] <= 'f') ||
+ (charName[1] >= 'A' && charName[1] <= 'F') ||
+ (charName[2] >= 'a' && charName[2] <= 'f') ||
+ (charName[2] >= 'A' && charName[2] <= 'F'))) {
+ hex = gTrue;
+ break;
+ }
+ }
+ }
+ for (code = 0; code < 256; ++code) {
+ if ((charName = gfxFont->getCharName(code))) {
+ if ((code2 = encoding->getCharCode(charName)) < 0) {
+ n = strlen(charName);
+ if (hex && n == 3 &&
+ (charName[0] == 'B' || charName[0] == 'C' ||
+ charName[0] == 'G') &&
+ isxdigit(charName[1]) && isxdigit(charName[2])) {
+ sscanf(charName+1, "%x", &code2);
+ } else if (!hex && n >= 2 && n <= 3 &&
+ isdigit(charName[0]) && isdigit(charName[1])) {
+ code2 = atoi(charName);
+ if (code2 >= 256)
+ code2 = -1;
+ } else if (!hex && n >= 3 && n <= 5 && isdigit(charName[1])) {
+ code2 = atoi(charName+1);
+ if (code2 >= 256)
+ code2 = -1;
+ }
+ //~ this is a kludge -- is there a standard internal encoding
+ //~ used by all/most Type 1 fonts?
+ if (code2 == 262) // hyphen
+ code2 = 45;
+ else if (code2 == 266) // emdash
+ code2 = 208;
+ }
+ if (code2 >= 0) {
+ map[code] = (Gushort)code2;
+ if (code2 < 256)
+ revMap[code2] = (Guchar)code;
+ } else {
+ map[code] = 0;
+ }
+ } else {
+ map[code] = 0;
+ }
+ }
+ } else {
+ code2 = 0; // to make gcc happy
+ //~ this is a hack to get around the fact that X won't draw
+ //~ chars 0..31; this works when the fonts have duplicate encodings
+ //~ for those chars
+ for (code = 0; code < 32; ++code) {
+ if ((charName = gfxFont->getCharName(code)) &&
+ (code2 = gfxFont->getCharCode(charName)) >= 0) {
+ map[code] = (Gushort)code2;
+ if (code2 < 256)
+ revMap[code2] = (Guchar)code;
+ }
+ }
+ for (code = 32; code < 256; ++code) {
+ map[code] = (Gushort)code;
+ revMap[code] = (Guchar)code;
+ }
+ }
+ }
+
+ // compute size, normalize matrix
+ size = sqrt(mat21*mat21 + mat22*mat22);
+ mat11 = mat11 / size;
+ mat12 = -mat12 / size;
+ mat21 = mat21 / size;
+ mat22 = -mat22 / size;
+ startSize = (int)size;
+
+ // try to get a rotated font?
+ rotated = !(mat11 > 0 && mat22 > 0 && fabs(mat11/mat22 - 1) < 0.2 &&
+ fabs(mat12) < 0.01 && fabs(mat21) < 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
+ //~ This does a linear search -- it should get a list of fonts from
+ //~ the server and pick the closest.
+ if (rotated)
+ sprintf(fontSize, "[%s%0.2f %s%0.2f %s%0.2f %s%0.2f]",
+ mat11<0 ? "~" : "", fabs(mat11 * startSize),
+ mat12<0 ? "~" : "", fabs(mat12 * startSize),
+ mat21<0 ? "~" : "", fabs(mat21 * startSize),
+ mat22<0 ? "~" : "", fabs(mat22 * startSize));
+ else
+ sprintf(fontSize, "%d", startSize);
+ sprintf(fontName, fontNameFmt, fontSize);
+ xFont = XLoadQueryFont(display, fontName);
+ if (!xFont) {
+ for (sz = startSize; sz >= startSize/2 && sz >= 1; --sz) {
+ sprintf(fontSize, "%d", sz);
+ sprintf(fontName, fontNameFmt, fontSize);
+ if ((xFont = XLoadQueryFont(display, fontName)))
+ break;
+ }
+ if (!xFont) {
+ for (sz = startSize + 1; sz < startSize + 10; ++sz) {
+ sprintf(fontSize, "%d", sz);
+ sprintf(fontName, fontNameFmt, fontSize);
+ if ((xFont = XLoadQueryFont(display, fontName)))
+ break;
+ }
+ if (!xFont) {
+ sprintf(fontSize, "%d", startSize);
+ sprintf(fontName, fontNameFmt, fontSize);
+ error(-1, "Failed to open font: '%s'", fontName);
+ return;
+ }
+ }
+ }
+}
+
+XOutputFont::~XOutputFont() {
+ if (xFont)
+ XFreeFont(display, xFont);
+}
+
+//------------------------------------------------------------------------
+// XOutputFontCache
+//------------------------------------------------------------------------
+
+XOutputFontCache::XOutputFontCache(Display *display1) {
+ int i;
+
+ display = display1;
+ for (i = 0; i < fontCacheSize; ++i)
+ fonts[i] = NULL;
+ numFonts = 0;
+}
+
+XOutputFontCache::~XOutputFontCache() {
+ int i;
+
+ for (i = 0; i < numFonts; ++i)
+ delete fonts[i];
+}
+
+XOutputFont *XOutputFontCache::getFont(GfxFont *gfxFont,
+ double m11, double m12,
+ double m21, double m22) {
+ XOutputFont *font;
+ int i, j;
+
+ // is it the most recently used font?
+ if (numFonts > 0 && fonts[0]->matches(gfxFont->getID(),
+ m11, m12, m21, m22))
+ return fonts[0];
+
+ // is it in the cache?
+ for (i = 1; i < numFonts; ++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;
+ }
+ }
+
+ // make a new font
+ font = new XOutputFont(gfxFont, m11, m12, m21, m22, display);
+ if (!font->getXFont()) {
+ delete font;
+ return NULL;
+ }
+
+ // insert font in cache
+ if (numFonts == fontCacheSize) {
+ --numFonts;
+ delete fonts[numFonts];
+ }
+ for (j = numFonts; j > 0; --j)
+ fonts[j] = fonts[j-1];
+ fonts[0] = font;
+ ++numFonts;
+
+ // return it
+ return font;
+}
+
+//------------------------------------------------------------------------
+// XOutputDev
+//------------------------------------------------------------------------
+
+XOutputDev::XOutputDev(Display *display1, Pixmap pixmap1, Guint depth1,
+ Colormap colormap, unsigned long paperColor) {
+ XVisualInfo visualTempl;
+ XVisualInfo *visualList;
+ int nVisuals;
+ Gulong mask;
+ XGCValues gcValues;
+ XColor xcolor;
+ XColor *xcolors;
+ int r, g, b, n, m, i;
+ GBool ok;
+
+ // get display/pixmap info
+ display = display1;
+ screenNum = DefaultScreen(display);
+ pixmap = pixmap1;
+ depth = depth1;
+
+ // check for TrueColor visual
+ trueColor = gFalse;
+ if (depth == 0) {
+ depth = DefaultDepth(display, screenNum);
+ visualList = XGetVisualInfo(display, 0, &visualTempl, &nVisuals);
+ for (i = 0; i < nVisuals; ++i) {
+ if (visualList[i].visual == DefaultVisual(display, screenNum)) {
+ if (visualList[i].c_class == TrueColor) {
+ trueColor = gTrue;
+ mask = visualList[i].red_mask;
+ rShift = 0;
+ while (mask && !(mask & 1)) {
+ ++rShift;
+ mask >>= 1;
+ }
+ rMul = (int)mask;
+ mask = visualList[i].green_mask;
+ gShift = 0;
+ while (mask && !(mask & 1)) {
+ ++gShift;
+ mask >>= 1;
+ }
+ gMul = (int)mask;
+ mask = visualList[i].blue_mask;
+ bShift = 0;
+ while (mask && !(mask & 1)) {
+ ++bShift;
+ mask >>= 1;
+ }
+ bMul = (int)mask;
+ }
+ break;
+ }
+ }
+ XFree((XPointer)visualList);
+ }
+
+ // allocate a color cube
+ if (!trueColor) {
+
+ // 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;
+ ++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);
+ } 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;
+ 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);
+ }
+ }
+ }
+
+ // 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);
+
+ // no clip region yet
+ clipRegion = NULL;
+
+ // get user font map
+ for (n = 0; devFontMap[n].pdfFont; ++n) ;
+ userFontMap = (FontMapEntry *)gmalloc((n+1) * sizeof(FontMapEntry));
+ for (i = 0; i < n; ++i) {
+ userFontMap[i].pdfFont = devFontMap[i].pdfFont;
+ userFontMap[i].xFont = devFontMap[i].devFont;
+ m = strlen(userFontMap[i].xFont);
+ if (m >= 10 && !strcmp(userFontMap[i].xFont + m - 10, "-iso8859-2"))
+ userFontMap[i].encoding = &isoLatin2Encoding;
+ else if (m >= 13 && !strcmp(userFontMap[i].xFont + m - 13,
+ "-fontspecific"))
+ userFontMap[i].encoding = NULL;
+ else
+ userFontMap[i].encoding = &isoLatin1Encoding;
+ }
+ userFontMap[n].pdfFont = NULL;
+
+ // set up the font cache and fonts
+ gfxFont = NULL;
+ font = NULL;
+ fontCache = new XOutputFontCache(display);
+
+ // empty state stack
+ save = NULL;
+
+ // create text object
+ text = new TextPage(gFalse);
+}
+
+XOutputDev::~XOutputDev() {
+ gfree(userFontMap);
+ delete fontCache;
+ XFreeGC(display, strokeGC);
+ XFreeGC(display, fillGC);
+ XFreeGC(display, paperGC);
+ if (clipRegion)
+ XDestroyRegion(clipRegion);
+ delete text;
+}
+
+void XOutputDev::startPage(int pageNum, GfxState *state) {
+ XOutputState *s;
+ XGCValues gcValues;
+ XRectangle rect;
+
+ // clear state stack
+ while (save) {
+ s = save;
+ save = save->next;
+ XFreeGC(display, s->strokeGC);
+ XFreeGC(display, s->fillGC);
+ XDestroyRegion(s->clipRegion);
+ delete s;
+ }
+ save = NULL;
+
+ // default line flatness
+ flatness = 0;
+
+ // reset GCs
+ gcValues.foreground = BlackPixel(display, screenNum);
+ gcValues.background = WhitePixel(display, screenNum);
+ gcValues.line_width = 0;
+ gcValues.line_style = LineSolid;
+ XChangeGC(display, strokeGC,
+ GCForeground | GCBackground | GCLineWidth | GCLineStyle,
+ &gcValues);
+ XChangeGC(display, fillGC,
+ GCForeground | GCBackground | GCLineWidth | GCLineStyle,
+ &gcValues);
+
+ // clear clipping region
+ if (clipRegion)
+ XDestroyRegion(clipRegion);
+ 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() {
+ text->coalesce();
+}
+
+void XOutputDev::drawLinkBorder(double x1, double y1, double x2, double y2,
+ double w) {
+ GfxColor color;
+ XPoint points[5];
+ int x, y;
+
+ color.setRGB(0, 0, 1);
+ XSetForeground(display, strokeGC, findColor(&color));
+ 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);
+ 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) {
+ XSetForeground(display, fillGC, findColor(state->getFillColor()));
+}
+
+void XOutputDev::updateStrokeColor(GfxState *state) {
+ XSetForeground(display, strokeGC, findColor(state->getStrokeColor()));
+}
+
+void XOutputDev::updateFont(GfxState *state) {
+ double m11, m12, m21, m22;
+
+ if (!(gfxFont = state->getFont())) {
+ font = NULL;
+ return;
+ }
+ state->getFontTransMat(&m11, &m12, &m21, &m22);
+ m11 *= state->getHorizScaling();
+ m21 *= state->getHorizScaling();
+ font = fontCache->getFont(gfxFont, m11, m12, m21, m22);
+ if (font) {
+ XSetFont(display, fillGC, font->getXFont()->fid);
+ XSetFont(display, strokeGC, font->getXFont()->fid);
+ }
+}
+
+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
+ region = XPolygonRegion(points, lengths[0], rule);
+ j = lengths[0] + 1;
+ for (i = 1; i < n; ++i) {
+ region2 = XPolygonRegion(points + j, lengths[i], rule);
+ 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);
+ }
+
+ // 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].xMin > rect.xMin && rects[ii].xMin < rect.xMax) ||
+ (rects[ii].xMax > rect.xMin && rects[ii].xMax < rect.xMax) ||
+ (rects[ii].xMin < rect.xMin && rects[ii].xMax > rect.xMax)) &&
+ ((rects[ii].yMin > rect.yMin && rects[ii].yMin < rect.yMax) ||
+ (rects[ii].yMax > rect.yMin && rects[ii].yMax < rect.yMax) ||
+ (rects[ii].yMin < rect.yMin && rects[ii].yMax > rect.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, s, font ? font->isHex() : gFalse);
+}
+
+void XOutputDev::endString(GfxState *state) {
+ text->endString();
+}
+
+void XOutputDev::drawChar(GfxState *state, double x, double y,
+ double dx, double dy, Guchar c) {
+ Gushort c1;
+ char buf;
+ char *p;
+ int n, i;
+ double x1, y1;
+ double tx;
+
+ text->addChar(state, x, y, dx, dy, c);
+
+ if (!font)
+ return;
+
+ // check for invisible text -- this is used by Acrobat Capture
+ if ((state->getRender() & 3) == 3)
+ return;
+
+ state->transform(x, y, &x1, &y1);
+ c1 = font->mapChar(c);
+ if (c1 <= lastRegularChar) {
+ buf = (char)c1;
+ XDrawString(display, pixmap,
+ (state->getRender() & 1) ? strokeGC : fillGC,
+ xoutRound(x1), xoutRound(y1), &buf, 1);
+ } else if (c1 <= lastSubstChar) {
+ buf = (char)substChars[c1 - firstSubstChar];
+ XDrawString(display, pixmap,
+ (state->getRender() & 1) ? strokeGC : fillGC,
+ xoutRound(x1), xoutRound(y1), &buf, 1);
+ } else if (c1 <= lastConstrChar) {
+ //~ need to deal with rotated text here
+ switch (c1 - firstConstrChar) {
+ case 0: // bullet
+ tx = 0.25 * state->getTransformedFontSize() *
+ gfxFont->getWidth(c);
+ XFillRectangle(display, pixmap,
+ (state->getRender() & 1) ? strokeGC : fillGC,
+ xoutRound(x1 + tx),
+ xoutRound(y1 - 0.4 * font->getXFont()->ascent - tx),
+ xoutRound(2 * tx), xoutRound(2 * tx));
+ break;
+ case 1: // trademark
+//~ this should use a smaller font
+// tx = state->getTransformedFontSize() *
+// (gfxFont->getWidth(c) -
+// gfxFont->getWidth(font->revCharMap('M')));
+ tx = 0.9 * state->getTransformedFontSize() *
+ gfxFont->getWidth(font->revMapChar('T'));
+ y1 -= 0.33 * (double)font->getXFont()->ascent;
+ buf = 'T';
+ XDrawString(display, pixmap,
+ (state->getRender() & 1) ? strokeGC : fillGC,
+ xoutRound(x1), xoutRound(y1), &buf, 1);
+ x1 += tx;
+ buf = 'M';
+ XDrawString(display, pixmap,
+ (state->getRender() & 1) ? strokeGC : fillGC,
+ xoutRound(x1), xoutRound(y1), &buf, 1);
+ break;
+ }
+ } else if (c1 <= lastMultiChar) {
+ p = multiChars[c1 - firstMultiChar];
+ n = strlen(p);
+ tx = gfxFont->getWidth(c);
+ tx -= gfxFont->getWidth(font->revMapChar(p[n-1]));
+ tx = tx * state->getTransformedFontSize() / (double)(n - 1);
+ for (i = 0; i < n; ++i) {
+ XDrawString(display, pixmap,
+ (state->getRender() & 1) ? strokeGC : fillGC,
+ xoutRound(x1), xoutRound(y1), p + i, 1);
+ x1 += tx;
+ }
+ }
+}
+
+void XOutputDev::drawChar16(GfxState *state, double x, double y,
+ double dx, double dy, int c) {
+ int c1;
+ XChar2b c2[4];
+ double x1, y1;
+#if JAPANESE_SUPPORT
+ int t1, t2;
+ double x2;
+ char *p;
+ int n, i;
+#endif
+
+ if (!font)
+ return;
+
+ // check for invisible text -- this is used by Acrobat Capture
+ if ((state->getRender() & 3) == 3)
+ return;
+
+ state->transform(x, y, &x1, &y1);
+
+ c1 = 0;
+ switch (gfxFont->getCharSet16()) {
+
+ // convert Adobe-Japan1-2 to JIS X 0208-1983
+ case font16AdobeJapan12:
+#if JAPANESE_SUPPORT
+ if (c <= 96) {
+ c1 = japan12Map[c];
+ } else if (c <= 632) {
+ if (c <= 230)
+ c1 = 0;
+ else if (c <= 324)
+ c1 = japan12Map[c - 230];
+ else if (c <= 421)
+ c1 = japan12KanaMap1[c - 325];
+ else if (c <= 500)
+ c1 = 0;
+ else if (c <= 598)
+ c1 = japan12KanaMap2[c - 501];
+ else
+ c1 = 0;
+ } else if (c <= 1124) {
+ if (c <= 779) {
+ if (c <= 726)
+ c1 = 0x2121 + (c - 633);
+ else if (c <= 740)
+ c1 = 0x2221 + (c - 727);
+ else if (c <= 748)
+ c1 = 0x223a + (c - 741);
+ else if (c <= 755)
+ c1 = 0x224a + (c - 749);
+ else if (c <= 770)
+ c1 = 0x225c + (c - 756);
+ else if (c <= 778)
+ c1 = 0x2272 + (c - 771);
+ else
+ c1 = 0x227e;
+ } else if (c <= 841) {
+ if (c <= 789)
+ c1 = 0x2330 + (c - 780);
+ else if (c <= 815)
+ c1 = 0x2341 + (c - 790);
+ else
+ c1 = 0x2361 + (c - 816);
+ } else if (c <= 1010) {
+ if (c <= 924)
+ c1 = 0x2421 + (c - 842);
+ else
+ c1 = 0x2521 + (c - 925);
+ } else {
+ if (c <= 1034)
+ c1 = 0x2621 + (c - 1011);
+ else if (c <= 1058)
+ c1 = 0x2641 + (c - 1035);
+ else if (c <= 1091)
+ c1 = 0x2721 + (c - 1059);
+ else
+ c1 = 0x2751 + (c - 1092);
+ }
+ } else if (c <= 4089) {
+ t1 = (c - 1125) / 94;
+ t2 = (c - 1125) % 94;
+ c1 = 0x3021 + (t1 << 8) + t2;
+ } else if (c <= 7477) {
+ t1 = (c - 4090) / 94;
+ t2 = (c - 4090) % 94;
+ c1 = 0x5021 + (t1 << 8) + t2;
+ } else if (c <= 7554) {
+ c1 = 0;
+ } else if (c <= 7563) { // circled Arabic numbers 1..9
+ c1 = 0x2331 + (c - 7555);
+ c2[0].byte1 = c1 >> 8;
+ c2[0].byte2 = c1 & 0xff;
+ XDrawString16(display, pixmap,
+ (state->getRender() & 1) ? strokeGC : fillGC,
+ xoutRound(x1), xoutRound(y1), c2, 1);
+ c1 = 0x227e;
+ c2[0].byte1 = c1 >> 8;
+ c2[0].byte2 = c1 & 0xff;
+ XDrawString16(display, pixmap,
+ (state->getRender() & 1) ? strokeGC : fillGC,
+ xoutRound(x1), xoutRound(y1), c2, 1);
+ c1 = -1;
+ } else if (c <= 7574) { // circled Arabic numbers 10..20
+ n = c - 7564 + 10;
+ x2 = x1;
+ for (i = 0; i < 2; ++i) {
+ c1 = 0x2330 + (i == 0 ? (n / 10) : (n % 10));
+ c2[0].byte1 = c1 >> 8;
+ c2[0].byte2 = c1 & 0xff;
+ XDrawString16(display, pixmap,
+ (state->getRender() & 1) ? strokeGC : fillGC,
+ xoutRound(x2), xoutRound(y1), c2, 1);
+ x2 += 0.5 * state->getTransformedFontSize();
+ }
+ c1 = 0x227e;
+ c2[0].byte1 = c1 >> 8;
+ c2[0].byte2 = c1 & 0xff;
+ XDrawString16(display, pixmap,
+ (state->getRender() & 1) ? strokeGC : fillGC,
+ xoutRound(x1), xoutRound(y1), c2, 1);
+ c1 = -1;
+ } else if (c <= 7584) { // Roman numbers I..X
+ p = japan12Roman[c - 7575];
+ n = strlen(p);
+ for (; *p; ++p) {
+ if (*p == 'I')
+ c1 = 0x2349;
+ else if (*p == 'V')
+ c1 = 0x2356;
+ else // 'X'
+ c1 = 0x2358;
+ c2[0].byte1 = c1 >> 8;
+ c2[0].byte2 = c1 & 0xff;
+ XDrawString16(display, pixmap,
+ (state->getRender() & 1) ? strokeGC : fillGC,
+ xoutRound(x1), xoutRound(y1), c2, 1);
+ if (*p == 'I')
+ x1 += 0.2 * state->getTransformedFontSize();
+ else
+ x1 += 0.5 * state->getTransformedFontSize();
+ }
+ c1 = -1;
+ } else if (c <= 7632) {
+ if (c <= 7600) {
+ c1 = 0;
+ } else if (c <= 7606) {
+ p = japan12Abbrev1[c - 7601];
+ n = strlen(p);
+ for (; *p; ++p) {
+ c1 = 0x2300 + *p;
+ c2[0].byte1 = c1 >> 8;
+ c2[0].byte2 = c1 & 0xff;
+ XDrawString16(display, pixmap,
+ (state->getRender() & 1) ? strokeGC : fillGC,
+ xoutRound(x1), xoutRound(y1), c2, 1);
+ x1 += 0.5 * state->getTransformedFontSize();
+ }
+ c1 = -1;
+ } else {
+ c1 = 0;
+ }
+ } else {
+ c1 = 0;
+ }
+#if 0 //~
+ if (c1 == 0)
+ error(-1, "Unsupported Adobe-Japan1-2 character: %d", c);
+#endif
+#endif // JAPANESE_SUPPORT
+ break;
+ }
+
+ if (c1 > 0) {
+ c2[0].byte1 = c1 >> 8;
+ c2[0].byte2 = c1 & 0xff;
+ XDrawString16(display, pixmap,
+ (state->getRender() & 1) ? strokeGC : fillGC,
+ xoutRound(x1), xoutRound(y1), c2, 1);
+ }
+}
+
+void XOutputDev::drawImageMask(GfxState *state, Stream *str,
+ int width, int height, GBool invert,
+ GBool inlineImg) {
+ XImage *image;
+ int x0, y0; // top left corner of image
+ int w0, h0, w1, h1; // size of image
+ int x2, y2;
+ int w2, h2;
+ double xt, yt, wt, ht;
+ GBool rotate, xFlip, yFlip;
+ int x, y;
+ int ix, iy;
+ int px1, px2, qx, dx;
+ int py1, py2, qy, dy;
+ Guchar pixBuf;
+ Gulong color;
+ int i, j;
+
+ // get image position and size
+ state->transform(0, 0, &xt, &yt);
+ state->transformDelta(1, 1, &wt, &ht);
+ if (wt > 0) {
+ x0 = xoutRound(xt);
+ w0 = xoutRound(wt);
+ } else {
+ x0 = xoutRound(xt + wt);
+ w0 = xoutRound(-wt);
+ }
+ if (ht > 0) {
+ y0 = xoutRound(yt);
+ h0 = xoutRound(ht);
+ } else {
+ y0 = xoutRound(yt + ht);
+ h0 = xoutRound(-ht);
+ }
+ state->transformDelta(1, 0, &xt, &yt);
+ rotate = fabs(xt) < fabs(yt);
+ if (rotate) {
+ w1 = h0;
+ h1 = w0;
+ xFlip = ht < 0;
+ yFlip = wt > 0;
+ } else {
+ w1 = w0;
+ h1 = h0;
+ xFlip = wt < 0;
+ yFlip = ht > 0;
+ }
+
+ // set up
+ color = findColor(state->getFillColor());
+
+ // check for tiny (zero width or height) images
+ if (w0 == 0 || h0 == 0) {
+ j = height * ((width + 7) / 8);
+ str->reset();
+ for (i = 0; i < j; ++i)
+ str->getChar();
+ return;
+ }
+
+ // Bresenham parameters
+ px1 = w1 / width;
+ px2 = w1 - px1 * width;
+ py1 = h1 / height;
+ py2 = h1 - py1 * height;
+
+ // allocate XImage
+ image = XCreateImage(display, DefaultVisual(display, screenNum),
+ depth, ZPixmap, 0, NULL, w0, h0, 8, 0);
+ image->data = (char *)gmalloc(h0 * image->bytes_per_line);
+ if (x0 + w0 > pixmapW)
+ w2 = pixmapW - x0;
+ else
+ w2 = w0;
+ if (x0 < 0) {
+ x2 = -x0;
+ w2 += x0;
+ x0 = 0;
+ } else {
+ x2 = 0;
+ }
+ if (y0 + h0 > pixmapH)
+ h2 = pixmapH - y0;
+ else
+ h2 = h0;
+ if (y0 < 0) {
+ y2 = -y0;
+ h2 += y0;
+ y0 = 0;
+ } else {
+ y2 = 0;
+ }
+ XGetSubImage(display, pixmap, x0, y0, w2, h2, (1 << depth) - 1, ZPixmap,
+ image, x2, y2);
+
+ // initialize the image stream
+ str->resetImage(width, 1, 1);
+
+ // first line (column)
+ y = yFlip ? h1 - 1 : 0;
+ qy = 0;
+
+ // read image
+ for (i = 0; i < height; ++i) {
+
+ // vertical (horizontal) Bresenham
+ dy = py1;
+ if ((qy += py2) >= height) {
+ ++dy;
+ qy -= height;
+ }
+
+ // drop a line (column)
+ if (dy == 0) {
+ str->skipImageLine();
+
+ } else {
+
+ // first column (line)
+ x = xFlip ? w1 - 1 : 0;
+ qx = 0;
+
+ // for each column (line)...
+ for (j = 0; j < width; ++j) {
+
+ // horizontal (vertical) Bresenham
+ dx = px1;
+ if ((qx += px2) >= width) {
+ ++dx;
+ qx -= width;
+ }
+
+ // get image pixel
+ str->getImagePixel(&pixBuf);
+ if (invert)
+ pixBuf ^= 1;
+
+ // draw image pixel
+ if (dx > 0 && pixBuf == 0) {
+ if (dx == 1 && dy == 1) {
+ if (rotate)
+ XPutPixel(image, y, x, color);
+ else
+ XPutPixel(image, x, y, color);
+ } else {
+ for (ix = 0; ix < dx; ++ix) {
+ for (iy = 0; iy < dy; ++iy) {
+ if (rotate)
+ XPutPixel(image, yFlip ? y - iy : y + iy,
+ xFlip ? x - ix : x + ix, color);
+ else
+ XPutPixel(image, xFlip ? x - ix : x + ix,
+ yFlip ? y - iy : y + iy, color);
+ }
+ }
+ }
+ }
+
+ // next column (line)
+ if (xFlip)
+ x -= dx;
+ else
+ x += dx;
+ }
+ }
+
+ // next line (column)
+ if (yFlip)
+ y -= dy;
+ else
+ y += dy;
+ }
+
+ // blit the image into the pixmap
+ XPutImage(display, pixmap, fillGC, image, x2, y2, x0, y0, w2, h2);
+
+ // free memory
+ gfree(image->data);
+ image->data = NULL;
+ XDestroyImage(image);
+}
+
+inline Gulong XOutputDev::findColor(RGBColor *x, RGBColor *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;
+}
+
+void XOutputDev::drawImage(GfxState *state, Stream *str, int width,
+ int height, GfxImageColorMap *colorMap,
+ GBool inlineImg) {
+ XImage *image;
+ int x0, y0; // top left corner of image
+ int w0, h0, w1, h1; // size of image
+ double xt, yt, wt, ht;
+ GBool rotate, xFlip, yFlip;
+ GBool dither;
+ int x, y;
+ int ix, iy;
+ int px1, px2, qx, dx;
+ int py1, py2, qy, dy;
+ Guchar pixBuf[4];
+ Gulong pixel;
+ int nComps, nVals, nBits;
+ double r1, g1, b1;
+ GfxColor color;
+ RGBColor color2, err;
+ RGBColor *errRight, *errDown;
+ int i, j;
+
+ // get image position and size
+ state->transform(0, 0, &xt, &yt);
+ state->transformDelta(1, 1, &wt, &ht);
+ if (wt > 0) {
+ x0 = xoutRound(xt);
+ w0 = xoutRound(wt);
+ } else {
+ x0 = xoutRound(xt + wt);
+ w0 = xoutRound(-wt);
+ }
+ if (ht > 0) {
+ y0 = xoutRound(yt);
+ h0 = xoutRound(ht);
+ } else {
+ y0 = xoutRound(yt + ht);
+ h0 = xoutRound(-ht);
+ }
+ state->transformDelta(1, 0, &xt, &yt);
+ rotate = fabs(xt) < fabs(yt);
+ if (rotate) {
+ w1 = h0;
+ h1 = w0;
+ xFlip = ht < 0;
+ yFlip = wt > 0;
+ } else {
+ w1 = w0;
+ h1 = h0;
+ xFlip = wt < 0;
+ yFlip = ht > 0;
+ }
+
+ // set up
+ nComps = colorMap->getNumPixelComps();
+ nVals = width * nComps;
+ nBits = colorMap->getBits();
+ dither = nComps > 1 || nBits > 1;
+
+ // check for tiny (zero width or height) images
+ if (w0 == 0 || h0 == 0) {
+ j = height * ((nVals * nBits + 7) / 8);
+ str->reset();
+ for (i = 0; i < j; ++i)
+ str->getChar();
+ return;
+ }
+
+ // Bresenham parameters
+ px1 = w1 / width;
+ px2 = w1 - px1 * width;
+ py1 = h1 / height;
+ py2 = h1 - py1 * height;
+
+ // allocate XImage
+ image = XCreateImage(display, DefaultVisual(display, screenNum),
+ depth, ZPixmap, 0, NULL, w0, h0, 8, 0);
+ image->data = (char *)gmalloc(h0 * image->bytes_per_line);
+
+ // allocate error diffusion accumulators
+ if (dither) {
+ errDown = (RGBColor *)gmalloc(w1 * sizeof(RGBColor));
+ errRight = (RGBColor *)gmalloc((py1 + 1) * sizeof(RGBColor));
+ for (j = 0; j < w1; ++j)
+ errDown[j].r = errDown[j].g = errDown[j].b = 0;
+ } else {
+ errDown = NULL;
+ errRight = NULL;
+ }
+
+ // initialize the image stream
+ str->resetImage(width, nComps, nBits);
+
+ // first line (column)
+ y = yFlip ? h1 - 1 : 0;
+ qy = 0;
+
+ // read image
+ for (i = 0; i < height; ++i) {
+
+ // vertical (horizontal) Bresenham
+ dy = py1;
+ if ((qy += py2) >= height) {
+ ++dy;
+ qy -= height;
+ }
+
+ // drop a line (column)
+ if (dy == 0) {
+ str->skipImageLine();
+
+ } else {
+
+ // first column (line)
+ x = xFlip ? w1 - 1 : 0;
+ qx = 0;
+
+ // clear error accumulator
+ if (dither) {
+ for (j = 0; j <= py1; ++j)
+ errRight[j].r = errRight[j].g = errRight[j].b = 0;
+ }
+
+ // for each column (line)...
+ for (j = 0; j < width; ++j) {
+
+ // horizontal (vertical) Bresenham
+ dx = px1;
+ if ((qx += px2) >= width) {
+ ++dx;
+ qx -= width;
+ }
+
+ // get image pixel
+ str->getImagePixel(pixBuf);
+
+ // draw image pixel
+ if (dx > 0) {
+ colorMap->getColor(pixBuf, &color);
+ r1 = color.getR();
+ g1 = color.getG();
+ b1 = color.getB();
+ if (dither) {
+ pixel = 0;
+ } else {
+ color2.r = r1;
+ color2.g = g1;
+ color2.b = b1;
+ pixel = findColor(&color2, &err);
+ }
+ if (dx == 1 && dy == 1) {
+ if (dither) {
+ color2.r = r1 + errRight[0].r + errDown[x].r;
+ if (color2.r > 1)
+ color2.r = 1;
+ else if (color2.r < 0)
+ color2.r = 0;
+ color2.g = g1 + errRight[0].g + errDown[x].g;
+ if (color2.g > 1)
+ color2.g = 1;
+ else if (color2.g < 0)
+ color2.g = 0;
+ color2.b = b1 + errRight[0].b + errDown[x].b;
+ if (color2.b > 1)
+ color2.b = 1;
+ else if (color2.b < 0)
+ color2.b = 0;
+ pixel = findColor(&color2, &err);
+ errRight[0].r = errDown[x].r = err.r / 2;
+ errRight[0].g = errDown[x].g = err.g / 2;
+ errRight[0].b = errDown[x].b = err.b / 2;
+ }
+ if (rotate)
+ XPutPixel(image, y, x, pixel);
+ else
+ XPutPixel(image, x, y, pixel);
+ } else {
+ for (iy = 0; iy < dy; ++iy) {
+ for (ix = 0; ix < dx; ++ix) {
+ if (dither) {
+ color2.r = r1 + errRight[iy].r +
+ errDown[xFlip ? x - ix : x + ix].r;
+ if (color2.r > 1)
+ color2.r = 1;
+ else if (color2.r < 0)
+ color2.r = 0;
+ color2.g = g1 + errRight[iy].g +
+ errDown[xFlip ? x - ix : x + ix].g;
+ if (color2.g > 1)
+ color2.g = 1;
+ else if (color2.g < 0)
+ color2.g = 0;
+ color2.b = b1 + errRight[iy].b +
+ errDown[xFlip ? x - ix : x + ix].b;
+ if (color2.b > 1)
+ color2.b = 1;
+ else if (color2.b < 0)
+ color2.b = 0;
+ pixel = findColor(&color2, &err);
+ errRight[iy].r = errDown[xFlip ? x - ix : x + ix].r =
+ err.r / 2;
+ errRight[iy].g = errDown[xFlip ? x - ix : x + ix].g =
+ err.g / 2;
+ errRight[iy].b = errDown[xFlip ? x - ix : x + ix].b =
+ err.b / 2;
+ }
+ if (rotate)
+ XPutPixel(image, yFlip ? y - iy : y + iy,
+ xFlip ? x - ix : x + ix, pixel);
+ else
+ XPutPixel(image, xFlip ? x - ix : x + ix,
+ yFlip ? y - iy : y + iy, pixel);
+ }
+ }
+ }
+ }
+
+ // next column (line)
+ if (xFlip)
+ x -= dx;
+ else
+ x += dx;
+ }
+ }
+
+ // next line (column)
+ if (yFlip)
+ y -= dy;
+ else
+ y += dy;
+ }
+
+ // blit the image into the pixmap
+ XPutImage(display, pixmap, fillGC, image, 0, 0, x0, y0, w0, h0);
+
+ // free memory
+ gfree(image->data);
+ image->data = NULL;
+ XDestroyImage(image);
+ gfree(errRight);
+ gfree(errDown);
+}
+
+Gulong XOutputDev::findColor(GfxColor *color) {
+ int r, g, b;
+ double gray;
+ Gulong pixel;
+
+ if (trueColor) {
+ r = xoutRound(color->getR() * rMul);
+ g = xoutRound(color->getG() * gMul);
+ b = xoutRound(color->getB() * bMul);
+ pixel = ((Gulong)r << rShift) +
+ ((Gulong)g << gShift) +
+ ((Gulong)b << bShift);
+ } else if (numColors == 1) {
+ gray = color->getGray();
+ if (gray < 0.5)
+ pixel = colors[0];
+ else
+ pixel = colors[1];
+ } else {
+ r = xoutRound(color->getR() * (numColors - 1));
+ g = xoutRound(color->getG() * (numColors - 1));
+ b = xoutRound(color->getB() * (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;
+}
+
+GBool XOutputDev::findText(char *s, 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, 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);
+}