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