]> www.fi.muni.cz Git - evince.git/blob - pdf/xpdf/XOutputDev.cc
Imported Xpdf 2.03 and fixed build.
[evince.git] / pdf / xpdf / XOutputDev.cc
1 //========================================================================
2 //
3 // XOutputDev.cc
4 //
5 // Copyright 1996-2003 Glyph & Cog, LLC
6 //
7 //========================================================================
8
9 #include <aconf.h>
10
11 #ifdef USE_GCC_PRAGMAS
12 #pragma implementation
13 #endif
14
15 #include <stdio.h>
16 #include <stdlib.h>
17 #include <stddef.h>
18 #include <unistd.h>
19 #include <string.h>
20 #include <ctype.h>
21 #include <math.h>
22 #include "gmem.h"
23 #include "gfile.h"
24 #include "GString.h"
25 #include "GList.h"
26 #include "Object.h"
27 #include "Stream.h"
28 #include "Link.h"
29 #include "GfxState.h"
30 #include "GfxFont.h"
31 #include "UnicodeMap.h"
32 #include "CharCodeToUnicode.h"
33 #include "FontFile.h"
34 #include "Error.h"
35 #include "TextOutputDev.h"
36 #include "XOutputDev.h"
37 #if HAVE_T1LIB_H
38 #include "T1Font.h"
39 #endif
40 #if FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H)
41 #include "FTFont.h"
42 #endif
43 #if !FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H)
44 #include "TTFont.h"
45 #endif
46
47 #ifdef VMS
48 #if (__VMS_VER < 70000000)
49 extern "C" int unlink(char *filename);
50 #endif
51 #endif
52
53 #ifdef XlibSpecificationRelease
54 #if XlibSpecificationRelease < 5
55 typedef char *XPointer;
56 #endif
57 #else
58 typedef char *XPointer;
59 #endif
60
61 //------------------------------------------------------------------------
62 // Constants and macros
63 //------------------------------------------------------------------------
64
65 #define xoutRound(x) ((int)(x + 0.5))
66
67 #define maxCurveSplits 6        // max number of splits when recursively
68                                 //   drawing Bezier curves
69
70 //------------------------------------------------------------------------
71 // Font substitutions
72 //------------------------------------------------------------------------
73
74 struct XOutFontSubst {
75   char *name;
76   double mWidth;
77 };
78
79 // index: {symbolic:12, fixed:8, serif:4, sans-serif:0} + bold*2 + italic
80 static XOutFontSubst xOutSubstFonts[16] = {
81   {"Helvetica",             0.833},
82   {"Helvetica-Oblique",     0.833},
83   {"Helvetica-Bold",        0.889},
84   {"Helvetica-BoldOblique", 0.889},
85   {"Times-Roman",           0.788},
86   {"Times-Italic",          0.722},
87   {"Times-Bold",            0.833},
88   {"Times-BoldItalic",      0.778},
89   {"Courier",               0.600},
90   {"Courier-Oblique",       0.600},
91   {"Courier-Bold",          0.600},
92   {"Courier-BoldOblique",   0.600},
93   {"Symbol",                0.576},
94   {"Symbol",                0.576},
95   {"Symbol",                0.576},
96   {"Symbol",                0.576}
97 };
98
99 //------------------------------------------------------------------------
100
101 static void outputToFile(void *stream, char *data, int len) {
102   fwrite(data, 1, len, (FILE *)stream);
103 }
104
105 //------------------------------------------------------------------------
106 // XOutputFont
107 //------------------------------------------------------------------------
108
109 XOutputFont::XOutputFont(Ref *idA, double m11OrigA, double m12OrigA,
110                          double m21OrigA, double m22OrigA,
111                          double m11A, double m12A, double m21A, double m22A,
112                          Display *displayA, XOutputDev *xOutA) {
113   id = *idA;
114   display = displayA;
115   xOut = xOutA;
116   m11Orig = m11OrigA;
117   m12Orig = m12OrigA;
118   m21Orig = m21OrigA;
119   m22Orig = m22OrigA;
120   m11 = m11A;
121   m12 = m12A;
122   m21 = m21A;
123   m22 = m22A;
124 }
125
126 XOutputFont::~XOutputFont() {
127 }
128
129 void XOutputFont::getCharPath(GfxState *state,
130                               CharCode c, Unicode *u, int ulen) {
131 }
132
133 #if HAVE_T1LIB_H
134 //------------------------------------------------------------------------
135 // XOutputT1Font
136 //------------------------------------------------------------------------
137
138 XOutputT1Font::XOutputT1Font(Ref *idA, T1FontFile *fontFileA,
139                              double m11OrigA, double m12OrigA,
140                              double m21OrigA, double m22OrigA,
141                              double m11A, double m12A,
142                              double m21A, double m22A,
143                              Display *displayA, XOutputDev *xOutA):
144   XOutputFont(idA, m11OrigA, m12OrigA, m21OrigA, m22OrigA,
145               m11A, m12A, m21A, m22A, displayA, xOutA)
146 {
147   double matrix[4];
148
149   fontFile = fontFileA;
150
151   // create the transformed instance
152   matrix[0] = m11;
153   matrix[1] = -m12;
154   matrix[2] = m21;
155   matrix[3] = -m22;
156   font = new T1Font(fontFile, matrix);
157 }
158
159 XOutputT1Font::~XOutputT1Font() {
160   if (font) {
161     delete font;
162   }
163 }
164
165 GBool XOutputT1Font::isOk() {
166   return font != NULL;
167 }
168
169 void XOutputT1Font::updateGC(GC gc) {
170 }
171
172 void XOutputT1Font::drawChar(GfxState *state, Pixmap pixmap, int w, int h,
173                              GC gc, GfxRGB *rgb,
174                              double x, double y, double dx, double dy,
175                              CharCode c, Unicode *u, int uLen) {
176   font->drawChar(pixmap, w, h, gc, xoutRound(x), xoutRound(y),
177                  (int)(rgb->r * 65535), (int)(rgb->g * 65535),
178                  (int)(rgb->b * 65535), c, u[0]);
179 }
180
181 void XOutputT1Font::getCharPath(GfxState *state,
182                                 CharCode c, Unicode *u, int uLen) {
183   font->getCharPath(c, u[0], state);
184 }
185 #endif // HAVE_T1LIB_H
186
187 #if FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H)
188 //------------------------------------------------------------------------
189 // XOutputFTFont
190 //------------------------------------------------------------------------
191
192 XOutputFTFont::XOutputFTFont(Ref *idA, FTFontFile *fontFileA,
193                              double m11OrigA, double m12OrigA,
194                              double m21OrigA, double m22OrigA,
195                              double m11A, double m12A,
196                              double m21A, double m22A,
197                              Display *displayA, XOutputDev *xOutA):
198   XOutputFont(idA, m11OrigA, m12OrigA, m21OrigA, m22OrigA,
199               m11A, m12A, m21A, m22A, displayA, xOutA)
200 {
201   double matrix[4];
202
203   fontFile = fontFileA;
204
205   // create the transformed instance
206   matrix[0] = m11;
207   matrix[1] = -m12;
208   matrix[2] = m21;
209   matrix[3] = -m22;
210   font = new FTFont(fontFile, matrix);
211 }
212
213 XOutputFTFont::~XOutputFTFont() {
214   if (font) {
215     delete font;
216   }
217 }
218
219 GBool XOutputFTFont::isOk() {
220   return font != NULL;
221 }
222
223 void XOutputFTFont::updateGC(GC gc) {
224 }
225
226 void XOutputFTFont::drawChar(GfxState *state, Pixmap pixmap, int w, int h,
227                              GC gc, GfxRGB *rgb,
228                              double x, double y, double dx, double dy,
229                              CharCode c, Unicode *u, int uLen) {
230   font->drawChar(pixmap, w, h, gc, xoutRound(x), xoutRound(y),
231                  (int)(rgb->r * 65535), (int)(rgb->g * 65535),
232                  (int)(rgb->b * 65535), c, uLen > 0 ? u[0] : 0);
233 }
234
235 void XOutputFTFont::getCharPath(GfxState *state,
236                                 CharCode c, Unicode *u, int uLen) {
237   font->getCharPath(c, u[0], state);
238 }
239 #endif // FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H)
240
241 #if !FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H)
242 //------------------------------------------------------------------------
243 // XOutputTTFont
244 //------------------------------------------------------------------------
245
246 XOutputTTFont::XOutputTTFont(Ref *idA, TTFontFile *fontFileA,
247                              double m11OrigA, double m12OrigA,
248                              double m21OrigA, double m22OrigA,
249                              double m11A, double m12A,
250                              double m21A, double m22A,
251                              Display *displayA, XOutputDev *xOutA):
252   XOutputFont(idA, m11OrigA, m12OrigA, m21OrigA, m22OrigA,
253               m11A, m12A, m21A, m22A, displayA, xOutA)
254 {
255   double matrix[4];
256
257   fontFile = fontFileA;
258
259   // create the transformed instance
260   matrix[0] = m11;
261   matrix[1] = -m12;
262   matrix[2] = m21;
263   matrix[3] = -m22;
264   font = new TTFont(fontFile, matrix);
265 }
266
267 XOutputTTFont::~XOutputTTFont() {
268   if (font) {
269     delete font;
270   }
271 }
272
273 GBool XOutputTTFont::isOk() {
274   return font != NULL;
275 }
276
277 void XOutputTTFont::updateGC(GC gc) {
278 }
279
280 void XOutputTTFont::drawChar(GfxState *state, Pixmap pixmap, int w, int h,
281                              GC gc, GfxRGB *rgb,
282                              double x, double y, double dx, double dy,
283                              CharCode c, Unicode *u, int uLen) {
284   font->drawChar(pixmap, w, h, gc, xoutRound(x), xoutRound(y),
285                  (int)(rgb->r * 65535), (int)(rgb->g * 65535),
286                  (int)(rgb->b * 65535), c, u[0]);
287 }
288 #endif // !FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H)
289
290 //------------------------------------------------------------------------
291 // XOutputServer8BitFont
292 //------------------------------------------------------------------------
293
294 // Copy <fmt>, substituting <val> for one occurrence of "%s", into
295 // <buf>.
296 static void stringSubst(char *buf, int bufSize, char *fmt, char *val) {
297   char *p, *q;
298   int i;
299
300   i = 0;
301   p = fmt;
302   while (*p) {
303     if (p[0] == '%' && p[1] == 's') {
304       q = val;
305       while (*q && i < bufSize - 1) {
306         buf[i++] = *q++;
307       }
308       p += 2;
309     } else {
310       if (i < bufSize - 1) {
311         buf[i++] = *p;
312       }
313       ++p;
314     }
315   }
316   buf[i] = '\0';
317 }
318
319 XOutputServer8BitFont::XOutputServer8BitFont(Ref *idA, GString *xlfdFmt, 
320                                              UnicodeMap *xUMapA,
321                                              CharCodeToUnicode *fontUMap,
322                                              double m11OrigA, double m12OrigA,
323                                              double m21OrigA, double m22OrigA,
324                                              double m11A, double m12A,
325                                              double m21A, double m22A,
326                                              Display *displayA,
327                                              XOutputDev *xOutA):
328   XOutputFont(idA, m11OrigA, m12OrigA, m21OrigA, m22OrigA,
329               m11A, m12A, m21A, m22A, displayA, xOutA)
330 {
331   double size, ntm11, ntm12, ntm21, ntm22;
332   GBool rotated;
333   int startSize, sz;
334   char fontName[500], fontSize[100];
335   Unicode u;
336   char buf;
337   int i;
338
339   // compute size and normalized transform matrix
340   size = sqrt(m21*m21 + m22*m22);
341   ntm11 = m11 / size;
342   ntm12 = -m12 / size;
343   ntm21 = m21 / size;
344   ntm22 = -m22 / size;
345
346   // try to get a rotated font?
347   rotated = !(ntm11 > 0 && ntm22 > 0 &&
348               fabs(ntm11 / ntm22 - 1) < 0.2 &&
349               fabs(ntm12) < 0.01 &&
350               fabs(ntm21) < 0.01);
351
352   // open X font -- if font is not found (which means the server can't
353   // scale fonts), try progressively smaller and then larger sizes
354   startSize = (int)size;
355   if (rotated) {
356     sprintf(fontSize, "[%s%0.2f %s%0.2f %s%0.2f %s%0.2f]",
357             ntm11<0 ? "~" : "", fabs(ntm11 * size),
358             ntm12<0 ? "~" : "", fabs(ntm12 * size),
359             ntm21<0 ? "~" : "", fabs(ntm21 * size),
360             ntm22<0 ? "~" : "", fabs(ntm22 * size));
361   } else {
362     sprintf(fontSize, "%d", startSize);
363   }
364   stringSubst(fontName, sizeof(fontName), xlfdFmt->getCString(), fontSize);
365   xFont = XLoadQueryFont(display, fontName);
366   if (!xFont) {
367     for (sz = startSize; sz >= startSize/2 && sz >= 1; --sz) {
368       sprintf(fontSize, "%d", sz);
369       stringSubst(fontName, sizeof(fontName), xlfdFmt->getCString(), fontSize);
370       if ((xFont = XLoadQueryFont(display, fontName)))
371         break;
372     }
373     if (!xFont) {
374       for (sz = startSize + 1; sz < startSize + 10; ++sz) {
375         sprintf(fontSize, "%d", sz);
376         stringSubst(fontName, sizeof(fontName), xlfdFmt->getCString(),
377                     fontSize);
378         if ((xFont = XLoadQueryFont(display, fontName))) {
379           break;
380         }
381       }
382       if (!xFont) {
383         sprintf(fontSize, "%d", startSize);
384         stringSubst(fontName, sizeof(fontName), xlfdFmt->getCString(),
385                     fontSize);
386         error(-1, "Failed to open font: '%s'", fontName);
387         return;
388       }
389     }
390   }
391
392   // Construct char code map.
393   xUMap = xUMapA;
394   for (i = 0; i < 256; ++i) {
395     if (fontUMap->mapToUnicode((CID)i, &u, 1) == 1 &&
396         xUMap->mapUnicode(u, &buf, 1) == 1) {
397       map[i] = buf & 0xff;
398     } else {
399       map[i] = 0;
400     }
401   }
402 }
403
404 XOutputServer8BitFont::~XOutputServer8BitFont() {
405   if (xFont) {
406     XFreeFont(display, xFont);
407   }
408 }
409
410 GBool XOutputServer8BitFont::isOk() {
411   return xFont != NULL;
412 }
413
414 void XOutputServer8BitFont::updateGC(GC gc) {
415   XSetFont(display, gc, xFont->fid);
416 }
417
418 void XOutputServer8BitFont::drawChar(GfxState *state, Pixmap pixmap,
419                                      int w, int h, GC gc, GfxRGB *rgb,
420                                      double x, double y, double dx, double dy,
421                                      CharCode c, Unicode *u, int uLen) {
422   Gushort c1;
423   char buf[8];
424   double dx1, dy1;
425   int m, n, i, j, k;
426
427   c1 = map[c];
428   if (c1 > 0) {
429     buf[0] = (char)c1;
430     XDrawString(display, pixmap, gc, xoutRound(x), xoutRound(y), buf, 1);
431   } else {
432     // substituted character, using more than one character
433     n = 0;
434     for (i = 0; i < uLen; ++i) {
435       n += xUMap->mapUnicode(u[i], buf, sizeof(buf));
436     }
437     if (n > 0) {
438       dx1 = dx / n;
439       dy1 = dy / n;
440       k = 0;
441       for (i = 0; i < uLen; ++i) {
442         m = xUMap->mapUnicode(u[i], buf, sizeof(buf));
443         for (j = 0; j < m; ++j) {
444           XDrawString(display, pixmap, gc,
445                       xoutRound(x + k*dx1), xoutRound(y + k*dy1),
446                       buf + j, 1);
447           ++k;
448         }
449       }
450     }
451   }
452 }
453
454 //------------------------------------------------------------------------
455 // XOutputServer16BitFont
456 //------------------------------------------------------------------------
457
458 XOutputServer16BitFont::XOutputServer16BitFont(Ref *idA, GString *xlfdFmt, 
459                                                UnicodeMap *xUMapA,
460                                                CharCodeToUnicode *fontUMap,
461                                                double m11OrigA,
462                                                double m12OrigA,
463                                                double m21OrigA,
464                                                double m22OrigA,
465                                                double m11A, double m12A,
466                                                double m21A, double m22A,
467                                                Display *displayA,
468                                                XOutputDev *xOutA):
469   XOutputFont(idA, m11OrigA, m12OrigA, m21OrigA, m22OrigA,
470               m11A, m12A, m21A, m22A, displayA, xOutA)
471 {
472   double size, ntm11, ntm12, ntm21, ntm22;
473   GBool rotated;
474   int startSize, sz;
475   char fontName[500], fontSize[100];
476
477   xUMap = xUMapA;
478   xUMap->incRefCnt();
479
480   // compute size and normalized transform matrix
481   size = sqrt(m21*m21 + m22*m22);
482   ntm11 = m11 / size;
483   ntm12 = -m12 / size;
484   ntm21 = m21 / size;
485   ntm22 = -m22 / size;
486
487   // try to get a rotated font?
488   rotated = !(ntm11 > 0 && ntm22 > 0 &&
489               fabs(ntm11 / ntm22 - 1) < 0.2 &&
490               fabs(ntm12) < 0.01 &&
491               fabs(ntm21) < 0.01);
492
493   // open X font -- if font is not found (which means the server can't
494   // scale fonts), try progressively smaller and then larger sizes
495   startSize = (int)size;
496   if (rotated) {
497     sprintf(fontSize, "[%s%0.2f %s%0.2f %s%0.2f %s%0.2f]",
498             ntm11<0 ? "~" : "", fabs(ntm11 * size),
499             ntm12<0 ? "~" : "", fabs(ntm12 * size),
500             ntm21<0 ? "~" : "", fabs(ntm21 * size),
501             ntm22<0 ? "~" : "", fabs(ntm22 * size));
502   } else {
503     sprintf(fontSize, "%d", startSize);
504   }
505   stringSubst(fontName, sizeof(fontName), xlfdFmt->getCString(), fontSize);
506   xFont = XLoadQueryFont(display, fontName);
507   if (!xFont) {
508     for (sz = startSize; sz >= startSize/2 && sz >= 1; --sz) {
509       sprintf(fontSize, "%d", sz);
510       stringSubst(fontName, sizeof(fontName), xlfdFmt->getCString(), fontSize);
511       if ((xFont = XLoadQueryFont(display, fontName)))
512         break;
513     }
514     if (!xFont) {
515       for (sz = startSize + 1; sz < startSize + 10; ++sz) {
516         sprintf(fontSize, "%d", sz);
517         stringSubst(fontName, sizeof(fontName), xlfdFmt->getCString(),
518                     fontSize);
519         if ((xFont = XLoadQueryFont(display, fontName))) {
520           break;
521         }
522       }
523       if (!xFont) {
524         sprintf(fontSize, "%d", startSize);
525         stringSubst(fontName, sizeof(fontName), xlfdFmt->getCString(),
526                     fontSize);
527         error(-1, "Failed to open font: '%s'", fontName);
528         return;
529       }
530     }
531   }
532 }
533
534 XOutputServer16BitFont::~XOutputServer16BitFont() {
535   xUMap->decRefCnt();
536   if (xFont) {
537     XFreeFont(display, xFont);
538   }
539 }
540
541 GBool XOutputServer16BitFont::isOk() {
542   return xFont != NULL;
543 }
544
545 void XOutputServer16BitFont::updateGC(GC gc) {
546   XSetFont(display, gc, xFont->fid);
547 }
548
549 void XOutputServer16BitFont::drawChar(GfxState *state, Pixmap pixmap,
550                                       int w, int h, GC gc, GfxRGB *rgb,
551                                       double x, double y, double dx, double dy,
552                                       CharCode c, Unicode *u, int uLen) {
553   char buf[16];
554   XChar2b c1;
555   double dx1, dy1;
556   int m, n, i, j, k;
557
558   n = 0;
559   for (i = 0; i < uLen; ++i) {
560     n += xUMap->mapUnicode(u[i], buf, sizeof(buf));
561   }
562   if (n > 0) {
563     dx1 = dx / n;
564     dy1 = dy / n;
565     k = 0;
566     for (i = 0; i < uLen; ++i) {
567       m = xUMap->mapUnicode(u[i], buf, sizeof(buf));
568       for (j = 0; j+1 < m; j += 2) {
569         c1.byte1 = buf[j];
570         c1.byte2 = buf[j+1];
571         XDrawString16(display, pixmap, gc,
572                       xoutRound(x + k*dx1), xoutRound(y + k*dy1),
573                       &c1, 1);
574         ++k;
575       }
576     }
577   } else if (c != 0) {
578     // some PDF files use CID 0, which is .notdef, so just ignore it
579     error(-1, "Unknown character (CID=%d Unicode=%04x)",
580           c, uLen > 0 ? u[0] : (Unicode)0);
581   }
582 }
583
584 //------------------------------------------------------------------------
585 // XOutputFontCache
586 //------------------------------------------------------------------------
587
588 #if HAVE_T1LIB_H
589 XOutputT1FontFile::~XOutputT1FontFile() {
590   delete fontFile;
591   if (tmpFileName) {
592     unlink(tmpFileName->getCString());
593     delete tmpFileName;
594   }
595 }
596 #endif
597
598 #if FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H)
599 XOutputFTFontFile::~XOutputFTFontFile() {
600   delete fontFile;
601   if (tmpFileName) {
602     unlink(tmpFileName->getCString());
603     delete tmpFileName;
604   }
605 }
606 #endif
607
608 #if !FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H)
609 XOutputTTFontFile::~XOutputTTFontFile() {
610   delete fontFile;
611   if (tmpFileName) {
612     unlink(tmpFileName->getCString());
613     delete tmpFileName;
614   }
615 }
616 #endif
617
618 XOutputFontCache::XOutputFontCache(Display *displayA, Guint depthA,
619                                    XOutputDev *xOutA,
620                                    FontRastControl t1libControlA,
621                                    FontRastControl freetypeControlA) {
622   display = displayA;
623   depth = depthA;
624   xOut = xOutA;
625
626 #if HAVE_T1LIB_H
627   t1libControl = t1libControlA;
628   t1Engine = NULL;
629   t1FontFiles = NULL;
630 #endif
631
632 #if FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H)
633   freetypeControl = freetypeControlA;
634   ftEngine = NULL;
635   ftFontFiles = NULL;
636 #endif
637 #if !FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H)
638   freetypeControl = freetypeControlA;
639   ttEngine = NULL;
640   ttFontFiles = NULL;
641 #endif
642
643   clear();
644 }
645
646 XOutputFontCache::~XOutputFontCache() {
647   delFonts();
648 }
649
650 void XOutputFontCache::startDoc(int screenNum, Visual *visual,
651                                 Colormap colormap, GBool trueColor,
652                                 int rMul, int gMul, int bMul,
653                                 int rShift, int gShift, int bShift,
654                                 Gulong *colors, int numColors) {
655   delFonts();
656   clear();
657
658 #if HAVE_T1LIB_H
659   if (t1libControl != fontRastNone) {
660     t1Engine = new T1FontEngine(display, visual, depth, colormap,
661                                 t1libControl == fontRastAALow ||
662                                   t1libControl == fontRastAAHigh,
663                                 t1libControl == fontRastAAHigh);
664     if (t1Engine->isOk()) {
665       if (trueColor) {
666         t1Engine->useTrueColor(rMul, rShift, gMul, gShift, bMul, bShift);
667       } else {
668         t1Engine->useColorCube(colors, numColors);
669       }
670     } else {
671       delete t1Engine;
672       t1Engine = NULL;
673     }
674   }
675 #endif // HAVE_T1LIB_H
676
677 #if FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H)
678   if (freetypeControl != fontRastNone) {
679     ftEngine = new FTFontEngine(display, visual, depth, colormap,
680                                 freetypeControl == fontRastAALow ||
681                                   freetypeControl == fontRastAAHigh);
682     if (ftEngine->isOk()) {
683       if (trueColor) {
684         ftEngine->useTrueColor(rMul, rShift, gMul, gShift, bMul, bShift);
685       } else {
686         ftEngine->useColorCube(colors, numColors);
687       }
688     } else {
689       delete ftEngine;
690       ftEngine = NULL;
691     }
692   }
693 #endif // FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H)
694
695 #if !FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H)
696   if (freetypeControl != fontRastNone) {
697     ttEngine = new TTFontEngine(display, visual, depth, colormap,
698                                 freetypeControl == fontRastAALow ||
699                                   freetypeControl == fontRastAAHigh);
700     if (ttEngine->isOk()) {
701       if (trueColor) {
702         ttEngine->useTrueColor(rMul, rShift, gMul, gShift, bMul, bShift);
703       } else {
704         ttEngine->useColorCube(colors, numColors);
705       }
706     } else {
707       delete ttEngine;
708       ttEngine = NULL;
709     }
710   }
711 #endif // !FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H)
712 }
713
714 void XOutputFontCache::delFonts() {
715   int i;
716
717   for (i = 0; i < nFonts; ++i) {
718     delete fonts[i];
719   }
720
721 #if HAVE_T1LIB_H
722   // delete Type 1 font files
723   if (t1FontFiles) {
724     deleteGList(t1FontFiles, XOutputT1FontFile);
725     t1FontFiles = NULL;
726   }
727   if (t1Engine) {
728     delete t1Engine;
729     t1Engine = NULL;
730   }
731 #endif
732
733 #if FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H)
734   // delete FreeType font files
735   if (ftFontFiles) {
736     deleteGList(ftFontFiles, XOutputFTFontFile);
737     ftFontFiles = NULL;
738   }
739   if (ftEngine) {
740     delete ftEngine;
741     ftEngine = NULL;
742   }
743 #endif
744
745 #if !FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H)
746   // delete TrueType fonts
747   if (ttFontFiles) {
748     deleteGList(ttFontFiles, XOutputTTFontFile);
749     ttFontFiles = NULL;
750   }
751   if (ttEngine) {
752     delete ttEngine;
753     ttEngine = NULL;
754   }
755 #endif
756 }
757
758 void XOutputFontCache::clear() {
759   int i;
760
761   for (i = 0; i < xOutFontCacheSize; ++i) {
762     fonts[i] = NULL;
763   }
764   nFonts = 0;
765
766 #if HAVE_T1LIB_H
767   // clear Type 1 font files
768   t1FontFiles = new GList();
769 #endif
770
771 #if FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H)
772   // clear FreeType font cache
773   ftFontFiles = new GList();
774 #endif
775
776 #if !FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H)
777   // clear TrueType font cache
778   ttFontFiles = new GList();
779 #endif
780 }
781
782 XOutputFont *XOutputFontCache::getFont(XRef *xref, GfxFont *gfxFont,
783                                        double m11, double m12,
784                                        double m21, double m22) {
785   XOutputFont *font;
786   DisplayFontParam *dfp;
787   GString *substName;
788   double m11New, m12New, m21New, m22New;
789   double w1, w2, v;
790   double *fm;
791   char *name;
792   int index;
793   int code;
794   int i, j;
795
796   // is it the most recently used font?
797   if (nFonts > 0 && fonts[0]->matches(gfxFont->getID(), m11, m12, m21, m22)) {
798     return fonts[0];
799   }
800
801   // is it in the cache?
802   for (i = 1; i < nFonts; ++i) {
803     if (fonts[i]->matches(gfxFont->getID(), m11, m12, m21, m22)) {
804       font = fonts[i];
805       for (j = i; j > 0; --j) {
806         fonts[j] = fonts[j-1];
807       }
808       fonts[0] = font;
809       return font;
810     }
811   }
812
813   // try for a cached FontFile, an embedded font, or an external font
814   // file
815   font = NULL;
816   switch (gfxFont->getType()) {
817   case fontType1:
818   case fontType1C:
819 #if HAVE_T1LIB_H
820     if (t1libControl != fontRastNone) {
821       font = tryGetT1Font(xref, gfxFont, m11, m12, m21, m22);
822     }
823 #endif
824 #if FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H)
825     if (!font) {
826       if (freetypeControl != fontRastNone) {
827         font = tryGetFTFont(xref, gfxFont, m11, m12, m21, m22);
828       }
829     }
830 #endif
831     break;
832   case fontTrueType:
833   case fontCIDType2:
834 #if FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H)
835     if (freetypeControl != fontRastNone) {
836       font = tryGetFTFont(xref, gfxFont, m11, m12, m21, m22);
837     }
838 #endif
839 #if !FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H)
840     if (freetypeControl != fontRastNone) {
841       font = tryGetTTFont(xref, gfxFont, m11, m12, m21, m22);
842     }
843 #endif
844     break;
845   case fontCIDType0:
846   case fontCIDType0C:
847 #if FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H)
848     if (freetypeControl != fontRastNone) {
849       font = tryGetFTFont(xref, gfxFont, m11, m12, m21, m22);
850     }
851 #endif
852     break;
853   default:
854     break;
855   }
856
857   if (!font) {
858
859     // search for a display font mapping
860     dfp = NULL;
861     if (gfxFont->isCIDFont()) {
862       if (((GfxCIDFont *)gfxFont)->getCollection()) {
863         dfp = globalParams->
864                 getDisplayCIDFont(gfxFont->getName(),
865                                   ((GfxCIDFont *)gfxFont)->getCollection());
866       } else {
867         // this error (no CMap file) was already reported by GfxFont
868         return NULL;
869       }
870     } else {
871       if (gfxFont->getName()) {
872         dfp = globalParams->getDisplayFont(gfxFont->getName());
873       }
874     }
875     if (dfp) {
876       font = tryGetFont(xref, dfp, gfxFont, m11, m12, m21, m22,
877                         m11, m12, m21, m22, gFalse);
878     }
879
880     // substitute a font (8-bit fonts only)
881     if (!font && !gfxFont->isCIDFont()) {
882
883       // choose a substitute font
884       if (gfxFont->isFixedWidth()) {
885         index = 8;
886       } else if (gfxFont->isSerif()) {
887         index = 4;
888       } else {
889         index = 0;
890       }
891       if (gfxFont->isBold()) {
892         index += 2;
893       }
894       if (gfxFont->isItalic()) {
895         index += 1;
896       }
897       substName = new GString(xOutSubstFonts[index].name);
898
899       // adjust the font matrix -- compare the width of 'm' in the
900       // original font and the substituted font
901       m11New = m11;
902       m12New = m12;
903       m21New = m21;
904       m22New = m22;
905       for (code = 0; code < 256; ++code) {
906         if ((name = ((Gfx8BitFont *)gfxFont)->getCharName(code)) &&
907             name[0] == 'm' && name[1] == '\0') {
908           break;
909         }
910       }
911       if (code < 256) {
912         w1 = ((Gfx8BitFont *)gfxFont)->getWidth(code);
913         w2 = xOutSubstFonts[index].mWidth;
914         if (gfxFont->getType() == fontType3) {
915           // This is a hack which makes it possible to substitute for some
916           // Type 3 fonts.  The problem is that it's impossible to know what
917           // the base coordinate system used in the font is without actually
918           // rendering the font.  This code tries to guess by looking at the
919           // width of the character 'm' (which breaks if the font is a
920           // subset that doesn't contain 'm').
921           if (w1 > 0 && (w1 > 1.1 * w2 || w1 < 0.9 * w2)) {
922             w1 /= w2;
923             m11New *= w1;
924             m12New *= w1;
925             m21New *= w1;
926             m22New *= w1;
927           }
928           fm = gfxFont->getFontMatrix();
929           v = (fm[0] == 0) ? 1 : (fm[3] / fm[0]);
930           m21New *= v;
931           m22New *= v;
932         } else if (!gfxFont->isSymbolic()) {
933           // if real font is substantially narrower than substituted
934           // font, reduce the font size accordingly
935           if (w1 > 0.01 && w1 < 0.9 * w2) {
936             w1 /= w2;
937             m11New *= w1;
938             m21New *= w1;
939           }
940         }
941       }
942
943       // get the font
944       dfp = globalParams->getDisplayFont(substName);
945       delete substName;
946       if (!dfp) {
947         // this should never happen since GlobalParams sets up default
948         // mappings for the Base-14 fonts
949         error(-1, "Couldn't find a font for '%s'",
950               gfxFont->getName()->getCString());
951         return NULL;
952       }
953       font = tryGetFont(xref, dfp, gfxFont, m11, m12, m21, m22,
954                         m11New, m12New, m21New, m22New, gTrue);
955     }
956   }
957
958   // check for error
959   if (!font) {
960     // This will happen if the user specifies a bogus font in the
961     // config file (a non-existent font file or a font that requires a
962     // rasterizer that is disabled or wasn't built in), or if a CID
963     // font has no associated font in the config file.
964     if (gfxFont->isCIDFont()) {
965       error(-1, "Couldn't find a font for the '%s' character collection",
966             ((GfxCIDFont *)gfxFont)->getCollection()->getCString());
967     } else {
968       error(-1, "Couldn't find a font for '%s'",
969             gfxFont->getName() ?
970                 gfxFont->getName()->getCString() : "[unnamed]");
971     }
972     return NULL;
973   }
974
975   // insert font in cache
976   if (nFonts == xOutFontCacheSize) {
977     --nFonts;
978     delete fonts[nFonts];
979   }
980   for (j = nFonts; j > 0; --j) {
981     fonts[j] = fonts[j-1];
982   }
983   fonts[0] = font;
984   ++nFonts;
985
986   return font;
987 }
988
989 XOutputFont *XOutputFontCache::tryGetFont(XRef *xref, DisplayFontParam *dfp,
990                                           GfxFont *gfxFont,
991                                           double m11Orig, double m12Orig,
992                                           double m21Orig, double m22Orig,
993                                           double m11, double m12,
994                                           double m21, double m22,
995                                           GBool subst) {
996   XOutputFont *font;
997
998   font = NULL;
999
1000   // create the new font
1001   switch (dfp->kind) {
1002
1003   case displayFontX:
1004     font = tryGetServerFont(dfp->x.xlfd, dfp->x.encoding, gfxFont,
1005                             m11Orig, m12Orig, m21Orig, m22Orig,
1006                             m11, m12, m21, m22);
1007     break;
1008
1009   case displayFontT1:
1010 #if HAVE_T1LIB_H
1011     if (t1libControl != fontRastNone && !gfxFont->isCIDFont()) {
1012       font = tryGetT1FontFromFile(xref, dfp->t1.fileName, gFalse, gfxFont,
1013                                   m11Orig, m12Orig, m21Orig, m22Orig,
1014                                   m11, m12, m21, m22, subst);
1015     }
1016 #endif
1017 #if FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H)
1018     if (!font) {
1019       if (freetypeControl != fontRastNone) {
1020         font = tryGetFTFontFromFile(xref, dfp->t1.fileName, gFalse, gfxFont,
1021                                     m11Orig, m12Orig, m21Orig, m22Orig,
1022                                     m11, m12, m21, m22, gFalse, subst);
1023       }
1024     }
1025 #endif
1026 #if !((FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H)) || defined(HAVE_T1LIB_H))
1027     error(-1, "Config file specifies a Type 1 font,");
1028     error(-1, "but xpdf was not built with t1lib or FreeType2 support");
1029 #endif
1030     break;
1031
1032   case displayFontTT:
1033 #if FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H)
1034     if (freetypeControl != fontRastNone) {
1035       font = tryGetFTFontFromFile(xref, dfp->tt.fileName, gFalse, gfxFont,
1036                                   m11Orig, m12Orig, m21Orig, m22Orig,
1037                                   m11, m12, m21, m22, gFalse, subst);
1038     }
1039 #endif
1040 #if !FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H)
1041     if (freetypeControl != fontRastNone) {
1042       font = tryGetTTFontFromFile(xref, dfp->tt.fileName, gFalse, gfxFont,
1043                                   m11Orig, m12Orig, m21Orig, m22Orig,
1044                                   m11, m12, m21, m22, subst);
1045     }
1046 #endif
1047 #if !(HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H)
1048     error(-1, "Config file specifies a TrueType font,");
1049     error(-1, "but xpdf was not built with FreeType support");
1050     dfp = NULL;
1051 #endif
1052     break;
1053   }
1054
1055   return font;
1056 }
1057
1058 #if HAVE_T1LIB_H
1059 XOutputFont *XOutputFontCache::tryGetT1Font(XRef *xref,
1060                                             GfxFont *gfxFont,
1061                                             double m11, double m12,
1062                                             double m21, double m22) {
1063   Ref *id;
1064   XOutputT1FontFile *xFontFile;
1065   XOutputFont *font;
1066   Ref embRef;
1067   GString *fileName;
1068   FILE *f;
1069   char *fontBuf;
1070   int fontLen;
1071   Type1CFontFile *ff;
1072   Object refObj, strObj;
1073   int c;
1074   int i;
1075
1076   // check the already available font files
1077   id = gfxFont->getID();
1078   for (i = 0; i < t1FontFiles->getLength(); ++i) {
1079     xFontFile = (XOutputT1FontFile *)t1FontFiles->get(i);
1080     if (xFontFile->num == id->num && xFontFile->gen == id->gen &&
1081         !xFontFile->subst) {
1082       font = new XOutputT1Font(id, xFontFile->fontFile,
1083                                m11, m12, m21, m22,
1084                                m11, m12, m21, m22, display, xOut);
1085       if (!font->isOk()) {
1086         delete font;
1087         return NULL;
1088       }
1089       return font;
1090     }
1091   }
1092
1093   // check for an embedded font
1094   if (gfxFont->getEmbeddedFontID(&embRef)) {
1095
1096     // create the font file
1097     fileName = NULL;
1098     if (!openTempFile(&fileName, &f, "wb", NULL)) {
1099       error(-1, "Couldn't create temporary Type 1 font file");
1100       return NULL;
1101     }
1102     if (gfxFont->getType() == fontType1C) {
1103       if (!(fontBuf = gfxFont->readEmbFontFile(xref, &fontLen))) {
1104         fclose(f);
1105         unlink(fileName->getCString());
1106         delete fileName;
1107         return NULL;
1108       }
1109       ff = new Type1CFontFile(fontBuf, fontLen);
1110       if (!ff->isOk()) {
1111         delete ff;
1112         gfree(fontBuf);
1113         unlink(fileName->getCString());
1114         delete fileName;
1115         return NULL;
1116       }
1117       ff->convertToType1(outputToFile, f);
1118       delete ff;
1119       gfree(fontBuf);
1120     } else { // fontType1
1121       refObj.initRef(embRef.num, embRef.gen);
1122       refObj.fetch(xref, &strObj);
1123       refObj.free();
1124       strObj.streamReset();
1125       while ((c = strObj.streamGetChar()) != EOF) {
1126         fputc(c, f);
1127       }
1128       strObj.streamClose();
1129       strObj.free();
1130     }
1131     fclose(f);
1132
1133     // create the Font
1134     font = tryGetT1FontFromFile(xref, fileName, gTrue, gfxFont,
1135                                 m11, m12, m21, m22,
1136                                 m11, m12, m21, m22, gFalse);
1137
1138     // on systems with Unix hard link semantics, this will remove the
1139     // last link to the temp file
1140     unlink(fileName->getCString());
1141
1142     delete fileName;
1143
1144   // check for an external font file
1145   } else if ((fileName = gfxFont->getExtFontFile())) {
1146     font = tryGetT1FontFromFile(xref, fileName, gFalse, gfxFont,
1147                                 m11, m12, m21, m22,
1148                                 m11, m12, m21, m22, gFalse);
1149
1150   } else {
1151     font = NULL;
1152   }
1153
1154   return font;
1155 }
1156
1157 XOutputFont *XOutputFontCache::tryGetT1FontFromFile(XRef *xref,
1158                                                     GString *fileName,
1159                                                     GBool deleteFile,
1160                                                     GfxFont *gfxFont,
1161                                                     double m11Orig,
1162                                                     double m12Orig,
1163                                                     double m21Orig,
1164                                                     double m22Orig,
1165                                                     double m11, double m12,
1166                                                     double m21, double m22,
1167                                                     GBool subst) {
1168   Ref *id;
1169   T1FontFile *fontFile;
1170   XOutputFont *font;
1171
1172   // create the t1lib font file
1173   fontFile = new T1FontFile(t1Engine, fileName->getCString(),
1174                             ((Gfx8BitFont *)gfxFont)->getEncoding(),
1175                             gfxFont->getFontBBox());
1176   if (!fontFile->isOk()) {
1177     error(-1, "Couldn't create t1lib font from '%s'",
1178           fileName->getCString());
1179     delete fontFile;
1180     if (deleteFile) {
1181       unlink(fileName->getCString());
1182     }
1183     return NULL;
1184   }
1185
1186   // add to list
1187   id = gfxFont->getID();
1188   t1FontFiles->append(new XOutputT1FontFile(id->num, id->gen,
1189                                             subst, fontFile,
1190                                             deleteFile ? fileName->copy()
1191                                                        : (GString *)NULL));
1192
1193   // create the Font
1194   font = new XOutputT1Font(gfxFont->getID(), fontFile,
1195                            m11Orig, m12Orig, m21Orig, m22Orig,
1196                            m11, m12, m21, m22, display, xOut);
1197   if (!font->isOk()) {
1198     delete font;
1199     return NULL;
1200   }
1201   return font;
1202 }
1203 #endif // HAVE_T1LIB_H
1204
1205 #if FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H)
1206 XOutputFont *XOutputFontCache::tryGetFTFont(XRef *xref,
1207                                             GfxFont *gfxFont,
1208                                             double m11, double m12,
1209                                             double m21, double m22) {
1210   Ref *id;
1211   XOutputFTFontFile *xFontFile;
1212   XOutputFont *font;
1213   Ref embRef;
1214   GString *fileName;
1215   FILE *f;
1216 #if 1 //~ need this until FT can handle fonts with missing tables
1217   char *fontBuf;
1218   int fontLen;
1219   TrueTypeFontFile *ff;
1220 #endif
1221   Object refObj, strObj;
1222   int c;
1223   int i;
1224
1225   // check the already available font files
1226   id = gfxFont->getID();
1227   for (i = 0; i < ftFontFiles->getLength(); ++i) {
1228     xFontFile = (XOutputFTFontFile *)ftFontFiles->get(i);
1229     if (xFontFile->num == id->num && xFontFile->gen == id->gen &&
1230         !xFontFile->subst) {
1231       font = new XOutputFTFont(id, xFontFile->fontFile,
1232                                m11, m12, m21, m22,
1233                                m11, m12, m21, m22, display, xOut);
1234       if (!font->isOk()) {
1235         delete font;
1236         return NULL;
1237       }
1238       return font;
1239     }
1240   }
1241
1242   // check for an embedded font
1243   if (gfxFont->getEmbeddedFontID(&embRef)) {
1244
1245     // create the font file
1246     fileName = NULL;
1247     if (!openTempFile(&fileName, &f, "wb", NULL)) {
1248       error(-1, "Couldn't create temporary TrueType font file");
1249       return NULL;
1250     }
1251 #if 1 //~ need this until FT can handle fonts with missing tables
1252     if (gfxFont->getType() == fontTrueType ||
1253         gfxFont->getType() == fontCIDType2) {
1254       if (!(fontBuf = gfxFont->readEmbFontFile(xref, &fontLen))) {
1255         fclose(f);
1256         unlink(fileName->getCString());
1257         delete fileName;
1258         return NULL;
1259       }
1260       ff = new TrueTypeFontFile(fontBuf, fontLen);
1261       ff->writeTTF(f);
1262       delete ff;
1263       gfree(fontBuf);
1264     } else {
1265       refObj.initRef(embRef.num, embRef.gen);
1266       refObj.fetch(xref, &strObj);
1267       refObj.free();
1268       strObj.streamReset();
1269       while ((c = strObj.streamGetChar()) != EOF) {
1270         fputc(c, f);
1271       }
1272       strObj.streamClose();
1273       strObj.free();
1274     }
1275 #else
1276     refObj.initRef(embRef.num, embRef.gen);
1277     refObj.fetch(xref, &strObj);
1278     refObj.free();
1279     strObj.streamReset();
1280     while ((c = strObj.streamGetChar()) != EOF) {
1281       fputc(c, f);
1282     }
1283     strObj.streamClose();
1284     strObj.free();
1285 #endif
1286     fclose(f);
1287
1288     // create the Font
1289     font = tryGetFTFontFromFile(xref, fileName, gTrue, gfxFont,
1290                                 m11, m12, m21, m22,
1291                                 m11, m12, m21, m22, gTrue, gFalse);
1292
1293     // on systems with Unix hard link semantics, this will remove the
1294     // last link to the temp file
1295     unlink(fileName->getCString());
1296
1297     delete fileName;
1298
1299   // check for an external font file
1300   } else if ((fileName = gfxFont->getExtFontFile())) {
1301     font = tryGetFTFontFromFile(xref, fileName, gFalse, gfxFont,
1302                                 m11, m12, m21, m22,
1303                                 m11, m12, m21, m22, gFalse, gFalse);
1304
1305   } else {
1306     font = NULL;
1307   }
1308
1309   return font;
1310 }
1311
1312 XOutputFont *XOutputFontCache::tryGetFTFontFromFile(XRef *xref,
1313                                                     GString *fileName,
1314                                                     GBool deleteFile,
1315                                                     GfxFont *gfxFont,
1316                                                     double m11Orig,
1317                                                     double m12Orig,
1318                                                     double m21Orig,
1319                                                     double m22Orig,
1320                                                     double m11, double m12,
1321                                                     double m21, double m22,
1322                                                     GBool embedded,
1323                                                     GBool subst) {
1324   Ref *id;
1325   FTFontFile *fontFile;
1326   XOutputFont *font;
1327   char *buf;
1328   int len;
1329   FILE *f;
1330   TrueTypeFontFile *ff;
1331   Gushort *codeToGID;
1332
1333   // create the FreeType font file
1334   if (gfxFont->isCIDFont()) {
1335     if (gfxFont->getType() == fontCIDType2) {
1336       fontFile = new FTFontFile(ftEngine, fileName->getCString(),
1337                                 ((GfxCIDFont *)gfxFont)->getCIDToGID(),
1338                                 ((GfxCIDFont *)gfxFont)->getCIDToGIDLen(),
1339                                 embedded);
1340     } else { // fontCIDType0, fontCIDType0C
1341       fontFile = new FTFontFile(ftEngine, fileName->getCString(), embedded);
1342     }
1343   } else {
1344     if (!(f = fopen(fileName->getCString(), "rb"))) {
1345       return NULL;
1346     }
1347     fseek(f, 0, SEEK_END);
1348     len = (int)ftell(f);
1349     fseek(f, 0, SEEK_SET);
1350     buf = (char *)gmalloc(len);
1351     if ((int)fread(buf, 1, len, f) != len) {
1352       gfree(buf);
1353       fclose(f);
1354       return NULL;
1355     }
1356     fclose(f);
1357     ff = new TrueTypeFontFile(buf, len);
1358     codeToGID = ((Gfx8BitFont *)gfxFont)->getCodeToGIDMap(ff);
1359     fontFile = new FTFontFile(ftEngine, fileName->getCString(),
1360                               ((Gfx8BitFont *)gfxFont)->getEncoding(),
1361                               codeToGID);
1362     gfree(codeToGID);
1363     delete ff;
1364     gfree(buf);
1365   }
1366   if (!fontFile->isOk()) {
1367     error(-1, "Couldn't create FreeType font from '%s'",
1368           fileName->getCString());
1369     delete fontFile;
1370     if (deleteFile) {
1371       unlink(fileName->getCString());
1372     }
1373     return NULL;
1374   }
1375
1376   // add to list
1377   id = gfxFont->getID();
1378   ftFontFiles->append(new XOutputFTFontFile(id->num, id->gen,
1379                                             subst, fontFile,
1380                                             deleteFile ? fileName->copy()
1381                                                        : (GString *)NULL));
1382
1383   // create the Font
1384   font = new XOutputFTFont(gfxFont->getID(), fontFile,
1385                            m11Orig, m12Orig, m21Orig, m22Orig,
1386                            m11, m12, m21, m22, display, xOut);
1387   if (!font->isOk()) {
1388     delete font;
1389     return NULL;
1390   }
1391   return font;
1392 }
1393 #endif // FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H)
1394
1395 #if !FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H)
1396 XOutputFont *XOutputFontCache::tryGetTTFont(XRef *xref,
1397                                             GfxFont *gfxFont,
1398                                             double m11, double m12,
1399                                             double m21, double m22) {
1400   Ref *id;
1401   XOutputTTFontFile *xFontFile;
1402   XOutputFont *font;
1403   Ref embRef;
1404   GString *fileName;
1405   FILE *f;
1406   Object refObj, strObj;
1407   int c;
1408   int i;
1409
1410   // check the already available font files
1411   id = gfxFont->getID();
1412   xFontFile = NULL;
1413   for (i = 0; i < ttFontFiles->getLength(); ++i) {
1414     xFontFile = (XOutputTTFontFile *)ttFontFiles->get(i);
1415     if (xFontFile->num == id->num && xFontFile->gen == id->gen &&
1416         !xFontFile->subst) {
1417       font = new XOutputTTFont(id, xFontFile->fontFile,
1418                                m11, m12, m21, m22,
1419                                m11, m12, m21, m22, display, xOut);
1420       if (!font->isOk()) {
1421         delete font;
1422         return NULL;
1423       }
1424       return font;
1425     }
1426   }
1427
1428   // check for an embedded font
1429   if (gfxFont->getEmbeddedFontID(&embRef)) {
1430
1431     // create the font file
1432     fileName = NULL;
1433     if (!openTempFile(&fileName, &f, "wb", NULL)) {
1434       error(-1, "Couldn't create temporary TrueType font file");
1435       return NULL;
1436     }
1437     refObj.initRef(embRef.num, embRef.gen);
1438     refObj.fetch(xref, &strObj);
1439     refObj.free();
1440     strObj.streamReset();
1441     while ((c = strObj.streamGetChar()) != EOF) {
1442       fputc(c, f);
1443     }
1444     strObj.streamClose();
1445     strObj.free();
1446     fclose(f);
1447
1448     // create the Font
1449     font = tryGetTTFontFromFile(xref, fileName, gTrue, gfxFont,
1450                                 m11, m12, m21, m22,
1451                                 m11, m12, m21, m22, gFalse);
1452
1453     // on systems with Unix hard link semantics, this will remove the
1454     // last link to the temp file
1455     unlink(fileName->getCString());
1456
1457     delete fileName;
1458
1459   } else if ((fileName = gfxFont->getExtFontFile())) {
1460     font = tryGetTTFontFromFile(xref, fileName, gFalse, gfxFont,
1461                                 m11, m12, m21, m22,
1462                                 m11, m12, m21, m22, gFalse);
1463
1464   } else {
1465     font = NULL;
1466   }
1467
1468   return font;
1469 }
1470
1471 XOutputFont *XOutputFontCache::tryGetTTFontFromFile(XRef *xref,
1472                                                     GString *fileName,
1473                                                     GBool deleteFile,
1474                                                     GfxFont *gfxFont,
1475                                                     double m11Orig,
1476                                                     double m12Orig,
1477                                                     double m21Orig,
1478                                                     double m22Orig,
1479                                                     double m11, double m12,
1480                                                     double m21, double m22,
1481                                                     GBool subst) {
1482   Ref *id;
1483   TTFontFile *fontFile;
1484   XOutputFont *font;
1485
1486   // create the FreeType font file
1487   if (gfxFont->isCIDFont()) {
1488     // fontCIDType2
1489     fontFile = new TTFontFile(ttEngine, fileName->getCString(),
1490                               ((GfxCIDFont *)gfxFont)->getCIDToGID(),
1491                               ((GfxCIDFont *)gfxFont)->getCIDToGIDLen());
1492   } else {
1493     fontFile = new TTFontFile(ttEngine, fileName->getCString(),
1494                               ((Gfx8BitFont *)gfxFont)->getEncoding(),
1495                               ((Gfx8BitFont *)gfxFont)->getHasEncoding());
1496   }
1497   if (!fontFile->isOk()) {
1498     error(-1, "Couldn't create FreeType font from '%s'",
1499           fileName->getCString());
1500     delete fontFile;
1501     if (deleteFile) {
1502       unlink(fileName->getCString());
1503     }
1504     return NULL;
1505   }
1506
1507   // add to list
1508   id = gfxFont->getID();
1509   ttFontFiles->append(new XOutputTTFontFile(id->num, id->gen,
1510                                             subst, fontFile,
1511                                             deleteFile ? fileName->copy()
1512                                                        : (GString *)NULL));
1513
1514   // create the Font
1515   font = new XOutputTTFont(gfxFont->getID(), fontFile,
1516                            m11Orig, m12Orig, m21Orig, m22Orig,
1517                            m11, m12, m21, m22, display, xOut);
1518   if (!font->isOk()) {
1519     delete font;
1520     return NULL;
1521   }
1522   return font;
1523 }
1524 #endif // !FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H)
1525
1526 XOutputFont *XOutputFontCache::tryGetServerFont(GString *xlfd,
1527                                                 GString *encodingName,
1528                                                 GfxFont *gfxFont,
1529                                                 double m11Orig, double m12Orig,
1530                                                 double m21Orig, double m22Orig,
1531                                                 double m11, double m12,
1532                                                 double m21, double m22) {
1533   XOutputFont *font;
1534   UnicodeMap *uMap;
1535   CharCodeToUnicode *ctu;
1536
1537   uMap = globalParams->getUnicodeMap(encodingName);
1538   if (gfxFont->isCIDFont()) {
1539     ctu = ((GfxCIDFont *)gfxFont)->getToUnicode();
1540     font = new XOutputServer16BitFont(gfxFont->getID(), xlfd, uMap, ctu,
1541                                       m11Orig, m12Orig, m21Orig, m22Orig,
1542                                       m11, m12, m21, m22,
1543                                       display, xOut);
1544     ctu->decRefCnt();
1545   } else {
1546     ctu = ((Gfx8BitFont *)gfxFont)->getToUnicode();
1547     font = new XOutputServer8BitFont(gfxFont->getID(), xlfd, uMap, ctu,
1548                                      m11Orig, m12Orig, m21Orig, m22Orig,
1549                                      m11, m12, m21, m22,
1550                                      display, xOut);
1551     ctu->decRefCnt();
1552   }
1553   uMap->decRefCnt();
1554   if (!font->isOk()) {
1555     delete font;
1556     return NULL;
1557   }
1558   return font;
1559 }
1560
1561 //------------------------------------------------------------------------
1562 // T3FontCache
1563 //------------------------------------------------------------------------
1564
1565 struct T3FontCacheTag {
1566   Gushort code;
1567   Gushort mru;                  // valid bit (0x8000) and MRU index
1568   double wx, wy;                // untransformed glyph metrics
1569 };
1570
1571 class T3FontCache {
1572 public:
1573
1574   T3FontCache(Ref *fontID, double m11A, double m12A,
1575               double m21A, double m22A,
1576               int glyphXA, int glyphYA, int glyphWA, int glyphHA,
1577               Display *displayA, Visual *visual, Guint depth,
1578               Pixmap origPixmap);
1579   ~T3FontCache();
1580   GBool matches(Ref *idA, double m11A, double m12A,
1581                 double m21A, double m22A)
1582     { return fontID.num == idA->num && fontID.gen == idA->gen &&
1583              m11 == m11A && m12 == m12A && m21 == m21A && m22 == m22A; }
1584
1585   Ref fontID;                   // PDF font ID
1586   double m11, m12, m21, m22;    // transform matrix
1587   int glyphX, glyphY;           // pixel offset of glyph pixmaps
1588   int glyphW, glyphH;           // size of glyph pixmaps, in pixels
1589   int glyphSize;                // size of glyph pixmaps, in bytes
1590   int cacheSets;                // number of sets in cache
1591   int cacheAssoc;               // cache associativity (glyphs per set)
1592   Guchar *cacheData;            // glyph pixmap cache
1593   T3FontCacheTag *cacheTags;    // cache tags, i.e., char codes
1594   Display *display;
1595   Pixmap pixmap;
1596   XImage *image;
1597 };
1598
1599 T3FontCache::T3FontCache(Ref *fontIDA, double m11A, double m12A,
1600                          double m21A, double m22A,
1601                          int glyphXA, int glyphYA, int glyphWA, int glyphHA,
1602                          Display *displayA, Visual *visual, Guint depth,
1603                          Pixmap origPixmap) {
1604   int i;
1605
1606   fontID = *fontIDA;
1607   m11 = m11A;
1608   m12 = m12A;
1609   m21 = m21A;
1610   m22 = m22A;
1611   glyphX = glyphXA;
1612   glyphY = glyphYA;
1613   glyphW = glyphWA;
1614   glyphH = glyphHA;
1615   glyphSize = glyphW * glyphH;
1616   cacheAssoc = 8;
1617   if (glyphSize <= 256) {
1618     cacheSets = 8;
1619   } else if (glyphSize <= 512) {
1620     cacheSets = 4;
1621   } else if (glyphSize <= 1024) {
1622     cacheSets = 2;
1623   } else {
1624     cacheSets = 1;
1625   }
1626   cacheData = (Guchar *)gmalloc(cacheSets * cacheAssoc * glyphSize);
1627   cacheTags = (T3FontCacheTag *)gmalloc(cacheSets * cacheAssoc *
1628                                         sizeof(T3FontCacheTag));
1629   for (i = 0; i < cacheSets * cacheAssoc; ++i) {
1630     cacheTags[i].mru = i & (cacheAssoc - 1);
1631   }
1632   display = displayA;
1633   pixmap = XCreatePixmap(display, origPixmap, glyphW, glyphH, depth);
1634   image = XCreateImage(display, visual, depth, ZPixmap, 0, NULL,
1635                        glyphW, glyphH, 8, 0);
1636   image->data = (char *)gmalloc(glyphH * image->bytes_per_line);
1637 }
1638
1639 T3FontCache::~T3FontCache() {
1640   gfree(cacheData);
1641   gfree(cacheTags);
1642   XFreePixmap(display, pixmap);
1643   gfree(image->data);
1644   image->data = NULL;
1645   XDestroyImage(image);
1646 }
1647
1648 struct T3GlyphStack {
1649   GBool cacheable;
1650   Gushort code;
1651   T3FontCache *cache;
1652   int cacheIdx;
1653   T3FontCacheTag *cacheTag;
1654   Guchar *cacheData;
1655   double x, y;
1656   Unicode *u;
1657   int uLen;
1658   GfxRGB color;
1659   int origPixmapW, origPixmapH;
1660   Pixmap origPixmap;
1661   GC origStrokeGC;
1662   GC origFillGC;
1663   Region origClipRegion;
1664   double origCTM4, origCTM5;
1665   double wx, wy;                // untransformed glyph metrics
1666   T3GlyphStack *next;
1667 };
1668
1669 //------------------------------------------------------------------------
1670 // XOutputDev
1671 //------------------------------------------------------------------------
1672
1673 XOutputDev::XOutputDev(Display *displayA, int screenNumA,
1674                        Visual *visualA, Colormap colormapA,
1675                        GBool reverseVideoA, unsigned long paperColorA,
1676                        GBool installCmap, int rgbCubeSize,
1677                        int forceDepth) {
1678   XVisualInfo visualTempl;
1679   XVisualInfo *visualList;
1680   int nVisuals;
1681   Gulong mask;
1682   XColor xcolor;
1683   XColor *xcolors;
1684   int r, g, b, n, m;
1685   GBool ok;
1686
1687   // no document yet
1688   xref = NULL;
1689
1690   // display / screen / visual / colormap
1691   display = displayA;
1692   screenNum = screenNumA;
1693   visual = visualA;
1694   colormap = colormapA;
1695
1696   // no pixmap yet
1697   pixmapW = pixmapH = 0;
1698
1699   // check for TrueColor visual
1700   if (forceDepth != 0) {
1701     depth = forceDepth;
1702     trueColor = depth >= 16;
1703   } else {
1704     visualTempl.visualid = XVisualIDFromVisual(visual);
1705     visualList = XGetVisualInfo(display, VisualIDMask,
1706                                 &visualTempl, &nVisuals);
1707     if (nVisuals < 1) {
1708       // this shouldn't happen
1709       XFree((XPointer)visualList);
1710       visualList = XGetVisualInfo(display, VisualNoMask, &visualTempl,
1711                                   &nVisuals);
1712     }
1713     depth = visualList->depth;
1714     if (visualList->c_class == TrueColor) {
1715       trueColor = gTrue;
1716       for (mask = visualList->red_mask, rShift = 0;
1717            mask && !(mask & 1);
1718            mask >>= 1, ++rShift) ;
1719       rMul = (int)mask;
1720       for (mask = visualList->green_mask, gShift = 0;
1721            mask && !(mask & 1);
1722            mask >>= 1, ++gShift) ;
1723       gMul = (int)mask;
1724       for (mask = visualList->blue_mask, bShift = 0;
1725            mask && !(mask & 1);
1726            mask >>= 1, ++bShift) ;
1727       bMul = (int)mask;
1728     } else {
1729       trueColor = gFalse;
1730     }
1731     XFree((XPointer)visualList);
1732   }
1733
1734   // allocate a color cube
1735   if (!trueColor) {
1736     redMap[BlackPixel(display, screenNum) & 0xff] = 0;
1737     redMap[WhitePixel(display, screenNum) & 0xff] = 1;
1738
1739     // set colors in private colormap
1740     if (installCmap) {
1741       for (numColors = 6; numColors >= 2; --numColors) {
1742         m = numColors * numColors * numColors;
1743         if (XAllocColorCells(display, colormap, False, NULL, 0, colors, m)) {
1744           break;
1745         }
1746       }
1747       if (numColors >= 2) {
1748         m = numColors * numColors * numColors;
1749         xcolors = (XColor *)gmalloc(m * sizeof(XColor));
1750         n = 0;
1751         for (r = 0; r < numColors; ++r) {
1752           for (g = 0; g < numColors; ++g) {
1753             for (b = 0; b < numColors; ++b) {
1754               xcolors[n].pixel = colors[n];
1755               xcolors[n].red = (r * 65535) / (numColors - 1);
1756               xcolors[n].green = (g * 65535) / (numColors - 1);
1757               xcolors[n].blue = (b * 65535) / (numColors - 1);
1758               xcolors[n].flags = DoRed | DoGreen | DoBlue;
1759               redMap[xcolors[n].pixel & 0xff] = xcolors[n].red / 65535.0;
1760               ++n;
1761             }
1762           }
1763         }
1764         XStoreColors(display, colormap, xcolors, m);
1765         gfree(xcolors);
1766       } else {
1767         numColors = 1;
1768         colors[0] = BlackPixel(display, screenNum);
1769         colors[1] = WhitePixel(display, screenNum);
1770       }
1771
1772     // allocate colors in shared colormap
1773     } else {
1774       if (rgbCubeSize > maxRGBCube) {
1775         rgbCubeSize = maxRGBCube;
1776       }
1777       ok = gFalse;
1778       for (numColors = rgbCubeSize; numColors >= 2; --numColors) {
1779         ok = gTrue;
1780         n = 0;
1781         for (r = 0; r < numColors && ok; ++r) {
1782           for (g = 0; g < numColors && ok; ++g) {
1783             for (b = 0; b < numColors && ok; ++b) {
1784               if (n == 0) {
1785                 colors[n] = BlackPixel(display, screenNum);
1786                 redMap[colors[n] & 0xff] = 0;
1787                 ++n;
1788               } else {
1789                 xcolor.red = (r * 65535) / (numColors - 1);
1790                 xcolor.green = (g * 65535) / (numColors - 1);
1791                 xcolor.blue = (b * 65535) / (numColors - 1);
1792                 if (XAllocColor(display, colormap, &xcolor)) {
1793                   colors[n++] = xcolor.pixel;
1794                   redMap[xcolor.pixel & 0xff] = xcolor.red / 65535.0;
1795                 } else {
1796                   ok = gFalse;
1797                 }
1798               }
1799             }
1800           }
1801         }
1802         if (ok) {
1803           break;
1804         }
1805         XFreeColors(display, colormap, &colors[1], n-1, 0);
1806       }
1807       if (!ok) {
1808         numColors = 1;
1809         colors[0] = BlackPixel(display, screenNum);
1810         colors[1] = WhitePixel(display, screenNum);
1811       }
1812     }
1813   }
1814
1815   // misc parameters
1816   reverseVideo = reverseVideoA;
1817   paperColor = paperColorA;
1818
1819   // set up the font cache and fonts
1820   gfxFont = NULL;
1821   font = NULL;
1822   needFontUpdate = gFalse;
1823   fontCache = new XOutputFontCache(display, depth, this,
1824                                    globalParams->getT1libControl(),
1825                                    globalParams->getFreeTypeControl());
1826   nT3Fonts = 0;
1827   t3GlyphStack = NULL;
1828
1829   // no text outline clipping path
1830   textClipPath = NULL;
1831
1832   // empty state stack
1833   save = NULL;
1834
1835   // create text object
1836   text = new TextPage(gFalse);
1837 }
1838
1839 XOutputDev::~XOutputDev() {
1840   int i;
1841
1842   delete fontCache;
1843   for (i = 0; i < nT3Fonts; ++i) {
1844     delete t3FontCache[i];
1845   }
1846   delete text;
1847 }
1848
1849 void XOutputDev::startDoc(XRef *xrefA) {
1850   int i;
1851
1852   xref = xrefA;
1853   fontCache->startDoc(screenNum, visual, colormap, trueColor, rMul, gMul, bMul,
1854                       rShift, gShift, bShift, colors, numColors);
1855   for (i = 0; i < nT3Fonts; ++i) {
1856     delete t3FontCache[i];
1857   }
1858   nT3Fonts = 0;
1859 }
1860
1861 void XOutputDev::startPage(int pageNum, GfxState *state) {
1862   XGCValues gcValues;
1863   XRectangle rect;
1864
1865   // default line flatness
1866   flatness = 0;
1867
1868   // allocate GCs
1869   gcValues.foreground = BlackPixel(display, screenNum);
1870   gcValues.background = WhitePixel(display, screenNum);
1871   gcValues.line_width = 0;
1872   gcValues.line_style = LineSolid;
1873   strokeGC = XCreateGC(display, pixmap,
1874                        GCForeground | GCBackground | GCLineWidth | GCLineStyle,
1875                        &gcValues);
1876   fillGC = XCreateGC(display, pixmap,
1877                      GCForeground | GCBackground | GCLineWidth | GCLineStyle,
1878                      &gcValues);
1879   gcValues.foreground = paperColor;
1880   paperGC = XCreateGC(display, pixmap,
1881                       GCForeground | GCBackground | GCLineWidth | GCLineStyle,
1882                       &gcValues);
1883
1884   // initialize clip region
1885   clipRegion = XCreateRegion();
1886   rect.x = rect.y = 0;
1887   rect.width = pixmapW;
1888   rect.height = pixmapH;
1889   XUnionRectWithRegion(&rect, clipRegion, clipRegion);
1890   XSetRegion(display, strokeGC, clipRegion);
1891   XSetRegion(display, fillGC, clipRegion);
1892
1893   // clear font
1894   gfxFont = NULL;
1895   font = NULL;
1896
1897   // clear window
1898   XFillRectangle(display, pixmap, paperGC, 0, 0, pixmapW, pixmapH);
1899
1900   // clear text object
1901   text->startPage(state);
1902 }
1903
1904 void XOutputDev::endPage() {
1905   XOutputState *s;
1906
1907   text->coalesce(gTrue);
1908
1909   // clear state stack, free all GCs, free the clip region
1910   while (save) {
1911     s = save;
1912     save = save->next;
1913     XFreeGC(display, s->strokeGC);
1914     XFreeGC(display, s->fillGC);
1915     XDestroyRegion(s->clipRegion);
1916     delete s;
1917   }
1918   XFreeGC(display, strokeGC);
1919   XFreeGC(display, fillGC);
1920   XFreeGC(display, paperGC);
1921   XDestroyRegion(clipRegion);
1922 }
1923
1924 void XOutputDev::drawLink(Link *link, Catalog *catalog) {
1925   double x1, y1, x2, y2;
1926   LinkBorderStyle *borderStyle;
1927   GfxRGB rgb;
1928   double *dash;
1929   char dashList[20];
1930   int dashLength;
1931   XPoint points[5];
1932   int x, y, i;
1933
1934   link->getRect(&x1, &y1, &x2, &y2);
1935   borderStyle = link->getBorderStyle();
1936   if (borderStyle->getWidth() > 0) {
1937     borderStyle->getColor(&rgb.r, &rgb.g, &rgb.b);
1938     XSetForeground(display, strokeGC, findColor(&rgb));
1939     borderStyle->getDash(&dash, &dashLength);
1940     if (borderStyle->getType() == linkBorderDashed && dashLength > 0) {
1941       if (dashLength > 20) {
1942         dashLength = 20;
1943       }
1944       for (i = 0; i < dashLength; ++i) {
1945         if ((dashList[i] = xoutRound(dash[i])) == 0) {
1946           dashList[i] = 1;
1947         }
1948       }
1949       XSetLineAttributes(display, strokeGC, xoutRound(borderStyle->getWidth()),
1950                          LineOnOffDash, CapButt, JoinMiter);
1951       XSetDashes(display, strokeGC, 0, dashList, dashLength);
1952     } else {
1953       XSetLineAttributes(display, strokeGC, xoutRound(borderStyle->getWidth()),
1954                          LineSolid, CapButt, JoinMiter);
1955     }
1956     if (borderStyle->getType() == linkBorderUnderlined) {
1957       cvtUserToDev(x1, y1, &x, &y);
1958       points[0].x = x;
1959       points[0].y = y;
1960       cvtUserToDev(x2, y1, &x, &y);
1961       points[1].x = x;
1962       points[1].y = y;
1963       XDrawLine(display, pixmap, strokeGC, points[0].x, points[0].y,
1964                 points[1].x, points[1].y);
1965     } else {
1966       cvtUserToDev(x1, y1, &x, &y);
1967       points[0].x = points[4].x = x;
1968       points[0].y = points[4].y = y;
1969       cvtUserToDev(x2, y1, &x, &y);
1970       points[1].x = x;
1971       points[1].y = y;
1972       cvtUserToDev(x2, y2, &x, &y);
1973       points[2].x = x;
1974       points[2].y = y;
1975       cvtUserToDev(x1, y2, &x, &y);
1976       points[3].x = x;
1977       points[3].y = y;
1978       XDrawLines(display, pixmap, strokeGC, points, 5, CoordModeOrigin);
1979     }
1980   }
1981 }
1982
1983 void XOutputDev::saveState(GfxState *state) {
1984   XOutputState *s;
1985   XGCValues values;
1986
1987   // save current state
1988   s = new XOutputState;
1989   s->strokeGC = strokeGC;
1990   s->fillGC = fillGC;
1991   s->clipRegion = clipRegion;
1992
1993   // push onto state stack
1994   s->next = save;
1995   save = s;
1996
1997   // create a new current state by copying
1998   strokeGC = XCreateGC(display, pixmap, 0, &values);
1999   XCopyGC(display, s->strokeGC, 0xffffffff, strokeGC);
2000   fillGC = XCreateGC(display, pixmap, 0, &values);
2001   XCopyGC(display, s->fillGC, 0xffffffff, fillGC);
2002   clipRegion = XCreateRegion();
2003   XUnionRegion(s->clipRegion, clipRegion, clipRegion);
2004   XSetRegion(display, strokeGC, clipRegion);
2005   XSetRegion(display, fillGC, clipRegion);
2006 }
2007
2008 void XOutputDev::restoreState(GfxState *state) {
2009   XOutputState *s;
2010
2011   if (save) {
2012     // kill current state
2013     XFreeGC(display, strokeGC);
2014     XFreeGC(display, fillGC);
2015     XDestroyRegion(clipRegion);
2016
2017     // restore state
2018     flatness = state->getFlatness();
2019     strokeGC = save->strokeGC;
2020     fillGC = save->fillGC;
2021     clipRegion = save->clipRegion;
2022     XSetRegion(display, strokeGC, clipRegion);
2023     XSetRegion(display, fillGC, clipRegion);
2024
2025     // pop state stack
2026     s = save;
2027     save = save->next;
2028     delete s;
2029
2030     // we'll need to restore the font
2031     needFontUpdate = gTrue;
2032   }
2033 }
2034
2035 void XOutputDev::updateAll(GfxState *state) {
2036   updateLineAttrs(state, gTrue);
2037   updateFlatness(state);
2038   updateMiterLimit(state);
2039   updateFillColor(state);
2040   updateStrokeColor(state);
2041   needFontUpdate = gTrue;
2042 }
2043
2044 void XOutputDev::updateCTM(GfxState *state, double m11, double m12,
2045                            double m21, double m22, double m31, double m32) {
2046   updateLineAttrs(state, gTrue);
2047 }
2048
2049 void XOutputDev::updateLineDash(GfxState *state) {
2050   updateLineAttrs(state, gTrue);
2051 }
2052
2053 void XOutputDev::updateFlatness(GfxState *state) {
2054   flatness = state->getFlatness();
2055 }
2056
2057 void XOutputDev::updateLineJoin(GfxState *state) {
2058   updateLineAttrs(state, gFalse);
2059 }
2060
2061 void XOutputDev::updateLineCap(GfxState *state) {
2062   updateLineAttrs(state, gFalse);
2063 }
2064
2065 // unimplemented
2066 void XOutputDev::updateMiterLimit(GfxState *state) {
2067 }
2068
2069 void XOutputDev::updateLineWidth(GfxState *state) {
2070   updateLineAttrs(state, gFalse);
2071 }
2072
2073 void XOutputDev::updateLineAttrs(GfxState *state, GBool updateDash) {
2074   double width;
2075   int cap, join;
2076   double *dashPattern;
2077   int dashLength;
2078   double dashStart;
2079   char dashList[20];
2080   int i;
2081
2082   width = state->getTransformedLineWidth();
2083   switch (state->getLineCap()) {
2084   case 0: cap = CapButt; break;
2085   case 1: cap = CapRound; break;
2086   case 2: cap = CapProjecting; break;
2087   default:
2088     error(-1, "Bad line cap style (%d)", state->getLineCap());
2089     cap = CapButt;
2090     break;
2091   }
2092   switch (state->getLineJoin()) {
2093   case 0: join = JoinMiter; break;
2094   case 1: join = JoinRound; break;
2095   case 2: join = JoinBevel; break;
2096   default:
2097     error(-1, "Bad line join style (%d)", state->getLineJoin());
2098     join = JoinMiter;
2099     break;
2100   }
2101   state->getLineDash(&dashPattern, &dashLength, &dashStart);
2102 #if 1 //~ work around a bug in XFree86 (???)
2103   if (dashLength > 0 && cap == CapProjecting) {
2104     cap = CapButt;
2105   }
2106 #endif
2107   XSetLineAttributes(display, strokeGC, xoutRound(width),
2108                      dashLength > 0 ? LineOnOffDash : LineSolid,
2109                      cap, join);
2110   if (updateDash && dashLength > 0) {
2111     if (dashLength > 20)
2112       dashLength = 20;
2113     for (i = 0; i < dashLength; ++i) {
2114       dashList[i] = xoutRound(state->transformWidth(dashPattern[i]));
2115       if (dashList[i] == 0)
2116         dashList[i] = 1;
2117     }
2118     XSetDashes(display, strokeGC, xoutRound(dashStart), dashList, dashLength);
2119   }
2120 }
2121
2122 void XOutputDev::updateFillColor(GfxState *state) {
2123   GfxRGB rgb;
2124
2125   state->getFillRGB(&rgb);
2126   if (reverseVideo) {
2127     rgb.r = 1 - rgb.r;
2128     rgb.g = 1 - rgb.g;
2129     rgb.b = 1 - rgb.b;
2130   }
2131   XSetForeground(display, fillGC, findColor(&rgb));
2132 }
2133
2134 void XOutputDev::updateStrokeColor(GfxState *state) {
2135   GfxRGB rgb;
2136
2137   state->getStrokeRGB(&rgb);
2138   if (reverseVideo) {
2139     rgb.r = 1 - rgb.r;
2140     rgb.g = 1 - rgb.g;
2141     rgb.b = 1 - rgb.b;
2142   }
2143   XSetForeground(display, strokeGC, findColor(&rgb));
2144 }
2145
2146 void XOutputDev::updateFont(GfxState *state) {
2147   double m11, m12, m21, m22;
2148
2149   needFontUpdate = gFalse;
2150
2151   text->updateFont(state);
2152
2153   if (!(gfxFont = state->getFont())) {
2154     font = NULL;
2155     return;
2156   }
2157   if (gfxFont->getType() == fontType3) {
2158     font = NULL;
2159     return;
2160   }
2161   state->getFontTransMat(&m11, &m12, &m21, &m22);
2162   m11 *= state->getHorizScaling();
2163   m12 *= state->getHorizScaling();
2164   font = fontCache->getFont(xref, gfxFont, m11, m12, m21, m22);
2165   if (font) {
2166     font->updateGC(fillGC);
2167     font->updateGC(strokeGC);
2168   }
2169 }
2170
2171 void XOutputDev::stroke(GfxState *state) {
2172   XPoint *points;
2173   int *lengths;
2174   int n, size, numPoints, i, j;
2175
2176   // transform points
2177   n = convertPath(state, state->getPath(),
2178                   &points, &size, &numPoints, &lengths, gFalse);
2179
2180   // draw each subpath
2181   j = 0;
2182   for (i = 0; i < n; ++i) {
2183     XDrawLines(display, pixmap, strokeGC, points + j, lengths[i],
2184                CoordModeOrigin);
2185     j += lengths[i];
2186   }
2187
2188   // free points and lengths arrays
2189   if (points != tmpPoints)
2190     gfree(points);
2191   if (lengths != tmpLengths)
2192     gfree(lengths);
2193 }
2194
2195 void XOutputDev::fill(GfxState *state) {
2196   doFill(state, WindingRule);
2197 }
2198
2199 void XOutputDev::eoFill(GfxState *state) {
2200   doFill(state, EvenOddRule);
2201 }
2202
2203 //
2204 //  X doesn't color the pixels on the right-most and bottom-most
2205 //  borders of a polygon.  This means that one-pixel-thick polygons
2206 //  are not colored at all.  I think this is supposed to be a
2207 //  feature, but I can't figure out why.  So after it fills a
2208 //  polygon, it also draws lines around the border.  This is done
2209 //  only for single-component polygons, since it's not very
2210 //  compatible with the compound polygon kludge (see convertPath()).
2211 //
2212 void XOutputDev::doFill(GfxState *state, int rule) {
2213   XPoint *points;
2214   int *lengths;
2215   int n, size, numPoints, i, j;
2216
2217   // set fill rule
2218   XSetFillRule(display, fillGC, rule);
2219
2220   // transform points, build separate polygons
2221   n = convertPath(state, state->getPath(),
2222                   &points, &size, &numPoints, &lengths, gTrue);
2223
2224   // fill them
2225   j = 0;
2226   for (i = 0; i < n; ++i) {
2227     XFillPolygon(display, pixmap, fillGC, points + j, lengths[i],
2228                  Complex, CoordModeOrigin);
2229     if (state->getPath()->getNumSubpaths() == 1) {
2230       XDrawLines(display, pixmap, fillGC, points + j, lengths[i],
2231                  CoordModeOrigin);
2232     }
2233     j += lengths[i] + 1;
2234   }
2235
2236   // free points and lengths arrays
2237   if (points != tmpPoints)
2238     gfree(points);
2239   if (lengths != tmpLengths)
2240     gfree(lengths);
2241 }
2242
2243 void XOutputDev::clip(GfxState *state) {
2244   doClip(state, state->getPath(), WindingRule);
2245 }
2246
2247 void XOutputDev::eoClip(GfxState *state) {
2248   doClip(state, state->getPath(), EvenOddRule);
2249 }
2250
2251 void XOutputDev::doClip(GfxState *state, GfxPath *path, int rule) {
2252   GfxSubpath *subpath;
2253   Region region, region2;
2254   XPoint rect[5];
2255   XPoint *points;
2256   int *lengths;
2257   double x0, y0, x1, y1, x2, y2, x3, y3;
2258   GBool gotRect;
2259   int n, size, numPoints, i, j;
2260
2261   // special case for rectangular clipping paths -- this is a common
2262   // case, and we want to make sure not to clip an extra pixel on the
2263   // right and bottom edges due to the difference between the PDF and
2264   // X rendering models
2265   gotRect = gFalse;
2266   if (path->getNumSubpaths() == 1) {
2267     subpath = path->getSubpath(0);
2268     if ((subpath->isClosed() && subpath->getNumPoints() == 5) ||
2269         (!subpath->isClosed() && subpath->getNumPoints() == 4)) {
2270       state->transform(subpath->getX(0), subpath->getY(0), &x0, &y0);
2271       state->transform(subpath->getX(1), subpath->getY(1), &x1, &y1);
2272       state->transform(subpath->getX(2), subpath->getY(2), &x2, &y2);
2273       state->transform(subpath->getX(3), subpath->getY(3), &x3, &y3);
2274       if (fabs(x0-x1) < 1 && fabs(x2-x3) < 1 &&
2275           fabs(y0-y3) < 1 && fabs(y1-y2) < 1) {
2276         if (x0 < x2) {
2277           rect[0].x = rect[1].x = rect[4].x = (int)floor(x0);
2278           rect[2].x = rect[3].x = (int)floor(x2) + 1;
2279         } else {
2280           rect[0].x = rect[1].x = rect[4].x = (int)floor(x0) + 1;
2281           rect[2].x = rect[3].x = (int)floor(x2);
2282         }
2283         if (y0 < y1) {
2284           rect[0].y = rect[3].y = rect[4].y = (int)floor(y0);
2285           rect[1].y = rect[2].y = (int)floor(y1) + 1;
2286         } else {
2287           rect[0].y = rect[3].y = rect[4].y = (int)floor(y0) + 1;
2288           rect[1].y = rect[2].y = (int)floor(y1);
2289         }
2290         gotRect = gTrue;
2291       } else if (fabs(x0-x3) < 1 && fabs(x1-x2) < 1 &&
2292                  fabs(y0-y1) < 1 && fabs(y2-y3) < 1) {
2293         if (x0 < x1) {
2294           rect[0].x = rect[3].x = rect[4].x = (int)floor(x0);
2295           rect[1].x = rect[2].x = (int)floor(x1) + 1;
2296         } else {
2297           rect[0].x = rect[3].x = rect[4].x = (int)floor(x0) + 1;
2298           rect[1].x = rect[2].x = (int)floor(x1);
2299         }
2300         if (y0 < y2) {
2301           rect[0].y = rect[1].y = rect[4].y = (int)floor(y0);
2302           rect[2].y = rect[3].y = (int)floor(y2) + 1;
2303         } else {
2304           rect[0].y = rect[1].y = rect[4].y = (int)floor(y0) + 1;
2305           rect[2].y = rect[3].y = (int)floor(y2);
2306         }
2307         gotRect = gTrue;
2308       }
2309     }
2310   }
2311
2312   if (gotRect) {
2313     region = XPolygonRegion(rect, 5, EvenOddRule);
2314
2315   } else {
2316     // transform points, build separate polygons
2317     n = convertPath(state, path, &points, &size, &numPoints, &lengths, gTrue);
2318
2319     // construct union of subpath regions
2320     // (XPolygonRegion chokes if there aren't at least three points --
2321     // this happens if the PDF file does moveto/closepath/clip, which
2322     // sets an empty clipping region)
2323     if (lengths[0] > 2) {
2324       region = XPolygonRegion(points, lengths[0], rule);
2325     } else {
2326       region = XCreateRegion();
2327     }
2328     j = lengths[0] + 1;
2329     for (i = 1; i < n; ++i) {
2330       if (lengths[i] > 2) {
2331         region2 = XPolygonRegion(points + j, lengths[i], rule);
2332       } else {
2333         region2 = XCreateRegion();
2334       }
2335       XUnionRegion(region2, region, region);
2336       XDestroyRegion(region2);
2337       j += lengths[i] + 1;
2338     }
2339
2340     // free points and lengths arrays
2341     if (points != tmpPoints) {
2342       gfree(points);
2343     }
2344     if (lengths != tmpLengths) {
2345       gfree(lengths);
2346     }
2347   }
2348
2349   // intersect region with clipping region
2350   XIntersectRegion(region, clipRegion, clipRegion);
2351   XDestroyRegion(region);
2352   XSetRegion(display, strokeGC, clipRegion);
2353   XSetRegion(display, fillGC, clipRegion);
2354 }
2355
2356 //
2357 // Transform points in the path and convert curves to line segments.
2358 // Builds a set of subpaths and returns the number of subpaths.
2359 // If <fillHack> is set, close any unclosed subpaths and activate a
2360 // kludge for polygon fills:  First, it divides up the subpaths into
2361 // non-overlapping polygons by simply comparing bounding rectangles.
2362 // Then it connects subaths within a single compound polygon to a single
2363 // point so that X can fill the polygon (sort of).
2364 //
2365 int XOutputDev::convertPath(GfxState *state, GfxPath *path,
2366                             XPoint **points, int *size,
2367                             int *numPoints, int **lengths, GBool fillHack) {
2368   BoundingRect *rects;
2369   BoundingRect rect;
2370   int n, i, ii, j, k, k0;
2371
2372   // get path and number of subpaths
2373   n = path->getNumSubpaths();
2374
2375   // allocate lengths array
2376   if (n < numTmpSubpaths)
2377     *lengths = tmpLengths;
2378   else
2379     *lengths = (int *)gmalloc(n * sizeof(int));
2380
2381   // allocate bounding rectangles array
2382   if (fillHack) {
2383     if (n < numTmpSubpaths)
2384       rects = tmpRects;
2385     else
2386       rects = (BoundingRect *)gmalloc(n * sizeof(BoundingRect));
2387   } else {
2388     rects = NULL;
2389   }
2390
2391   // do each subpath
2392   *points = tmpPoints;
2393   *size = numTmpPoints;
2394   *numPoints = 0;
2395   for (i = 0; i < n; ++i) {
2396
2397     // transform the points
2398     j = *numPoints;
2399     convertSubpath(state, path->getSubpath(i), points, size, numPoints);
2400
2401     // construct bounding rectangle
2402     if (fillHack) {
2403       rects[i].xMin = rects[i].xMax = (*points)[j].x;
2404       rects[i].yMin = rects[i].yMax = (*points)[j].y;
2405       for (k = j + 1; k < *numPoints; ++k) {
2406         if ((*points)[k].x < rects[i].xMin)
2407           rects[i].xMin = (*points)[k].x;
2408         else if ((*points)[k].x > rects[i].xMax)
2409           rects[i].xMax = (*points)[k].x;
2410         if ((*points)[k].y < rects[i].yMin)
2411           rects[i].yMin = (*points)[k].y;
2412         else if ((*points)[k].y > rects[i].yMax)
2413           rects[i].yMax = (*points)[k].y;
2414       }
2415     }
2416
2417     // close subpath if necessary
2418     if (fillHack && ((*points)[*numPoints-1].x != (*points)[j].x ||
2419                      (*points)[*numPoints-1].y != (*points)[j].y)) {
2420       addPoint(points, size, numPoints, (*points)[j].x, (*points)[j].y);
2421     }
2422
2423     // length of this subpath
2424     (*lengths)[i] = *numPoints - j;
2425
2426     // leave an extra point for compound fill hack
2427     if (fillHack)
2428       addPoint(points, size, numPoints, 0, 0);
2429   }
2430
2431   // kludge: munge any points that are *way* out of bounds - these can
2432   // crash certain (buggy) X servers
2433   for (i = 0; i < *numPoints; ++i) {
2434     if ((*points)[i].x < -4 * pixmapW) {
2435       (*points)[i].x = -4 * pixmapW;
2436     } else if ((*points)[i].x > 4 * pixmapW) {
2437       (*points)[i].x = 4 * pixmapW;
2438     }
2439     if ((*points)[i].y < -pixmapH) {
2440       (*points)[i].y = -4 * pixmapH;
2441     } else if ((*points)[i].y > 4 * pixmapH) {
2442       (*points)[i].y = 4 * pixmapH;
2443     }
2444   }
2445
2446   // combine compound polygons
2447   if (fillHack) {
2448     i = j = k = 0;
2449     while (i < n) {
2450
2451       // start with subpath i
2452       rect = rects[i];
2453       (*lengths)[j] = (*lengths)[i];
2454       k0 = k;
2455       (*points)[k + (*lengths)[i]] = (*points)[k0];
2456       k += (*lengths)[i] + 1;
2457       ++i;
2458
2459       // combine overlapping polygons
2460       do {
2461
2462         // look for the first subsequent subpath, if any, which overlaps
2463         for (ii = i; ii < n; ++ii) {
2464           if (rects[ii].xMax > rects[i].xMin &&
2465               rects[ii].xMin < rects[i].xMax &&
2466               rects[ii].yMax > rects[i].yMin &&
2467               rects[ii].yMin < rects[i].yMax) {
2468             break;
2469           }
2470         }
2471
2472         // if there is an overlap, combine the polygons
2473         if (ii < n) {
2474           for (; i <= ii; ++i) {
2475             if (rects[i].xMin < rect.xMin)
2476               rect.xMin = rects[j].xMin;
2477             if (rects[i].xMax > rect.xMax)
2478               rect.xMax = rects[j].xMax;
2479             if (rects[i].yMin < rect.yMin)
2480               rect.yMin = rects[j].yMin;
2481             if (rects[i].yMax > rect.yMax)
2482               rect.yMax = rects[j].yMax;
2483             (*lengths)[j] += (*lengths)[i] + 1;
2484             (*points)[k + (*lengths)[i]] = (*points)[k0];
2485             k += (*lengths)[i] + 1;
2486           }
2487         }
2488       } while (ii < n && i < n);
2489
2490       ++j;
2491     }
2492
2493     // free bounding rectangles
2494     if (rects != tmpRects)
2495       gfree(rects);
2496
2497     n = j;
2498   }
2499
2500   return n;
2501 }
2502
2503 //
2504 // Transform points in a single subpath and convert curves to line
2505 // segments.
2506 //
2507 void XOutputDev::convertSubpath(GfxState *state, GfxSubpath *subpath,
2508                                 XPoint **points, int *size, int *n) {
2509   double x0, y0, x1, y1, x2, y2, x3, y3;
2510   int m, i;
2511
2512   m = subpath->getNumPoints();
2513   i = 0;
2514   while (i < m) {
2515     if (i >= 1 && subpath->getCurve(i)) {
2516       state->transform(subpath->getX(i-1), subpath->getY(i-1), &x0, &y0);
2517       state->transform(subpath->getX(i), subpath->getY(i), &x1, &y1);
2518       state->transform(subpath->getX(i+1), subpath->getY(i+1), &x2, &y2);
2519       state->transform(subpath->getX(i+2), subpath->getY(i+2), &x3, &y3);
2520       doCurve(points, size, n, x0, y0, x1, y1, x2, y2, x3, y3);
2521       i += 3;
2522     } else {
2523       state->transform(subpath->getX(i), subpath->getY(i), &x1, &y1);
2524       addPoint(points, size, n, xoutRound(x1), xoutRound(y1));
2525       ++i;
2526     }
2527   }
2528 }
2529
2530 //
2531 // Subdivide a Bezier curve.  This uses floating point to avoid
2532 // propagating rounding errors.  (The curves look noticeably more
2533 // jagged with integer arithmetic.)
2534 //
2535 void XOutputDev::doCurve(XPoint **points, int *size, int *n,
2536                          double x0, double y0, double x1, double y1,
2537                          double x2, double y2, double x3, double y3) {
2538   double x[(1<<maxCurveSplits)+1][3];
2539   double y[(1<<maxCurveSplits)+1][3];
2540   int next[1<<maxCurveSplits];
2541   int p1, p2, p3;
2542   double xx1, yy1, xx2, yy2;
2543   double dx, dy, mx, my, d1, d2;
2544   double xl0, yl0, xl1, yl1, xl2, yl2;
2545   double xr0, yr0, xr1, yr1, xr2, yr2, xr3, yr3;
2546   double xh, yh;
2547   double flat;
2548
2549   flat = (double)(flatness * flatness);
2550   if (flat < 1)
2551     flat = 1;
2552
2553   // initial segment
2554   p1 = 0;
2555   p2 = 1<<maxCurveSplits;
2556   x[p1][0] = x0;  y[p1][0] = y0;
2557   x[p1][1] = x1;  y[p1][1] = y1;
2558   x[p1][2] = x2;  y[p1][2] = y2;
2559   x[p2][0] = x3;  y[p2][0] = y3;
2560   next[p1] = p2;
2561
2562   while (p1 < (1<<maxCurveSplits)) {
2563
2564     // get next segment
2565     xl0 = x[p1][0];  yl0 = y[p1][0];
2566     xx1 = x[p1][1];  yy1 = y[p1][1];
2567     xx2 = x[p1][2];  yy2 = y[p1][2];
2568     p2 = next[p1];
2569     xr3 = x[p2][0];  yr3 = y[p2][0];
2570
2571     // compute distances from control points to midpoint of the
2572     // straight line (this is a bit of a hack, but it's much faster
2573     // than computing the actual distances to the line)
2574     mx = (xl0 + xr3) * 0.5;
2575     my = (yl0 + yr3) * 0.5;
2576     dx = xx1 - mx;
2577     dy = yy1 - my;
2578     d1 = dx*dx + dy*dy;
2579     dx = xx2 - mx;
2580     dy = yy2 - my;
2581     d2 = dx*dx + dy*dy;
2582
2583     // if curve is flat enough, or no more divisions allowed then
2584     // add the straight line segment
2585     if (p2 - p1 <= 1 || (d1 <= flat && d2 <= flat)) {
2586       addPoint(points, size, n, xoutRound(xr3), xoutRound(yr3));
2587       p1 = p2;
2588
2589     // otherwise, subdivide the curve
2590     } else {
2591       xl1 = (xl0 + xx1) * 0.5;
2592       yl1 = (yl0 + yy1) * 0.5;
2593       xh = (xx1 + xx2) * 0.5;
2594       yh = (yy1 + yy2) * 0.5;
2595       xl2 = (xl1 + xh) * 0.5;
2596       yl2 = (yl1 + yh) * 0.5;
2597       xr2 = (xx2 + xr3) * 0.5;
2598       yr2 = (yy2 + yr3) * 0.5;
2599       xr1 = (xh + xr2) * 0.5;
2600       yr1 = (yh + yr2) * 0.5;
2601       xr0 = (xl2 + xr1) * 0.5;
2602       yr0 = (yl2 + yr1) * 0.5;
2603
2604       // add the new subdivision points
2605       p3 = (p1 + p2) / 2;
2606       x[p1][1] = xl1;  y[p1][1] = yl1;
2607       x[p1][2] = xl2;  y[p1][2] = yl2;
2608       next[p1] = p3;
2609       x[p3][0] = xr0;  y[p3][0] = yr0;
2610       x[p3][1] = xr1;  y[p3][1] = yr1;
2611       x[p3][2] = xr2;  y[p3][2] = yr2;
2612       next[p3] = p2;
2613     }
2614   }
2615 }
2616
2617 //
2618 // Add a point to the points array.  (This would use a generic resizable
2619 // array type if C++ supported parameterized types in some reasonable
2620 // way -- templates are a disgusting kludge.)
2621 //
2622 void XOutputDev::addPoint(XPoint **points, int *size, int *k, int x, int y) {
2623   if (*k >= *size) {
2624     *size += 32;
2625     if (*points == tmpPoints) {
2626       *points = (XPoint *)gmalloc(*size * sizeof(XPoint));
2627       memcpy(*points, tmpPoints, *k * sizeof(XPoint));
2628     } else {
2629       *points = (XPoint *)grealloc(*points, *size * sizeof(XPoint));
2630     }
2631   }
2632   (*points)[*k].x = x;
2633   (*points)[*k].y = y;
2634   ++(*k);
2635 }
2636
2637 void XOutputDev::beginString(GfxState *state, GString *s) {
2638   text->beginWord(state, state->getCurX(), state->getCurY());
2639 }
2640
2641 void XOutputDev::endString(GfxState *state) {
2642   text->endWord();
2643 }
2644
2645 void XOutputDev::drawChar(GfxState *state, double x, double y,
2646                           double dx, double dy,
2647                           double originX, double originY,
2648                           CharCode code, Unicode *u, int uLen) {
2649   int render;
2650   double x1, y1, dx1, dy1;
2651   GfxRGB rgb;
2652   double saveCurX, saveCurY;
2653   double *ctm;
2654   double saveCTM[6];
2655
2656   if (needFontUpdate) {
2657     updateFont(state);
2658   }
2659
2660   text->addChar(state, x, y, dx, dy, code, u, uLen);
2661
2662   if (!font) {
2663     return;
2664   }
2665
2666   // check for invisible text -- this is used by Acrobat Capture
2667   render = state->getRender();
2668   if (render == 3) {
2669     return;
2670   }
2671
2672   x -= originX;
2673   y -= originY;
2674   state->transform(x, y, &x1, &y1);
2675   state->transformDelta(dx, dy, &dx1, &dy1);
2676
2677   // fill
2678   if (!(render & 1)) {
2679     state->getFillRGB(&rgb);
2680     if (reverseVideo) {
2681       rgb.r = 1 - rgb.r;
2682       rgb.g = 1 - rgb.g;
2683       rgb.b = 1 - rgb.b;
2684     }
2685     font->drawChar(state, pixmap, pixmapW, pixmapH, fillGC, &rgb,
2686                    x1, y1, dx1, dy1, code, u, uLen);
2687   }
2688
2689   // stroke
2690   if ((render & 3) == 1 || (render & 3) == 2) {
2691     if (font->hasGetCharPath()) {
2692       saveCurX = state->getCurX();
2693       saveCurY = state->getCurY();
2694       ctm = state->getCTM();
2695       memcpy(saveCTM, ctm, 6 * sizeof(double));
2696       state->setCTM(1, 0, 0, 1, x1, y1);
2697       font->getCharPath(state, code, u, uLen);
2698       stroke(state);
2699       state->clearPath();
2700       state->setCTM(saveCTM[0], saveCTM[1], saveCTM[2], saveCTM[3],
2701                     saveCTM[4], saveCTM[5]);
2702       state->moveTo(saveCurX, saveCurY);
2703     } else {
2704       // can't stroke the outline, so just fill it using the stroke
2705       // color
2706       state->getStrokeRGB(&rgb);
2707       if (reverseVideo) {
2708         rgb.r = 1 - rgb.r;
2709         rgb.g = 1 - rgb.g;
2710         rgb.b = 1 - rgb.b;
2711       }
2712       font->drawChar(state, pixmap, pixmapW, pixmapH, strokeGC, &rgb,
2713                      x1, y1, dx1, dy1, code, u, uLen);
2714     }
2715   }
2716
2717   // clip
2718   if (render & 4) {
2719     if (font->hasGetCharPath()) {
2720       saveCurX = state->getCurX();
2721       saveCurY = state->getCurY();
2722       font->getCharPath(state, code, u, uLen);
2723       state->getPath()->offset(x1, y1);
2724       if (textClipPath) {
2725         textClipPath->append(state->getPath());
2726       } else {
2727         textClipPath = state->getPath()->copy();
2728       }
2729       state->clearPath();
2730       state->moveTo(saveCurX, saveCurY);
2731     }
2732   }
2733 }
2734
2735 GBool XOutputDev::beginType3Char(GfxState *state,
2736                                  CharCode code, Unicode *u, int uLen) {
2737   Ref *fontID;
2738   double *ctm, *bbox;
2739   GfxRGB color;
2740   T3FontCache *t3Font;
2741   T3GlyphStack *t3gs;
2742   double x1, y1, xMin, yMin, xMax, yMax, xt, yt;
2743   int i, j;
2744
2745   if (needFontUpdate) {
2746     updateFont(state);
2747   }
2748   if (!gfxFont) {
2749     return gFalse;
2750   }
2751   fontID = gfxFont->getID();
2752   ctm = state->getCTM();
2753   state->transform(0, 0, &xt, &yt);
2754
2755   // is it the first (MRU) font in the cache?
2756   if (!(nT3Fonts > 0 &&
2757         t3FontCache[0]->matches(fontID, ctm[0], ctm[1], ctm[2], ctm[3]))) {
2758
2759     // is the font elsewhere in the cache?
2760     for (i = 1; i < nT3Fonts; ++i) {
2761       if (t3FontCache[i]->matches(fontID, ctm[0], ctm[1], ctm[2], ctm[3])) {
2762         t3Font = t3FontCache[i];
2763         for (j = i; j > 0; --j) {
2764           t3FontCache[j] = t3FontCache[j - 1];
2765         }
2766         t3FontCache[0] = t3Font;
2767         break;
2768       }
2769     }
2770     if (i >= nT3Fonts) {
2771
2772       // create new entry in the font cache
2773       if (nT3Fonts == xOutT3FontCacheSize) {
2774         delete t3FontCache[nT3Fonts - 1];
2775         --nT3Fonts;
2776       }
2777       for (j = nT3Fonts; j > 0; --j) {
2778         t3FontCache[j] = t3FontCache[j - 1];
2779       }
2780       ++nT3Fonts;
2781       bbox = gfxFont->getFontBBox();
2782       if (bbox[0] == 0 && bbox[1] == 0 && bbox[2] == 0 && bbox[3] == 0) {
2783         // broken bounding box -- just take a guess
2784         xMin = xt - 5;
2785         xMax = xMin + 30;
2786         yMax = yt + 15;
2787         yMin = yMax - 45;
2788       } else {
2789         state->transform(bbox[0], bbox[1], &x1, &y1);
2790         xMin = xMax = x1;
2791         yMin = yMax = y1;
2792         state->transform(bbox[0], bbox[3], &x1, &y1);
2793         if (x1 < xMin) {
2794           xMin = x1;
2795         } else if (x1 > xMax) {
2796           xMax = x1;
2797         }
2798         if (y1 < yMin) {
2799           yMin = y1;
2800         } else if (y1 > yMax) {
2801           yMax = y1;
2802         }
2803         state->transform(bbox[2], bbox[1], &x1, &y1);
2804         if (x1 < xMin) {
2805           xMin = x1;
2806         } else if (x1 > xMax) {
2807           xMax = x1;
2808         }
2809         if (y1 < yMin) {
2810           yMin = y1;
2811         } else if (y1 > yMax) {
2812           yMax = y1;
2813         }
2814         state->transform(bbox[2], bbox[3], &x1, &y1);
2815         if (x1 < xMin) {
2816           xMin = x1;
2817         } else if (x1 > xMax) {
2818           xMax = x1;
2819         }
2820         if (y1 < yMin) {
2821           yMin = y1;
2822         } else if (y1 > yMax) {
2823           yMax = y1;
2824         }
2825       }
2826       t3FontCache[0] = new T3FontCache(fontID, ctm[0], ctm[1], ctm[2], ctm[3],
2827                                        (int)floor(xMin - xt),
2828                                        (int)floor(yMin - yt),
2829                                        (int)ceil(xMax) - (int)floor(xMin) + 3,
2830                                        (int)ceil(yMax) - (int)floor(yMin) + 3,
2831                                        display, visual, depth, pixmap);
2832     }
2833   }
2834   t3Font = t3FontCache[0];
2835
2836   // is the glyph in the cache?
2837   i = (code & (t3Font->cacheSets - 1)) * t3Font->cacheAssoc;
2838   for (j = 0; j < t3Font->cacheAssoc; ++j) {
2839     if ((t3Font->cacheTags[i+j].mru & 0x8000) &&
2840         t3Font->cacheTags[i+j].code == code) {
2841       state->getFillRGB(&color);
2842       if (reverseVideo) {
2843         color.r = 1 - color.r;
2844         color.g = 1 - color.g;
2845         color.b = 1 - color.b;
2846       }
2847       text->addChar(state, 0, 0,
2848                     t3Font->cacheTags[i+j].wx, t3Font->cacheTags[i+j].wy,
2849                     code, u, uLen);
2850       drawType3Glyph(t3Font, &t3Font->cacheTags[i+j],
2851                      t3Font->cacheData + (i+j) * t3Font->glyphSize,
2852                      xt, yt, &color);
2853       return gTrue;
2854     }
2855   }
2856
2857   // push a new Type 3 glyph record
2858   t3gs = new T3GlyphStack();
2859   t3gs->next = t3GlyphStack;
2860   t3GlyphStack = t3gs;
2861   t3GlyphStack->cacheable = gFalse;
2862   t3GlyphStack->code = code;
2863   t3GlyphStack->cache = t3Font;
2864   t3GlyphStack->cacheIdx = i;
2865   t3GlyphStack->x = xt;
2866   t3GlyphStack->y = yt;
2867   t3GlyphStack->u = u;
2868   t3GlyphStack->uLen = uLen;
2869
2870   return gFalse;
2871 }
2872
2873 void XOutputDev::endType3Char(GfxState *state) {
2874   XImage *image;
2875   Guchar *p;
2876   int x, y;
2877   Gulong pixel;
2878   double alpha;
2879   T3GlyphStack *t3gs;
2880   double *ctm;
2881
2882   if (t3GlyphStack->cacheable) {
2883     image = t3GlyphStack->cache->image;
2884     XGetSubImage(display, pixmap, 0, 0,
2885                  t3GlyphStack->cache->glyphW, t3GlyphStack->cache->glyphH,
2886                  (1 << depth) - 1, ZPixmap, image, 0, 0);
2887     p = t3GlyphStack->cacheData;
2888     for (y = 0; y < t3GlyphStack->cache->glyphH; ++y) {
2889       for (x = 0; x < t3GlyphStack->cache->glyphW; ++x) {
2890         pixel = XGetPixel(image, x, y);
2891         if (trueColor) {
2892           alpha = (double)((pixel >> rShift) & rMul) / (double)rMul;
2893         } else {
2894           alpha = redMap[pixel & 0xff];
2895         }
2896         if (alpha <= 0.2) {
2897           *p++ = 4;
2898         } else if (alpha <= 0.4) {
2899           *p++ = 3;
2900         } else if (alpha <= 0.6) {
2901           *p++ = 2;
2902         } else if (alpha <= 0.8) {
2903           *p++ = 1;
2904         } else {
2905           *p++ = 0;
2906         }
2907       }
2908     }
2909     XDestroyRegion(clipRegion);
2910     XFreeGC(display, strokeGC);
2911     XFreeGC(display, fillGC);
2912     pixmapW = t3GlyphStack->origPixmapW;
2913     pixmapH = t3GlyphStack->origPixmapH;
2914     pixmap = t3GlyphStack->origPixmap;
2915     strokeGC = t3GlyphStack->origStrokeGC;
2916     fillGC = t3GlyphStack->origFillGC;
2917     clipRegion = t3GlyphStack->origClipRegion;
2918     drawType3Glyph(t3GlyphStack->cache,
2919                    t3GlyphStack->cacheTag, t3GlyphStack->cacheData,
2920                    t3GlyphStack->x, t3GlyphStack->y, &t3GlyphStack->color);
2921     // the CTM must be restored here in order for TextPage::addChar to
2922     // work correctly
2923     ctm = state->getCTM();
2924     state->setCTM(ctm[0], ctm[1], ctm[2], ctm[3],
2925                   t3GlyphStack->origCTM4, t3GlyphStack->origCTM5);
2926   }
2927   text->addChar(state, 0, 0, t3GlyphStack->wx, t3GlyphStack->wy,
2928                 t3GlyphStack->code, t3GlyphStack->u, t3GlyphStack->uLen);
2929   t3gs = t3GlyphStack;
2930   t3GlyphStack = t3gs->next;
2931   delete t3gs;
2932 }
2933
2934 void XOutputDev::drawType3Glyph(T3FontCache *t3Font,
2935                                 T3FontCacheTag *tag, Guchar *data,
2936                                 double x, double y, GfxRGB *color) {
2937   XImage *image;
2938   XColor xcolor;
2939   GfxRGB bg, rgb;
2940   Gulong map[5];
2941   Gulong pixel;
2942   Guchar *p;
2943   int x0, y0, w0, h0, x1, y1;
2944   int ix, iy;
2945
2946   // compute: (x0,y0) = position in destination pixmap
2947   //          (x1,y1) = position in the XImage
2948   //          (w0,h0) = size of XImage transfer
2949   x0 = xoutRound(x + t3Font->glyphX);
2950   y0 = xoutRound(y + t3Font->glyphY);
2951   x1 = 0;
2952   y1 = 0;
2953   w0 = t3Font->glyphW;
2954   h0 = t3Font->glyphH;
2955   if (x0 < 0) {
2956     x1 = -x0;
2957     w0 += x0;
2958     x0 = 0;
2959   }
2960   if (x0 + w0 > pixmapW) {
2961     w0 = pixmapW - x0;
2962   }
2963   if (w0 <= 0) {
2964     return;
2965   }
2966   if (y0 < 0) {
2967     y1 = -y0;
2968     h0 += y0;
2969     y0 = 0;
2970   }
2971   if (y0 + h0 > pixmapH) {
2972     h0 = pixmapH - y0;
2973   }
2974   if (h0 <= 0) {
2975     return;
2976   }
2977
2978   image = t3Font->image;
2979   XGetSubImage(display, pixmap, x0, y0, w0, h0,
2980                (1 << depth) - 1, ZPixmap, image, x1, y1);
2981   xcolor.pixel = XGetPixel(image, t3Font->glyphW / 2, t3Font->glyphH / 2);
2982   XQueryColor(display, colormap, &xcolor);
2983   bg.r = xcolor.red / 65535.0;
2984   bg.g = xcolor.green / 65535.0;
2985   bg.b = xcolor.blue / 65535.0;
2986   rgb.r = 0.25 * (color->r + 3 * bg.r);
2987   rgb.g = 0.25 * (color->g + 3 * bg.g);
2988   rgb.b = 0.25 * (color->b + 3 * bg.b);
2989   map[1] = findColor(&rgb);
2990   rgb.r = 0.5 * (color->r + bg.r);
2991   rgb.g = 0.5 * (color->g + bg.g);
2992   rgb.b = 0.5 * (color->b + bg.b);
2993   map[2] = findColor(&rgb);
2994   rgb.r = 0.25 * (3 * color->r + bg.r);
2995   rgb.g = 0.25 * (3 * color->g + bg.g);
2996   rgb.b = 0.25 * (3 * color->b + bg.b);
2997   map[3] = findColor(&rgb);
2998   map[4] = findColor(color);
2999   p = data;
3000   for (iy = 0; iy < t3Font->glyphH; ++iy) {
3001     for (ix = 0; ix < t3Font->glyphW; ++ix) {
3002       pixel = *p++;
3003       if (pixel > 0) {
3004         XPutPixel(image, ix, iy, map[pixel]);
3005       }
3006     }
3007   }
3008   XPutImage(display, pixmap, fillGC, image, x1, y1, x0, y0, w0, h0);
3009 }
3010
3011 void XOutputDev::type3D0(GfxState *state, double wx, double wy) {
3012   t3GlyphStack->wx = wx;
3013   t3GlyphStack->wy = wy;
3014 }
3015
3016 void XOutputDev::type3D1(GfxState *state, double wx, double wy,
3017                          double llx, double lly, double urx, double ury) {
3018   GfxColor fgColor;
3019   XGCValues gcValues;
3020   XRectangle rect;
3021   double *ctm;
3022   T3FontCache *t3Font;
3023   double xt, yt, xMin, xMax, yMin, yMax, x1, y1;
3024   int i, j;
3025
3026   t3Font = t3GlyphStack->cache;
3027   t3GlyphStack->wx = wx;
3028   t3GlyphStack->wy = wy;
3029
3030   // check for a valid bbox
3031   state->transform(0, 0, &xt, &yt);
3032   state->transform(llx, lly, &x1, &y1);
3033   xMin = xMax = x1;
3034   yMin = yMax = y1;
3035   state->transform(llx, ury, &x1, &y1);
3036   if (x1 < xMin) {
3037     xMin = x1;
3038   } else if (x1 > xMax) {
3039     xMax = x1;
3040   }
3041   if (y1 < yMin) {
3042     yMin = y1;
3043   } else if (y1 > yMax) {
3044     yMax = y1;
3045   }
3046   state->transform(urx, lly, &x1, &y1);
3047   if (x1 < xMin) {
3048     xMin = x1;
3049   } else if (x1 > xMax) {
3050     xMax = x1;
3051   }
3052   if (y1 < yMin) {
3053     yMin = y1;
3054   } else if (y1 > yMax) {
3055     yMax = y1;
3056   }
3057   state->transform(urx, ury, &x1, &y1);
3058   if (x1 < xMin) {
3059     xMin = x1;
3060   } else if (x1 > xMax) {
3061     xMax = x1;
3062   }
3063   if (y1 < yMin) {
3064     yMin = y1;
3065   } else if (y1 > yMax) {
3066     yMax = y1;
3067   }
3068   if (xMin - xt < t3Font->glyphX ||
3069       yMin - yt < t3Font->glyphY ||
3070       xMax - xt > t3Font->glyphX + t3Font->glyphW ||
3071       yMax - yt > t3Font->glyphY + t3Font->glyphH) {
3072     error(-1, "Bad bounding box in Type 3 glyph");
3073     return;
3074   }
3075
3076   // allocate a cache entry
3077   t3GlyphStack->cacheable = gTrue;
3078   i = t3GlyphStack->cacheIdx;
3079   for (j = 0; j < t3Font->cacheAssoc; ++j) {
3080     if ((t3Font->cacheTags[i+j].mru & 0x7fff) == t3Font->cacheAssoc - 1) {
3081       t3Font->cacheTags[i+j].mru = 0x8000;
3082       t3Font->cacheTags[i+j].code = t3GlyphStack->code;
3083       t3GlyphStack->cacheTag = &t3Font->cacheTags[i+j];
3084       t3GlyphStack->cacheData = t3Font->cacheData + (i+j) * t3Font->glyphSize;
3085     } else {
3086       ++t3Font->cacheTags[i+j].mru;
3087     }
3088   }
3089   t3GlyphStack->cacheTag->wx = wx;
3090   t3GlyphStack->cacheTag->wy = wy;
3091
3092   // prepare to rasterize the glyph
3093   //~ do we need to handle both fill and stroke color?
3094   state->getFillRGB(&t3GlyphStack->color);
3095   if (reverseVideo) {
3096     t3GlyphStack->color.r = 1 - t3GlyphStack->color.r;
3097     t3GlyphStack->color.g = 1 - t3GlyphStack->color.g;
3098     t3GlyphStack->color.b = 1 - t3GlyphStack->color.b;
3099   }
3100   fgColor.c[0] = reverseVideo ? 1 : 0;
3101   state->setFillColorSpace(new GfxDeviceGrayColorSpace());
3102   state->setFillColor(&fgColor);
3103   state->setStrokeColorSpace(new GfxDeviceGrayColorSpace());
3104   state->setStrokeColor(&fgColor);
3105   t3GlyphStack->origPixmapW = pixmapW;
3106   t3GlyphStack->origPixmapH = pixmapH;
3107   t3GlyphStack->origPixmap = pixmap;
3108   t3GlyphStack->origStrokeGC = strokeGC;
3109   t3GlyphStack->origFillGC = fillGC;
3110   t3GlyphStack->origClipRegion = clipRegion;
3111   pixmapW = t3GlyphStack->cache->glyphW;
3112   pixmapH = t3GlyphStack->cache->glyphH;
3113   pixmap = t3GlyphStack->cache->pixmap;
3114   gcValues.foreground = BlackPixel(display, screenNum);
3115   gcValues.background = WhitePixel(display, screenNum);
3116   gcValues.line_width = 0;
3117   gcValues.line_style = LineSolid;
3118   strokeGC = XCreateGC(display, pixmap,
3119                        GCForeground | GCBackground | GCLineWidth | GCLineStyle,
3120                        &gcValues);
3121   updateLineAttrs(state, gTrue);
3122   gcValues.foreground = WhitePixel(display, screenNum);
3123   fillGC = XCreateGC(display, pixmap,
3124                      GCForeground | GCBackground | GCLineWidth | GCLineStyle,
3125                      &gcValues);
3126   XFillRectangle(display, pixmap, fillGC, 0, 0, pixmapW, pixmapH);
3127   XSetForeground(display, fillGC, BlackPixel(display, screenNum));
3128   clipRegion = XCreateRegion();
3129   rect.x = rect.y = 0;
3130   rect.width = pixmapW;
3131   rect.height = pixmapH;
3132   XUnionRectWithRegion(&rect, clipRegion, clipRegion);
3133   XSetRegion(display, strokeGC, clipRegion);
3134   XSetRegion(display, fillGC, clipRegion);
3135   ctm = state->getCTM();
3136   t3GlyphStack->origCTM4 = ctm[4];
3137   t3GlyphStack->origCTM5 = ctm[5];
3138   state->setCTM(ctm[0], ctm[1], ctm[2], ctm[3], 
3139                 -t3GlyphStack->cache->glyphX, -t3GlyphStack->cache->glyphY);
3140 }
3141
3142 void XOutputDev::endTextObject(GfxState *state) {
3143   double *ctm;
3144   double saveCTM[6];
3145
3146   if (textClipPath) {
3147     ctm = state->getCTM();
3148     memcpy(saveCTM, ctm, 6 * sizeof(double));
3149     state->setCTM(1, 0, 0, 1, 0, 0);
3150     doClip(state, textClipPath, WindingRule);
3151     state->setCTM(saveCTM[0], saveCTM[1], saveCTM[2], saveCTM[3],
3152                   saveCTM[4], saveCTM[5]);
3153     delete textClipPath;
3154     textClipPath = NULL;
3155   }
3156 }
3157
3158 inline Gulong XOutputDev::findColor(GfxRGB *x, GfxRGB *actual) {
3159   double gray;
3160   int r, g, b;
3161   Gulong pixel;
3162
3163   if (trueColor) {
3164     r = xoutRound(x->r * rMul);
3165     g = xoutRound(x->g * gMul);
3166     b = xoutRound(x->b * bMul);
3167     pixel = ((Gulong)r << rShift) +
3168             ((Gulong)g << gShift) +
3169             ((Gulong)b << bShift);
3170     actual->r = (double)r / rMul;
3171     actual->g = (double)g / gMul;
3172     actual->b = (double)b / bMul;
3173   } else if (numColors == 1) {
3174     gray = 0.299 * x->r + 0.587 * x->g + 0.114 * x->b;
3175     if (gray < 0.5) {
3176       pixel = colors[0];
3177       actual->r = actual->g = actual->b = 0;
3178     } else {
3179       pixel = colors[1];
3180       actual->r = actual->g = actual->b = 1;
3181     }
3182   } else {
3183     r = xoutRound(x->r * (numColors - 1));
3184     g = xoutRound(x->g * (numColors - 1));
3185     b = xoutRound(x->b * (numColors - 1));
3186     pixel = colors[(r * numColors + g) * numColors + b];
3187     actual->r = (double)r / (numColors - 1);
3188     actual->g = (double)g / (numColors - 1); 
3189     actual->b = (double)b / (numColors - 1);
3190   }
3191   return pixel;
3192 }
3193
3194 Gulong XOutputDev::findColor(GfxRGB *rgb) {
3195   int r, g, b;
3196   double gray;
3197   Gulong pixel;
3198
3199   if (trueColor) {
3200     r = xoutRound(rgb->r * rMul);
3201     g = xoutRound(rgb->g * gMul);
3202     b = xoutRound(rgb->b * bMul);
3203     pixel = ((Gulong)r << rShift) +
3204             ((Gulong)g << gShift) +
3205             ((Gulong)b << bShift);
3206   } else if (numColors == 1) {
3207     gray = 0.299 * rgb->r + 0.587 * rgb->g + 0.114 * rgb->b;
3208     if (gray < 0.5)
3209       pixel = colors[0];
3210     else
3211       pixel = colors[1];
3212   } else {
3213     r = xoutRound(rgb->r * (numColors - 1));
3214     g = xoutRound(rgb->g * (numColors - 1));
3215     b = xoutRound(rgb->b * (numColors - 1));
3216 #if 0 // this makes things worse as often as better
3217     // even a very light color shouldn't map to white
3218     if (r == numColors - 1 && g == numColors - 1 && b == numColors - 1) {
3219       if (color->getR() < 0.95)
3220         --r;
3221       if (color->getG() < 0.95)
3222         --g;
3223       if (color->getB() < 0.95)
3224         --b;
3225     }
3226 #endif
3227     pixel = colors[(r * numColors + g) * numColors + b];
3228   }
3229   return pixel;
3230 }
3231
3232 void XOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
3233                                int width, int height, GBool invert,
3234                                GBool inlineImg) {
3235   ImageStream *imgStr;
3236   XImage *image;
3237   double *ctm;
3238   GBool rot;
3239   double xScale, yScale, xShear, yShear;
3240   int tx, ty, scaledWidth, scaledHeight, xSign, ySign;
3241   int ulx, uly, llx, lly, urx, ury, lrx, lry;
3242   int ulx1, uly1, llx1, lly1, urx1, ury1, lrx1, lry1;
3243   int bx0, by0, bx1, by1, bw, bh;
3244   int cx0, cy0, cx1, cy1, cw, ch;
3245   int yp, yq, yt, yStep, lastYStep;
3246   int xp, xq, xt, xStep, xSrc;
3247   GfxRGB rgb;
3248   Guchar *pixBuf;
3249   int imgPix;
3250   double alpha;
3251   XColor xcolor;
3252   Gulong lastPixel;
3253   GfxRGB rgb2;
3254   double r0, g0, b0, r1, g1, b1;
3255   Gulong pix;
3256   Guchar *p;
3257   int x, y, x1, y1, x2, y2;
3258   int n, m, i, j;
3259
3260   // get CTM, check for singular matrix
3261   ctm = state->getCTM();
3262   if (fabs(ctm[0] * ctm[3] - ctm[1] * ctm[2]) < 0.000001) {
3263     error(-1, "Singular CTM in drawImage");
3264     if (inlineImg) {
3265       j = height * ((width + 7) / 8);
3266       str->reset();
3267       for (i = 0; i < j; ++i) {
3268         str->getChar();
3269       }
3270       str->close();
3271     }
3272     return;
3273   }
3274
3275   // compute scale, shear, rotation, translation parameters
3276   rot = fabs(ctm[1]) > fabs(ctm[0]);
3277   if (rot) {
3278     xScale = -ctm[1];
3279     yScale = -ctm[2] + (ctm[0] * ctm[3]) / ctm[1];
3280     xShear = ctm[3] / yScale;
3281     yShear = -ctm[0] / ctm[1];
3282   } else {
3283     xScale = ctm[0];
3284     yScale = -ctm[3] + (ctm[1] * ctm[2]) / ctm[0];
3285     xShear = -ctm[2] / yScale;
3286     yShear = ctm[1] / ctm[0];
3287   }
3288   tx = xoutRound(ctm[2] + ctm[4]);
3289   ty = xoutRound(ctm[3] + ctm[5]);
3290   // use ceil() to avoid gaps between "striped" images
3291   scaledWidth = (int)ceil(fabs(xScale));
3292   xSign = (xScale < 0) ? -1 : 1;
3293   scaledHeight = (int)ceil(fabs(yScale));
3294   ySign = (yScale < 0) ? -1 : 1;
3295
3296   // compute corners in device space
3297   ulx1 = 0;
3298   uly1 = 0;
3299   urx1 = xSign * (scaledWidth - 1);
3300   ury1 = xoutRound(yShear * urx1);
3301   llx1 = xoutRound(xShear * ySign * (scaledHeight - 1));
3302   lly1 = ySign * (scaledHeight - 1) + xoutRound(yShear * llx1);
3303   lrx1 = xSign * (scaledWidth - 1) +
3304            xoutRound(xShear * ySign * (scaledHeight - 1));
3305   lry1 = ySign * (scaledHeight - 1) + xoutRound(yShear * lrx1);
3306   if (rot) {
3307     ulx = tx + uly1;    uly = ty - ulx1;
3308     urx = tx + ury1;    ury = ty - urx1;
3309     llx = tx + lly1;    lly = ty - llx1;
3310     lrx = tx + lry1;    lry = ty - lrx1;
3311   } else {
3312     ulx = tx + ulx1;    uly = ty + uly1;
3313     urx = tx + urx1;    ury = ty + ury1;
3314     llx = tx + llx1;    lly = ty + lly1;
3315     lrx = tx + lrx1;    lry = ty + lry1;
3316   }
3317
3318   // bounding box:
3319   //   (bx0, by0) = upper-left corner
3320   //   (bx1, by1) = lower-right corner
3321   //   (bw, bh) = size
3322   bx0 = (ulx < urx) ? (ulx < llx) ? (ulx < lrx) ? ulx : lrx
3323                                   : (llx < lrx) ? llx : lrx
3324                     : (urx < llx) ? (urx < lrx) ? urx : lrx
3325                                   : (llx < lrx) ? llx : lrx;
3326   bx1 = (ulx > urx) ? (ulx > llx) ? (ulx > lrx) ? ulx : lrx
3327                                   : (llx > lrx) ? llx : lrx
3328                     : (urx > llx) ? (urx > lrx) ? urx : lrx
3329                                   : (llx > lrx) ? llx : lrx;
3330   by0 = (uly < ury) ? (uly < lly) ? (uly < lry) ? uly : lry
3331                                   : (lly < lry) ? lly : lry
3332                     : (ury < lly) ? (ury < lry) ? ury : lry
3333                                   : (lly < lry) ? lly : lry;
3334   by1 = (uly > ury) ? (uly > lly) ? (uly > lry) ? uly : lry
3335                                   : (lly > lry) ? lly : lry
3336                     : (ury > lly) ? (ury > lry) ? ury : lry
3337                                   : (lly > lry) ? lly : lry;
3338   bw = bx1 - bx0 + 1;
3339   bh = by1 - by0 + 1;
3340
3341   // Bounding box clipped to pixmap, i.e., "valid" rectangle:
3342   //   (cx0, cy0) = upper-left corner of valid rectangle in Pixmap
3343   //   (cx1, cy1) = upper-left corner of valid rectangle in XImage
3344   //   (cw, ch) = size of valid rectangle
3345   // These values will be used to transfer the XImage from/to the
3346   // Pixmap.
3347   cw = (bx1 >= pixmapW) ? pixmapW - bx0 : bw;
3348   if (bx0 < 0) {
3349     cx0 = 0;
3350     cx1 = -bx0;
3351     cw += bx0;
3352   } else {
3353     cx0 = bx0;
3354     cx1 = 0;
3355   }
3356   ch = (by1 >= pixmapH) ? pixmapH - by0 : bh;
3357   if (by0 < 0) {
3358     cy0 = 0;
3359     cy1 = -by0;
3360     ch += by0;
3361   } else {
3362     cy0 = by0;
3363     cy1 = 0;
3364   }
3365
3366   // check for tiny (zero width or height) images
3367   // and off-page images
3368   if (scaledWidth <= 0 || scaledHeight <= 0 || cw <= 0 || ch <= 0) {
3369     if (inlineImg) {
3370       j = height * ((width + 7) / 8);
3371       str->reset();
3372       for (i = 0; i < j; ++i) {
3373         str->getChar();
3374       }
3375       str->close();
3376     }
3377     return;
3378   }
3379
3380   // compute Bresenham parameters for x and y scaling
3381   yp = height / scaledHeight;
3382   yq = height % scaledHeight;
3383   xp = width / scaledWidth;
3384   xq = width % scaledWidth;
3385
3386   // allocate pixel buffer
3387   pixBuf = (Guchar *)gmalloc((yp + 1) * width * sizeof(Guchar));
3388
3389   // allocate XImage and read from page pixmap
3390   image = XCreateImage(display, visual, depth, ZPixmap, 0, NULL, bw, bh, 8, 0);
3391   image->data = (char *)gmalloc(bh * image->bytes_per_line);
3392   XGetSubImage(display, pixmap, cx0, cy0, cw, ch, (1 << depth) - 1, ZPixmap,
3393                image, cx1, cy1);
3394
3395   // get mask color
3396   state->getFillRGB(&rgb);
3397   if (reverseVideo) {
3398     rgb.r = 1 - rgb.r;
3399     rgb.g = 1 - rgb.g;
3400     rgb.b = 1 - rgb.b;
3401   }
3402   r0 = rgb.r;
3403   g0 = rgb.g;
3404   b0 = rgb.b;
3405
3406   // initialize background color
3407   // (the specific pixel value doesn't matter here, as long as
3408   // r1,g1,b1 correspond correctly to lastPixel)
3409   xcolor.pixel = lastPixel = 0;
3410   XQueryColor(display, colormap, &xcolor);
3411   r1 = (double)xcolor.red / 65535.0;
3412   g1 = (double)xcolor.green / 65535.0;
3413   b1 = (double)xcolor.blue / 65535.0;
3414
3415   // initialize the image stream
3416   imgStr = new ImageStream(str, width, 1, 1);
3417   imgStr->reset();
3418
3419   // init y scale Bresenham
3420   yt = 0;
3421   lastYStep = 1;
3422
3423   for (y = 0; y < scaledHeight; ++y) {
3424
3425     // y scale Bresenham
3426     yStep = yp;
3427     yt += yq;
3428     if (yt >= scaledHeight) {
3429       yt -= scaledHeight;
3430       ++yStep;
3431     }
3432
3433     // read row(s) from image
3434     n = (yp > 0) ? yStep : lastYStep;
3435     if (n > 0) {
3436       p = pixBuf;
3437       for (i = 0; i < n; ++i) {
3438         memcpy(p, imgStr->getLine(), width);
3439         if (invert) {
3440           for (j = 0; j < width; ++j) {
3441             p[j] ^= 1;
3442           }
3443         }
3444         p += width;
3445       }
3446     }
3447     lastYStep = yStep;
3448
3449     // init x scale Bresenham
3450     xt = 0;
3451     xSrc = 0;
3452
3453     for (x = 0; x < scaledWidth; ++x) {
3454
3455       // x scale Bresenham
3456       xStep = xp;
3457       xt += xq;
3458       if (xt >= scaledWidth) {
3459         xt -= scaledWidth;
3460         ++xStep;
3461       }
3462
3463       // x shear
3464       x1 = xSign * x + xoutRound(xShear * ySign * y);
3465
3466       // y shear
3467       y1 = ySign * y + xoutRound(yShear * x1);
3468
3469       // rotation
3470       if (rot) {
3471         x2 = y1;
3472         y2 = -x1;
3473       } else {
3474         x2 = x1;
3475         y2 = y1;
3476       }
3477
3478       // compute the filtered pixel at (x,y) after the
3479       // x and y scaling operations
3480       n = yStep > 0 ? yStep : 1;
3481       m = xStep > 0 ? xStep : 1;
3482       p = pixBuf + xSrc;
3483       imgPix = 0;
3484       for (i = 0; i < n; ++i) {
3485         for (j = 0; j < m; ++j) {
3486           imgPix += *p++;
3487         }
3488         p += width - m;
3489       }
3490
3491       // x scale Bresenham
3492       xSrc += xStep;
3493
3494       // blend image pixel with background
3495       alpha = (double)imgPix / (double)(n * m);
3496       xcolor.pixel = XGetPixel(image, tx + x2 - bx0, ty + y2 - by0);
3497       if (xcolor.pixel != lastPixel) {
3498         XQueryColor(display, colormap, &xcolor);
3499         r1 = (double)xcolor.red / 65535.0;
3500         g1 = (double)xcolor.green / 65535.0;
3501         b1 = (double)xcolor.blue / 65535.0;
3502         lastPixel = xcolor.pixel;
3503       }
3504       rgb2.r = r0 * (1 - alpha) + r1 * alpha;
3505       rgb2.g = g0 * (1 - alpha) + g1 * alpha;
3506       rgb2.b = b0 * (1 - alpha) + b1 * alpha;
3507       pix = findColor(&rgb2);
3508
3509       // set pixel
3510       XPutPixel(image, tx + x2 - bx0, ty + y2 - by0, pix);
3511     }
3512   }
3513
3514   // blit the image into the pixmap
3515   XPutImage(display, pixmap, fillGC, image, cx1, cy1, cx0, cy0, cw, ch);
3516
3517   // free memory
3518   delete imgStr;
3519   gfree(pixBuf);
3520   gfree(image->data);
3521   image->data = NULL;
3522   XDestroyImage(image);
3523 }
3524
3525 void XOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
3526                            int width, int height, GfxImageColorMap *colorMap,
3527                            int *maskColors, GBool inlineImg) {
3528   ImageStream *imgStr;
3529   XImage *image;
3530   int nComps, nVals, nBits;
3531   GBool dither;
3532   double *ctm;
3533   GBool rot;
3534   double xScale, yScale, xShear, yShear;
3535   int tx, ty, scaledWidth, scaledHeight, xSign, ySign;
3536   int ulx, uly, llx, lly, urx, ury, lrx, lry;
3537   int ulx1, uly1, llx1, lly1, urx1, ury1, lrx1, lry1;
3538   int bx0, by0, bx1, by1, bw, bh;
3539   int cx0, cy0, cx1, cy1, cw, ch;
3540   int yp, yq, yt, yStep, lastYStep;
3541   int xp, xq, xt, xStep, xSrc;
3542   GfxRGB *pixBuf;
3543   Guchar *pixBuf1, *alphaBuf;
3544   Guchar pixBuf2[gfxColorMaxComps];
3545   GfxRGB color2, color3, actual, err, errRight;
3546   GfxRGB *errDown0, *errDown1, *errDownTmp;
3547   double r0, g0, b0, alpha, mul;
3548   Gulong pix;
3549   GfxRGB *p;
3550   Guchar *q, *p1, *p2;
3551   GBool oneBitMode;
3552   GfxRGB oneBitRGB[2];
3553   int x, y, x1, y1, x2, y2;
3554   int n, m, i, j, k;
3555
3556   // image parameters
3557   nComps = colorMap->getNumPixelComps();
3558   nVals = width * nComps;
3559   nBits = colorMap->getBits();
3560   oneBitMode = nComps == 1 && nBits == 1 && !maskColors;
3561   dither = nComps > 1 || nBits > 1;
3562
3563   // get CTM, check for singular matrix
3564   ctm = state->getCTM();
3565   if (fabs(ctm[0] * ctm[3] - ctm[1] * ctm[2]) < 0.000001) {
3566     error(-1, "Singular CTM in drawImage");
3567     if (inlineImg) {
3568       str->reset();
3569       j = height * ((nVals * nBits + 7) / 8);
3570       for (i = 0; i < j; ++i) {
3571         str->getChar();
3572       }
3573       str->close();
3574     }
3575     return;
3576   }
3577
3578   // compute scale, shear, rotation, translation parameters
3579   rot = fabs(ctm[1]) > fabs(ctm[0]);
3580   if (rot) {
3581     xScale = -ctm[1];
3582     yScale = -ctm[2] + (ctm[0] * ctm[3]) / ctm[1];
3583     xShear = ctm[3] / yScale;
3584     yShear = -ctm[0] / ctm[1];
3585   } else {
3586     xScale = ctm[0];
3587     yScale = -ctm[3] + (ctm[1] * ctm[2]) / ctm[0];
3588     xShear = -ctm[2] / yScale;
3589     yShear = ctm[1] / ctm[0];
3590   }
3591   tx = xoutRound(ctm[2] + ctm[4]);
3592   ty = xoutRound(ctm[3] + ctm[5]);
3593   if (xScale < 0) {
3594     // this is the right edge which needs to be (left + width - 1)
3595     --tx;
3596   }
3597   if (yScale < 0) {
3598     // this is the bottom edge which needs to be (top + height - 1)
3599     --ty;
3600   }
3601   // use ceil() to avoid gaps between "striped" images
3602   scaledWidth = (int)ceil(fabs(xScale));
3603   xSign = (xScale < 0) ? -1 : 1;
3604   scaledHeight = (int)ceil(fabs(yScale));
3605   ySign = (yScale < 0) ? -1 : 1;
3606
3607   // compute corners in device space
3608   ulx1 = 0;
3609   uly1 = 0;
3610   urx1 = xSign * (scaledWidth - 1);
3611   ury1 = xoutRound(yShear * urx1);
3612   llx1 = xoutRound(xShear * ySign * (scaledHeight - 1));
3613   lly1 = ySign * (scaledHeight - 1) + xoutRound(yShear * llx1);
3614   lrx1 = xSign * (scaledWidth - 1) +
3615            xoutRound(xShear * ySign * (scaledHeight - 1));
3616   lry1 = ySign * (scaledHeight - 1) + xoutRound(yShear * lrx1);
3617   if (rot) {
3618     ulx = tx + uly1;    uly = ty - ulx1;
3619     urx = tx + ury1;    ury = ty - urx1;
3620     llx = tx + lly1;    lly = ty - llx1;
3621     lrx = tx + lry1;    lry = ty - lrx1;
3622   } else {
3623     ulx = tx + ulx1;    uly = ty + uly1;
3624     urx = tx + urx1;    ury = ty + ury1;
3625     llx = tx + llx1;    lly = ty + lly1;
3626     lrx = tx + lrx1;    lry = ty + lry1;
3627   }
3628
3629   // bounding box:
3630   //   (bx0, by0) = upper-left corner
3631   //   (bx1, by1) = lower-right corner
3632   //   (bw, bh) = size
3633   bx0 = (ulx < urx) ? (ulx < llx) ? (ulx < lrx) ? ulx : lrx
3634                                   : (llx < lrx) ? llx : lrx
3635                     : (urx < llx) ? (urx < lrx) ? urx : lrx
3636                                   : (llx < lrx) ? llx : lrx;
3637   bx1 = (ulx > urx) ? (ulx > llx) ? (ulx > lrx) ? ulx : lrx
3638                                   : (llx > lrx) ? llx : lrx
3639                     : (urx > llx) ? (urx > lrx) ? urx : lrx
3640                                   : (llx > lrx) ? llx : lrx;
3641   by0 = (uly < ury) ? (uly < lly) ? (uly < lry) ? uly : lry
3642                                   : (lly < lry) ? lly : lry
3643                     : (ury < lly) ? (ury < lry) ? ury : lry
3644                                   : (lly < lry) ? lly : lry;
3645   by1 = (uly > ury) ? (uly > lly) ? (uly > lry) ? uly : lry
3646                                   : (lly > lry) ? lly : lry
3647                     : (ury > lly) ? (ury > lry) ? ury : lry
3648                                   : (lly > lry) ? lly : lry;
3649   bw = bx1 - bx0 + 1;
3650   bh = by1 - by0 + 1;
3651
3652   // Bounding box clipped to pixmap, i.e., "valid" rectangle:
3653   //   (cx0, cy0) = upper-left corner of valid rectangle in Pixmap
3654   //   (cx1, cy1) = upper-left corner of valid rectangle in XImage
3655   //   (cw, ch) = size of valid rectangle
3656   // These values will be used to transfer the XImage from/to the
3657   // Pixmap.
3658   cw = (bx1 >= pixmapW) ? pixmapW - bx0 : bw;
3659   if (bx0 < 0) {
3660     cx0 = 0;
3661     cx1 = -bx0;
3662     cw += bx0;
3663   } else {
3664     cx0 = bx0;
3665     cx1 = 0;
3666   }
3667   ch = (by1 >= pixmapH) ? pixmapH - by0 : bh;
3668   if (by0 < 0) {
3669     cy0 = 0;
3670     cy1 = -by0;
3671     ch += by0;
3672   } else {
3673     cy0 = by0;
3674     cy1 = 0;
3675   }
3676
3677   // check for tiny (zero width or height) images
3678   // and off-page images
3679   if (scaledWidth <= 0 || scaledHeight <= 0 || cw <= 0 || ch <= 0) {
3680     if (inlineImg) {
3681       str->reset();
3682       j = height * ((nVals * nBits + 7) / 8);
3683       for (i = 0; i < j; ++i)
3684         str->getChar();
3685       str->close();
3686     }
3687     return;
3688   }
3689
3690   // compute Bresenham parameters for x and y scaling
3691   yp = height / scaledHeight;
3692   yq = height % scaledHeight;
3693   xp = width / scaledWidth;
3694   xq = width % scaledWidth;
3695
3696   // allocate pixel buffer
3697   if (oneBitMode) {
3698     pixBuf1 = (Guchar *)gmalloc((yp + 1) * width * sizeof(Guchar));
3699     pixBuf = NULL;
3700   } else {
3701     pixBuf = (GfxRGB *)gmalloc((yp + 1) * width * sizeof(GfxRGB));
3702     pixBuf1 = NULL;
3703   }
3704   if (maskColors) {
3705     alphaBuf = (Guchar *)gmalloc((yp + 1) * width * sizeof(Guchar));
3706   } else {
3707     alphaBuf = NULL;
3708   }
3709
3710   // allocate XImage
3711   image = XCreateImage(display, visual, depth, ZPixmap, 0, NULL, bw, bh, 8, 0);
3712   image->data = (char *)gmalloc(bh * image->bytes_per_line);
3713
3714   // if the transform is anything other than a 0/90/180/270 degree
3715   // rotation/flip, or if there is color key masking, read the
3716   // backgound pixmap to fill in the corners
3717   if (!((ulx == llx && uly == ury) ||
3718         (uly == lly && ulx == urx)) ||
3719       maskColors) {
3720     XGetSubImage(display, pixmap, cx0, cy0, cw, ch, (1 << depth) - 1, ZPixmap,
3721                  image, cx1, cy1);
3722   }
3723
3724   // allocate error diffusion accumulators
3725   if (dither) {
3726     errDown0 = (GfxRGB *)gmalloc((scaledWidth + 2) * sizeof(GfxRGB));
3727     errDown1 = (GfxRGB *)gmalloc((scaledWidth + 2) * sizeof(GfxRGB));
3728     for (j = 0; j < scaledWidth + 2; ++j) {
3729       errDown0[j].r = errDown0[j].g = errDown0[j].b = 0;
3730       errDown1[j].r = errDown1[j].g = errDown1[j].b = 0;
3731     }
3732   } else {
3733     errDown0 = errDown1 = NULL;
3734   }
3735
3736   // optimize the one-bit-deep image case
3737   if (oneBitMode) {
3738     pixBuf2[0] = 0;
3739     colorMap->getRGB(pixBuf2, &oneBitRGB[0]);
3740     pixBuf2[0] = 1;
3741     colorMap->getRGB(pixBuf2, &oneBitRGB[1]);
3742   }
3743
3744   // initialize the image stream
3745   imgStr = new ImageStream(str, width, nComps, nBits);
3746   imgStr->reset();
3747
3748   // init y scale Bresenham
3749   yt = 0;
3750   lastYStep = 1;
3751
3752   for (y = 0; y < scaledHeight; ++y) {
3753
3754     // initialize error diffusion accumulators
3755     if (dither) {
3756       errDownTmp = errDown0;
3757       errDown0 = errDown1;
3758       errDown1 = errDownTmp;
3759       for (j = 0; j < scaledWidth + 2; ++j) {
3760         errDown1[j].r = errDown1[j].g = errDown1[j].b = 0;
3761       }
3762       errRight.r = errRight.g = errRight.b = 0;
3763     }
3764
3765     // y scale Bresenham
3766     yStep = yp;
3767     yt += yq;
3768     if (yt >= scaledHeight) {
3769       yt -= scaledHeight;
3770       ++yStep;
3771     }
3772
3773     // read row(s) from image
3774     n = (yp > 0) ? yStep : lastYStep;
3775     if (n > 0) {
3776       if (oneBitMode) {
3777         p1 = pixBuf1;
3778         for (i = 0; i < n; ++i) {
3779           p2 = imgStr->getLine();
3780           memcpy(p1, p2, width);
3781           p1 += width;
3782         }
3783       } else {
3784         p = pixBuf;
3785         q = alphaBuf;
3786         for (i = 0; i < n; ++i) {
3787           p2 = imgStr->getLine();
3788           for (j = 0; j < width; ++j) {
3789             colorMap->getRGB(p2, p);
3790             ++p;
3791             if (q) {
3792               *q = 1;
3793               for (k = 0; k < nComps; ++k) {
3794                 if (p2[k] < maskColors[2*k] ||
3795                     p2[k] > maskColors[2*k+1]) {
3796                   *q = 0;
3797                   break;
3798                 }
3799               }
3800               ++q;
3801             }
3802             p2 += nComps;
3803           }
3804         }
3805       }
3806     }
3807     lastYStep = yStep;
3808
3809     // init x scale Bresenham
3810     xt = 0;
3811     xSrc = 0;
3812
3813     for (x = 0; x < scaledWidth; ++x) {
3814
3815       // x scale Bresenham
3816       xStep = xp;
3817       xt += xq;
3818       if (xt >= scaledWidth) {
3819         xt -= scaledWidth;
3820         ++xStep;
3821       }
3822
3823       // x shear
3824       x1 = xSign * x + xoutRound(xShear * ySign * y);
3825
3826       // y shear
3827       y1 = ySign * y + xoutRound(yShear * x1);
3828
3829       // rotation
3830       if (rot) {
3831         x2 = y1;
3832         y2 = -x1;
3833       } else {
3834         x2 = x1;
3835         y2 = y1;
3836       }
3837
3838       // compute the filtered pixel at (x,y) after the
3839       // x and y scaling operations
3840       n = yStep > 0 ? yStep : 1;
3841       m = xStep > 0 ? xStep : 1;
3842       if (oneBitMode) {
3843         p1 = pixBuf1 + xSrc;
3844         k = 0;
3845         for (i = 0; i < n; ++i) {
3846           for (j = 0; j < m; ++j) {
3847             k += *p1++;
3848           }
3849           p1 += width - m;
3850         }
3851         mul = (double)k / (double)(n * m);
3852         r0 = mul * oneBitRGB[1].r + (1 - mul) * oneBitRGB[0].r;
3853         g0 = mul * oneBitRGB[1].g + (1 - mul) * oneBitRGB[0].g;
3854         b0 = mul * oneBitRGB[1].b + (1 - mul) * oneBitRGB[0].b;
3855         alpha = 0;
3856       } else {
3857         p = pixBuf + xSrc;
3858         q = alphaBuf ? alphaBuf + xSrc : (Guchar *)NULL;
3859         alpha = 0;
3860         r0 = g0 = b0 = 0;
3861         for (i = 0; i < n; ++i) {
3862           for (j = 0; j < m; ++j) {
3863             r0 += p->r;
3864             g0 += p->g;
3865             b0 += p->b;
3866             ++p;
3867             if (q) {
3868               alpha += *q++;
3869             }
3870           }
3871           p += width - m;
3872         }
3873         mul = 1 / (double)(n * m);
3874         r0 *= mul;
3875         g0 *= mul;
3876         b0 *= mul;
3877         alpha *= mul;
3878       }
3879
3880       // x scale Bresenham
3881       xSrc += xStep;
3882
3883       // compute pixel
3884       if (dither) {
3885         color2.r = r0 + errRight.r + errDown0[x + 1].r;
3886         if (color2.r > 1) {
3887           color3.r = 1;
3888         } else if (color2.r < 0) {
3889           color3.r = 0;
3890         } else {
3891           color3.r = color2.r;
3892         }
3893         color2.g = g0 + errRight.g + errDown0[x + 1].g;
3894         if (color2.g > 1) {
3895           color3.g = 1;
3896         } else if (color2.g < 0) {
3897           color3.g = 0;
3898         } else {
3899           color3.g = color2.g;
3900         }
3901         color2.b = b0 + errRight.b + errDown0[x + 1].b;
3902         if (color2.b > 1) {
3903           color3.b = 1;
3904         } else if (color2.b < 0) {
3905           color3.b = 0;
3906         } else {
3907           color3.b = color2.b;
3908         }
3909         pix = findColor(&color3, &actual);
3910         err.r = (color2.r - actual.r) / 16;
3911         err.g = (color2.g - actual.g) / 16;
3912         err.b = (color2.b - actual.b) / 16;
3913         errRight.r = 7 * err.r;
3914         errRight.g = 7 * err.g;
3915         errRight.b = 7 * err.b;
3916         errDown1[x].r += 3 * err.r;
3917         errDown1[x].g += 3 * err.g;
3918         errDown1[x].b += 3 * err.b;
3919         errDown1[x + 1].r += 5 * err.r;
3920         errDown1[x + 1].g += 5 * err.g;
3921         errDown1[x + 1].b += 5 * err.b;
3922         errDown1[x + 2].r = err.r;
3923         errDown1[x + 2].g = err.g;
3924         errDown1[x + 2].b = err.b;
3925       } else {
3926         color2.r = r0;
3927         color2.g = g0;
3928         color2.b = b0;
3929         pix = findColor(&color2, &actual);
3930       }
3931
3932       // set pixel
3933       //~ this should do a blend when 0 < alpha < 1
3934       if (alpha < 0.75) {
3935         XPutPixel(image, tx + x2 - bx0, ty + y2 - by0, pix);
3936       }
3937     }
3938   }
3939
3940   // blit the image into the pixmap
3941   XPutImage(display, pixmap, fillGC, image, cx1, cy1, cx0, cy0, cw, ch);
3942
3943   // free memory
3944   delete imgStr;
3945   if (oneBitMode) {
3946     gfree(pixBuf1);
3947   } else {
3948     gfree(pixBuf);
3949   }
3950   if (maskColors) {
3951     gfree(alphaBuf);
3952   }
3953   gfree(image->data);
3954   image->data = NULL;
3955   XDestroyImage(image);
3956   gfree(errDown0);
3957   gfree(errDown1);
3958 }
3959
3960 GBool XOutputDev::findText(Unicode *s, int len,
3961                            GBool startAtTop, GBool stopAtBottom,
3962                            GBool startAtLast, GBool stopAtLast,
3963                            int *xMin, int *yMin,
3964                            int *xMax, int *yMax) {
3965   double xMin1, yMin1, xMax1, yMax1;
3966   
3967   xMin1 = (double)*xMin;
3968   yMin1 = (double)*yMin;
3969   xMax1 = (double)*xMax;
3970   yMax1 = (double)*yMax;
3971   if (text->findText(s, len, startAtTop, stopAtBottom,
3972                      startAtLast, stopAtLast,
3973                      &xMin1, &yMin1, &xMax1, &yMax1)) {
3974     *xMin = xoutRound(xMin1);
3975     *xMax = xoutRound(xMax1);
3976     *yMin = xoutRound(yMin1);
3977     *yMax = xoutRound(yMax1);
3978     return gTrue;
3979   }
3980   return gFalse;
3981 }
3982
3983 GString *XOutputDev::getText(int xMin, int yMin, int xMax, int yMax) {
3984   return text->getText((double)xMin, (double)yMin,
3985                        (double)xMax, (double)yMax);
3986 }