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