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