]> www.fi.muni.cz Git - evince.git/blob - pdf/xpdf/XOutputDev.cc
Fixed up lists of sources so that distcheck works. Also, only build gpdf
[evince.git] / pdf / xpdf / XOutputDev.cc
1 //========================================================================
2 //
3 // XOutputDev.cc
4 //
5 // Copyright 1996 Derek B. Noonburg
6 //
7 //========================================================================
8
9 #ifdef __GNUC__
10 #pragma implementation
11 #endif
12
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <stddef.h>
16 #include <unistd.h>
17 #include <string.h>
18 #include <ctype.h>
19 #include <math.h>
20 #include "gmem.h"
21 #include "GString.h"
22 #include "Object.h"
23 #include "Stream.h"
24 #include "GfxState.h"
25 #include "GfxFont.h"
26 #include "FontFile.h"
27 #include "FontEncoding.h"
28 #include "Error.h"
29 #include "Params.h"
30 #include "TextOutputDev.h"
31 #include "XOutputDev.h"
32
33 #include "XOutputFontInfo.h"
34
35 #ifdef XlibSpecificationRelease
36 #if XlibSpecificationRelease < 5
37 typedef char *XPointer;
38 #endif
39 #else
40 typedef char *XPointer;
41 #endif
42
43 //------------------------------------------------------------------------
44 // Constants and macros
45 //------------------------------------------------------------------------
46
47 #define xoutRound(x) ((int)(x + 0.5))
48
49 #define maxCurveSplits 6        // max number of splits when recursively
50                                 //   drawing Bezier curves
51
52 //------------------------------------------------------------------------
53 // Parameters
54 //------------------------------------------------------------------------
55
56 GBool installCmap = gFalse;
57
58 int rgbCubeSize = defaultRGBCube;
59
60 #if HAVE_T1LIB_H
61 GString *t1libControl = NULL;
62 #endif
63
64 GString *t1Courier = NULL;
65 GString *t1CourierBold = NULL;
66 GString *t1CourierBoldOblique = NULL;
67 GString *t1CourierOblique = NULL;
68 GString *t1Helvetica = NULL;
69 GString *t1HelveticaBold = NULL;
70 GString *t1HelveticaBoldOblique = NULL;
71 GString *t1HelveticaOblique = NULL;
72 GString *t1Symbol = NULL;
73 GString *t1TimesBold = NULL;
74 GString *t1TimesBoldItalic = NULL;
75 GString *t1TimesItalic = NULL;
76 GString *t1TimesRoman = NULL;
77 GString *t1ZapfDingbats = NULL;
78
79 GBool useEUCJP = gFalse;
80 #if JAPANESE_SUPPORT
81 GString *japan12Font = NULL;
82 #endif
83
84 //------------------------------------------------------------------------
85 // Font map
86 //------------------------------------------------------------------------
87
88 struct FontMapEntry {
89   char *pdfFont;
90   char *xFont;
91   GString **t1Font;
92   FontEncoding *encoding;
93 };
94
95 static FontMapEntry fontMap[] = {
96   {"Courier",               "-*-courier-medium-r-normal-*-%s-*-*-*-*-*-iso8859-1",          &t1Courier,              &isoLatin1Encoding},
97   {"Courier-Bold",          "-*-courier-bold-r-normal-*-%s-*-*-*-*-*-iso8859-1",            &t1CourierBold,          &isoLatin1Encoding},
98   {"Courier-BoldOblique",   "-*-courier-bold-o-normal-*-%s-*-*-*-*-*-iso8859-1",            &t1CourierBoldOblique,   &isoLatin1Encoding},
99   {"Courier-Oblique",       "-*-courier-medium-o-normal-*-%s-*-*-*-*-*-iso8859-1",          &t1CourierOblique,       &isoLatin1Encoding},
100   {"Helvetica",             "-*-helvetica-medium-r-normal-*-%s-*-*-*-*-*-iso8859-1",        &t1Helvetica,            &isoLatin1Encoding},
101   {"Helvetica-Bold",        "-*-helvetica-bold-r-normal-*-%s-*-*-*-*-*-iso8859-1",          &t1HelveticaBold,        &isoLatin1Encoding},
102   {"Helvetica-BoldOblique", "-*-helvetica-bold-o-normal-*-%s-*-*-*-*-*-iso8859-1",          &t1HelveticaBoldOblique, &isoLatin1Encoding},
103   {"Helvetica-Oblique",     "-*-helvetica-medium-o-normal-*-%s-*-*-*-*-*-iso8859-1",        &t1HelveticaOblique,     &isoLatin1Encoding},
104   {"Symbol",                "-*-symbol-medium-r-normal-*-%s-*-*-*-*-*-adobe-fontspecific",  &t1Symbol,               &symbolEncoding},
105   {"Times-Bold",            "-*-times-bold-r-normal-*-%s-*-*-*-*-*-iso8859-1",              &t1TimesBold,            &isoLatin1Encoding},
106   {"Times-BoldItalic",      "-*-times-bold-i-normal-*-%s-*-*-*-*-*-iso8859-1",              &t1TimesBoldItalic,      &isoLatin1Encoding},
107   {"Times-Italic",          "-*-times-medium-i-normal-*-%s-*-*-*-*-*-iso8859-1",            &t1TimesItalic,          &isoLatin1Encoding},
108   {"Times-Roman",           "-*-times-medium-r-normal-*-%s-*-*-*-*-*-iso8859-1",            &t1TimesRoman,           &isoLatin1Encoding},
109   {"ZapfDingbats",          "-*-zapfdingbats-medium-r-normal-*-%s-*-*-*-*-*-*-*",           &t1ZapfDingbats,         &zapfDingbatsEncoding},
110   {NULL}
111 };
112
113 static FontMapEntry *userFontMap;
114
115 //------------------------------------------------------------------------
116 // Font substitutions
117 //------------------------------------------------------------------------
118
119 struct FontSubst {
120   char *xFont;
121   GString **t1Font;
122   double mWidth;
123 };
124
125 // index: {symbolic:12, fixed:8, serif:4, sans-serif:0} + bold*2 + italic
126 static FontSubst fontSubst[16] = {
127   {"-*-helvetica-medium-r-normal-*-%s-*-*-*-*-*-iso8859-1",       &t1Helvetica,            0.833},
128   {"-*-helvetica-medium-o-normal-*-%s-*-*-*-*-*-iso8859-1",       &t1HelveticaOblique,     0.833},
129   {"-*-helvetica-bold-r-normal-*-%s-*-*-*-*-*-iso8859-1",         &t1HelveticaBold,        0.889},
130   {"-*-helvetica-bold-o-normal-*-%s-*-*-*-*-*-iso8859-1",         &t1HelveticaBoldOblique, 0.889},
131   {"-*-times-medium-r-normal-*-%s-*-*-*-*-*-iso8859-1",           &t1TimesRoman,           0.788},
132   {"-*-times-medium-i-normal-*-%s-*-*-*-*-*-iso8859-1",           &t1TimesItalic,          0.722},
133   {"-*-times-bold-r-normal-*-%s-*-*-*-*-*-iso8859-1",             &t1TimesBold,            0.833},
134   {"-*-times-bold-i-normal-*-%s-*-*-*-*-*-iso8859-1",             &t1TimesBoldItalic,      0.778},
135   {"-*-courier-medium-r-normal-*-%s-*-*-*-*-*-iso8859-1",         &t1Courier,              0.600},
136   {"-*-courier-medium-o-normal-*-%s-*-*-*-*-*-iso8859-1",         &t1CourierOblique,       0.600},
137   {"-*-courier-bold-r-normal-*-%s-*-*-*-*-*-iso8859-1",           &t1CourierBold,          0.600},
138   {"-*-courier-bold-o-normal-*-%s-*-*-*-*-*-iso8859-1",           &t1CourierBoldOblique,   0.600},
139   {"-*-symbol-medium-r-normal-*-%s-*-*-*-*-*-adobe-fontspecific", &t1Symbol,               0.576},
140   {"-*-symbol-medium-r-normal-*-%s-*-*-*-*-*-adobe-fontspecific", &t1Symbol,               0.576},
141   {"-*-symbol-medium-r-normal-*-%s-*-*-*-*-*-adobe-fontspecific", &t1Symbol,               0.576},
142   {"-*-symbol-medium-r-normal-*-%s-*-*-*-*-*-adobe-fontspecific", &t1Symbol,               0.576}
143 };
144
145 //------------------------------------------------------------------------
146 // 16-bit fonts
147 //------------------------------------------------------------------------
148
149 #if JAPANESE_SUPPORT
150
151 static char *japan12DefFont =
152     "-*-fixed-medium-r-normal-*-%s-*-*-*-*-*-jisx0208.1983-0";
153
154 // CID 0 .. 96
155 static Gushort japan12Map[96] = {
156   0x2120, 0x2120, 0x212a, 0x2149, 0x2174, 0x2170, 0x2173, 0x2175, // 00 .. 07
157   0x2147, 0x214a, 0x214b, 0x2176, 0x215c, 0x2124, 0x213e, 0x2123, // 08 .. 0f
158   0x213f, 0x2330, 0x2331, 0x2332, 0x2333, 0x2334, 0x2335, 0x2336, // 10 .. 17
159   0x2337, 0x2338, 0x2339, 0x2127, 0x2128, 0x2163, 0x2161, 0x2164, // 18 .. 1f
160   0x2129, 0x2177, 0x2341, 0x2342, 0x2343, 0x2344, 0x2345, 0x2346, // 20 .. 27
161   0x2347, 0x2348, 0x2349, 0x234a, 0x234b, 0x234c, 0x234d, 0x234e, // 28 .. 2f
162   0x234f, 0x2350, 0x2351, 0x2352, 0x2353, 0x2354, 0x2355, 0x2356, // 30 .. 37
163   0x2357, 0x2358, 0x2359, 0x235a, 0x214e, 0x216f, 0x214f, 0x2130, // 38 .. 3f
164   0x2132, 0x2146, 0x2361, 0x2362, 0x2363, 0x2364, 0x2365, 0x2366, // 40 .. 47
165   0x2367, 0x2368, 0x2369, 0x236a, 0x236b, 0x236c, 0x236d, 0x236e, // 48 .. 4f
166   0x236f, 0x2370, 0x2371, 0x2372, 0x2373, 0x2374, 0x2375, 0x2376, // 50 .. 57
167   0x2377, 0x2378, 0x2379, 0x237a, 0x2150, 0x2143, 0x2151, 0x2141  // 58 .. 5f
168 };
169
170 // CID 325 .. 421
171 static Gushort japan12KanaMap1[97] = {
172   0x2131, 0x2121, 0x2123, 0x2156, 0x2157, 0x2122, 0x2126, 0x2572,
173   0x2521, 0x2523, 0x2525, 0x2527, 0x2529, 0x2563, 0x2565, 0x2567,
174   0x2543, 0x213c, 0x2522, 0x2524, 0x2526, 0x2528, 0x252a, 0x252b,
175   0x252d, 0x252f, 0x2531, 0x2533, 0x2535, 0x2537, 0x2539, 0x253b,
176   0x253d, 0x253f, 0x2541, 0x2544, 0x2546, 0x2548, 0x254a, 0x254b,
177   0x254c, 0x254d, 0x254e, 0x254f, 0x2552, 0x2555, 0x2558, 0x255b,
178   0x255e, 0x255f, 0x2560, 0x2561, 0x2562, 0x2564, 0x2566, 0x2568,
179   0x2569, 0x256a, 0x256b, 0x256c, 0x256d, 0x256f, 0x2573, 0x212b,
180   0x212c, 0x212e, 0x2570, 0x2571, 0x256e, 0x2575, 0x2576, 0x2574,
181   0x252c, 0x252e, 0x2530, 0x2532, 0x2534, 0x2536, 0x2538, 0x253a,
182   0x253c, 0x253e, 0x2540, 0x2542, 0x2545, 0x2547, 0x2549, 0x2550,
183   0x2551, 0x2553, 0x2554, 0x2556, 0x2557, 0x2559, 0x255a, 0x255c,
184   0x255d
185 };
186
187 // CID 501 .. 598
188 static Gushort japan12KanaMap2[98] = {
189   0x212d, 0x212f, 0x216d, 0x214c, 0x214d, 0x2152, 0x2153, 0x2154,
190   0x2155, 0x2158, 0x2159, 0x215a, 0x215b, 0x213d, 0x2121, 0x2472,
191   0x2421, 0x2423, 0x2425, 0x2427, 0x2429, 0x2463, 0x2465, 0x2467,
192   0x2443, 0x2422, 0x2424, 0x2426, 0x2428, 0x242a, 0x242b, 0x242d,
193   0x242f, 0x2431, 0x2433, 0x2435, 0x2437, 0x2439, 0x243b, 0x243d,
194   0x243f, 0x2441, 0x2444, 0x2446, 0x2448, 0x244a, 0x244b, 0x244c,
195   0x244d, 0x244e, 0x244f, 0x2452, 0x2455, 0x2458, 0x245b, 0x245e,
196   0x245f, 0x2460, 0x2461, 0x2462, 0x2464, 0x2466, 0x2468, 0x2469,
197   0x246a, 0x246b, 0x246c, 0x246d, 0x246f, 0x2473, 0x2470, 0x2471,
198   0x246e, 0x242c, 0x242e, 0x2430, 0x2432, 0x2434, 0x2436, 0x2438,
199   0x243a, 0x243c, 0x243e, 0x2440, 0x2442, 0x2445, 0x2447, 0x2449,
200   0x2450, 0x2451, 0x2453, 0x2454, 0x2456, 0x2457, 0x2459, 0x245a,
201   0x245c, 0x245d
202 };
203
204 static char *japan12Roman[10] = {
205   "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX", "X"
206 };
207
208 static char *japan12Abbrev1[6] = {
209   "mm", "cm", "km", "mg", "kg", "cc"
210 };
211
212 #endif
213
214 //------------------------------------------------------------------------
215 // Constructed characters
216 //------------------------------------------------------------------------
217
218 #define lastRegularChar 0x0ff
219 #define firstSubstChar  0x100
220 #define lastSubstChar   0x104
221 #define firstConstrChar 0x105
222 #define lastConstrChar  0x106
223 #define firstMultiChar  0x107
224 #define lastMultiChar   0x110
225
226 // substituted chars
227 static Guchar substChars[] = {
228   0x27,                         // 100: quotesingle --> quoteright
229   0x2d,                         // 101: emdash --> hyphen
230   0xad,                         // 102: hyphen --> endash
231   0x2f,                         // 103: fraction --> slash
232   0xb0,                         // 104: ring --> degree
233 };
234
235 // constructed chars
236 // 105: bullet
237 // 106: trademark
238
239 // built-up chars
240 static char *multiChars[] = {
241   "fi",                         // 107: fi
242   "fl",                         // 108: fl
243   "ff",                         // 109: ff
244   "ffi",                        // 10a: ffi
245   "ffl",                        // 10b: ffl
246   "OE",                         // 10c: OE
247   "oe",                         // 10d: oe
248   "...",                        // 10e: ellipsis
249   "``",                         // 10f: quotedblleft
250   "''"                          // 110: quotedblright
251 };
252
253 // ignored chars
254 // 111: Lslash
255 //    : Scaron
256 //    : Zcaron
257 //    : Ydieresis
258 //    : breve
259 //    : caron
260 //    : circumflex
261 //    : dagger
262 //    : daggerdbl
263 //    : dotaccent
264 //    : dotlessi
265 //    : florin
266 //    : grave
267 //    : guilsinglleft
268 //    : guilsinglright
269 //    : hungarumlaut
270 //    : lslash
271 //    : ogonek
272 //    : perthousand
273 //    : quotedblbase
274 //    : quotesinglbase
275 //    : scaron
276 //    : tilde
277 //    : zcaron
278
279 //------------------------------------------------------------------------
280 // XOutputFont
281 //------------------------------------------------------------------------
282
283 XOutputFont::XOutputFont(GfxFont *gfxFont, double m11, double m12,
284                          double m21, double m22, Display *display,
285                          XOutputFontCache *cache) {
286   int code;
287   char *charName;
288
289   id = gfxFont->getID();
290   this->display = display;
291   tm11 = m11;
292   tm12 = m12;
293   tm21 = m21;
294   tm22 = m22;
295
296   // check for hex char names
297   hex = gFalse;
298   if (!gfxFont->is16Bit()) {
299     for (code = 0; code < 256; ++code) {
300       if ((charName = gfxFont->getCharName(code))) {
301         if ((charName[0] == 'B' || charName[0] == 'C' ||
302              charName[0] == 'G') &&
303             strlen(charName) == 3 &&
304             ((charName[1] >= 'a' && charName[1] <= 'f') ||
305              (charName[1] >= 'A' && charName[1] <= 'F') ||
306              (charName[2] >= 'a' && charName[2] <= 'f') ||
307              (charName[2] >= 'A' && charName[2] <= 'F'))) {
308           hex = gTrue;
309           break;
310         }
311       }
312     }
313   }
314 }
315
316 XOutputFont::~XOutputFont() {
317 }
318
319 #if HAVE_T1LIB_H
320 //------------------------------------------------------------------------
321 // XOutputT1Font
322 //------------------------------------------------------------------------
323
324 XOutputT1Font::XOutputT1Font(GfxFont *gfxFont, GString *pdfBaseFont,
325                              double m11, double m12, double m21, double m22,
326                              double size, double ntm11, double ntm12,
327                              double ntm21, double ntm22,
328                              Display *display, XOutputFontCache *cache):
329   XOutputFont(gfxFont, m11, m12, m21, m22, display, cache)
330 {
331   Ref embRef;
332   T1_TMATRIX xform;
333
334   t1ID = -1;
335   t1libAA = cache->getT1libAA();
336
337   // keep size info (for drawChar())
338   this->size = (float)size;
339
340   // we can only handle 8-bit, Type 1/1C, with embedded font file
341   // or user-specified base fonts
342   //~ also look for external font files
343   if (!(pdfBaseFont ||
344         (!gfxFont->is16Bit() &&
345          (gfxFont->getType() == fontType1 ||
346           gfxFont->getType() == fontType1C) &&
347          gfxFont->getEmbeddedFontID(&embRef)))) {
348     return;
349   }
350
351   // load the font
352   if ((t1ID = cache->getT1Font(gfxFont, pdfBaseFont)) < 0)
353     return;
354
355   // transform the font
356   xform.cxx = ntm11;
357   xform.cxy = -ntm12;
358   xform.cyx = ntm21;
359   xform.cyy = -ntm22;
360   T1_TransformFont(t1ID, &xform);
361 }
362
363 XOutputT1Font::~XOutputT1Font() {
364   if (t1ID >= 0) {
365     T1_DeleteFont(t1ID);
366   }
367 }
368
369 GBool XOutputT1Font::isOk() {
370   return t1ID >= 0;
371 }
372
373 void XOutputT1Font::updateGC(GC gc) {
374 }
375
376 void XOutputT1Font::drawChar(GfxState *state, Pixmap pixmap, GC gc,
377                              double x, double y, int c) {
378   if (t1libAA) {
379     T1_AASetCharX(pixmap, gc, 0, xoutRound(x), xoutRound(y),
380                   t1ID, c, size, NULL);
381   } else {
382     T1_SetCharX(pixmap, gc, 0, xoutRound(x), xoutRound(y),
383                 t1ID, c, size, NULL);
384   }
385 }
386 #endif // HAVE_T1LIB_H
387
388 //------------------------------------------------------------------------
389 // XOutputServerFont
390 //------------------------------------------------------------------------
391
392 XOutputServerFont::XOutputServerFont(GfxFont *gfxFont, char *fontNameFmt,
393                                      FontEncoding *encoding,
394                                      double m11, double m12,
395                                      double m21, double m22,
396                                      double size,
397                                      double ntm11, double ntm12,
398                                      double ntm21, double ntm22,
399                                      Display *display,
400                                      XOutputFontCache *cache):
401   XOutputFont(gfxFont, m11, m12, m21, m22, display, cache)
402 {
403   char fontName[200], fontSize[100];
404   GBool rotated;
405   int startSize, sz;
406   int code, code2;
407   char *charName;
408   int n;
409
410   xFont = NULL;
411
412   // Construct forward and reverse map.
413   // This tries to deal with font subset character names of the
414   // form 'Bxx', 'Cxx', 'Gxx', with decimal or hex numbering.
415   if (!gfxFont->is16Bit()) {
416     for (code = 0; code < 256; ++code)
417       revMap[code] = 0;
418     if (encoding) {
419       for (code = 0; code < 256; ++code) {
420         if ((charName = gfxFont->getCharName(code))) {
421           if ((code2 = encoding->getCharCode(charName)) < 0) {
422             n = strlen(charName);
423             if (hex && n == 3 &&
424                 (charName[0] == 'B' || charName[0] == 'C' ||
425                  charName[0] == 'G') &&
426                 isxdigit(charName[1]) && isxdigit(charName[2])) {
427               sscanf(charName+1, "%x", &code2);
428             } else if (!hex && n >= 2 && n <= 3 &&
429                        isdigit(charName[0]) && isdigit(charName[1])) {
430               code2 = atoi(charName);
431               if (code2 >= 256)
432                 code2 = -1;
433             } else if (!hex && n >= 3 && n <= 5 && isdigit(charName[1])) {
434               code2 = atoi(charName+1);
435               if (code2 >= 256)
436                 code2 = -1;
437             }
438             //~ this is a kludge -- is there a standard internal encoding
439             //~ used by all/most Type 1 fonts?
440             if (code2 == 262)           // hyphen
441               code2 = 45;
442             else if (code2 == 266)      // emdash
443               code2 = 208;
444           }
445           if (code2 >= 0) {
446             map[code] = (Gushort)code2;
447             if (code2 < 256)
448               revMap[code2] = (Guchar)code;
449           } else {
450             map[code] = 0;
451           }
452         } else {
453           map[code] = 0;
454         }
455       }
456     } else {
457       code2 = 0; // to make gcc happy
458       //~ this is a hack to get around the fact that X won't draw
459       //~ chars 0..31; this works when the fonts have duplicate encodings
460       //~ for those chars
461       for (code = 0; code < 32; ++code) {
462         if ((charName = gfxFont->getCharName(code)) &&
463             (code2 = gfxFont->getCharCode(charName)) >= 0) {
464           map[code] = (Gushort)code2;
465           if (code2 < 256)
466             revMap[code2] = (Guchar)code;
467         }
468       }
469       for (code = 32; code < 256; ++code) {
470         map[code] = (Gushort)code;
471         revMap[code] = (Guchar)code;
472       }
473     }
474   }
475
476   // adjust transform for the X transform convention
477   ntm12 = -ntm12;
478   ntm22 = -ntm22;
479
480   // try to get a rotated font?
481   rotated = !(ntm11 > 0 && ntm22 > 0 &&
482               fabs(ntm11 / ntm22 - 1) < 0.2 &&
483               fabs(ntm12) < 0.01 &&
484               fabs(ntm21) < 0.01);
485
486   // open X font -- if font is not found (which means the server can't
487   // scale fonts), try progressively smaller and then larger sizes
488   //~ This does a linear search -- it should get a list of fonts from
489   //~ the server and pick the closest.
490   startSize = (int)size;
491   if (rotated)
492     sprintf(fontSize, "[%s%0.2f %s%0.2f %s%0.2f %s%0.2f]",
493             ntm11<0 ? "~" : "", fabs(ntm11 * size),
494             ntm12<0 ? "~" : "", fabs(ntm12 * size),
495             ntm21<0 ? "~" : "", fabs(ntm21 * size),
496             ntm22<0 ? "~" : "", fabs(ntm22 * size));
497   else
498     sprintf(fontSize, "%d", startSize);
499   sprintf(fontName, fontNameFmt, fontSize);
500   xFont = XLoadQueryFont(display, fontName);
501   if (!xFont) {
502     for (sz = startSize; sz >= startSize/2 && sz >= 1; --sz) {
503       sprintf(fontSize, "%d", sz);
504       sprintf(fontName, fontNameFmt, fontSize);
505       if ((xFont = XLoadQueryFont(display, fontName)))
506         break;
507     }
508     if (!xFont) {
509       for (sz = startSize + 1; sz < startSize + 10; ++sz) {
510         sprintf(fontSize, "%d", sz);
511         sprintf(fontName, fontNameFmt, fontSize);
512         if ((xFont = XLoadQueryFont(display, fontName)))
513           break;
514       }
515       if (!xFont) {
516         sprintf(fontSize, "%d", startSize);
517         sprintf(fontName, fontNameFmt, fontSize);
518         error(-1, "Failed to open font: '%s'", fontName);
519         return;
520       }
521     }
522   }
523 }
524
525 XOutputServerFont::~XOutputServerFont() {
526   if (xFont)
527     XFreeFont(display, xFont);
528 }
529
530 GBool XOutputServerFont::isOk() {
531   return xFont != NULL;
532 }
533
534 void XOutputServerFont::updateGC(GC gc) {
535   XSetFont(display, gc, xFont->fid);
536 }
537
538 void XOutputServerFont::drawChar(GfxState *state, Pixmap pixmap, GC gc,
539                                  double x, double y, int c) {
540   GfxFont *gfxFont;
541   Gushort c1;
542   char buf;
543   char *p;
544   int n, i;
545   double tx;
546
547   c1 = map[c];
548   if (c1 <= lastRegularChar) {
549     buf = (char)c1;
550     XDrawString(display, pixmap, gc, xoutRound(x), xoutRound(y), &buf, 1);
551   } else if (c1 <= lastSubstChar) {
552     buf = (char)substChars[c1 - firstSubstChar];
553     XDrawString(display, pixmap, gc, xoutRound(x), xoutRound(y), &buf, 1);
554   } else if (c1 <= lastConstrChar) {
555     gfxFont = state->getFont();
556     //~ need to deal with rotated text here
557     switch (c1 - firstConstrChar) {
558     case 0: // bullet
559       tx = 0.25 * state->getTransformedFontSize() * gfxFont->getWidth(c);
560       XFillRectangle(display, pixmap, gc,
561                      xoutRound(x + tx),
562                      xoutRound(y - 0.4 * xFont->ascent - tx),
563                      xoutRound(2 * tx), xoutRound(2 * tx));
564       break;
565     case 1: // trademark
566 //~ this should use a smaller font
567 //      tx = state->getTransformedFontSize() *
568 //           (gfxFont->getWidth(c) -
569 //            gfxFont->getWidth(font->revMap['M']));
570       tx = 0.9 * state->getTransformedFontSize() *
571            gfxFont->getWidth(revMap['T']);
572       y -= 0.33 * (double)xFont->ascent;
573       buf = 'T';
574       XDrawString(display, pixmap, gc,
575                   xoutRound(x), xoutRound(y), &buf, 1);
576       x += tx;
577       buf = 'M';
578       XDrawString(display, pixmap, gc,
579                   xoutRound(x), xoutRound(y), &buf, 1);
580       break;
581     }
582   } else if (c1 <= lastMultiChar) {
583     gfxFont = state->getFont();
584     p = multiChars[c1 - firstMultiChar];
585     n = strlen(p);
586     tx = gfxFont->getWidth(c);
587     tx -= gfxFont->getWidth(revMap[p[n-1]]);
588     tx = tx * state->getTransformedFontSize() / (double)(n - 1);
589     for (i = 0; i < n; ++i) {
590       XDrawString(display, pixmap, gc,
591                   xoutRound(x), xoutRound(y), p + i, 1);
592       x += tx;
593     }
594   }
595 }
596
597 //------------------------------------------------------------------------
598 // XOutputFontCache
599 //------------------------------------------------------------------------
600
601 XOutputFontCache::XOutputFontCache(Display *display) {
602   this->display = display;
603 #if HAVE_T1LIB_H
604   t1Init = gFalse;
605   if (t1libControl) {
606     useT1lib = t1libControl->cmp("none") != 0;
607     t1libAA = t1libControl->cmp("plain") != 0;
608     t1libAAHigh = t1libControl->cmp("high") == 0;
609   } else {
610     useT1lib = gFalse;
611     t1libAA = gFalse;
612     t1libAAHigh = gFalse;
613   }
614 #endif
615   clear();
616 }
617
618 XOutputFontCache::~XOutputFontCache() {
619   delFonts();
620 }
621
622 void XOutputFontCache::startDoc(int screenNum, Guint depth,
623                                 Colormap colormap) {
624   delFonts();
625   clear();
626
627 #if HAVE_T1LIB_H
628   if (useT1lib) {
629     if (T1_InitLib(NO_LOGFILE | IGNORE_CONFIGFILE | IGNORE_FONTDATABASE |
630                    T1_NO_AFM)) {
631       if (t1libAA) {
632         T1_AASetLevel(t1libAAHigh ? T1_AA_HIGH : T1_AA_LOW);
633         if (depth <= 8)
634           T1_AASetBitsPerPixel(8);
635         else if (depth <= 16)
636           T1_AASetBitsPerPixel(16);
637         else
638           T1_AASetBitsPerPixel(32);
639       }
640       T1_SetX11Params(display, DefaultVisual(display, screenNum),
641                       depth, colormap);
642       t1Init = gTrue;
643     } else {
644       useT1lib = gFalse;
645     }
646   }
647 #endif // HAVE_T1LIB_H
648 }
649
650 void XOutputFontCache::delFonts() {
651   int i;
652
653 #if HAVE_T1LIB_H
654   // delete Type 1 fonts
655   for (i = 0; i < nT1Fonts; ++i)
656     delete t1Fonts[i];
657   for (i = 0; i < t1BaseFontsSize && t1BaseFonts[i].num >= 0; ++i) {
658     T1_DeleteFont(t1BaseFonts[i].t1ID);
659     gfree(t1BaseFonts[i].encStr);
660     gfree(t1BaseFonts[i].enc);
661   }
662   gfree(t1BaseFonts);
663   if (t1Init) {
664     T1_CloseLib();
665   }
666 #endif
667
668   // delete server fonts
669   for (i = 0; i < nServerFonts; ++i)
670     delete serverFonts[i];
671 }
672
673 void XOutputFontCache::clear() {
674   int i;
675
676 #if HAVE_T1LIB_H
677   // clear Type 1 font cache
678   for (i = 0; i < t1FontCacheSize; ++i)
679     t1Fonts[i] = NULL;
680   nT1Fonts = 0;
681   t1BaseFonts = NULL;
682   t1BaseFontsSize = 0;
683 #endif
684
685   // clear server font cache
686   for (i = 0; i < serverFontCacheSize; ++i)
687     serverFonts[i] = NULL;
688   nServerFonts = 0;
689 }
690
691 XOutputFont *XOutputFontCache::getFont(GfxFont *gfxFont,
692                                        double m11, double m12,
693                                        double m21, double m22) {
694 #if HAVE_T1LIB_H
695   XOutputT1Font *t1Font;
696 #endif
697   XOutputServerFont *serverFont;
698   FontMapEntry *fme;
699   GString *t1FontName;
700   char *xFontName;
701   FontEncoding *xEncoding;
702   double size;
703   double ntm11, ntm12, ntm21, ntm22;
704   double w1, w2, v;
705   double *fm;
706   int index;
707   int code;
708   int i, j;
709
710   // is it the most recently used Type 1 or server font?
711 #if HAVE_T1LIB_H
712   if (useT1lib && nT1Fonts > 0 &&
713       t1Fonts[0]->matches(gfxFont->getID(), m11, m12, m21, m22)) {
714     return t1Fonts[0];
715   }
716 #endif
717   if (nServerFonts > 0 && serverFonts[0]->matches(gfxFont->getID(),
718                                                   m11, m12, m21, m22))
719     return serverFonts[0];
720
721 #if HAVE_T1LIB_H
722   // is it in the Type 1 cache?
723   if (useT1lib) {
724     for (i = 1; i < nT1Fonts; ++i) {
725       if (t1Fonts[i]->matches(gfxFont->getID(), m11, m12, m21, m22)) {
726         t1Font = t1Fonts[i];
727         for (j = i; j > 0; --j)
728           t1Fonts[j] = t1Fonts[j-1];
729         t1Fonts[0] = t1Font;
730         return t1Font;
731       }
732     }
733   }
734 #endif
735
736   // is it in the server cache?
737   for (i = 1; i < nServerFonts; ++i) {
738     if (serverFonts[i]->matches(gfxFont->getID(), m11, m12, m21, m22)) {
739       serverFont = serverFonts[i];
740       for (j = i; j > 0; --j)
741         serverFonts[j] = serverFonts[j-1];
742       serverFonts[0] = serverFont;
743       return serverFont;
744     }
745   }
746
747   // compute size and normalized transform matrix
748   size = sqrt(m21*m21 + m22*m22);
749   ntm11 = m11 / size;
750   ntm12 = m12 / size;
751   ntm21 = m21 / size;
752   ntm22 = m22 / size;
753
754   // search for a font map entry
755   t1FontName = NULL;
756   xFontName = NULL;
757   xEncoding = NULL;
758   if (!gfxFont->is16Bit() && gfxFont->getName()) {
759     for (fme = userFontMap; fme->pdfFont; ++fme) {
760       if (!gfxFont->getName()->cmp(fme->pdfFont)) {
761         break;
762       }
763     }
764     if (!fme->pdfFont) {
765       for (fme = fontMap; fme->pdfFont; ++fme) {
766         if (!gfxFont->getName()->cmp(fme->pdfFont)) {
767           break;
768         }
769       }
770     }
771     if (fme && fme->t1Font) {
772       t1FontName = *fme->t1Font;
773     }
774     if (fme && fme->xFont && fme->encoding) {
775       xFontName = fme->xFont;
776       xEncoding = fme->encoding;
777     }
778   }
779
780   // no font map entry found, so substitute a font
781   if (!t1FontName && !xFontName) {
782     if (gfxFont->is16Bit()) {
783       xFontName = fontSubst[0].xFont;
784       t1FontName = NULL;
785       switch (gfxFont->getCharSet16()) {
786       case font16AdobeJapan12:
787 #if JAPANESE_SUPPORT
788         xFontName = japan12Font ? japan12Font->getCString() : japan12DefFont;
789 #endif
790         break;
791       }
792     } else {
793       if (gfxFont->isFixedWidth()) {
794         index = 8;
795       } else if (gfxFont->isSerif()) {
796         index = 4;
797       } else {
798         index = 0;
799       }
800       if (gfxFont->isBold())
801         index += 2;
802       if (gfxFont->isItalic())
803         index += 1;
804       xFontName = fontSubst[index].xFont;
805       t1FontName = *fontSubst[index].t1Font;
806       xEncoding = &isoLatin1Encoding;
807       // un-normalize
808       ntm11 = m11;
809       ntm12 = m12;
810       ntm21 = m21;
811       ntm22 = m22;
812       // get width of 'm' in real font and substituted font
813       if ((code = gfxFont->getCharCode("m")) >= 0)
814         w1 = gfxFont->getWidth(code);
815       else
816         w1 = 0;
817       w2 = fontSubst[index].mWidth;
818       if (gfxFont->getType() == fontType3) {
819         // This is a hack which makes it possible to substitute for some
820         // Type 3 fonts.  The problem is that it's impossible to know what
821         // the base coordinate system used in the font is without actually
822         // rendering the font.  This code tries to guess by looking at the
823         // width of the character 'm' (which breaks if the font is a
824         // subset that doesn't contain 'm').
825         if (w1 > 0 && (w1 > 1.1 * w2 || w1 < 0.9 * w2)) {
826           w1 /= w2;
827           ntm11 = m11 * w1;
828           ntm12 = m12 * w1;
829           ntm21 = m21 * w1;
830           ntm22 = m22 * w1;
831         }
832         fm = gfxFont->getFontMatrix();
833         v = (fm[0] == 0) ? 1 : (fm[3] / fm[0]);
834         ntm12 *= v;
835         ntm22 *= v;
836       } else if (!gfxFont->isSymbolic()) {
837         // if real font is substantially narrower than substituted
838         // font, reduce the font size accordingly
839         if (w1 > 0.01 && w1 < 0.9 * w2) {
840           w1 /= w2;
841           if (w1 < 0.8)
842             w1 = 0.8;
843           ntm11 = m11 * w1;
844           ntm12 = m12 * w1;
845           ntm21 = m21 * w1;
846           ntm22 = m22 * w1;
847         }
848       }
849       // renormalize
850       size = sqrt(ntm21*ntm21 + ntm22*ntm22);
851       ntm11 /= size;
852       ntm12 /= size;
853       ntm21 /= size;
854       ntm22 /= size;
855     }
856   }
857
858 #if HAVE_T1LIB_H
859   // try to create a new Type 1 font
860   if (useT1lib) {
861     t1Font = new XOutputT1Font(gfxFont, t1FontName,
862                                m11, m12, m21, m22,
863                                size, ntm11, ntm12, ntm21, ntm22,
864                                display, this);
865     if (t1Font->isOk()) {
866
867       // insert in cache
868       if (nT1Fonts == t1FontCacheSize) {
869         --nT1Fonts;
870         delete t1Fonts[nT1Fonts];
871       }
872       for (j = nT1Fonts; j > 0; --j)
873         t1Fonts[j] = t1Fonts[j-1];
874       t1Fonts[0] = t1Font;
875       ++nT1Fonts;
876
877       return t1Font;
878     }
879     delete t1Font;
880   }
881 #endif
882
883   // create a new server font
884   serverFont = new XOutputServerFont(gfxFont, xFontName, xEncoding,
885                                      m11, m12, m21, m22,
886                                      size, ntm11, ntm12, ntm21, ntm22,
887                                      display, this);
888   if (serverFont->isOk()) {
889
890     // insert in cache
891     if (nServerFonts == serverFontCacheSize) {
892       --nServerFonts;
893       delete serverFonts[nServerFonts];
894     }
895     for (j = nServerFonts; j > 0; --j)
896       serverFonts[j] = serverFonts[j-1];
897     serverFonts[0] = serverFont;
898     ++nServerFonts;
899
900     return serverFont;
901   }
902   delete serverFont;
903
904   return NULL;
905 }
906
907 #if HAVE_T1LIB_H
908 int XOutputFontCache::getT1Font(GfxFont *gfxFont, GString *pdfBaseFont) {
909   Ref id;
910   char *fileName;
911   char tmpFileName[60];
912   FILE *f;
913   char *fontBuf;
914   int fontLen;
915   Type1CFontConverter *cvt;
916   Ref embRef;
917   Object refObj, strObj;
918   FontEncoding *enc;
919   int encStrSize;
920   char *encPtr;
921   int t1ID;
922   int c;
923   int i, j;
924
925   id = gfxFont->getID();
926
927   // check available fonts
928   t1ID = -1;
929   for (i = 0; i < t1BaseFontsSize && t1BaseFonts[i].num >= 0; ++i) {
930     if (t1BaseFonts[i].num == id.num && t1BaseFonts[i].gen == id.gen) {
931       t1ID = t1BaseFonts[i].t1ID;
932     }
933   }
934
935   // create a new base font
936   if (t1ID < 0) {
937
938     // resize t1BaseFonts if necessary
939     if (i == t1BaseFontsSize) {
940       t1BaseFonts = (XOutputT1BaseFont *)
941         grealloc(t1BaseFonts,
942                  (t1BaseFontsSize + 16) * sizeof(XOutputT1BaseFont));
943       for (j = 0; j < 16; ++j) {
944         t1BaseFonts[t1BaseFontsSize + j].num = -1;
945       }
946       t1BaseFontsSize += 16;
947     }
948
949     // create the font file
950     tmpFileName[0] = '\0';
951     if (!gfxFont->is16Bit() &&
952         (gfxFont->getType() == fontType1 ||
953          gfxFont->getType() == fontType1C) &&
954         gfxFont->getEmbeddedFontID(&embRef)) {
955       tmpnam(tmpFileName);
956       if (!(f = fopen(tmpFileName, "wb"))) {
957         error(-1, "Couldn't open temporary Type 1 font file '%s'",
958               tmpFileName);
959         return -1;
960       }
961       if (gfxFont->getType() == fontType1C) {
962         fontBuf = gfxFont->readEmbFontFile(&fontLen);
963         cvt = new Type1CFontConverter(fontBuf, fontLen, f);
964         cvt->convert();
965         delete cvt;
966         gfree(fontBuf);
967       } else {
968         gfxFont->getEmbeddedFontID(&embRef);
969         refObj.initRef(embRef.num, embRef.gen);
970         refObj.fetch(&strObj);
971         refObj.free();
972         strObj.streamReset();
973         while ((c = strObj.streamGetChar()) != EOF)
974           fputc(c, f);
975       }
976       fclose(f);
977       strObj.free();
978       fileName = tmpFileName;
979     } else {
980       fileName = pdfBaseFont->getCString();
981     }
982
983     // create the t1lib font
984     if ((t1ID = T1_AddFont(fileName)) < 0) {
985       error(-1, "Couldn't create t1lib font from '%s'", fileName);
986       return -1;
987     }
988     T1_LoadFont(t1ID);
989     t1BaseFonts[i].num = id.num;
990     t1BaseFonts[i].gen = id.gen;
991     t1BaseFonts[i].t1ID = t1ID;
992
993     // remove the font file
994     if (tmpFileName[0]) {
995       unlink(tmpFileName);
996     }
997
998     // reencode it
999     enc = gfxFont->getEncoding();
1000     encStrSize = 0;
1001     for (j = 0; j < 256 && j < enc->getSize(); ++j) {
1002       if (enc->getCharName(j)) {
1003         encStrSize += strlen(enc->getCharName(j)) + 1;
1004       }
1005     }
1006     t1BaseFonts[i].enc = (char **)gmalloc(257 * sizeof(char *));
1007     encPtr = (char *)gmalloc(encStrSize * sizeof(char));
1008     t1BaseFonts[i].encStr = encPtr;
1009     for (j = 0; j < 256 && j < enc->getSize(); ++j) {
1010       if (enc->getCharName(j)) {
1011         strcpy(encPtr, enc->getCharName(j));
1012         t1BaseFonts[i].enc[j] = encPtr;
1013         encPtr += strlen(encPtr) + 1;
1014       } else {
1015         t1BaseFonts[i].enc[j] = ".notdef";
1016       }
1017     }
1018     for (; j < 256; ++j) {
1019       t1BaseFonts[i].enc[j] = ".notdef";
1020     }
1021     t1BaseFonts[i].enc[256] = "custom";
1022     T1_ReencodeFont(t1BaseFonts[i].t1ID, t1BaseFonts[i].enc);
1023   }
1024
1025   // copy it
1026   t1ID = T1_CopyFont(t1ID);
1027
1028   return t1ID;
1029 }
1030 #endif
1031
1032 //------------------------------------------------------------------------
1033 // XOutputDev
1034 //------------------------------------------------------------------------
1035
1036 XOutputDev::XOutputDev(Display *display, Pixmap pixmap, Guint depth,
1037                        Colormap colormap, unsigned long paperColor) {
1038   XVisualInfo visualTempl;
1039   XVisualInfo *visualList;
1040   int nVisuals;
1041   Gulong mask;
1042   XGCValues gcValues;
1043   XColor xcolor;
1044   XColor *xcolors;
1045   int r, g, b, n, m, i;
1046   GBool ok;
1047
1048   // get display/pixmap info
1049   this->display = display;
1050   screenNum = DefaultScreen(display);
1051   this->pixmap = pixmap;
1052   this->depth = depth;
1053   this->colormap = colormap;
1054
1055   // check for TrueColor visual
1056   trueColor = gFalse;
1057   if (depth == 0) {
1058     this->depth = DefaultDepth(display, screenNum);
1059     visualList = XGetVisualInfo(display, 0, &visualTempl, &nVisuals);
1060     for (i = 0; i < nVisuals; ++i) {
1061       if (visualList[i].visual == DefaultVisual(display, screenNum)) {
1062         if (visualList[i].c_class == TrueColor) {
1063           trueColor = gTrue;
1064           mask = visualList[i].red_mask;
1065           rShift = 0;
1066           while (mask && !(mask & 1)) {
1067             ++rShift;
1068             mask >>= 1;
1069           }
1070           rMul = (int)mask;
1071           mask = visualList[i].green_mask;
1072           gShift = 0;
1073           while (mask && !(mask & 1)) {
1074             ++gShift;
1075             mask >>= 1;
1076           }
1077           gMul = (int)mask;
1078           mask = visualList[i].blue_mask;
1079           bShift = 0;
1080           while (mask && !(mask & 1)) {
1081             ++bShift;
1082             mask >>= 1;
1083           }
1084           bMul = (int)mask;
1085         }
1086         break;
1087       }
1088     }
1089     XFree((XPointer)visualList);
1090   }
1091
1092   // allocate a color cube
1093   if (!trueColor) {
1094
1095     // set colors in private colormap
1096     if (installCmap) {
1097       for (numColors = 6; numColors >= 2; --numColors) {
1098         m = numColors * numColors * numColors;
1099         if (XAllocColorCells(display, colormap, False, NULL, 0, colors, m))
1100           break;
1101       }
1102       if (numColors >= 2) {
1103         m = numColors * numColors * numColors;
1104         xcolors = (XColor *)gmalloc(m * sizeof(XColor));
1105         n = 0;
1106         for (r = 0; r < numColors; ++r) {
1107           for (g = 0; g < numColors; ++g) {
1108             for (b = 0; b < numColors; ++b) {
1109               xcolors[n].pixel = colors[n];
1110               xcolors[n].red = (r * 65535) / (numColors - 1);
1111               xcolors[n].green = (g * 65535) / (numColors - 1);
1112               xcolors[n].blue = (b * 65535) / (numColors - 1);
1113               xcolors[n].flags = DoRed | DoGreen | DoBlue;
1114               ++n;
1115             }
1116           }
1117         }
1118         XStoreColors(display, colormap, xcolors, m);
1119         gfree(xcolors);
1120       } else {
1121         numColors = 1;
1122         colors[0] = BlackPixel(display, screenNum);
1123         colors[1] = WhitePixel(display, screenNum);
1124       }
1125
1126     // allocate colors in shared colormap
1127     } else {
1128       if (rgbCubeSize > maxRGBCube)
1129         rgbCubeSize = maxRGBCube;
1130       ok = gFalse;
1131       for (numColors = rgbCubeSize; numColors >= 2; --numColors) {
1132         ok = gTrue;
1133         n = 0;
1134         for (r = 0; r < numColors && ok; ++r) {
1135           for (g = 0; g < numColors && ok; ++g) {
1136             for (b = 0; b < numColors && ok; ++b) {
1137               if (n == 0) {
1138                 colors[n++] = BlackPixel(display, screenNum);
1139               } else {
1140                 xcolor.red = (r * 65535) / (numColors - 1);
1141                 xcolor.green = (g * 65535) / (numColors - 1);
1142                 xcolor.blue = (b * 65535) / (numColors - 1);
1143                 if (XAllocColor(display, colormap, &xcolor))
1144                   colors[n++] = xcolor.pixel;
1145                 else
1146                   ok = gFalse;
1147               }
1148             }
1149           }
1150         }
1151         if (ok)
1152           break;
1153         XFreeColors(display, colormap, &colors[1], n-1, 0);
1154       }
1155       if (!ok) {
1156         numColors = 1;
1157         colors[0] = BlackPixel(display, screenNum);
1158         colors[1] = WhitePixel(display, screenNum);
1159       }
1160     }
1161   }
1162
1163   // allocate GCs
1164   gcValues.foreground = BlackPixel(display, screenNum);
1165   gcValues.background = WhitePixel(display, screenNum);
1166   gcValues.line_width = 0;
1167   gcValues.line_style = LineSolid;
1168   strokeGC = XCreateGC(display, pixmap,
1169                        GCForeground | GCBackground | GCLineWidth | GCLineStyle,
1170                        &gcValues);
1171   fillGC = XCreateGC(display, pixmap,
1172                      GCForeground | GCBackground | GCLineWidth | GCLineStyle,
1173                      &gcValues);
1174   gcValues.foreground = paperColor;
1175   paperGC = XCreateGC(display, pixmap,
1176                       GCForeground | GCBackground | GCLineWidth | GCLineStyle,
1177                       &gcValues);
1178
1179   // no clip region yet
1180   clipRegion = NULL;
1181
1182   // get user font map
1183   for (n = 0; devFontMap[n].pdfFont; ++n) ;
1184   userFontMap = (FontMapEntry *)gmalloc((n+1) * sizeof(FontMapEntry));
1185   for (i = 0; i < n; ++i) {
1186     userFontMap[i].pdfFont = devFontMap[i].pdfFont;
1187     userFontMap[i].xFont = devFontMap[i].devFont;
1188     m = strlen(userFontMap[i].xFont);
1189     if (m >= 10 && !strcmp(userFontMap[i].xFont + m - 10, "-iso8859-2"))
1190       userFontMap[i].encoding = &isoLatin2Encoding;
1191     else if (m >= 13 && !strcmp(userFontMap[i].xFont + m - 13,
1192                                 "-fontspecific"))
1193       userFontMap[i].encoding = NULL;
1194     else
1195       userFontMap[i].encoding = &isoLatin1Encoding;
1196     userFontMap[i].t1Font = NULL;
1197   }
1198   userFontMap[n].pdfFont = NULL;
1199
1200   // set up the font cache and fonts
1201   gfxFont = NULL;
1202   font = NULL;
1203   fontCache = new XOutputFontCache(display);
1204
1205   // empty state stack
1206   save = NULL;
1207
1208   // create text object
1209   text = new TextPage(useEUCJP, gFalse);
1210 }
1211
1212 XOutputDev::~XOutputDev() {
1213   gfree(userFontMap);
1214   delete fontCache;
1215   XFreeGC(display, strokeGC);
1216   XFreeGC(display, fillGC);
1217   XFreeGC(display, paperGC);
1218   if (clipRegion)
1219     XDestroyRegion(clipRegion);
1220   delete text;
1221 }
1222
1223 void XOutputDev::startDoc() {
1224   fontCache->startDoc(screenNum, depth, colormap);
1225 }
1226
1227 void XOutputDev::startPage(int pageNum, GfxState *state) {
1228   XOutputState *s;
1229   XGCValues gcValues;
1230   XRectangle rect;
1231
1232   // clear state stack
1233   while (save) {
1234     s = save;
1235     save = save->next;
1236     XFreeGC(display, s->strokeGC);
1237     XFreeGC(display, s->fillGC);
1238     XDestroyRegion(s->clipRegion);
1239     delete s;
1240   }
1241   save = NULL;
1242
1243   // default line flatness
1244   flatness = 0;
1245
1246   // reset GCs
1247   gcValues.foreground = BlackPixel(display, screenNum);
1248   gcValues.background = WhitePixel(display, screenNum);
1249   gcValues.line_width = 0;
1250   gcValues.line_style = LineSolid;
1251   XChangeGC(display, strokeGC,
1252             GCForeground | GCBackground | GCLineWidth | GCLineStyle,
1253             &gcValues);
1254   XChangeGC(display, fillGC,
1255             GCForeground | GCBackground | GCLineWidth | GCLineStyle,
1256             &gcValues);
1257
1258   // clear clipping region
1259   if (clipRegion)
1260     XDestroyRegion(clipRegion);
1261   clipRegion = XCreateRegion();
1262   rect.x = rect.y = 0;
1263   rect.width = pixmapW;
1264   rect.height = pixmapH;
1265   XUnionRectWithRegion(&rect, clipRegion, clipRegion);
1266   XSetRegion(display, strokeGC, clipRegion);
1267   XSetRegion(display, fillGC, clipRegion);
1268
1269   // clear font
1270   gfxFont = NULL;
1271   font = NULL;
1272
1273   // clear window
1274   XFillRectangle(display, pixmap, paperGC, 0, 0, pixmapW, pixmapH);
1275
1276   // clear text object
1277   text->clear();
1278 }
1279
1280 void XOutputDev::endPage() {
1281   text->coalesce();
1282 }
1283
1284 void XOutputDev::drawLinkBorder(double x1, double y1, double x2, double y2,
1285                                 double w) {
1286   GfxColor color;
1287   XPoint points[5];
1288   int x, y;
1289
1290   color.setRGB(0, 0, 1);
1291   XSetForeground(display, strokeGC, findColor(&color));
1292   XSetLineAttributes(display, strokeGC, xoutRound(w),
1293                      LineSolid, CapRound, JoinRound);
1294   cvtUserToDev(x1, y1, &x, &y);
1295   points[0].x = points[4].x = x;
1296   points[0].y = points[4].y = y;
1297   cvtUserToDev(x2, y1, &x, &y);
1298   points[1].x = x;
1299   points[1].y = y;
1300   cvtUserToDev(x2, y2, &x, &y);
1301   points[2].x = x;
1302   points[2].y = y;
1303   cvtUserToDev(x1, y2, &x, &y);
1304   points[3].x = x;
1305   points[3].y = y;
1306   XDrawLines(display, pixmap, strokeGC, points, 5, CoordModeOrigin);
1307 }
1308
1309 void XOutputDev::saveState(GfxState *state) {
1310   XOutputState *s;
1311   XGCValues values;
1312
1313   // save current state
1314   s = new XOutputState;
1315   s->strokeGC = strokeGC;
1316   s->fillGC = fillGC;
1317   s->clipRegion = clipRegion;
1318
1319   // push onto state stack
1320   s->next = save;
1321   save = s;
1322
1323   // create a new current state by copying
1324   strokeGC = XCreateGC(display, pixmap, 0, &values);
1325   XCopyGC(display, s->strokeGC, 0xffffffff, strokeGC);
1326   fillGC = XCreateGC(display, pixmap, 0, &values);
1327   XCopyGC(display, s->fillGC, 0xffffffff, fillGC);
1328   clipRegion = XCreateRegion();
1329   XUnionRegion(s->clipRegion, clipRegion, clipRegion);
1330   XSetRegion(display, strokeGC, clipRegion);
1331   XSetRegion(display, fillGC, clipRegion);
1332 }
1333
1334 void XOutputDev::restoreState(GfxState *state) {
1335   XOutputState *s;
1336
1337   if (save) {
1338     // kill current state
1339     XFreeGC(display, strokeGC);
1340     XFreeGC(display, fillGC);
1341     XDestroyRegion(clipRegion);
1342
1343     // restore state
1344     flatness = state->getFlatness();
1345     strokeGC = save->strokeGC;
1346     fillGC = save->fillGC;
1347     clipRegion = save->clipRegion;
1348     XSetRegion(display, strokeGC, clipRegion);
1349     XSetRegion(display, fillGC, clipRegion);
1350
1351     // pop state stack
1352     s = save;
1353     save = save->next;
1354     delete s;
1355   }
1356 }
1357
1358 void XOutputDev::updateAll(GfxState *state) {
1359   updateLineAttrs(state, gTrue);
1360   updateFlatness(state);
1361   updateMiterLimit(state);
1362   updateFillColor(state);
1363   updateStrokeColor(state);
1364   updateFont(state);
1365 }
1366
1367 void XOutputDev::updateCTM(GfxState *state, double m11, double m12,
1368                            double m21, double m22, double m31, double m32) {
1369   updateLineAttrs(state, gTrue);
1370 }
1371
1372 void XOutputDev::updateLineDash(GfxState *state) {
1373   updateLineAttrs(state, gTrue);
1374 }
1375
1376 void XOutputDev::updateFlatness(GfxState *state) {
1377   flatness = state->getFlatness();
1378 }
1379
1380 void XOutputDev::updateLineJoin(GfxState *state) {
1381   updateLineAttrs(state, gFalse);
1382 }
1383
1384 void XOutputDev::updateLineCap(GfxState *state) {
1385   updateLineAttrs(state, gFalse);
1386 }
1387
1388 // unimplemented
1389 void XOutputDev::updateMiterLimit(GfxState *state) {
1390 }
1391
1392 void XOutputDev::updateLineWidth(GfxState *state) {
1393   updateLineAttrs(state, gFalse);
1394 }
1395
1396 void XOutputDev::updateLineAttrs(GfxState *state, GBool updateDash) {
1397   double width;
1398   int cap, join;
1399   double *dashPattern;
1400   int dashLength;
1401   double dashStart;
1402   char dashList[20];
1403   int i;
1404
1405   width = state->getTransformedLineWidth();
1406   switch (state->getLineCap()) {
1407   case 0: cap = CapButt; break;
1408   case 1: cap = CapRound; break;
1409   case 2: cap = CapProjecting; break;
1410   default:
1411     error(-1, "Bad line cap style (%d)", state->getLineCap());
1412     cap = CapButt;
1413     break;
1414   }
1415 #if 1 //~ work around a bug in XFree86 (???)
1416   if (dashLength > 0 && cap == CapProjecting) {
1417     cap = CapButt;
1418   }
1419 #endif
1420   switch (state->getLineJoin()) {
1421   case 0: join = JoinMiter; break;
1422   case 1: join = JoinRound; break;
1423   case 2: join = JoinBevel; break;
1424   default:
1425     error(-1, "Bad line join style (%d)", state->getLineJoin());
1426     join = JoinMiter;
1427     break;
1428   }
1429   state->getLineDash(&dashPattern, &dashLength, &dashStart);
1430   XSetLineAttributes(display, strokeGC, xoutRound(width),
1431                      dashLength > 0 ? LineOnOffDash : LineSolid,
1432                      cap, join);
1433   if (updateDash && dashLength > 0) {
1434     if (dashLength > 20)
1435       dashLength = 20;
1436     for (i = 0; i < dashLength; ++i) {
1437       dashList[i] = xoutRound(state->transformWidth(dashPattern[i]));
1438       if (dashList[i] == 0)
1439         dashList[i] = 1;
1440     }
1441     XSetDashes(display, strokeGC, xoutRound(dashStart), dashList, dashLength);
1442   }
1443 }
1444
1445 void XOutputDev::updateFillColor(GfxState *state) {
1446   XSetForeground(display, fillGC, findColor(state->getFillColor()));
1447 }
1448
1449 void XOutputDev::updateStrokeColor(GfxState *state) {
1450   XSetForeground(display, strokeGC, findColor(state->getStrokeColor()));
1451 }
1452
1453 void XOutputDev::updateFont(GfxState *state) {
1454   double m11, m12, m21, m22;
1455
1456   if (!(gfxFont = state->getFont())) {
1457     font = NULL;
1458     return;
1459   }
1460   state->getFontTransMat(&m11, &m12, &m21, &m22);
1461   m11 *= state->getHorizScaling();
1462   m21 *= state->getHorizScaling();
1463   font = fontCache->getFont(gfxFont, m11, m12, m21, m22);
1464   if (font) {
1465     font->updateGC(fillGC);
1466     font->updateGC(strokeGC);
1467   }
1468 }
1469
1470 void XOutputDev::stroke(GfxState *state) {
1471   XPoint *points;
1472   int *lengths;
1473   int n, size, numPoints, i, j;
1474
1475   // transform points
1476   n = convertPath(state, &points, &size, &numPoints, &lengths, gFalse);
1477
1478   // draw each subpath
1479   j = 0;
1480   for (i = 0; i < n; ++i) {
1481     XDrawLines(display, pixmap, strokeGC, points + j, lengths[i],
1482                CoordModeOrigin);
1483     j += lengths[i];
1484   }
1485
1486   // free points and lengths arrays
1487   if (points != tmpPoints)
1488     gfree(points);
1489   if (lengths != tmpLengths)
1490     gfree(lengths);
1491 }
1492
1493 void XOutputDev::fill(GfxState *state) {
1494   doFill(state, WindingRule);
1495 }
1496
1497 void XOutputDev::eoFill(GfxState *state) {
1498   doFill(state, EvenOddRule);
1499 }
1500
1501 //
1502 //  X doesn't color the pixels on the right-most and bottom-most
1503 //  borders of a polygon.  This means that one-pixel-thick polygons
1504 //  are not colored at all.  I think this is supposed to be a
1505 //  feature, but I can't figure out why.  So after it fills a
1506 //  polygon, it also draws lines around the border.  This is done
1507 //  only for single-component polygons, since it's not very
1508 //  compatible with the compound polygon kludge (see convertPath()).
1509 //
1510 void XOutputDev::doFill(GfxState *state, int rule) {
1511   XPoint *points;
1512   int *lengths;
1513   int n, size, numPoints, i, j;
1514
1515   // set fill rule
1516   XSetFillRule(display, fillGC, rule);
1517
1518   // transform points, build separate polygons
1519   n = convertPath(state, &points, &size, &numPoints, &lengths, gTrue);
1520
1521   // fill them
1522   j = 0;
1523   for (i = 0; i < n; ++i) {
1524     XFillPolygon(display, pixmap, fillGC, points + j, lengths[i],
1525                  Complex, CoordModeOrigin);
1526     if (state->getPath()->getNumSubpaths() == 1) {
1527       XDrawLines(display, pixmap, fillGC, points + j, lengths[i],
1528                  CoordModeOrigin);
1529     }
1530     j += lengths[i] + 1;
1531   }
1532
1533   // free points and lengths arrays
1534   if (points != tmpPoints)
1535     gfree(points);
1536   if (lengths != tmpLengths)
1537     gfree(lengths);
1538 }
1539
1540 void XOutputDev::clip(GfxState *state) {
1541   doClip(state, WindingRule);
1542 }
1543
1544 void XOutputDev::eoClip(GfxState *state) {
1545   doClip(state, EvenOddRule);
1546 }
1547
1548 void XOutputDev::doClip(GfxState *state, int rule) {
1549   Region region, region2;
1550   XPoint *points;
1551   int *lengths;
1552   int n, size, numPoints, i, j;
1553
1554   // transform points, build separate polygons
1555   n = convertPath(state, &points, &size, &numPoints, &lengths, gTrue);
1556
1557   // construct union of subpath regions
1558   region = XPolygonRegion(points, lengths[0], rule);
1559   j = lengths[0] + 1;
1560   for (i = 1; i < n; ++i) {
1561     region2 = XPolygonRegion(points + j, lengths[i], rule);
1562     XUnionRegion(region2, region, region);
1563     XDestroyRegion(region2);
1564     j += lengths[i] + 1;
1565   }
1566
1567   // intersect region with clipping region
1568   XIntersectRegion(region, clipRegion, clipRegion);
1569   XDestroyRegion(region);
1570   XSetRegion(display, strokeGC, clipRegion);
1571   XSetRegion(display, fillGC, clipRegion);
1572
1573   // free points and lengths arrays
1574   if (points != tmpPoints)
1575     gfree(points);
1576   if (lengths != tmpLengths)
1577     gfree(lengths);
1578 }
1579
1580 //
1581 // Transform points in the path and convert curves to line segments.
1582 // Builds a set of subpaths and returns the number of subpaths.
1583 // If <fillHack> is set, close any unclosed subpaths and activate a
1584 // kludge for polygon fills:  First, it divides up the subpaths into
1585 // non-overlapping polygons by simply comparing bounding rectangles.
1586 // Then it connects subaths within a single compound polygon to a single
1587 // point so that X can fill the polygon (sort of).
1588 //
1589 int XOutputDev::convertPath(GfxState *state, XPoint **points, int *size,
1590                             int *numPoints, int **lengths, GBool fillHack) {
1591   GfxPath *path;
1592   BoundingRect *rects;
1593   BoundingRect rect;
1594   int n, i, ii, j, k, k0;
1595
1596   // get path and number of subpaths
1597   path = state->getPath();
1598   n = path->getNumSubpaths();
1599
1600   // allocate lengths array
1601   if (n < numTmpSubpaths)
1602     *lengths = tmpLengths;
1603   else
1604     *lengths = (int *)gmalloc(n * sizeof(int));
1605
1606   // allocate bounding rectangles array
1607   if (fillHack) {
1608     if (n < numTmpSubpaths)
1609       rects = tmpRects;
1610     else
1611       rects = (BoundingRect *)gmalloc(n * sizeof(BoundingRect));
1612   } else {
1613     rects = NULL;
1614   }
1615
1616   // do each subpath
1617   *points = tmpPoints;
1618   *size = numTmpPoints;
1619   *numPoints = 0;
1620   for (i = 0; i < n; ++i) {
1621
1622     // transform the points
1623     j = *numPoints;
1624     convertSubpath(state, path->getSubpath(i), points, size, numPoints);
1625
1626     // construct bounding rectangle
1627     if (fillHack) {
1628       rects[i].xMin = rects[i].xMax = (*points)[j].x;
1629       rects[i].yMin = rects[i].yMax = (*points)[j].y;
1630       for (k = j + 1; k < *numPoints; ++k) {
1631         if ((*points)[k].x < rects[i].xMin)
1632           rects[i].xMin = (*points)[k].x;
1633         else if ((*points)[k].x > rects[i].xMax)
1634           rects[i].xMax = (*points)[k].x;
1635         if ((*points)[k].y < rects[i].yMin)
1636           rects[i].yMin = (*points)[k].y;
1637         else if ((*points)[k].y > rects[i].yMax)
1638           rects[i].yMax = (*points)[k].y;
1639       }
1640     }
1641
1642     // close subpath if necessary
1643     if (fillHack && ((*points)[*numPoints-1].x != (*points)[j].x ||
1644                    (*points)[*numPoints-1].y != (*points)[j].y)) {
1645       addPoint(points, size, numPoints, (*points)[j].x, (*points)[j].y);
1646     }
1647
1648     // length of this subpath
1649     (*lengths)[i] = *numPoints - j;
1650
1651     // leave an extra point for compound fill hack
1652     if (fillHack)
1653       addPoint(points, size, numPoints, 0, 0);
1654   }
1655
1656   // combine compound polygons
1657   if (fillHack) {
1658     i = j = k = 0;
1659     while (i < n) {
1660
1661       // start with subpath i
1662       rect = rects[i];
1663       (*lengths)[j] = (*lengths)[i];
1664       k0 = k;
1665       (*points)[k + (*lengths)[i]] = (*points)[k0];
1666       k += (*lengths)[i] + 1;
1667       ++i;
1668
1669       // combine overlapping polygons
1670       do {
1671
1672         // look for the first subsequent subpath, if any, which overlaps
1673         for (ii = i; ii < n; ++ii) {
1674           if (((rects[ii].xMin > rect.xMin && rects[ii].xMin < rect.xMax) ||
1675                (rects[ii].xMax > rect.xMin && rects[ii].xMax < rect.xMax) ||
1676                (rects[ii].xMin < rect.xMin && rects[ii].xMax > rect.xMax)) &&
1677               ((rects[ii].yMin > rect.yMin && rects[ii].yMin < rect.yMax) ||
1678                (rects[ii].yMax > rect.yMin && rects[ii].yMax < rect.yMax) ||
1679                (rects[ii].yMin < rect.yMin && rects[ii].yMax > rect.yMax)))
1680             break;
1681         }
1682
1683         // if there is an overlap, combine the polygons
1684         if (ii < n) {
1685           for (; i <= ii; ++i) {
1686             if (rects[i].xMin < rect.xMin)
1687               rect.xMin = rects[j].xMin;
1688             if (rects[i].xMax > rect.xMax)
1689               rect.xMax = rects[j].xMax;
1690             if (rects[i].yMin < rect.yMin)
1691               rect.yMin = rects[j].yMin;
1692             if (rects[i].yMax > rect.yMax)
1693               rect.yMax = rects[j].yMax;
1694             (*lengths)[j] += (*lengths)[i] + 1;
1695             (*points)[k + (*lengths)[i]] = (*points)[k0];
1696             k += (*lengths)[i] + 1;
1697           }
1698         }
1699       } while (ii < n && i < n);
1700
1701       ++j;
1702     }
1703
1704     // free bounding rectangles
1705     if (rects != tmpRects)
1706       gfree(rects);
1707
1708     n = j;
1709   }
1710
1711   return n;
1712 }
1713
1714 //
1715 // Transform points in a single subpath and convert curves to line
1716 // segments.
1717 //
1718 void XOutputDev::convertSubpath(GfxState *state, GfxSubpath *subpath,
1719                                 XPoint **points, int *size, int *n) {
1720   double x0, y0, x1, y1, x2, y2, x3, y3;
1721   int m, i;
1722
1723   m = subpath->getNumPoints();
1724   i = 0;
1725   while (i < m) {
1726     if (i >= 1 && subpath->getCurve(i)) {
1727       state->transform(subpath->getX(i-1), subpath->getY(i-1), &x0, &y0);
1728       state->transform(subpath->getX(i), subpath->getY(i), &x1, &y1);
1729       state->transform(subpath->getX(i+1), subpath->getY(i+1), &x2, &y2);
1730       state->transform(subpath->getX(i+2), subpath->getY(i+2), &x3, &y3);
1731       doCurve(points, size, n, x0, y0, x1, y1, x2, y2, x3, y3);
1732       i += 3;
1733     } else {
1734       state->transform(subpath->getX(i), subpath->getY(i), &x1, &y1);
1735       addPoint(points, size, n, xoutRound(x1), xoutRound(y1));
1736       ++i;
1737     }
1738   }
1739 }
1740
1741 //
1742 // Subdivide a Bezier curve.  This uses floating point to avoid
1743 // propagating rounding errors.  (The curves look noticeably more
1744 // jagged with integer arithmetic.)
1745 //
1746 void XOutputDev::doCurve(XPoint **points, int *size, int *n,
1747                          double x0, double y0, double x1, double y1,
1748                          double x2, double y2, double x3, double y3) {
1749   double x[(1<<maxCurveSplits)+1][3];
1750   double y[(1<<maxCurveSplits)+1][3];
1751   int next[1<<maxCurveSplits];
1752   int p1, p2, p3;
1753   double xx1, yy1, xx2, yy2;
1754   double dx, dy, mx, my, d1, d2;
1755   double xl0, yl0, xl1, yl1, xl2, yl2;
1756   double xr0, yr0, xr1, yr1, xr2, yr2, xr3, yr3;
1757   double xh, yh;
1758   double flat;
1759
1760   flat = (double)(flatness * flatness);
1761   if (flat < 1)
1762     flat = 1;
1763
1764   // initial segment
1765   p1 = 0;
1766   p2 = 1<<maxCurveSplits;
1767   x[p1][0] = x0;  y[p1][0] = y0;
1768   x[p1][1] = x1;  y[p1][1] = y1;
1769   x[p1][2] = x2;  y[p1][2] = y2;
1770   x[p2][0] = x3;  y[p2][0] = y3;
1771   next[p1] = p2;
1772
1773   while (p1 < (1<<maxCurveSplits)) {
1774
1775     // get next segment
1776     xl0 = x[p1][0];  yl0 = y[p1][0];
1777     xx1 = x[p1][1];  yy1 = y[p1][1];
1778     xx2 = x[p1][2];  yy2 = y[p1][2];
1779     p2 = next[p1];
1780     xr3 = x[p2][0];  yr3 = y[p2][0];
1781
1782     // compute distances from control points to midpoint of the
1783     // straight line (this is a bit of a hack, but it's much faster
1784     // than computing the actual distances to the line)
1785     mx = (xl0 + xr3) * 0.5;
1786     my = (yl0 + yr3) * 0.5;
1787     dx = xx1 - mx;
1788     dy = yy1 - my;
1789     d1 = dx*dx + dy*dy;
1790     dx = xx2 - mx;
1791     dy = yy2 - my;
1792     d2 = dx*dx + dy*dy;
1793
1794     // if curve is flat enough, or no more divisions allowed then
1795     // add the straight line segment
1796     if (p2 - p1 <= 1 || (d1 <= flat && d2 <= flat)) {
1797       addPoint(points, size, n, xoutRound(xr3), xoutRound(yr3));
1798       p1 = p2;
1799
1800     // otherwise, subdivide the curve
1801     } else {
1802       xl1 = (xl0 + xx1) * 0.5;
1803       yl1 = (yl0 + yy1) * 0.5;
1804       xh = (xx1 + xx2) * 0.5;
1805       yh = (yy1 + yy2) * 0.5;
1806       xl2 = (xl1 + xh) * 0.5;
1807       yl2 = (yl1 + yh) * 0.5;
1808       xr2 = (xx2 + xr3) * 0.5;
1809       yr2 = (yy2 + yr3) * 0.5;
1810       xr1 = (xh + xr2) * 0.5;
1811       yr1 = (yh + yr2) * 0.5;
1812       xr0 = (xl2 + xr1) * 0.5;
1813       yr0 = (yl2 + yr1) * 0.5;
1814
1815       // add the new subdivision points
1816       p3 = (p1 + p2) / 2;
1817       x[p1][1] = xl1;  y[p1][1] = yl1;
1818       x[p1][2] = xl2;  y[p1][2] = yl2;
1819       next[p1] = p3;
1820       x[p3][0] = xr0;  y[p3][0] = yr0;
1821       x[p3][1] = xr1;  y[p3][1] = yr1;
1822       x[p3][2] = xr2;  y[p3][2] = yr2;
1823       next[p3] = p2;
1824     }
1825   }
1826 }
1827
1828 //
1829 // Add a point to the points array.  (This would use a generic resizable
1830 // array type if C++ supported parameterized types in some reasonable
1831 // way -- templates are a disgusting kludge.)
1832 //
1833 void XOutputDev::addPoint(XPoint **points, int *size, int *k, int x, int y) {
1834   if (*k >= *size) {
1835     *size += 32;
1836     if (*points == tmpPoints) {
1837       *points = (XPoint *)gmalloc(*size * sizeof(XPoint));
1838       memcpy(*points, tmpPoints, *k * sizeof(XPoint));
1839     } else {
1840       *points = (XPoint *)grealloc(*points, *size * sizeof(XPoint));
1841     }
1842   }
1843   (*points)[*k].x = x;
1844   (*points)[*k].y = y;
1845   ++(*k);
1846 }
1847
1848 void XOutputDev::beginString(GfxState *state, GString *s) {
1849   text->beginString(state, s, font ? font->isHex() : gFalse);
1850 }
1851
1852 void XOutputDev::endString(GfxState *state) {
1853   text->endString();
1854 }
1855
1856 void XOutputDev::drawChar(GfxState *state, double x, double y,
1857                           double dx, double dy, Guchar c) {
1858   double x1, y1;
1859
1860   text->addChar(state, x, y, dx, dy, c);
1861
1862   if (!font)
1863     return;
1864
1865   // check for invisible text -- this is used by Acrobat Capture
1866   if ((state->getRender() & 3) == 3)
1867     return;
1868
1869   state->transform(x, y, &x1, &y1);
1870
1871   font->drawChar(state, pixmap, (state->getRender() & 1) ? strokeGC : fillGC,
1872                  xoutRound(x1), xoutRound(y1), c);
1873 }
1874
1875 void XOutputDev::drawChar16(GfxState *state, double x, double y,
1876                             double dx, double dy, int c) {
1877   int c1;
1878   XChar2b c2[4];
1879   double x1, y1;
1880 #if JAPANESE_SUPPORT
1881   int t1, t2;
1882   double x2;
1883   char *p;
1884   int n, i;
1885 #endif
1886
1887   if (gfxFont) {
1888     text->addChar16(state, x, y, dx, dy, c, gfxFont->getCharSet16());
1889   }
1890
1891   //~ assumes font is an XOutputServerFont
1892
1893   if (!font)
1894     return;
1895
1896   // check for invisible text -- this is used by Acrobat Capture
1897   if ((state->getRender() & 3) == 3)
1898     return;
1899
1900   // handle origin offset for vertical fonts
1901   if (gfxFont->getWMode16() == 1) {
1902     x -= gfxFont->getOriginX16(c) * state->getFontSize();
1903     y -= gfxFont->getOriginY16(c) * state->getFontSize();
1904   }
1905
1906   state->transform(x, y, &x1, &y1);
1907
1908   c1 = 0;
1909   switch (gfxFont->getCharSet16()) {
1910
1911   // convert Adobe-Japan1-2 to JIS X 0208-1983
1912   case font16AdobeJapan12:
1913 #if JAPANESE_SUPPORT
1914     if (c <= 96) {
1915       c1 = japan12Map[c];
1916     } else if (c <= 632) {
1917       if (c <= 230)
1918         c1 = 0;
1919       else if (c <= 324)
1920         c1 = japan12Map[c - 230];
1921       else if (c <= 421)
1922         c1 = japan12KanaMap1[c - 325];
1923       else if (c <= 500)
1924         c1 = 0;
1925       else if (c <= 598)
1926         c1 = japan12KanaMap2[c - 501];
1927       else
1928         c1 = 0;
1929     } else if (c <= 1124) {
1930       if (c <= 779) {
1931         if (c <= 726)
1932           c1 = 0x2121 + (c - 633);
1933         else if (c <= 740)
1934           c1 = 0x2221 + (c - 727);
1935         else if (c <= 748)
1936           c1 = 0x223a + (c - 741);
1937         else if (c <= 755)
1938           c1 = 0x224a + (c - 749);
1939         else if (c <= 770)
1940           c1 = 0x225c + (c - 756);
1941         else if (c <= 778)
1942           c1 = 0x2272 + (c - 771);
1943         else
1944           c1 = 0x227e;
1945       } else if (c <= 841) {
1946         if (c <= 789)
1947           c1 = 0x2330 + (c - 780);
1948         else if (c <= 815)
1949           c1 = 0x2341 + (c - 790);
1950         else
1951           c1 = 0x2361 + (c - 816);
1952       } else if (c <= 1010) {
1953         if (c <= 924)
1954           c1 = 0x2421 + (c - 842);
1955         else
1956           c1 = 0x2521 + (c - 925);
1957       } else {
1958         if (c <= 1034)
1959           c1 = 0x2621 + (c - 1011);
1960         else if (c <= 1058)
1961           c1 = 0x2641 + (c - 1035);
1962         else if (c <= 1091)
1963           c1 = 0x2721 + (c - 1059);
1964         else
1965           c1 = 0x2751 + (c - 1092);
1966       }
1967     } else if (c <= 4089) {
1968       t1 = (c - 1125) / 94;
1969       t2 = (c - 1125) % 94;
1970       c1 = 0x3021 + (t1 << 8) + t2;
1971     } else if (c <= 7477) {
1972       t1 = (c - 4090) / 94;
1973       t2 = (c - 4090) % 94;
1974       c1 = 0x5021 + (t1 << 8) + t2;
1975     } else if (c <= 7554) {
1976       c1 = 0;
1977     } else if (c <= 7563) {     // circled Arabic numbers 1..9
1978       c1 = 0x2331 + (c - 7555);
1979       c2[0].byte1 = c1 >> 8;
1980       c2[0].byte2 = c1 & 0xff;
1981       XDrawString16(display, pixmap,
1982                     (state->getRender() & 1) ? strokeGC : fillGC,
1983                     xoutRound(x1), xoutRound(y1), c2, 1);
1984       c1 = 0x227e;
1985       c2[0].byte1 = c1 >> 8;
1986       c2[0].byte2 = c1 & 0xff;
1987       XDrawString16(display, pixmap,
1988                     (state->getRender() & 1) ? strokeGC : fillGC,
1989                     xoutRound(x1), xoutRound(y1), c2, 1);
1990       c1 = -1;
1991     } else if (c <= 7574) {     // circled Arabic numbers 10..20
1992       n = c - 7564 + 10;
1993       x2 = x1;
1994       for (i = 0; i < 2; ++i) {
1995         c1 = 0x2330 + (i == 0 ? (n / 10) : (n % 10));
1996         c2[0].byte1 = c1 >> 8;
1997         c2[0].byte2 = c1 & 0xff;
1998         XDrawString16(display, pixmap,
1999                       (state->getRender() & 1) ? strokeGC : fillGC,
2000                       xoutRound(x2), xoutRound(y1), c2, 1);
2001         x2 += 0.5 * state->getTransformedFontSize();
2002       }
2003       c1 = 0x227e;
2004       c2[0].byte1 = c1 >> 8;
2005       c2[0].byte2 = c1 & 0xff;
2006       XDrawString16(display, pixmap,
2007                     (state->getRender() & 1) ? strokeGC : fillGC,
2008                     xoutRound(x1), xoutRound(y1), c2, 1);
2009       c1 = -1;
2010     } else if (c <= 7584) {     // Roman numbers I..X
2011       p = japan12Roman[c - 7575];
2012       n = strlen(p);
2013       for (; *p; ++p) {
2014         if (*p == 'I')
2015           c1 = 0x2349;
2016         else if (*p == 'V')
2017           c1 = 0x2356;
2018         else // 'X'
2019           c1 = 0x2358;
2020         c2[0].byte1 = c1 >> 8;
2021         c2[0].byte2 = c1 & 0xff;
2022         XDrawString16(display, pixmap,
2023                       (state->getRender() & 1) ? strokeGC : fillGC,
2024                       xoutRound(x1), xoutRound(y1), c2, 1);
2025         if (*p == 'I')
2026           x1 += 0.2 * state->getTransformedFontSize();
2027         else
2028           x1 += 0.5 * state->getTransformedFontSize();
2029       }
2030       c1 = -1;
2031     } else if (c <= 7632) {
2032       if (c <= 7600) {
2033         c1 = 0;
2034       } else if (c <= 7606) {
2035         p = japan12Abbrev1[c - 7601];
2036         n = strlen(p);
2037         for (; *p; ++p) {
2038           c1 = 0x2300 + *p;
2039           c2[0].byte1 = c1 >> 8;
2040           c2[0].byte2 = c1 & 0xff;
2041           XDrawString16(display, pixmap,
2042                         (state->getRender() & 1) ? strokeGC : fillGC,
2043                         xoutRound(x1), xoutRound(y1), c2, 1);
2044           x1 += 0.5 * state->getTransformedFontSize();
2045         }
2046         c1 = -1;
2047       } else {
2048         c1 = 0;
2049       }
2050     } else {
2051       c1 = 0;
2052     }
2053 #if 0 //~
2054     if (c1 == 0)
2055       error(-1, "Unsupported Adobe-Japan1-2 character: %d", c);
2056 #endif
2057 #endif // JAPANESE_SUPPORT
2058     break;
2059   }
2060
2061   if (c1 > 0) {
2062     c2[0].byte1 = c1 >> 8;
2063     c2[0].byte2 = c1 & 0xff;
2064     XDrawString16(display, pixmap,
2065                   (state->getRender() & 1) ? strokeGC : fillGC,
2066                   xoutRound(x1), xoutRound(y1), c2, 1);
2067   }
2068 }
2069
2070 void XOutputDev::drawImageMask(GfxState *state, Stream *str,
2071                                int width, int height, GBool invert,
2072                                GBool inlineImg) {
2073   ImageStream *imgStr;
2074   XImage *image;
2075   int x0, y0;                   // top left corner of image
2076   int w0, h0, w1, h1;           // size of image
2077   int x2, y2;
2078   int w2, h2;
2079   double xt, yt, wt, ht;
2080   GBool rotate, xFlip, yFlip;
2081   int x, y;
2082   int ix, iy;
2083   int px1, px2, qx, dx;
2084   int py1, py2, qy, dy;
2085   Guchar pixBuf;
2086   Gulong color;
2087   int i, j;
2088
2089   // get image position and size
2090   state->transform(0, 0, &xt, &yt);
2091   state->transformDelta(1, 1, &wt, &ht);
2092   if (wt > 0) {
2093     x0 = xoutRound(xt);
2094     w0 = xoutRound(wt);
2095   } else {
2096     x0 = xoutRound(xt + wt);
2097     w0 = xoutRound(-wt);
2098   }
2099   if (ht > 0) {
2100     y0 = xoutRound(yt);
2101     h0 = xoutRound(ht);
2102   } else {
2103     y0 = xoutRound(yt + ht);
2104     h0 = xoutRound(-ht);
2105   }
2106   state->transformDelta(1, 0, &xt, &yt);
2107   rotate = fabs(xt) < fabs(yt);
2108   if (rotate) {
2109     w1 = h0;
2110     h1 = w0;
2111     xFlip = ht < 0;
2112     yFlip = wt > 0;
2113   } else {
2114     w1 = w0;
2115     h1 = h0;
2116     xFlip = wt < 0;
2117     yFlip = ht > 0;
2118   }
2119
2120   // set up
2121   color = findColor(state->getFillColor());
2122
2123   // check for tiny (zero width or height) images
2124   if (w0 == 0 || h0 == 0) {
2125     j = height * ((width + 7) / 8);
2126     str->reset();
2127     for (i = 0; i < j; ++i)
2128       str->getChar();
2129     return;
2130   }
2131
2132   // Bresenham parameters
2133   px1 = w1 / width;
2134   px2 = w1 - px1 * width;
2135   py1 = h1 / height;
2136   py2 = h1 - py1 * height;
2137
2138   // allocate XImage
2139   image = XCreateImage(display, DefaultVisual(display, screenNum),
2140                        depth, ZPixmap, 0, NULL, w0, h0, 8, 0);
2141   image->data = (char *)gmalloc(h0 * image->bytes_per_line);
2142   if (x0 + w0 > pixmapW)
2143     w2 = pixmapW - x0;
2144   else
2145     w2 = w0;
2146   if (x0 < 0) {
2147     x2 = -x0;
2148     w2 += x0;
2149     x0 = 0;
2150   } else {
2151     x2 = 0;
2152   }
2153   if (y0 + h0 > pixmapH)
2154     h2 = pixmapH - y0;
2155   else
2156     h2 = h0;
2157   if (y0 < 0) {
2158     y2 = -y0;
2159     h2 += y0;
2160     y0 = 0;
2161   } else {
2162     y2 = 0;
2163   }
2164   XGetSubImage(display, pixmap, x0, y0, w2, h2, (1 << depth) - 1, ZPixmap,
2165                image, x2, y2);
2166
2167   // initialize the image stream
2168   imgStr = new ImageStream(str, width, 1, 1);
2169   imgStr->reset();
2170
2171   // first line (column)
2172   y = yFlip ? h1 - 1 : 0;
2173   qy = 0;
2174
2175   // read image
2176   for (i = 0; i < height; ++i) {
2177
2178     // vertical (horizontal) Bresenham
2179     dy = py1;
2180     if ((qy += py2) >= height) {
2181       ++dy;
2182       qy -= height;
2183     }
2184
2185     // drop a line (column)
2186     if (dy == 0) {
2187       imgStr->skipLine();
2188
2189     } else {
2190
2191       // first column (line)
2192       x = xFlip ? w1 - 1 : 0;
2193       qx = 0;
2194
2195       // for each column (line)...
2196       for (j = 0; j < width; ++j) {
2197
2198         // horizontal (vertical) Bresenham
2199         dx = px1;
2200         if ((qx += px2) >= width) {
2201           ++dx;
2202           qx -= width;
2203         }
2204
2205         // get image pixel
2206         imgStr->getPixel(&pixBuf);
2207         if (invert)
2208           pixBuf ^= 1;
2209
2210         // draw image pixel
2211         if (dx > 0 && pixBuf == 0) {
2212           if (dx == 1 && dy == 1) {
2213             if (rotate)
2214               XPutPixel(image, y, x, color);
2215             else
2216               XPutPixel(image, x, y, color);
2217           } else {
2218             for (ix = 0; ix < dx; ++ix) {
2219               for (iy = 0; iy < dy; ++iy) {
2220                 if (rotate)
2221                   XPutPixel(image, yFlip ? y - iy : y + iy,
2222                             xFlip ? x - ix : x + ix, color);
2223                 else
2224                   XPutPixel(image, xFlip ? x - ix : x + ix,
2225                             yFlip ? y - iy : y + iy, color);
2226               }
2227             }
2228           }
2229         }
2230
2231         // next column (line)
2232         if (xFlip)
2233           x -= dx;
2234         else
2235           x += dx;
2236       }
2237     }
2238
2239     // next line (column)
2240     if (yFlip)
2241       y -= dy;
2242     else
2243       y += dy;
2244   }
2245
2246   // blit the image into the pixmap
2247   XPutImage(display, pixmap, fillGC, image, x2, y2, x0, y0, w2, h2);
2248
2249   // free memory
2250   delete imgStr;
2251   gfree(image->data);
2252   image->data = NULL;
2253   XDestroyImage(image);
2254 }
2255
2256 inline Gulong XOutputDev::findColor(RGBColor *x, RGBColor *err) {
2257   double gray;
2258   int r, g, b;
2259   Gulong pixel;
2260
2261   if (trueColor) {
2262     r = xoutRound(x->r * rMul);
2263     g = xoutRound(x->g * gMul);
2264     b = xoutRound(x->b * bMul);
2265     pixel = ((Gulong)r << rShift) +
2266             ((Gulong)g << gShift) +
2267             ((Gulong)b << bShift);
2268     err->r = x->r - (double)r / rMul;
2269     err->g = x->g - (double)g / gMul;
2270     err->b = x->b - (double)b / bMul;
2271   } else if (numColors == 1) {
2272     gray = 0.299 * x->r + 0.587 * x->g + 0.114 * x->b;
2273     if (gray < 0.5) {
2274       pixel = colors[0];
2275       err->r = x->r;
2276       err->g = x->g;
2277       err->b = x->b;
2278     } else {
2279       pixel = colors[1];
2280       err->r = x->r - 1;
2281       err->g = x->g - 1;
2282       err->b = x->b - 1;
2283     }
2284   } else {
2285     r = xoutRound(x->r * (numColors - 1));
2286     g = xoutRound(x->g * (numColors - 1));
2287     b = xoutRound(x->b * (numColors - 1));
2288     pixel = colors[(r * numColors + g) * numColors + b];
2289     err->r = x->r - (double)r / (numColors - 1);
2290     err->g = x->g - (double)g / (numColors - 1); 
2291     err->b = x->b - (double)b / (numColors - 1);
2292   }
2293   return pixel;
2294 }
2295
2296 void XOutputDev::drawImage(GfxState *state, Stream *str, int width,
2297                            int height, GfxImageColorMap *colorMap,
2298                            GBool inlineImg) {
2299   ImageStream *imgStr;
2300   XImage *image;
2301   int x0, y0;                   // top left corner of image
2302   int w0, h0, w1, h1;           // size of image
2303   double xt, yt, wt, ht;
2304   GBool rotate, xFlip, yFlip;
2305   GBool dither;
2306   int x, y;
2307   int ix, iy;
2308   int px1, px2, qx, dx;
2309   int py1, py2, qy, dy;
2310   Guchar pixBuf[4];
2311   Gulong pixel;
2312   int nComps, nVals, nBits;
2313   double r1, g1, b1;
2314   GfxColor color;
2315   RGBColor color2, err;
2316   RGBColor *errRight, *errDown;
2317   int i, j;
2318
2319   // get image position and size
2320   state->transform(0, 0, &xt, &yt);
2321   state->transformDelta(1, 1, &wt, &ht);
2322   if (wt > 0) {
2323     x0 = xoutRound(xt);
2324     w0 = xoutRound(wt);
2325   } else {
2326     x0 = xoutRound(xt + wt);
2327     w0 = xoutRound(-wt);
2328   }
2329   if (ht > 0) {
2330     y0 = xoutRound(yt);
2331     h0 = xoutRound(ht);
2332   } else {
2333     y0 = xoutRound(yt + ht);
2334     h0 = xoutRound(-ht);
2335   }
2336   state->transformDelta(1, 0, &xt, &yt);
2337   rotate = fabs(xt) < fabs(yt);
2338   if (rotate) {
2339     w1 = h0;
2340     h1 = w0;
2341     xFlip = ht < 0;
2342     yFlip = wt > 0;
2343   } else {
2344     w1 = w0;
2345     h1 = h0;
2346     xFlip = wt < 0;
2347     yFlip = ht > 0;
2348   }
2349
2350   // set up
2351   nComps = colorMap->getNumPixelComps();
2352   nVals = width * nComps;
2353   nBits = colorMap->getBits();
2354   dither = nComps > 1 || nBits > 1;
2355
2356   // check for tiny (zero width or height) images
2357   if (w0 == 0 || h0 == 0) {
2358     j = height * ((nVals * nBits + 7) / 8);
2359     str->reset();
2360     for (i = 0; i < j; ++i)
2361       str->getChar();
2362     return;
2363   }
2364
2365   // Bresenham parameters
2366   px1 = w1 / width;
2367   px2 = w1 - px1 * width;
2368   py1 = h1 / height;
2369   py2 = h1 - py1 * height;
2370
2371   // allocate XImage
2372   image = XCreateImage(display, DefaultVisual(display, screenNum),
2373                        depth, ZPixmap, 0, NULL, w0, h0, 8, 0);
2374   image->data = (char *)gmalloc(h0 * image->bytes_per_line);
2375
2376   // allocate error diffusion accumulators
2377   if (dither) {
2378     errDown = (RGBColor *)gmalloc(w1 * sizeof(RGBColor));
2379     errRight = (RGBColor *)gmalloc((py1 + 1) * sizeof(RGBColor));
2380     for (j = 0; j < w1; ++j)
2381       errDown[j].r = errDown[j].g = errDown[j].b = 0;
2382   } else {
2383     errDown = NULL;
2384     errRight = NULL;
2385   }
2386
2387   // initialize the image stream
2388   imgStr = new ImageStream(str, width, nComps, nBits);
2389   imgStr->reset();
2390
2391   // first line (column)
2392   y = yFlip ? h1 - 1 : 0;
2393   qy = 0;
2394
2395   // read image
2396   for (i = 0; i < height; ++i) {
2397
2398     // vertical (horizontal) Bresenham
2399     dy = py1;
2400     if ((qy += py2) >= height) {
2401       ++dy;
2402       qy -= height;
2403     }
2404
2405     // drop a line (column)
2406     if (dy == 0) {
2407       imgStr->skipLine();
2408
2409     } else {
2410
2411       // first column (line)
2412       x = xFlip ? w1 - 1 : 0;
2413       qx = 0;
2414
2415       // clear error accumulator
2416       if (dither) {
2417         for (j = 0; j <= py1; ++j)
2418           errRight[j].r = errRight[j].g = errRight[j].b = 0;
2419       }
2420
2421       // for each column (line)...
2422       for (j = 0; j < width; ++j) {
2423
2424         // horizontal (vertical) Bresenham
2425         dx = px1;
2426         if ((qx += px2) >= width) {
2427           ++dx;
2428           qx -= width;
2429         }
2430
2431         // get image pixel
2432         imgStr->getPixel(pixBuf);
2433
2434         // draw image pixel
2435         if (dx > 0) {
2436           colorMap->getColor(pixBuf, &color);
2437           r1 = color.getR();
2438           g1 = color.getG();
2439           b1 = color.getB();
2440           if (dither) {
2441             pixel = 0;
2442           } else {
2443             color2.r = r1;
2444             color2.g = g1;
2445             color2.b = b1;
2446             pixel = findColor(&color2, &err);
2447           }
2448           if (dx == 1 && dy == 1) {
2449             if (dither) {
2450               color2.r = r1 + errRight[0].r + errDown[x].r;
2451               if (color2.r > 1)
2452                 color2.r = 1;
2453               else if (color2.r < 0)
2454                 color2.r = 0;
2455               color2.g = g1 + errRight[0].g + errDown[x].g;
2456               if (color2.g > 1)
2457                 color2.g = 1;
2458               else if (color2.g < 0)
2459                 color2.g = 0;
2460               color2.b = b1 + errRight[0].b + errDown[x].b;
2461               if (color2.b > 1)
2462                 color2.b = 1;
2463               else if (color2.b < 0)
2464                 color2.b = 0;
2465               pixel = findColor(&color2, &err);
2466               errRight[0].r = errDown[x].r = err.r / 2;
2467               errRight[0].g = errDown[x].g = err.g / 2;
2468               errRight[0].b = errDown[x].b = err.b / 2;
2469             }
2470             if (rotate)
2471               XPutPixel(image, y, x, pixel);
2472             else
2473               XPutPixel(image, x, y, pixel);
2474           } else {
2475             for (iy = 0; iy < dy; ++iy) {
2476               for (ix = 0; ix < dx; ++ix) {
2477                 if (dither) {
2478                   color2.r = r1 + errRight[iy].r +
2479                     errDown[xFlip ? x - ix : x + ix].r;
2480                   if (color2.r > 1)
2481                     color2.r = 1;
2482                   else if (color2.r < 0)
2483                     color2.r = 0;
2484                   color2.g = g1 + errRight[iy].g +
2485                     errDown[xFlip ? x - ix : x + ix].g;
2486                   if (color2.g > 1)
2487                     color2.g = 1;
2488                   else if (color2.g < 0)
2489                     color2.g = 0;
2490                   color2.b = b1 + errRight[iy].b +
2491                     errDown[xFlip ? x - ix : x + ix].b;
2492                   if (color2.b > 1)
2493                     color2.b = 1;
2494                   else if (color2.b < 0)
2495                     color2.b = 0;
2496                   pixel = findColor(&color2, &err);
2497                   errRight[iy].r = errDown[xFlip ? x - ix : x + ix].r =
2498                     err.r / 2;
2499                   errRight[iy].g = errDown[xFlip ? x - ix : x + ix].g =
2500                     err.g / 2;
2501                   errRight[iy].b = errDown[xFlip ? x - ix : x + ix].b =
2502                     err.b / 2;
2503                 }
2504                 if (rotate)
2505                   XPutPixel(image, yFlip ? y - iy : y + iy,
2506                             xFlip ? x - ix : x + ix, pixel);
2507                 else
2508                   XPutPixel(image, xFlip ? x - ix : x + ix,
2509                             yFlip ? y - iy : y + iy, pixel);
2510               }
2511             }
2512           }
2513         }
2514
2515         // next column (line)
2516         if (xFlip)
2517           x -= dx;
2518         else
2519           x += dx;
2520       }
2521     }
2522
2523     // next line (column)
2524     if (yFlip)
2525       y -= dy;
2526     else
2527       y += dy;
2528   }
2529
2530   // blit the image into the pixmap
2531   XPutImage(display, pixmap, fillGC, image, 0, 0, x0, y0, w0, h0);
2532
2533   // free memory
2534   delete imgStr;
2535   gfree(image->data);
2536   image->data = NULL;
2537   XDestroyImage(image);
2538   gfree(errRight);
2539   gfree(errDown);
2540 }
2541
2542 Gulong XOutputDev::findColor(GfxColor *color) {
2543   int r, g, b;
2544   double gray;
2545   Gulong pixel;
2546
2547   if (trueColor) {
2548     r = xoutRound(color->getR() * rMul);
2549     g = xoutRound(color->getG() * gMul);
2550     b = xoutRound(color->getB() * bMul);
2551     pixel = ((Gulong)r << rShift) +
2552             ((Gulong)g << gShift) +
2553             ((Gulong)b << bShift);
2554   } else if (numColors == 1) {
2555     gray = color->getGray();
2556     if (gray < 0.5)
2557       pixel = colors[0];
2558     else
2559       pixel = colors[1];
2560   } else {
2561     r = xoutRound(color->getR() * (numColors - 1));
2562     g = xoutRound(color->getG() * (numColors - 1));
2563     b = xoutRound(color->getB() * (numColors - 1));
2564 #if 0 //~ this makes things worse as often as better
2565     // even a very light color shouldn't map to white
2566     if (r == numColors - 1 && g == numColors - 1 && b == numColors - 1) {
2567       if (color->getR() < 0.95)
2568         --r;
2569       if (color->getG() < 0.95)
2570         --g;
2571       if (color->getB() < 0.95)
2572         --b;
2573     }
2574 #endif
2575     pixel = colors[(r * numColors + g) * numColors + b];
2576   }
2577   return pixel;
2578 }
2579
2580 GBool XOutputDev::findText(char *s, GBool top, GBool bottom,
2581                            int *xMin, int *yMin, int *xMax, int *yMax) {
2582   double xMin1, yMin1, xMax1, yMax1;
2583   
2584   xMin1 = (double)*xMin;
2585   yMin1 = (double)*yMin;
2586   xMax1 = (double)*xMax;
2587   yMax1 = (double)*yMax;
2588   if (text->findText(s, top, bottom, &xMin1, &yMin1, &xMax1, &yMax1)) {
2589     *xMin = xoutRound(xMin1);
2590     *xMax = xoutRound(xMax1);
2591     *yMin = xoutRound(yMin1);
2592     *yMax = xoutRound(yMax1);
2593     return gTrue;
2594   }
2595   return gFalse;
2596 }
2597
2598 GString *XOutputDev::getText(int xMin, int yMin, int xMax, int yMax) {
2599   return text->getText((double)xMin, (double)yMin,
2600                        (double)xMax, (double)yMax);
2601 }