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