home *** CD-ROM | disk | FTP | other *** search
- //========================================================================
- //
- // 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);
- }
-