filters

XOutputDev.cc

00001 //========================================================================
00002 //
00003 // XOutputDev.cc
00004 //
00005 // Copyright 1996-2002 Glyph & Cog, LLC
00006 //
00007 //========================================================================
00008 
00009 #include <aconf.h>
00010 
00011 #ifdef USE_GCC_PRAGMAS
00012 #pragma implementation
00013 #endif
00014 
00015 #include <stdio.h>
00016 #include <stdlib.h>
00017 #include <stddef.h>
00018 #include <unistd.h>
00019 #include <string.h>
00020 #include <ctype.h>
00021 #include <math.h>
00022 #include "gmem.h"
00023 #include "gfile.h"
00024 #include "GString.h"
00025 #include "GList.h"
00026 #include "Object.h"
00027 #include "Stream.h"
00028 #include "Link.h"
00029 #include "GfxState.h"
00030 #include "GfxFont.h"
00031 #include "UnicodeMap.h"
00032 #include "CharCodeToUnicode.h"
00033 #include "FontFile.h"
00034 #include "Error.h"
00035 #include "TextOutputDev.h"
00036 #include "XOutputDev.h"
00037 #if HAVE_T1LIB_H
00038 #include "T1Font.h"
00039 #endif
00040 #if FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H)
00041 #include "FTFont.h"
00042 #endif
00043 #if !FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H)
00044 #include "TTFont.h"
00045 #endif
00046 
00047 #ifdef VMS
00048 #if (__VMS_VER < 70000000)
00049 extern "C" int unlink(char *filename);
00050 #endif
00051 #endif
00052 
00053 #ifdef XlibSpecificationRelease
00054 #if XlibSpecificationRelease < 5
00055 typedef char *XPointer;
00056 #endif
00057 #else
00058 typedef char *XPointer;
00059 #endif
00060 
00061 //------------------------------------------------------------------------
00062 // Constants and macros
00063 //------------------------------------------------------------------------
00064 
00065 #define xoutRound(x) ((int)(x + 0.5))
00066 
00067 #define maxCurveSplits 6    // max number of splits when recursively
00068                 //   drawing Bezier curves
00069 
00070 //------------------------------------------------------------------------
00071 // Font substitutions
00072 //------------------------------------------------------------------------
00073 
00074 struct XOutFontSubst {
00075   char *name;
00076   double mWidth;
00077 };
00078 
00079 // index: {symbolic:12, fixed:8, serif:4, sans-serif:0} + bold*2 + italic
00080 static XOutFontSubst xOutSubstFonts[16] = {
00081   {"Helvetica",             0.833},
00082   {"Helvetica-Oblique",     0.833},
00083   {"Helvetica-Bold",        0.889},
00084   {"Helvetica-BoldOblique", 0.889},
00085   {"Times-Roman",           0.788},
00086   {"Times-Italic",          0.722},
00087   {"Times-Bold",            0.833},
00088   {"Times-BoldItalic",      0.778},
00089   {"Courier",               0.600},
00090   {"Courier-Oblique",       0.600},
00091   {"Courier-Bold",          0.600},
00092   {"Courier-BoldOblique",   0.600},
00093   {"Symbol",                0.576},
00094   {"Symbol",                0.576},
00095   {"Symbol",                0.576},
00096   {"Symbol",                0.576}
00097 };
00098 
00099 //------------------------------------------------------------------------
00100 
00101 static void outputToFile(void *stream, char *data, int len) {
00102   fwrite(data, 1, len, (FILE *)stream);
00103 }
00104 
00105 //------------------------------------------------------------------------
00106 // XOutputFont
00107 //------------------------------------------------------------------------
00108 
00109 XOutputFont::XOutputFont(Ref *idA, double m11OrigA, double m12OrigA,
00110              double m21OrigA, double m22OrigA,
00111              double m11A, double m12A, double m21A, double m22A,
00112              Display *displayA, XOutputDev *xOutA) {
00113   id = *idA;
00114   display = displayA;
00115   xOut = xOutA;
00116   m11Orig = m11OrigA;
00117   m12Orig = m12OrigA;
00118   m21Orig = m21OrigA;
00119   m22Orig = m22OrigA;
00120   m11 = m11A;
00121   m12 = m12A;
00122   m21 = m21A;
00123   m22 = m22A;
00124 }
00125 
00126 XOutputFont::~XOutputFont() {
00127 }
00128 
00129 void XOutputFont::getCharPath(GfxState *state,
00130                   CharCode c, Unicode *u, int ulen) {
00131 }
00132 
00133 #if HAVE_T1LIB_H
00134 //------------------------------------------------------------------------
00135 // XOutputT1Font
00136 //------------------------------------------------------------------------
00137 
00138 XOutputT1Font::XOutputT1Font(Ref *idA, T1FontFile *fontFileA,
00139                  double m11OrigA, double m12OrigA,
00140                  double m21OrigA, double m22OrigA,
00141                  double m11A, double m12A,
00142                  double m21A, double m22A,
00143                  Display *displayA, XOutputDev *xOutA):
00144   XOutputFont(idA, m11OrigA, m12OrigA, m21OrigA, m22OrigA,
00145           m11A, m12A, m21A, m22A, displayA, xOutA)
00146 {
00147   double matrix[4];
00148 
00149   fontFile = fontFileA;
00150 
00151   // create the transformed instance
00152   matrix[0] = m11;
00153   matrix[1] = -m12;
00154   matrix[2] = m21;
00155   matrix[3] = -m22;
00156   font = new T1Font(fontFile, matrix);
00157 }
00158 
00159 XOutputT1Font::~XOutputT1Font() {
00160   if (font) {
00161     delete font;
00162   }
00163 }
00164 
00165 GBool XOutputT1Font::isOk() {
00166   return font != NULL;
00167 }
00168 
00169 void XOutputT1Font::updateGC(GC gc) {
00170 }
00171 
00172 void XOutputT1Font::drawChar(GfxState *state, Pixmap pixmap, int w, int h,
00173                  GC gc, GfxRGB *rgb,
00174                  double x, double y, double dx, double dy,
00175                  CharCode c, Unicode *u, int uLen) {
00176   font->drawChar(pixmap, w, h, gc, xoutRound(x), xoutRound(y),
00177          (int)(rgb->r * 65535), (int)(rgb->g * 65535),
00178          (int)(rgb->b * 65535), c, u[0]);
00179 }
00180 
00181 void XOutputT1Font::getCharPath(GfxState *state,
00182                 CharCode c, Unicode *u, int uLen) {
00183   font->getCharPath(c, u[0], state);
00184 }
00185 #endif // HAVE_T1LIB_H
00186 
00187 #if FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H)
00188 //------------------------------------------------------------------------
00189 // XOutputFTFont
00190 //------------------------------------------------------------------------
00191 
00192 XOutputFTFont::XOutputFTFont(Ref *idA, FTFontFile *fontFileA,
00193                  double m11OrigA, double m12OrigA,
00194                  double m21OrigA, double m22OrigA,
00195                  double m11A, double m12A,
00196                  double m21A, double m22A,
00197                  Display *displayA, XOutputDev *xOutA):
00198   XOutputFont(idA, m11OrigA, m12OrigA, m21OrigA, m22OrigA,
00199           m11A, m12A, m21A, m22A, displayA, xOutA)
00200 {
00201   double matrix[4];
00202 
00203   fontFile = fontFileA;
00204 
00205   // create the transformed instance
00206   matrix[0] = m11;
00207   matrix[1] = -m12;
00208   matrix[2] = m21;
00209   matrix[3] = -m22;
00210   font = new FTFont(fontFile, matrix);
00211 }
00212 
00213 XOutputFTFont::~XOutputFTFont() {
00214   if (font) {
00215     delete font;
00216   }
00217 }
00218 
00219 GBool XOutputFTFont::isOk() {
00220   return font != NULL;
00221 }
00222 
00223 void XOutputFTFont::updateGC(GC gc) {
00224 }
00225 
00226 void XOutputFTFont::drawChar(GfxState *state, Pixmap pixmap, int w, int h,
00227                  GC gc, GfxRGB *rgb,
00228                  double x, double y, double dx, double dy,
00229                  CharCode c, Unicode *u, int uLen) {
00230   font->drawChar(pixmap, w, h, gc, xoutRound(x), xoutRound(y),
00231          (int)(rgb->r * 65535), (int)(rgb->g * 65535),
00232          (int)(rgb->b * 65535), c, uLen > 0 ? u[0] : 0);
00233 }
00234 
00235 void XOutputFTFont::getCharPath(GfxState *state,
00236                 CharCode c, Unicode *u, int uLen) {
00237   font->getCharPath(c, u[0], state);
00238 }
00239 #endif // FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H)
00240 
00241 #if !FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H)
00242 //------------------------------------------------------------------------
00243 // XOutputTTFont
00244 //------------------------------------------------------------------------
00245 
00246 XOutputTTFont::XOutputTTFont(Ref *idA, TTFontFile *fontFileA,
00247                  double m11OrigA, double m12OrigA,
00248                  double m21OrigA, double m22OrigA,
00249                  double m11A, double m12A,
00250                  double m21A, double m22A,
00251                  Display *displayA, XOutputDev *xOutA):
00252   XOutputFont(idA, m11OrigA, m12OrigA, m21OrigA, m22OrigA,
00253           m11A, m12A, m21A, m22A, displayA, xOutA)
00254 {
00255   double matrix[4];
00256 
00257   fontFile = fontFileA;
00258 
00259   // create the transformed instance
00260   matrix[0] = m11;
00261   matrix[1] = -m12;
00262   matrix[2] = m21;
00263   matrix[3] = -m22;
00264   font = new TTFont(fontFile, matrix);
00265 }
00266 
00267 XOutputTTFont::~XOutputTTFont() {
00268   if (font) {
00269     delete font;
00270   }
00271 }
00272 
00273 GBool XOutputTTFont::isOk() {
00274   return font != NULL;
00275 }
00276 
00277 void XOutputTTFont::updateGC(GC gc) {
00278 }
00279 
00280 void XOutputTTFont::drawChar(GfxState *state, Pixmap pixmap, int w, int h,
00281                  GC gc, GfxRGB *rgb,
00282                  double x, double y, double dx, double dy,
00283                  CharCode c, Unicode *u, int uLen) {
00284   font->drawChar(pixmap, w, h, gc, xoutRound(x), xoutRound(y),
00285          (int)(rgb->r * 65535), (int)(rgb->g * 65535),
00286          (int)(rgb->b * 65535), c, u[0]);
00287 }
00288 #endif // !FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H)
00289 
00290 //------------------------------------------------------------------------
00291 // XOutputServer8BitFont
00292 //------------------------------------------------------------------------
00293 
00294 // Copy <fmt>, substituting <val> for one occurrence of "%s", into
00295 // <buf>.
00296 static void stringSubst(char *buf, int bufSize, char *fmt, char *val) {
00297   char *p, *q;
00298   int i;
00299 
00300   i = 0;
00301   p = fmt;
00302   while (*p) {
00303     if (p[0] == '%' && p[1] == 's') {
00304       q = val;
00305       while (*q && i < bufSize - 1) {
00306     buf[i++] = *q++;
00307       }
00308       p += 2;
00309     } else {
00310       if (i < bufSize - 1) {
00311     buf[i++] = *p;
00312       }
00313       ++p;
00314     }
00315   }
00316   buf[i] = '\0';
00317 }
00318 
00319 XOutputServer8BitFont::XOutputServer8BitFont(Ref *idA, GString *xlfdFmt, 
00320                          UnicodeMap *xUMapA,
00321                          CharCodeToUnicode *fontUMap,
00322                          double m11OrigA, double m12OrigA,
00323                          double m21OrigA, double m22OrigA,
00324                          double m11A, double m12A,
00325                          double m21A, double m22A,
00326                          Display *displayA,
00327                          XOutputDev *xOutA):
00328   XOutputFont(idA, m11OrigA, m12OrigA, m21OrigA, m22OrigA,
00329           m11A, m12A, m21A, m22A, displayA, xOutA)
00330 {
00331   double size, ntm11, ntm12, ntm21, ntm22;
00332   GBool rotated;
00333   int startSize, sz;
00334   char fontName[500], fontSize[100];
00335   Unicode u;
00336   char buf;
00337   int i;
00338 
00339   // compute size and normalized transform matrix
00340   size = sqrt(m21*m21 + m22*m22);
00341   ntm11 = m11 / size;
00342   ntm12 = -m12 / size;
00343   ntm21 = m21 / size;
00344   ntm22 = -m22 / size;
00345 
00346   // try to get a rotated font?
00347   rotated = !(ntm11 > 0 && ntm22 > 0 &&
00348           fabs(ntm11 / ntm22 - 1) < 0.2 &&
00349           fabs(ntm12) < 0.01 &&
00350           fabs(ntm21) < 0.01);
00351 
00352   // open X font -- if font is not found (which means the server can't
00353   // scale fonts), try progressively smaller and then larger sizes
00354   startSize = (int)size;
00355   if (rotated) {
00356     sprintf(fontSize, "[%s%0.2f %s%0.2f %s%0.2f %s%0.2f]",
00357         ntm11<0 ? "~" : "", fabs(ntm11 * size),
00358         ntm12<0 ? "~" : "", fabs(ntm12 * size),
00359         ntm21<0 ? "~" : "", fabs(ntm21 * size),
00360         ntm22<0 ? "~" : "", fabs(ntm22 * size));
00361   } else {
00362     sprintf(fontSize, "%d", startSize);
00363   }
00364   stringSubst(fontName, sizeof(fontName), xlfdFmt->getCString(), fontSize);
00365   xFont = XLoadQueryFont(display, fontName);
00366   if (!xFont) {
00367     for (sz = startSize; sz >= startSize/2 && sz >= 1; --sz) {
00368       sprintf(fontSize, "%d", sz);
00369       stringSubst(fontName, sizeof(fontName), xlfdFmt->getCString(), fontSize);
00370       if ((xFont = XLoadQueryFont(display, fontName)))
00371     break;
00372     }
00373     if (!xFont) {
00374       for (sz = startSize + 1; sz < startSize + 10; ++sz) {
00375     sprintf(fontSize, "%d", sz);
00376     stringSubst(fontName, sizeof(fontName), xlfdFmt->getCString(),
00377             fontSize);
00378     if ((xFont = XLoadQueryFont(display, fontName))) {
00379       break;
00380     }
00381       }
00382       if (!xFont) {
00383     sprintf(fontSize, "%d", startSize);
00384     stringSubst(fontName, sizeof(fontName), xlfdFmt->getCString(),
00385             fontSize);
00386     error(-1, "Failed to open font: '%s'", fontName);
00387     return;
00388       }
00389     }
00390   }
00391 
00392   // Construct char code map.
00393   xUMap = xUMapA;
00394   for (i = 0; i < 256; ++i) {
00395     if (fontUMap->mapToUnicode((CID)i, &u, 1) == 1 &&
00396     xUMap->mapUnicode(u, &buf, 1) == 1) {
00397       map[i] = buf & 0xff;
00398     } else {
00399       map[i] = 0;
00400     }
00401   }
00402 }
00403 
00404 XOutputServer8BitFont::~XOutputServer8BitFont() {
00405   if (xFont) {
00406     XFreeFont(display, xFont);
00407   }
00408 }
00409 
00410 GBool XOutputServer8BitFont::isOk() {
00411   return xFont != NULL;
00412 }
00413 
00414 void XOutputServer8BitFont::updateGC(GC gc) {
00415   XSetFont(display, gc, xFont->fid);
00416 }
00417 
00418 void XOutputServer8BitFont::drawChar(GfxState *state, Pixmap pixmap,
00419                      int w, int h, GC gc, GfxRGB *rgb,
00420                      double x, double y, double dx, double dy,
00421                      CharCode c, Unicode *u, int uLen) {
00422   Gushort c1;
00423   char buf[8];
00424   double dx1, dy1;
00425   int m, n, i, j, k;
00426 
00427   c1 = map[c];
00428   if (c1 > 0) {
00429     buf[0] = (char)c1;
00430     XDrawString(display, pixmap, gc, xoutRound(x), xoutRound(y), buf, 1);
00431   } else {
00432     // substituted character, using more than one character
00433     n = 0;
00434     for (i = 0; i < uLen; ++i) {
00435       n += xUMap->mapUnicode(u[i], buf, sizeof(buf));
00436     }
00437     if (n > 0) {
00438       dx1 = dx / n;
00439       dy1 = dy / n;
00440       k = 0;
00441       for (i = 0; i < uLen; ++i) {
00442     m = xUMap->mapUnicode(u[i], buf, sizeof(buf));
00443     for (j = 0; j < m; ++j) {
00444       XDrawString(display, pixmap, gc,
00445               xoutRound(x + k*dx1), xoutRound(y + k*dy1),
00446               buf + j, 1);
00447       ++k;
00448     }
00449       }
00450     }
00451   }
00452 }
00453 
00454 //------------------------------------------------------------------------
00455 // XOutputServer16BitFont
00456 //------------------------------------------------------------------------
00457 
00458 XOutputServer16BitFont::XOutputServer16BitFont(Ref *idA, GString *xlfdFmt, 
00459                            UnicodeMap *xUMapA,
00460                            CharCodeToUnicode *fontUMap,
00461                            double m11OrigA,
00462                            double m12OrigA,
00463                            double m21OrigA,
00464                            double m22OrigA,
00465                            double m11A, double m12A,
00466                            double m21A, double m22A,
00467                            Display *displayA,
00468                            XOutputDev *xOutA):
00469   XOutputFont(idA, m11OrigA, m12OrigA, m21OrigA, m22OrigA,
00470           m11A, m12A, m21A, m22A, displayA, xOutA)
00471 {
00472   double size, ntm11, ntm12, ntm21, ntm22;
00473   GBool rotated;
00474   int startSize, sz;
00475   char fontName[500], fontSize[100];
00476 
00477   xUMap = xUMapA;
00478   xUMap->incRefCnt();
00479 
00480   // compute size and normalized transform matrix
00481   size = sqrt(m21*m21 + m22*m22);
00482   ntm11 = m11 / size;
00483   ntm12 = -m12 / size;
00484   ntm21 = m21 / size;
00485   ntm22 = -m22 / size;
00486 
00487   // try to get a rotated font?
00488   rotated = !(ntm11 > 0 && ntm22 > 0 &&
00489           fabs(ntm11 / ntm22 - 1) < 0.2 &&
00490           fabs(ntm12) < 0.01 &&
00491           fabs(ntm21) < 0.01);
00492 
00493   // open X font -- if font is not found (which means the server can't
00494   // scale fonts), try progressively smaller and then larger sizes
00495   startSize = (int)size;
00496   if (rotated) {
00497     sprintf(fontSize, "[%s%0.2f %s%0.2f %s%0.2f %s%0.2f]",
00498         ntm11<0 ? "~" : "", fabs(ntm11 * size),
00499         ntm12<0 ? "~" : "", fabs(ntm12 * size),
00500         ntm21<0 ? "~" : "", fabs(ntm21 * size),
00501         ntm22<0 ? "~" : "", fabs(ntm22 * size));
00502   } else {
00503     sprintf(fontSize, "%d", startSize);
00504   }
00505   stringSubst(fontName, sizeof(fontName), xlfdFmt->getCString(), fontSize);
00506   xFont = XLoadQueryFont(display, fontName);
00507   if (!xFont) {
00508     for (sz = startSize; sz >= startSize/2 && sz >= 1; --sz) {
00509       sprintf(fontSize, "%d", sz);
00510       stringSubst(fontName, sizeof(fontName), xlfdFmt->getCString(), fontSize);
00511       if ((xFont = XLoadQueryFont(display, fontName)))
00512     break;
00513     }
00514     if (!xFont) {
00515       for (sz = startSize + 1; sz < startSize + 10; ++sz) {
00516     sprintf(fontSize, "%d", sz);
00517     stringSubst(fontName, sizeof(fontName), xlfdFmt->getCString(),
00518             fontSize);
00519     if ((xFont = XLoadQueryFont(display, fontName))) {
00520       break;
00521     }
00522       }
00523       if (!xFont) {
00524     sprintf(fontSize, "%d", startSize);
00525     stringSubst(fontName, sizeof(fontName), xlfdFmt->getCString(),
00526             fontSize);
00527     error(-1, "Failed to open font: '%s'", fontName);
00528     return;
00529       }
00530     }
00531   }
00532 }
00533 
00534 XOutputServer16BitFont::~XOutputServer16BitFont() {
00535   xUMap->decRefCnt();
00536   if (xFont) {
00537     XFreeFont(display, xFont);
00538   }
00539 }
00540 
00541 GBool XOutputServer16BitFont::isOk() {
00542   return xFont != NULL;
00543 }
00544 
00545 void XOutputServer16BitFont::updateGC(GC gc) {
00546   XSetFont(display, gc, xFont->fid);
00547 }
00548 
00549 void XOutputServer16BitFont::drawChar(GfxState *state, Pixmap pixmap,
00550                       int w, int h, GC gc, GfxRGB *rgb,
00551                       double x, double y, double dx, double dy,
00552                       CharCode c, Unicode *u, int uLen) {
00553   char buf[16];
00554   XChar2b c1;
00555   double dx1, dy1;
00556   int m, n, i, j, k;
00557 
00558   n = 0;
00559   for (i = 0; i < uLen; ++i) {
00560     n += xUMap->mapUnicode(u[i], buf, sizeof(buf));
00561   }
00562   if (n > 0) {
00563     dx1 = dx / n;
00564     dy1 = dy / n;
00565     k = 0;
00566     for (i = 0; i < uLen; ++i) {
00567       m = xUMap->mapUnicode(u[i], buf, sizeof(buf));
00568       for (j = 0; j+1 < m; j += 2) {
00569     c1.byte1 = buf[j];
00570     c1.byte2 = buf[j+1];
00571     XDrawString16(display, pixmap, gc,
00572               xoutRound(x + k*dx1), xoutRound(y + k*dy1),
00573               &c1, 1);
00574     ++k;
00575       }
00576     }
00577   } else if (c != 0) {
00578     // some PDF files use CID 0, which is .notdef, so just ignore it
00579     error(-1, "Unknown character (CID=%d Unicode=%04x)",
00580       c, uLen > 0 ? u[0] : (Unicode)0);
00581   }
00582 }
00583 
00584 //------------------------------------------------------------------------
00585 // XOutputFontCache
00586 //------------------------------------------------------------------------
00587 
00588 #if HAVE_T1LIB_H
00589 XOutputT1FontFile::~XOutputT1FontFile() {
00590   delete fontFile;
00591   if (tmpFileName) {
00592     unlink(tmpFileName->getCString());
00593     delete tmpFileName;
00594   }
00595 }
00596 #endif
00597 
00598 #if FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H)
00599 XOutputFTFontFile::~XOutputFTFontFile() {
00600   delete fontFile;
00601   if (tmpFileName) {
00602     unlink(tmpFileName->getCString());
00603     delete tmpFileName;
00604   }
00605 }
00606 #endif
00607 
00608 #if !FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H)
00609 XOutputTTFontFile::~XOutputTTFontFile() {
00610   delete fontFile;
00611   if (tmpFileName) {
00612     unlink(tmpFileName->getCString());
00613     delete tmpFileName;
00614   }
00615 }
00616 #endif
00617 
00618 XOutputFontCache::XOutputFontCache(Display *displayA, Guint depthA,
00619                    XOutputDev *xOutA,
00620                    FontRastControl t1libControlA,
00621                    FontRastControl freetypeControlA) {
00622   display = displayA;
00623   depth = depthA;
00624   xOut = xOutA;
00625 
00626 #if HAVE_T1LIB_H
00627   t1Engine = NULL;
00628   t1libControl = t1libControlA;
00629 #endif
00630 
00631 #if FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H)
00632   ftEngine = NULL;
00633 #endif
00634 #if !FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H)
00635   ttEngine = NULL;
00636 #endif
00637 #if HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H
00638   freetypeControl = freetypeControlA;
00639 #endif
00640 
00641   clear();
00642 }
00643 
00644 XOutputFontCache::~XOutputFontCache() {
00645   delFonts();
00646 }
00647 
00648 void XOutputFontCache::startDoc(int screenNum, Visual *visual,
00649                 Colormap colormap, GBool trueColor,
00650                 int rMul, int gMul, int bMul,
00651                 int rShift, int gShift, int bShift,
00652                 Gulong *colors, int numColors) {
00653   delFonts();
00654   clear();
00655 
00656 #if HAVE_T1LIB_H
00657   if (t1libControl != fontRastNone) {
00658     t1Engine = new T1FontEngine(display, visual, depth, colormap,
00659                 t1libControl == fontRastAALow ||
00660                   t1libControl == fontRastAAHigh,
00661                 t1libControl == fontRastAAHigh);
00662     if (t1Engine->isOk()) {
00663       if (trueColor) {
00664     t1Engine->useTrueColor(rMul, rShift, gMul, gShift, bMul, bShift);
00665       } else {
00666     t1Engine->useColorCube(colors, numColors);
00667       }
00668     } else {
00669       delete t1Engine;
00670       t1Engine = NULL;
00671     }
00672   }
00673 #endif // HAVE_T1LIB_H
00674 
00675 #if FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H)
00676   if (freetypeControl != fontRastNone) {
00677     ftEngine = new FTFontEngine(display, visual, depth, colormap,
00678                 freetypeControl == fontRastAALow ||
00679                   freetypeControl == fontRastAAHigh);
00680     if (ftEngine->isOk()) {
00681       if (trueColor) {
00682     ftEngine->useTrueColor(rMul, rShift, gMul, gShift, bMul, bShift);
00683       } else {
00684     ftEngine->useColorCube(colors, numColors);
00685       }
00686     } else {
00687       delete ftEngine;
00688       ftEngine = NULL;
00689     }
00690   }
00691 #endif // FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H)
00692 
00693 #if !FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H)
00694   if (freetypeControl != fontRastNone) {
00695     ttEngine = new TTFontEngine(display, visual, depth, colormap,
00696                 freetypeControl == fontRastAALow ||
00697                   freetypeControl == fontRastAAHigh);
00698     if (ttEngine->isOk()) {
00699       if (trueColor) {
00700     ttEngine->useTrueColor(rMul, rShift, gMul, gShift, bMul, bShift);
00701       } else {
00702     ttEngine->useColorCube(colors, numColors);
00703       }
00704     } else {
00705       delete ttEngine;
00706       ttEngine = NULL;
00707     }
00708   }
00709 #endif // !FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H)
00710 }
00711 
00712 void XOutputFontCache::delFonts() {
00713   int i;
00714 
00715   for (i = 0; i < nFonts; ++i) {
00716     delete fonts[i];
00717   }
00718 
00719 #if HAVE_T1LIB_H
00720   // delete Type 1 font files
00721   deleteGList(t1FontFiles, XOutputT1FontFile);
00722   if (t1Engine) {
00723     delete t1Engine;
00724   }
00725 #endif
00726 
00727 #if FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H)
00728   // delete FreeType font files
00729   deleteGList(ftFontFiles, XOutputFTFontFile);
00730   if (ftEngine) {
00731     delete ftEngine;
00732   }
00733 #endif
00734 
00735 #if !FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H)
00736   // delete TrueType fonts
00737   deleteGList(ttFontFiles, XOutputTTFontFile);
00738   if (ttEngine) {
00739     delete ttEngine;
00740   }
00741 #endif
00742 }
00743 
00744 void XOutputFontCache::clear() {
00745   int i;
00746 
00747   for (i = 0; i < xOutFontCacheSize; ++i) {
00748     fonts[i] = NULL;
00749   }
00750   nFonts = 0;
00751 
00752 #if HAVE_T1LIB_H
00753   // clear Type 1 font files
00754   t1FontFiles = new GList();
00755 #endif
00756 
00757 #if FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H)
00758   // clear FreeType font cache
00759   ftFontFiles = new GList();
00760 #endif
00761 
00762 #if !FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H)
00763   // clear TrueType font cache
00764   ttFontFiles = new GList();
00765 #endif
00766 }
00767 
00768 XOutputFont *XOutputFontCache::getFont(XRef *xref, GfxFont *gfxFont,
00769                        double m11, double m12,
00770                        double m21, double m22) {
00771   XOutputFont *font;
00772   DisplayFontParam *dfp;
00773   GString *substName;
00774   double m11New, m12New, m21New, m22New;
00775   double w1, w2, v;
00776   double *fm;
00777   char *name;
00778   int index;
00779   int code;
00780   int i, j;
00781 
00782   // is it the most recently used font?
00783   if (nFonts > 0 && fonts[0]->matches(gfxFont->getID(), m11, m12, m21, m22)) {
00784     return fonts[0];
00785   }
00786 
00787   // is it in the cache?
00788   for (i = 1; i < nFonts; ++i) {
00789     if (fonts[i]->matches(gfxFont->getID(), m11, m12, m21, m22)) {
00790       font = fonts[i];
00791       for (j = i; j > 0; --j) {
00792     fonts[j] = fonts[j-1];
00793       }
00794       fonts[0] = font;
00795       return font;
00796     }
00797   }
00798 
00799   // try for a cached FontFile, an embedded font, or an external font
00800   // file
00801   font = NULL;
00802   switch (gfxFont->getType()) {
00803   case fontType1:
00804   case fontType1C:
00805 #if HAVE_T1LIB_H
00806     if (t1libControl != fontRastNone) {
00807       font = tryGetT1Font(xref, gfxFont, m11, m12, m21, m22);
00808     }
00809 #endif
00810 #if FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H)
00811     if (!font) {
00812       if (freetypeControl != fontRastNone) {
00813     font = tryGetFTFont(xref, gfxFont, m11, m12, m21, m22);
00814       }
00815     }
00816 #endif
00817     break;
00818   case fontTrueType:
00819   case fontCIDType2:
00820 #if FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H)
00821     if (freetypeControl != fontRastNone) {
00822       font = tryGetFTFont(xref, gfxFont, m11, m12, m21, m22);
00823     }
00824 #endif
00825 #if !FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H)
00826     if (freetypeControl != fontRastNone) {
00827       font = tryGetTTFont(xref, gfxFont, m11, m12, m21, m22);
00828     }
00829 #endif
00830     break;
00831   case fontCIDType0C:
00832 #if FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H)
00833     if (freetypeControl != fontRastNone) {
00834       font = tryGetFTFont(xref, gfxFont, m11, m12, m21, m22);
00835     }
00836 #endif
00837     break;
00838   default:
00839     break;
00840   }
00841 
00842   if (!font) {
00843 
00844     // search for a display font mapping
00845     dfp = NULL;
00846     if (gfxFont->isCIDFont()) {
00847       if (((GfxCIDFont *)gfxFont)->getCollection()) {
00848     dfp = globalParams->
00849             getDisplayCIDFont(gfxFont->getName(),
00850                   ((GfxCIDFont *)gfxFont)->getCollection());
00851       } else {
00852     // this error (no CMap file) was already reported by GfxFont
00853     return NULL;
00854       }
00855     } else {
00856       if (gfxFont->getName()) {
00857     dfp = globalParams->getDisplayFont(gfxFont->getName());
00858       }
00859     }
00860     if (dfp) {
00861       font = tryGetFont(xref, dfp, gfxFont, m11, m12, m21, m22,
00862             m11, m12, m21, m22, gFalse);
00863     }
00864 
00865     // substitute a font (8-bit fonts only)
00866     if (!font && !gfxFont->isCIDFont()) {
00867 
00868       // choose a substitute font
00869       if (gfxFont->isFixedWidth()) {
00870     index = 8;
00871       } else if (gfxFont->isSerif()) {
00872     index = 4;
00873       } else {
00874     index = 0;
00875       }
00876       if (gfxFont->isBold()) {
00877     index += 2;
00878       }
00879       if (gfxFont->isItalic()) {
00880     index += 1;
00881       }
00882       substName = new GString(xOutSubstFonts[index].name);
00883 
00884       // adjust the font matrix -- compare the width of 'm' in the
00885       // original font and the substituted font
00886       m11New = m11;
00887       m12New = m12;
00888       m21New = m21;
00889       m22New = m22;
00890       for (code = 0; code < 256; ++code) {
00891     if ((name = ((Gfx8BitFont *)gfxFont)->getCharName(code)) &&
00892         name[0] == 'm' && name[1] == '\0') {
00893       break;
00894     }
00895       }
00896       if (code < 256) {
00897     w1 = ((Gfx8BitFont *)gfxFont)->getWidth(code);
00898     w2 = xOutSubstFonts[index].mWidth;
00899     if (gfxFont->getType() == fontType3) {
00900       // This is a hack which makes it possible to substitute for some
00901       // Type 3 fonts.  The problem is that it's impossible to know what
00902       // the base coordinate system used in the font is without actually
00903       // rendering the font.  This code tries to guess by looking at the
00904       // width of the character 'm' (which breaks if the font is a
00905       // subset that doesn't contain 'm').
00906       if (w1 > 0 && (w1 > 1.1 * w2 || w1 < 0.9 * w2)) {
00907         w1 /= w2;
00908         m11New *= w1;
00909         m12New *= w1;
00910         m21New *= w1;
00911         m22New *= w1;
00912       }
00913       fm = gfxFont->getFontMatrix();
00914       v = (fm[0] == 0) ? 1 : (fm[3] / fm[0]);
00915       m21New *= v;
00916       m22New *= v;
00917     } else if (!gfxFont->isSymbolic()) {
00918       // if real font is substantially narrower than substituted
00919       // font, reduce the font size accordingly
00920       if (w1 > 0.01 && w1 < 0.9 * w2) {
00921         w1 /= w2;
00922         m11New *= w1;
00923         m21New *= w1;
00924       }
00925     }
00926       }
00927 
00928       // get the font
00929       dfp = globalParams->getDisplayFont(substName);
00930       delete substName;
00931       if (!dfp) {
00932     // this should never happen since GlobalParams sets up default
00933     // mappings for the Base-14 fonts
00934     error(-1, "Couldn't find a font for '%s'",
00935           gfxFont->getName()->getCString());
00936     return NULL;
00937       }
00938       font = tryGetFont(xref, dfp, gfxFont, m11, m12, m21, m22,
00939             m11New, m12New, m21New, m22New, gTrue);
00940     }
00941   }
00942 
00943   // check for error
00944   if (!font) {
00945     // This will happen if the user specifies a bogus font in the
00946     // config file (a non-existent font file or a font that requires a
00947     // rasterizer that is disabled or wasn't built in), or if a CID
00948     // font has no associated font in the config file.
00949     if (gfxFont->isCIDFont()) {
00950       error(-1, "Couldn't find a font for the '%s' character collection",
00951         ((GfxCIDFont *)gfxFont)->getCollection()->getCString());
00952     } else {
00953       error(-1, "Couldn't find a font for '%s'",
00954         gfxFont->getName() ?
00955             gfxFont->getName()->getCString() : "[unnamed]");
00956     }
00957     return NULL;
00958   }
00959 
00960   // insert font in cache
00961   if (nFonts == xOutFontCacheSize) {
00962     --nFonts;
00963     delete fonts[nFonts];
00964   }
00965   for (j = nFonts; j > 0; --j) {
00966     fonts[j] = fonts[j-1];
00967   }
00968   fonts[0] = font;
00969   ++nFonts;
00970 
00971   return font;
00972 }
00973 
00974 XOutputFont *XOutputFontCache::tryGetFont(XRef *xref, DisplayFontParam *dfp,
00975                       GfxFont *gfxFont,
00976                       double m11Orig, double m12Orig,
00977                       double m21Orig, double m22Orig,
00978                       double m11, double m12,
00979                       double m21, double m22,
00980                       GBool subst) {
00981   XOutputFont *font;
00982 
00983   font = NULL;
00984 
00985   // create the new font
00986   switch (dfp->kind) {
00987 
00988   case displayFontX:
00989     font = tryGetServerFont(dfp->x.xlfd, dfp->x.encoding, gfxFont,
00990                 m11Orig, m12Orig, m21Orig, m22Orig,
00991                 m11, m12, m21, m22);
00992     break;
00993 
00994   case displayFontT1:
00995 #if HAVE_T1LIB_H
00996     if (t1libControl != fontRastNone) {
00997       font = tryGetT1FontFromFile(xref, dfp->t1.fileName, gFalse, gfxFont,
00998                   m11Orig, m12Orig, m21Orig, m22Orig,
00999                   m11, m12, m21, m22, subst);
01000     }
01001 #endif
01002 #if FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H)
01003     if (!font) {
01004       if (freetypeControl != fontRastNone) {
01005     font = tryGetFTFontFromFile(xref, dfp->t1.fileName, gFalse, gfxFont,
01006                     m11Orig, m12Orig, m21Orig, m22Orig,
01007                     m11, m12, m21, m22, subst);
01008       }
01009     }
01010 #endif
01011 #if !((FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H)) || defined(HAVE_T1LIB_H))
01012     error(-1, "Config file specifies a Type 1 font,");
01013     error(-1, "but xpdf was not built with t1lib or FreeType2 support");
01014 #endif
01015     break;
01016 
01017   case displayFontTT:
01018 #if FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H)
01019     if (freetypeControl != fontRastNone) {
01020       font = tryGetFTFontFromFile(xref, dfp->tt.fileName, gFalse, gfxFont,
01021                   m11Orig, m12Orig, m21Orig, m22Orig,
01022                   m11, m12, m21, m22, subst);
01023     }
01024 #endif
01025 #if !FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H)
01026     if (freetypeControl != fontRastNone) {
01027       font = tryGetTTFontFromFile(xref, dfp->tt.fileName, gFalse, gfxFont,
01028                   m11Orig, m12Orig, m21Orig, m22Orig,
01029                   m11, m12, m21, m22, subst);
01030     }
01031 #endif
01032 #if !(HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H)
01033     error(-1, "Config file specifies a TrueType font,");
01034     error(-1, "but xpdf was not built with FreeType support");
01035     dfp = NULL;
01036 #endif
01037     break;
01038   }
01039 
01040   return font;
01041 }
01042 
01043 #if HAVE_T1LIB_H
01044 XOutputFont *XOutputFontCache::tryGetT1Font(XRef *xref,
01045                         GfxFont *gfxFont,
01046                         double m11, double m12,
01047                         double m21, double m22) {
01048   Ref *id;
01049   XOutputT1FontFile *xFontFile;
01050   XOutputFont *font;
01051   Ref embRef;
01052   GString *fileName;
01053   FILE *f;
01054   char *fontBuf;
01055   int fontLen;
01056   Type1CFontFile *ff;
01057   Object refObj, strObj;
01058   int c;
01059   int i;
01060 
01061   // check the already available font files
01062   id = gfxFont->getID();
01063   for (i = 0; i < t1FontFiles->getLength(); ++i) {
01064     xFontFile = (XOutputT1FontFile *)t1FontFiles->get(i);
01065     if (xFontFile->num == id->num && xFontFile->gen == id->gen &&
01066     !xFontFile->subst) {
01067       font = new XOutputT1Font(id, xFontFile->fontFile,
01068                    m11, m12, m21, m22,
01069                    m11, m12, m21, m22, display, xOut);
01070       if (!font->isOk()) {
01071     delete font;
01072     return NULL;
01073       }
01074       return font;
01075     }
01076   }
01077 
01078   // check for an embedded font
01079   if (gfxFont->getEmbeddedFontID(&embRef)) {
01080 
01081     // create the font file
01082     fileName = NULL;
01083     if (!openTempFile(&fileName, &f, "wb", NULL)) {
01084       error(-1, "Couldn't create temporary Type 1 font file");
01085       return NULL;
01086     }
01087     if (gfxFont->getType() == fontType1C) {
01088       if (!(fontBuf = gfxFont->readEmbFontFile(xref, &fontLen))) {
01089     fclose(f);
01090     return NULL;
01091       }
01092       ff = new Type1CFontFile(fontBuf, fontLen);
01093       ff->convertToType1(outputToFile, f);
01094       delete ff;
01095       gfree(fontBuf);
01096     } else { // fontType1
01097       refObj.initRef(embRef.num, embRef.gen);
01098       refObj.fetch(xref, &strObj);
01099       refObj.free();
01100       strObj.streamReset();
01101       while ((c = strObj.streamGetChar()) != EOF) {
01102     fputc(c, f);
01103       }
01104       strObj.streamClose();
01105       strObj.free();
01106     }
01107     fclose(f);
01108 
01109     // create the Font
01110     font = tryGetT1FontFromFile(xref, fileName, gTrue, gfxFont,
01111                 m11, m12, m21, m22,
01112                 m11, m12, m21, m22, gFalse);
01113 
01114     // on systems with Unix hard link semantics, this will remove the
01115     // last link to the temp file
01116     unlink(fileName->getCString());
01117 
01118     delete fileName;
01119 
01120   // check for an external font file
01121   } else if ((fileName = gfxFont->getExtFontFile())) {
01122     font = tryGetT1FontFromFile(xref, fileName, gFalse, gfxFont,
01123                 m11, m12, m21, m22,
01124                 m11, m12, m21, m22, gFalse);
01125 
01126   } else {
01127     font = NULL;
01128   }
01129 
01130   return font;
01131 }
01132 
01133 XOutputFont *XOutputFontCache::tryGetT1FontFromFile(XRef *xref,
01134                             GString *fileName,
01135                             GBool deleteFile,
01136                             GfxFont *gfxFont,
01137                             double m11Orig,
01138                             double m12Orig,
01139                             double m21Orig,
01140                             double m22Orig,
01141                             double m11, double m12,
01142                             double m21, double m22,
01143                             GBool subst) {
01144   Ref *id;
01145   T1FontFile *fontFile;
01146   XOutputFont *font;
01147 
01148   // create the t1lib font file
01149   fontFile = new T1FontFile(t1Engine, fileName->getCString(),
01150                 ((Gfx8BitFont *)gfxFont)->getEncoding(),
01151                 gfxFont->getFontBBox());
01152   if (!fontFile->isOk()) {
01153     error(-1, "Couldn't create t1lib font from '%s'",
01154       fileName->getCString());
01155     delete fontFile;
01156     if (deleteFile) {
01157       unlink(fileName->getCString());
01158     }
01159     return NULL;
01160   }
01161 
01162   // add to list
01163   id = gfxFont->getID();
01164   t1FontFiles->append(new XOutputT1FontFile(id->num, id->gen,
01165                         subst, fontFile,
01166                         deleteFile ? fileName->copy()
01167                                    : (GString *)NULL));
01168 
01169   // create the Font
01170   font = new XOutputT1Font(gfxFont->getID(), fontFile,
01171                m11Orig, m12Orig, m21Orig, m22Orig,
01172                m11, m12, m21, m22, display, xOut);
01173   if (!font->isOk()) {
01174     delete font;
01175     return NULL;
01176   }
01177   return font;
01178 }
01179 #endif // HAVE_T1LIB_H
01180 
01181 #if FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H)
01182 XOutputFont *XOutputFontCache::tryGetFTFont(XRef *xref,
01183                         GfxFont *gfxFont,
01184                         double m11, double m12,
01185                         double m21, double m22) {
01186   Ref *id;
01187   XOutputFTFontFile *xFontFile;
01188   XOutputFont *font;
01189   Ref embRef;
01190   GString *fileName;
01191   FILE *f;
01192 #if 1 //~ need this until FT can handle fonts with missing tables
01193   char *fontBuf;
01194   int fontLen;
01195   TrueTypeFontFile *ff;
01196 #endif
01197   Object refObj, strObj;
01198   int c;
01199   int i;
01200 
01201   // check the already available font files
01202   id = gfxFont->getID();
01203   for (i = 0; i < ftFontFiles->getLength(); ++i) {
01204     xFontFile = (XOutputFTFontFile *)ftFontFiles->get(i);
01205     if (xFontFile->num == id->num && xFontFile->gen == id->gen &&
01206     !xFontFile->subst) {
01207       font = new XOutputFTFont(id, xFontFile->fontFile,
01208                    m11, m12, m21, m22,
01209                    m11, m12, m21, m22, display, xOut);
01210       if (!font->isOk()) {
01211     delete font;
01212     return NULL;
01213       }
01214       return font;
01215     }
01216   }
01217 
01218   // check for an embedded font
01219   if (gfxFont->getEmbeddedFontID(&embRef)) {
01220 
01221     // create the font file
01222     fileName = NULL;
01223     if (!openTempFile(&fileName, &f, "wb", NULL)) {
01224       error(-1, "Couldn't create temporary TrueType font file");
01225       return NULL;
01226     }
01227 #if 1 //~ need this until FT can handle fonts with missing tables
01228     if (gfxFont->getType() == fontTrueType ||
01229     gfxFont->getType() == fontCIDType2) {
01230       if (!(fontBuf = gfxFont->readEmbFontFile(xref, &fontLen))) {
01231     fclose(f);
01232     return NULL;
01233       }
01234       ff = new TrueTypeFontFile(fontBuf, fontLen);
01235       ff->writeTTF(f);
01236       delete ff;
01237       gfree(fontBuf);
01238     } else {
01239       refObj.initRef(embRef.num, embRef.gen);
01240       refObj.fetch(xref, &strObj);
01241       refObj.free();
01242       strObj.streamReset();
01243       while ((c = strObj.streamGetChar()) != EOF) {
01244     fputc(c, f);
01245       }
01246       strObj.streamClose();
01247       strObj.free();
01248     }
01249 #else
01250     refObj.initRef(embRef.num, embRef.gen);
01251     refObj.fetch(xref, &strObj);
01252     refObj.free();
01253     strObj.streamReset();
01254     while ((c = strObj.streamGetChar()) != EOF) {
01255       fputc(c, f);
01256     }
01257     strObj.streamClose();
01258     strObj.free();
01259 #endif
01260     fclose(f);
01261 
01262     // create the Font
01263     font = tryGetFTFontFromFile(xref, fileName, gTrue, gfxFont,
01264                 m11, m12, m21, m22,
01265                 m11, m12, m21, m22, gFalse);
01266 
01267     // on systems with Unix hard link semantics, this will remove the
01268     // last link to the temp file
01269     unlink(fileName->getCString());
01270 
01271     delete fileName;
01272 
01273   // check for an external font file
01274   } else if ((fileName = gfxFont->getExtFontFile())) {
01275     font = tryGetFTFontFromFile(xref, fileName, gFalse, gfxFont,
01276                 m11, m12, m21, m22,
01277                 m11, m12, m21, m22, gFalse);
01278 
01279   } else {
01280     font = NULL;
01281   }
01282 
01283   return font;
01284 }
01285 
01286 XOutputFont *XOutputFontCache::tryGetFTFontFromFile(XRef *xref,
01287                             GString *fileName,
01288                             GBool deleteFile,
01289                             GfxFont *gfxFont,
01290                             double m11Orig,
01291                             double m12Orig,
01292                             double m21Orig,
01293                             double m22Orig,
01294                             double m11, double m12,
01295                             double m21, double m22,
01296                             GBool subst) {
01297   Ref *id;
01298   FTFontFile *fontFile;
01299   XOutputFont *font;
01300 
01301   // create the FreeType font file
01302   if (gfxFont->isCIDFont()) {
01303     if (gfxFont->getType() == fontCIDType2) {
01304       fontFile = new FTFontFile(ftEngine, fileName->getCString(),
01305                 ((GfxCIDFont *)gfxFont)->getCIDToGID(),
01306                 ((GfxCIDFont *)gfxFont)->getCIDToGIDLen());
01307     } else { // fontCIDType0C
01308       fontFile = new FTFontFile(ftEngine, fileName->getCString());
01309     }
01310   } else {
01311     fontFile = new FTFontFile(ftEngine, fileName->getCString(),
01312                   ((Gfx8BitFont *)gfxFont)->getEncoding(),
01313                   ((Gfx8BitFont *)gfxFont)->getHasEncoding());
01314   }
01315   if (!fontFile->isOk()) {
01316     error(-1, "Couldn't create FreeType font from '%s'",
01317       fileName->getCString());
01318     delete fontFile;
01319     if (deleteFile) {
01320       unlink(fileName->getCString());
01321     }
01322     return NULL;
01323   }
01324 
01325   // add to list
01326   id = gfxFont->getID();
01327   ftFontFiles->append(new XOutputFTFontFile(id->num, id->gen,
01328                         subst, fontFile,
01329                         deleteFile ? fileName->copy()
01330                                    : (GString *)NULL));
01331 
01332   // create the Font
01333   font = new XOutputFTFont(gfxFont->getID(), fontFile,
01334                m11Orig, m12Orig, m21Orig, m22Orig,
01335                m11, m12, m21, m22, display, xOut);
01336   if (!font->isOk()) {
01337     delete font;
01338     return NULL;
01339   }
01340   return font;
01341 }
01342 #endif // FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H)
01343 
01344 #if !FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H)
01345 XOutputFont *XOutputFontCache::tryGetTTFont(XRef *xref,
01346                         GfxFont *gfxFont,
01347                         double m11, double m12,
01348                         double m21, double m22) {
01349   Ref *id;
01350   XOutputTTFontFile *xFontFile;
01351   XOutputFont *font;
01352   Ref embRef;
01353   GString *fileName;
01354   FILE *f;
01355   Object refObj, strObj;
01356   int c;
01357   int i;
01358 
01359   // check the already available font files
01360   id = gfxFont->getID();
01361   xFontFile = NULL;
01362   for (i = 0; i < ttFontFiles->getLength(); ++i) {
01363     xFontFile = (XOutputTTFontFile *)ttFontFiles->get(i);
01364     if (xFontFile->num == id->num && xFontFile->gen == id->gen &&
01365     !xFontFile->subst) {
01366       font = new XOutputTTFont(id, xFontFile->fontFile,
01367                    m11, m12, m21, m22,
01368                    m11, m12, m21, m22, display, xOut);
01369       if (!font->isOk()) {
01370     delete font;
01371     return NULL;
01372       }
01373       return font;
01374     }
01375   }
01376 
01377   // check for an embedded font
01378   if (gfxFont->getEmbeddedFontID(&embRef)) {
01379 
01380     // create the font file
01381     fileName = NULL;
01382     if (!openTempFile(&fileName, &f, "wb", NULL)) {
01383       error(-1, "Couldn't create temporary TrueType font file");
01384       return NULL;
01385     }
01386     refObj.initRef(embRef.num, embRef.gen);
01387     refObj.fetch(xref, &strObj);
01388     refObj.free();
01389     strObj.streamReset();
01390     while ((c = strObj.streamGetChar()) != EOF) {
01391       fputc(c, f);
01392     }
01393     strObj.streamClose();
01394     strObj.free();
01395     fclose(f);
01396 
01397     // create the Font
01398     font = tryGetTTFontFromFile(xref, fileName, gTrue, gfxFont,
01399                 m11, m12, m21, m22,
01400                 m11, m12, m21, m22, gFalse);
01401 
01402     // on systems with Unix hard link semantics, this will remove the
01403     // last link to the temp file
01404     unlink(fileName->getCString());
01405 
01406     delete fileName;
01407 
01408   } else if ((fileName = gfxFont->getExtFontFile())) {
01409     font = tryGetTTFontFromFile(xref, fileName, gFalse, gfxFont,
01410                 m11, m12, m21, m22,
01411                 m11, m12, m21, m22, gFalse);
01412 
01413   } else {
01414     font = NULL;
01415   }
01416 
01417   return font;
01418 }
01419 
01420 XOutputFont *XOutputFontCache::tryGetTTFontFromFile(XRef *xref,
01421                             GString *fileName,
01422                             GBool deleteFile,
01423                             GfxFont *gfxFont,
01424                             double m11Orig,
01425                             double m12Orig,
01426                             double m21Orig,
01427                             double m22Orig,
01428                             double m11, double m12,
01429                             double m21, double m22,
01430                             GBool subst) {
01431   Ref *id;
01432   TTFontFile *fontFile;
01433   XOutputFont *font;
01434 
01435   // create the FreeType font file
01436   if (gfxFont->isCIDFont()) {
01437     // fontCIDType2
01438     fontFile = new TTFontFile(ttEngine, fileName->getCString(),
01439                   ((GfxCIDFont *)gfxFont)->getCIDToGID(),
01440                   ((GfxCIDFont *)gfxFont)->getCIDToGIDLen());
01441   } else {
01442     fontFile = new TTFontFile(ttEngine, fileName->getCString(),
01443                   ((Gfx8BitFont *)gfxFont)->getEncoding(),
01444                   ((Gfx8BitFont *)gfxFont)->getHasEncoding());
01445   }
01446   if (!fontFile->isOk()) {
01447     error(-1, "Couldn't create FreeType font from '%s'",
01448       fileName->getCString());
01449     delete fontFile;
01450     if (deleteFile) {
01451       unlink(fileName->getCString());
01452     }
01453     return NULL;
01454   }
01455 
01456   // add to list
01457   id = gfxFont->getID();
01458   ttFontFiles->append(new XOutputTTFontFile(id->num, id->gen,
01459                         subst, fontFile,
01460                         deleteFile ? fileName->copy()
01461                                    : (GString *)NULL));
01462 
01463   // create the Font
01464   font = new XOutputTTFont(gfxFont->getID(), fontFile,
01465                m11Orig, m12Orig, m21Orig, m22Orig,
01466                m11, m12, m21, m22, display, xOut);
01467   if (!font->isOk()) {
01468     delete font;
01469     return NULL;
01470   }
01471   return font;
01472 }
01473 #endif // !FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H)
01474 
01475 XOutputFont *XOutputFontCache::tryGetServerFont(GString *xlfd,
01476                         GString *encodingName,
01477                         GfxFont *gfxFont,
01478                         double m11Orig, double m12Orig,
01479                         double m21Orig, double m22Orig,
01480                         double m11, double m12,
01481                         double m21, double m22) {
01482   XOutputFont *font;
01483   UnicodeMap *uMap;
01484   CharCodeToUnicode *ctu;
01485 
01486   uMap = globalParams->getUnicodeMap(encodingName);
01487   if (gfxFont->isCIDFont()) {
01488     ctu = ((GfxCIDFont *)gfxFont)->getToUnicode();
01489     font = new XOutputServer16BitFont(gfxFont->getID(), xlfd, uMap, ctu,
01490                       m11Orig, m12Orig, m21Orig, m22Orig,
01491                       m11, m12, m21, m22,
01492                       display, xOut);
01493     ctu->decRefCnt();
01494   } else {
01495     ctu = ((Gfx8BitFont *)gfxFont)->getToUnicode();
01496     font = new XOutputServer8BitFont(gfxFont->getID(), xlfd, uMap, ctu,
01497                      m11Orig, m12Orig, m21Orig, m22Orig,
01498                      m11, m12, m21, m22,
01499                      display, xOut);
01500     ctu->decRefCnt();
01501   }
01502   uMap->decRefCnt();
01503   if (!font->isOk()) {
01504     delete font;
01505     return NULL;
01506   }
01507   return font;
01508 }
01509 
01510 //------------------------------------------------------------------------
01511 // T3FontCache
01512 //------------------------------------------------------------------------
01513 
01514 struct T3FontCacheTag {
01515   Gushort code;
01516   Gushort mru;          // valid bit (0x8000) and MRU index
01517   double wx, wy;        // untransformed glyph metrics
01518 };
01519 
01520 class T3FontCache {
01521 public:
01522 
01523   T3FontCache(Ref *fontID, double m11A, double m12A,
01524           double m21A, double m22A,
01525           int glyphXA, int glyphYA, int glyphWA, int glyphHA,
01526           Display *displayA, Visual *visual, Guint depth,
01527           Pixmap origPixmap);
01528   ~T3FontCache();
01529   GBool matches(Ref *idA, double m11A, double m12A,
01530         double m21A, double m22A)
01531     { return fontID.num == idA->num && fontID.gen == idA->gen &&
01532          m11 == m11A && m12 == m12A && m21 == m21A && m22 == m22A; }
01533 
01534   Ref fontID;           // PDF font ID
01535   double m11, m12, m21, m22;    // transform matrix
01536   int glyphX, glyphY;       // pixel offset of glyph pixmaps
01537   int glyphW, glyphH;       // size of glyph pixmaps, in pixels
01538   int glyphSize;        // size of glyph pixmaps, in bytes
01539   int cacheSets;        // number of sets in cache
01540   int cacheAssoc;       // cache associativity (glyphs per set)
01541   Guchar *cacheData;        // glyph pixmap cache
01542   T3FontCacheTag *cacheTags;    // cache tags, i.e., char codes
01543   Display *display;
01544   Pixmap pixmap;
01545   XImage *image;
01546 };
01547 
01548 T3FontCache::T3FontCache(Ref *fontIDA, double m11A, double m12A,
01549              double m21A, double m22A,
01550              int glyphXA, int glyphYA, int glyphWA, int glyphHA,
01551              Display *displayA, Visual *visual, Guint depth,
01552              Pixmap origPixmap) {
01553   int i;
01554 
01555   fontID = *fontIDA;
01556   m11 = m11A;
01557   m12 = m12A;
01558   m21 = m21A;
01559   m22 = m22A;
01560   glyphX = glyphXA;
01561   glyphY = glyphYA;
01562   glyphW = glyphWA;
01563   glyphH = glyphHA;
01564   glyphSize = glyphW * glyphH;
01565   cacheAssoc = 8;
01566   if (glyphSize <= 256) {
01567     cacheSets = 8;
01568   } else if (glyphSize <= 512) {
01569     cacheSets = 4;
01570   } else if (glyphSize <= 1024) {
01571     cacheSets = 2;
01572   } else {
01573     cacheSets = 1;
01574   }
01575   cacheData = (Guchar *)gmalloc(cacheSets * cacheAssoc * glyphSize);
01576   cacheTags = (T3FontCacheTag *)gmalloc(cacheSets * cacheAssoc *
01577                     sizeof(T3FontCacheTag));
01578   for (i = 0; i < cacheSets * cacheAssoc; ++i) {
01579     cacheTags[i].mru = i & (cacheAssoc - 1);
01580   }
01581   display = displayA;
01582   pixmap = XCreatePixmap(display, origPixmap, glyphW, glyphH, depth);
01583   image = XCreateImage(display, visual, depth, ZPixmap, 0, NULL,
01584                glyphW, glyphH, 8, 0);
01585   image->data = (char *)gmalloc(glyphH * image->bytes_per_line);
01586 }
01587 
01588 T3FontCache::~T3FontCache() {
01589   gfree(cacheData);
01590   gfree(cacheTags);
01591   XFreePixmap(display, pixmap);
01592   gfree(image->data);
01593   image->data = NULL;
01594   XDestroyImage(image);
01595 }
01596 
01597 struct T3GlyphStack {
01598   GBool cacheable;
01599   Gushort code;
01600   T3FontCache *cache;
01601   int cacheIdx;
01602   T3FontCacheTag *cacheTag;
01603   Guchar *cacheData;
01604   double x, y;
01605   Unicode *u;
01606   int uLen;
01607   GfxRGB color;
01608   int origPixmapW, origPixmapH;
01609   Pixmap origPixmap;
01610   GC origStrokeGC;
01611   GC origFillGC;
01612   Region origClipRegion;
01613   double origCTM4, origCTM5;
01614   double wx, wy;        // untransformed glyph metrics
01615   T3GlyphStack *next;
01616 };
01617 
01618 //------------------------------------------------------------------------
01619 // XOutputDev
01620 //------------------------------------------------------------------------
01621 
01622 XOutputDev::XOutputDev(Display *displayA, int screenNumA,
01623                Visual *visualA, Colormap colormapA,
01624                GBool reverseVideoA, unsigned long paperColorA,
01625                GBool installCmap, int rgbCubeSize,
01626                int forceDepth) {
01627   XVisualInfo visualTempl;
01628   XVisualInfo *visualList;
01629   int nVisuals;
01630   Gulong mask;
01631   XColor xcolor;
01632   XColor *xcolors;
01633   int r, g, b, n, m;
01634   GBool ok;
01635 
01636   // no document yet
01637   xref = NULL;
01638 
01639   // display / screen / visual / colormap
01640   display = displayA;
01641   screenNum = screenNumA;
01642   visual = visualA;
01643   colormap = colormapA;
01644 
01645   // no pixmap yet
01646   pixmapW = pixmapH = 0;
01647 
01648   // check for TrueColor visual
01649   if (forceDepth != 0) {
01650     depth = forceDepth;
01651     trueColor = depth >= 16;
01652   } else {
01653     visualTempl.visualid = XVisualIDFromVisual(visual);
01654     visualList = XGetVisualInfo(display, VisualIDMask,
01655                 &visualTempl, &nVisuals);
01656     if (nVisuals < 1) {
01657       // this shouldn't happen
01658       XFree((XPointer)visualList);
01659       visualList = XGetVisualInfo(display, VisualNoMask, &visualTempl,
01660                   &nVisuals);
01661     }
01662     depth = visualList->depth;
01663     if (visualList->c_class == TrueColor) {
01664       trueColor = gTrue;
01665       for (mask = visualList->red_mask, rShift = 0;
01666        mask && !(mask & 1);
01667        mask >>= 1, ++rShift) ;
01668       rMul = (int)mask;
01669       for (mask = visualList->green_mask, gShift = 0;
01670        mask && !(mask & 1);
01671        mask >>= 1, ++gShift) ;
01672       gMul = (int)mask;
01673       for (mask = visualList->blue_mask, bShift = 0;
01674        mask && !(mask & 1);
01675        mask >>= 1, ++bShift) ;
01676       bMul = (int)mask;
01677     } else {
01678       trueColor = gFalse;
01679     }
01680     XFree((XPointer)visualList);
01681   }
01682 
01683   // allocate a color cube
01684   if (!trueColor) {
01685     redMap[BlackPixel(display, screenNum) & 0xff] = 0;
01686     redMap[WhitePixel(display, screenNum) & 0xff] = 1;
01687 
01688     // set colors in private colormap
01689     if (installCmap) {
01690       for (numColors = 6; numColors >= 2; --numColors) {
01691     m = numColors * numColors * numColors;
01692     if (XAllocColorCells(display, colormap, False, NULL, 0, colors, m)) {
01693       break;
01694     }
01695       }
01696       if (numColors >= 2) {
01697     m = numColors * numColors * numColors;
01698     xcolors = (XColor *)gmalloc(m * sizeof(XColor));
01699     n = 0;
01700     for (r = 0; r < numColors; ++r) {
01701       for (g = 0; g < numColors; ++g) {
01702         for (b = 0; b < numColors; ++b) {
01703           xcolors[n].pixel = colors[n];
01704           xcolors[n].red = (r * 65535) / (numColors - 1);
01705           xcolors[n].green = (g * 65535) / (numColors - 1);
01706           xcolors[n].blue = (b * 65535) / (numColors - 1);
01707           xcolors[n].flags = DoRed | DoGreen | DoBlue;
01708           redMap[xcolors[n].pixel & 0xff] = xcolors[n].red / 65535.0;
01709           ++n;
01710         }
01711       }
01712     }
01713     XStoreColors(display, colormap, xcolors, m);
01714     gfree(xcolors);
01715       } else {
01716     numColors = 1;
01717     colors[0] = BlackPixel(display, screenNum);
01718     colors[1] = WhitePixel(display, screenNum);
01719       }
01720 
01721     // allocate colors in shared colormap
01722     } else {
01723       if (rgbCubeSize > maxRGBCube) {
01724     rgbCubeSize = maxRGBCube;
01725       }
01726       ok = gFalse;
01727       for (numColors = rgbCubeSize; numColors >= 2; --numColors) {
01728     ok = gTrue;
01729     n = 0;
01730     for (r = 0; r < numColors && ok; ++r) {
01731       for (g = 0; g < numColors && ok; ++g) {
01732         for (b = 0; b < numColors && ok; ++b) {
01733           if (n == 0) {
01734         colors[n] = BlackPixel(display, screenNum);
01735         redMap[colors[n] & 0xff] = 0;
01736         ++n;
01737           } else {
01738         xcolor.red = (r * 65535) / (numColors - 1);
01739         xcolor.green = (g * 65535) / (numColors - 1);
01740         xcolor.blue = (b * 65535) / (numColors - 1);
01741         if (XAllocColor(display, colormap, &xcolor)) {
01742           colors[n++] = xcolor.pixel;
01743           redMap[xcolor.pixel & 0xff] = xcolor.red / 65535.0;
01744         } else {
01745           ok = gFalse;
01746         }
01747           }
01748         }
01749       }
01750     }
01751     if (ok) {
01752       break;
01753     }
01754     XFreeColors(display, colormap, &colors[1], n-1, 0);
01755       }
01756       if (!ok) {
01757     numColors = 1;
01758     colors[0] = BlackPixel(display, screenNum);
01759     colors[1] = WhitePixel(display, screenNum);
01760       }
01761     }
01762   }
01763 
01764   // misc parameters
01765   reverseVideo = reverseVideoA;
01766   paperColor = paperColorA;
01767 
01768   // set up the font cache and fonts
01769   gfxFont = NULL;
01770   font = NULL;
01771   fontCache = new XOutputFontCache(display, depth, this,
01772                    globalParams->getT1libControl(),
01773                    globalParams->getFreeTypeControl());
01774   nT3Fonts = 0;
01775   t3GlyphStack = NULL;
01776 
01777   // empty state stack
01778   save = NULL;
01779 
01780   // create text object
01781   text = new TextPage(gFalse);
01782 }
01783 
01784 XOutputDev::~XOutputDev() {
01785   int i;
01786 
01787   delete fontCache;
01788   for (i = 0; i < nT3Fonts; ++i) {
01789     delete t3FontCache[i];
01790   }
01791   delete text;
01792 }
01793 
01794 void XOutputDev::startDoc(XRef *xrefA) {
01795   int i;
01796 
01797   xref = xrefA;
01798   fontCache->startDoc(screenNum, visual, colormap, trueColor, rMul, gMul, bMul,
01799               rShift, gShift, bShift, colors, numColors);
01800   for (i = 0; i < nT3Fonts; ++i) {
01801     delete t3FontCache[i];
01802   }
01803   nT3Fonts = 0;
01804 }
01805 
01806 void XOutputDev::startPage(int pageNum, GfxState *state) {
01807   XGCValues gcValues;
01808   XRectangle rect;
01809 
01810   // default line flatness
01811   flatness = 0;
01812 
01813   // allocate GCs
01814   gcValues.foreground = BlackPixel(display, screenNum);
01815   gcValues.background = WhitePixel(display, screenNum);
01816   gcValues.line_width = 0;
01817   gcValues.line_style = LineSolid;
01818   strokeGC = XCreateGC(display, pixmap,
01819                GCForeground | GCBackground | GCLineWidth | GCLineStyle,
01820                        &gcValues);
01821   fillGC = XCreateGC(display, pixmap,
01822              GCForeground | GCBackground | GCLineWidth | GCLineStyle,
01823              &gcValues);
01824   gcValues.foreground = paperColor;
01825   paperGC = XCreateGC(display, pixmap,
01826               GCForeground | GCBackground | GCLineWidth | GCLineStyle,
01827               &gcValues);
01828 
01829   // initialize clip region
01830   clipRegion = XCreateRegion();
01831   rect.x = rect.y = 0;
01832   rect.width = pixmapW;
01833   rect.height = pixmapH;
01834   XUnionRectWithRegion(&rect, clipRegion, clipRegion);
01835   XSetRegion(display, strokeGC, clipRegion);
01836   XSetRegion(display, fillGC, clipRegion);
01837 
01838   // clear font
01839   gfxFont = NULL;
01840   font = NULL;
01841 
01842   // clear window
01843   XFillRectangle(display, pixmap, paperGC, 0, 0, pixmapW, pixmapH);
01844 
01845   // clear text object
01846   text->clear();
01847 }
01848 
01849 void XOutputDev::endPage() {
01850   XOutputState *s;
01851 
01852   text->coalesce();
01853 
01854   // clear state stack, free all GCs, free the clip region
01855   while (save) {
01856     s = save;
01857     save = save->next;
01858     XFreeGC(display, s->strokeGC);
01859     XFreeGC(display, s->fillGC);
01860     XDestroyRegion(s->clipRegion);
01861     delete s;
01862   }
01863   XFreeGC(display, strokeGC);
01864   XFreeGC(display, fillGC);
01865   XFreeGC(display, paperGC);
01866   XDestroyRegion(clipRegion);
01867 }
01868 
01869 void XOutputDev::drawLink(Link *link, Catalog *catalog) {
01870   double x1, y1, x2, y2, w;
01871   GfxRGB rgb;
01872   XPoint points[5];
01873   int x, y;
01874 
01875   link->getBorder(&x1, &y1, &x2, &y2, &w);
01876   if (w > 0) {
01877     rgb.r = 0;
01878     rgb.g = 0;
01879     rgb.b = 1;
01880     XSetForeground(display, strokeGC, findColor(&rgb));
01881     XSetLineAttributes(display, strokeGC, xoutRound(w),
01882                LineSolid, CapRound, JoinRound);
01883     cvtUserToDev(x1, y1, &x, &y);
01884     points[0].x = points[4].x = x;
01885     points[0].y = points[4].y = y;
01886     cvtUserToDev(x2, y1, &x, &y);
01887     points[1].x = x;
01888     points[1].y = y;
01889     cvtUserToDev(x2, y2, &x, &y);
01890     points[2].x = x;
01891     points[2].y = y;
01892     cvtUserToDev(x1, y2, &x, &y);
01893     points[3].x = x;
01894     points[3].y = y;
01895     XDrawLines(display, pixmap, strokeGC, points, 5, CoordModeOrigin);
01896   }
01897 }
01898 
01899 void XOutputDev::saveState(GfxState *state) {
01900   XOutputState *s;
01901   XGCValues values;
01902 
01903   // save current state
01904   s = new XOutputState;
01905   s->strokeGC = strokeGC;
01906   s->fillGC = fillGC;
01907   s->clipRegion = clipRegion;
01908 
01909   // push onto state stack
01910   s->next = save;
01911   save = s;
01912 
01913   // create a new current state by copying
01914   strokeGC = XCreateGC(display, pixmap, 0, &values);
01915   XCopyGC(display, s->strokeGC, 0xffffffff, strokeGC);
01916   fillGC = XCreateGC(display, pixmap, 0, &values);
01917   XCopyGC(display, s->fillGC, 0xffffffff, fillGC);
01918   clipRegion = XCreateRegion();
01919   XUnionRegion(s->clipRegion, clipRegion, clipRegion);
01920   XSetRegion(display, strokeGC, clipRegion);
01921   XSetRegion(display, fillGC, clipRegion);
01922 }
01923 
01924 void XOutputDev::restoreState(GfxState *state) {
01925   XOutputState *s;
01926 
01927   if (save) {
01928     // kill current state
01929     XFreeGC(display, strokeGC);
01930     XFreeGC(display, fillGC);
01931     XDestroyRegion(clipRegion);
01932 
01933     // restore state
01934     flatness = state->getFlatness();
01935     strokeGC = save->strokeGC;
01936     fillGC = save->fillGC;
01937     clipRegion = save->clipRegion;
01938     XSetRegion(display, strokeGC, clipRegion);
01939     XSetRegion(display, fillGC, clipRegion);
01940 
01941     // pop state stack
01942     s = save;
01943     save = save->next;
01944     delete s;
01945   }
01946 }
01947 
01948 void XOutputDev::updateAll(GfxState *state) {
01949   updateLineAttrs(state, gTrue);
01950   updateFlatness(state);
01951   updateMiterLimit(state);
01952   updateFillColor(state);
01953   updateStrokeColor(state);
01954   updateFont(state);
01955 }
01956 
01957 void XOutputDev::updateCTM(GfxState *state, double m11, double m12,
01958                double m21, double m22, double m31, double m32) {
01959   updateLineAttrs(state, gTrue);
01960 }
01961 
01962 void XOutputDev::updateLineDash(GfxState *state) {
01963   updateLineAttrs(state, gTrue);
01964 }
01965 
01966 void XOutputDev::updateFlatness(GfxState *state) {
01967   flatness = state->getFlatness();
01968 }
01969 
01970 void XOutputDev::updateLineJoin(GfxState *state) {
01971   updateLineAttrs(state, gFalse);
01972 }
01973 
01974 void XOutputDev::updateLineCap(GfxState *state) {
01975   updateLineAttrs(state, gFalse);
01976 }
01977 
01978 // unimplemented
01979 void XOutputDev::updateMiterLimit(GfxState *state) {
01980 }
01981 
01982 void XOutputDev::updateLineWidth(GfxState *state) {
01983   updateLineAttrs(state, gFalse);
01984 }
01985 
01986 void XOutputDev::updateLineAttrs(GfxState *state, GBool updateDash) {
01987   double width;
01988   int cap, join;
01989   double *dashPattern;
01990   int dashLength;
01991   double dashStart;
01992   char dashList[20];
01993   int i;
01994 
01995   width = state->getTransformedLineWidth();
01996   switch (state->getLineCap()) {
01997   case 0: cap = CapButt; break;
01998   case 1: cap = CapRound; break;
01999   case 2: cap = CapProjecting; break;
02000   default:
02001     error(-1, "Bad line cap style (%d)", state->getLineCap());
02002     cap = CapButt;
02003     break;
02004   }
02005   switch (state->getLineJoin()) {
02006   case 0: join = JoinMiter; break;
02007   case 1: join = JoinRound; break;
02008   case 2: join = JoinBevel; break;
02009   default:
02010     error(-1, "Bad line join style (%d)", state->getLineJoin());
02011     join = JoinMiter;
02012     break;
02013   }
02014   state->getLineDash(&dashPattern, &dashLength, &dashStart);
02015 #if 1 //~ work around a bug in XFree86 (???)
02016   if (dashLength > 0 && cap == CapProjecting) {
02017     cap = CapButt;
02018   }
02019 #endif
02020   XSetLineAttributes(display, strokeGC, xoutRound(width),
02021              dashLength > 0 ? LineOnOffDash : LineSolid,
02022              cap, join);
02023   if (updateDash && dashLength > 0) {
02024     if (dashLength > 20)
02025       dashLength = 20;
02026     for (i = 0; i < dashLength; ++i) {
02027       dashList[i] = xoutRound(state->transformWidth(dashPattern[i]));
02028       if (dashList[i] == 0)
02029     dashList[i] = 1;
02030     }
02031     XSetDashes(display, strokeGC, xoutRound(dashStart), dashList, dashLength);
02032   }
02033 }
02034 
02035 void XOutputDev::updateFillColor(GfxState *state) {
02036   GfxRGB rgb;
02037 
02038   state->getFillRGB(&rgb);
02039   if (reverseVideo) {
02040     rgb.r = 1 - rgb.r;
02041     rgb.g = 1 - rgb.g;
02042     rgb.b = 1 - rgb.b;
02043   }
02044   XSetForeground(display, fillGC, findColor(&rgb));
02045 }
02046 
02047 void XOutputDev::updateStrokeColor(GfxState *state) {
02048   GfxRGB rgb;
02049 
02050   state->getStrokeRGB(&rgb);
02051   if (reverseVideo) {
02052     rgb.r = 1 - rgb.r;
02053     rgb.g = 1 - rgb.g;
02054     rgb.b = 1 - rgb.b;
02055   }
02056   XSetForeground(display, strokeGC, findColor(&rgb));
02057 }
02058 
02059 void XOutputDev::updateFont(GfxState *state) {
02060   double m11, m12, m21, m22;
02061 
02062   text->updateFont(state);
02063 
02064   if (!(gfxFont = state->getFont())) {
02065     font = NULL;
02066     return;
02067   }
02068   if (gfxFont->getType() == fontType3) {
02069     font = NULL;
02070     return;
02071   }
02072   state->getFontTransMat(&m11, &m12, &m21, &m22);
02073   m11 *= state->getHorizScaling();
02074   m12 *= state->getHorizScaling();
02075   font = fontCache->getFont(xref, gfxFont, m11, m12, m21, m22);
02076   if (font) {
02077     font->updateGC(fillGC);
02078     font->updateGC(strokeGC);
02079   }
02080 }
02081 
02082 void XOutputDev::stroke(GfxState *state) {
02083   XPoint *points;
02084   int *lengths;
02085   int n, size, numPoints, i, j;
02086 
02087   // transform points
02088   n = convertPath(state, &points, &size, &numPoints, &lengths, gFalse);
02089 
02090   // draw each subpath
02091   j = 0;
02092   for (i = 0; i < n; ++i) {
02093     XDrawLines(display, pixmap, strokeGC, points + j, lengths[i],
02094            CoordModeOrigin);
02095     j += lengths[i];
02096   }
02097 
02098   // free points and lengths arrays
02099   if (points != tmpPoints)
02100     gfree(points);
02101   if (lengths != tmpLengths)
02102     gfree(lengths);
02103 }
02104 
02105 void XOutputDev::fill(GfxState *state) {
02106   doFill(state, WindingRule);
02107 }
02108 
02109 void XOutputDev::eoFill(GfxState *state) {
02110   doFill(state, EvenOddRule);
02111 }
02112 
02113 //
02114 //  X doesn't color the pixels on the right-most and bottom-most
02115 //  borders of a polygon.  This means that one-pixel-thick polygons
02116 //  are not colored at all.  I think this is supposed to be a
02117 //  feature, but I can't figure out why.  So after it fills a
02118 //  polygon, it also draws lines around the border.  This is done
02119 //  only for single-component polygons, since it's not very
02120 //  compatible with the compound polygon kludge (see convertPath()).
02121 //
02122 void XOutputDev::doFill(GfxState *state, int rule) {
02123   XPoint *points;
02124   int *lengths;
02125   int n, size, numPoints, i, j;
02126 
02127   // set fill rule
02128   XSetFillRule(display, fillGC, rule);
02129 
02130   // transform points, build separate polygons
02131   n = convertPath(state, &points, &size, &numPoints, &lengths, gTrue);
02132 
02133   // fill them
02134   j = 0;
02135   for (i = 0; i < n; ++i) {
02136     XFillPolygon(display, pixmap, fillGC, points + j, lengths[i],
02137          Complex, CoordModeOrigin);
02138     if (state->getPath()->getNumSubpaths() == 1) {
02139       XDrawLines(display, pixmap, fillGC, points + j, lengths[i],
02140          CoordModeOrigin);
02141     }
02142     j += lengths[i] + 1;
02143   }
02144 
02145   // free points and lengths arrays
02146   if (points != tmpPoints)
02147     gfree(points);
02148   if (lengths != tmpLengths)
02149     gfree(lengths);
02150 }
02151 
02152 void XOutputDev::clip(GfxState *state) {
02153   doClip(state, WindingRule);
02154 }
02155 
02156 void XOutputDev::eoClip(GfxState *state) {
02157   doClip(state, EvenOddRule);
02158 }
02159 
02160 void XOutputDev::doClip(GfxState *state, int rule) {
02161   Region region, region2;
02162   XPoint *points;
02163   int *lengths;
02164   int n, size, numPoints, i, j;
02165 
02166   // transform points, build separate polygons
02167   n = convertPath(state, &points, &size, &numPoints, &lengths, gTrue);
02168 
02169   // construct union of subpath regions
02170   // (XPolygonRegion chokes if there aren't at least three points --
02171   // this happens if the PDF file does moveto/closepath/clip, which
02172   // sets an empty clipping region)
02173   if (lengths[0] > 2) {
02174     region = XPolygonRegion(points, lengths[0], rule);
02175   } else {
02176     region = XCreateRegion();
02177   }
02178   j = lengths[0] + 1;
02179   for (i = 1; i < n; ++i) {
02180     if (lengths[i] > 2) {
02181       region2 = XPolygonRegion(points + j, lengths[i], rule);
02182     } else {
02183       region2 = XCreateRegion();
02184     }
02185     XUnionRegion(region2, region, region);
02186     XDestroyRegion(region2);
02187     j += lengths[i] + 1;
02188   }
02189 
02190   // intersect region with clipping region
02191   XIntersectRegion(region, clipRegion, clipRegion);
02192   XDestroyRegion(region);
02193   XSetRegion(display, strokeGC, clipRegion);
02194   XSetRegion(display, fillGC, clipRegion);
02195 
02196   // free points and lengths arrays
02197   if (points != tmpPoints)
02198     gfree(points);
02199   if (lengths != tmpLengths)
02200     gfree(lengths);
02201 }
02202 
02203 //
02204 // Transform points in the path and convert curves to line segments.
02205 // Builds a set of subpaths and returns the number of subpaths.
02206 // If <fillHack> is set, close any unclosed subpaths and activate a
02207 // kludge for polygon fills:  First, it divides up the subpaths into
02208 // non-overlapping polygons by simply comparing bounding rectangles.
02209 // Then it connects subaths within a single compound polygon to a single
02210 // point so that X can fill the polygon (sort of).
02211 //
02212 int XOutputDev::convertPath(GfxState *state, XPoint **points, int *size,
02213                 int *numPoints, int **lengths, GBool fillHack) {
02214   GfxPath *path;
02215   BoundingRect *rects;
02216   BoundingRect rect;
02217   int n, i, ii, j, k, k0;
02218 
02219   // get path and number of subpaths
02220   path = state->getPath();
02221   n = path->getNumSubpaths();
02222 
02223   // allocate lengths array
02224   if (n < numTmpSubpaths)
02225     *lengths = tmpLengths;
02226   else
02227     *lengths = (int *)gmalloc(n * sizeof(int));
02228 
02229   // allocate bounding rectangles array
02230   if (fillHack) {
02231     if (n < numTmpSubpaths)
02232       rects = tmpRects;
02233     else
02234       rects = (BoundingRect *)gmalloc(n * sizeof(BoundingRect));
02235   } else {
02236     rects = NULL;
02237   }
02238 
02239   // do each subpath
02240   *points = tmpPoints;
02241   *size = numTmpPoints;
02242   *numPoints = 0;
02243   for (i = 0; i < n; ++i) {
02244 
02245     // transform the points
02246     j = *numPoints;
02247     convertSubpath(state, path->getSubpath(i), points, size, numPoints);
02248 
02249     // construct bounding rectangle
02250     if (fillHack) {
02251       rects[i].xMin = rects[i].xMax = (*points)[j].x;
02252       rects[i].yMin = rects[i].yMax = (*points)[j].y;
02253       for (k = j + 1; k < *numPoints; ++k) {
02254     if ((*points)[k].x < rects[i].xMin)
02255       rects[i].xMin = (*points)[k].x;
02256     else if ((*points)[k].x > rects[i].xMax)
02257       rects[i].xMax = (*points)[k].x;
02258     if ((*points)[k].y < rects[i].yMin)
02259       rects[i].yMin = (*points)[k].y;
02260     else if ((*points)[k].y > rects[i].yMax)
02261       rects[i].yMax = (*points)[k].y;
02262       }
02263     }
02264 
02265     // close subpath if necessary
02266     if (fillHack && ((*points)[*numPoints-1].x != (*points)[j].x ||
02267              (*points)[*numPoints-1].y != (*points)[j].y)) {
02268       addPoint(points, size, numPoints, (*points)[j].x, (*points)[j].y);
02269     }
02270 
02271     // length of this subpath
02272     (*lengths)[i] = *numPoints - j;
02273 
02274     // leave an extra point for compound fill hack
02275     if (fillHack)
02276       addPoint(points, size, numPoints, 0, 0);
02277   }
02278 
02279   // kludge: munge any points that are *way* out of bounds - these can
02280   // crash certain (buggy) X servers
02281   for (i = 0; i < *numPoints; ++i) {
02282     if ((*points)[i].x < -pixmapW) {
02283       (*points)[i].x = -pixmapW;
02284     } else if ((*points)[i].x > 2 * pixmapW) {
02285       (*points)[i].x = 2 * pixmapW;
02286     }
02287     if ((*points)[i].y < -pixmapH) {
02288       (*points)[i].y = -pixmapH;
02289     } else if ((*points)[i].y > 2 * pixmapH) {
02290       (*points)[i].y = 2 * pixmapH;
02291     }
02292   }
02293 
02294   // combine compound polygons
02295   if (fillHack) {
02296     i = j = k = 0;
02297     while (i < n) {
02298 
02299       // start with subpath i
02300       rect = rects[i];
02301       (*lengths)[j] = (*lengths)[i];
02302       k0 = k;
02303       (*points)[k + (*lengths)[i]] = (*points)[k0];
02304       k += (*lengths)[i] + 1;
02305       ++i;
02306 
02307       // combine overlapping polygons
02308       do {
02309 
02310     // look for the first subsequent subpath, if any, which overlaps
02311     for (ii = i; ii < n; ++ii) {
02312       if (rects[ii].xMax > rects[i].xMin &&
02313           rects[ii].xMin < rects[i].xMax &&
02314           rects[ii].yMax > rects[i].yMin &&
02315           rects[ii].yMin < rects[i].yMax) {
02316         break;
02317       }
02318     }
02319 
02320     // if there is an overlap, combine the polygons
02321     if (ii < n) {
02322       for (; i <= ii; ++i) {
02323         if (rects[i].xMin < rect.xMin)
02324           rect.xMin = rects[j].xMin;
02325         if (rects[i].xMax > rect.xMax)
02326           rect.xMax = rects[j].xMax;
02327         if (rects[i].yMin < rect.yMin)
02328           rect.yMin = rects[j].yMin;
02329         if (rects[i].yMax > rect.yMax)
02330           rect.yMax = rects[j].yMax;
02331         (*lengths)[j] += (*lengths)[i] + 1;
02332         (*points)[k + (*lengths)[i]] = (*points)[k0];
02333         k += (*lengths)[i] + 1;
02334       }
02335     }
02336       } while (ii < n && i < n);
02337 
02338       ++j;
02339     }
02340 
02341     // free bounding rectangles
02342     if (rects != tmpRects)
02343       gfree(rects);
02344 
02345     n = j;
02346   }
02347 
02348   return n;
02349 }
02350 
02351 //
02352 // Transform points in a single subpath and convert curves to line
02353 // segments.
02354 //
02355 void XOutputDev::convertSubpath(GfxState *state, GfxSubpath *subpath,
02356                 XPoint **points, int *size, int *n) {
02357   double x0, y0, x1, y1, x2, y2, x3, y3;
02358   int m, i;
02359 
02360   m = subpath->getNumPoints();
02361   i = 0;
02362   while (i < m) {
02363     if (i >= 1 && subpath->getCurve(i)) {
02364       state->transform(subpath->getX(i-1), subpath->getY(i-1), &x0, &y0);
02365       state->transform(subpath->getX(i), subpath->getY(i), &x1, &y1);
02366       state->transform(subpath->getX(i+1), subpath->getY(i+1), &x2, &y2);
02367       state->transform(subpath->getX(i+2), subpath->getY(i+2), &x3, &y3);
02368       doCurve(points, size, n, x0, y0, x1, y1, x2, y2, x3, y3);
02369       i += 3;
02370     } else {
02371       state->transform(subpath->getX(i), subpath->getY(i), &x1, &y1);
02372       addPoint(points, size, n, xoutRound(x1), xoutRound(y1));
02373       ++i;
02374     }
02375   }
02376 }
02377 
02378 //
02379 // Subdivide a Bezier curve.  This uses floating point to avoid
02380 // propagating rounding errors.  (The curves look noticeably more
02381 // jagged with integer arithmetic.)
02382 //
02383 void XOutputDev::doCurve(XPoint **points, int *size, int *n,
02384              double x0, double y0, double x1, double y1,
02385              double x2, double y2, double x3, double y3) {
02386   double x[(1<<maxCurveSplits)+1][3];
02387   double y[(1<<maxCurveSplits)+1][3];
02388   int next[1<<maxCurveSplits];
02389   int p1, p2, p3;
02390   double xx1, yy1, xx2, yy2;
02391   double dx, dy, mx, my, d1, d2;
02392   double xl0, yl0, xl1, yl1, xl2, yl2;
02393   double xr0, yr0, xr1, yr1, xr2, yr2, xr3, yr3;
02394   double xh, yh;
02395   double flat;
02396 
02397   flat = (double)(flatness * flatness);
02398   if (flat < 1)
02399     flat = 1;
02400 
02401   // initial segment
02402   p1 = 0;
02403   p2 = 1<<maxCurveSplits;
02404   x[p1][0] = x0;  y[p1][0] = y0;
02405   x[p1][1] = x1;  y[p1][1] = y1;
02406   x[p1][2] = x2;  y[p1][2] = y2;
02407   x[p2][0] = x3;  y[p2][0] = y3;
02408   next[p1] = p2;
02409 
02410   while (p1 < (1<<maxCurveSplits)) {
02411 
02412     // get next segment
02413     xl0 = x[p1][0];  yl0 = y[p1][0];
02414     xx1 = x[p1][1];  yy1 = y[p1][1];
02415     xx2 = x[p1][2];  yy2 = y[p1][2];
02416     p2 = next[p1];
02417     xr3 = x[p2][0];  yr3 = y[p2][0];
02418 
02419     // compute distances from control points to midpoint of the
02420     // straight line (this is a bit of a hack, but it's much faster
02421     // than computing the actual distances to the line)
02422     mx = (xl0 + xr3) * 0.5;
02423     my = (yl0 + yr3) * 0.5;
02424     dx = xx1 - mx;
02425     dy = yy1 - my;
02426     d1 = dx*dx + dy*dy;
02427     dx = xx2 - mx;
02428     dy = yy2 - my;
02429     d2 = dx*dx + dy*dy;
02430 
02431     // if curve is flat enough, or no more divisions allowed then
02432     // add the straight line segment
02433     if (p2 - p1 <= 1 || (d1 <= flat && d2 <= flat)) {
02434       addPoint(points, size, n, xoutRound(xr3), xoutRound(yr3));
02435       p1 = p2;
02436 
02437     // otherwise, subdivide the curve
02438     } else {
02439       xl1 = (xl0 + xx1) * 0.5;
02440       yl1 = (yl0 + yy1) * 0.5;
02441       xh = (xx1 + xx2) * 0.5;
02442       yh = (yy1 + yy2) * 0.5;
02443       xl2 = (xl1 + xh) * 0.5;
02444       yl2 = (yl1 + yh) * 0.5;
02445       xr2 = (xx2 + xr3) * 0.5;
02446       yr2 = (yy2 + yr3) * 0.5;
02447       xr1 = (xh + xr2) * 0.5;
02448       yr1 = (yh + yr2) * 0.5;
02449       xr0 = (xl2 + xr1) * 0.5;
02450       yr0 = (yl2 + yr1) * 0.5;
02451 
02452       // add the new subdivision points
02453       p3 = (p1 + p2) / 2;
02454       x[p1][1] = xl1;  y[p1][1] = yl1;
02455       x[p1][2] = xl2;  y[p1][2] = yl2;
02456       next[p1] = p3;
02457       x[p3][0] = xr0;  y[p3][0] = yr0;
02458       x[p3][1] = xr1;  y[p3][1] = yr1;
02459       x[p3][2] = xr2;  y[p3][2] = yr2;
02460       next[p3] = p2;
02461     }
02462   }
02463 }
02464 
02465 //
02466 // Add a point to the points array.  (This would use a generic resizable
02467 // array type if C++ supported parameterized types in some reasonable
02468 // way -- templates are a disgusting kludge.)
02469 //
02470 void XOutputDev::addPoint(XPoint **points, int *size, int *k, int x, int y) {
02471   if (*k >= *size) {
02472     *size += 32;
02473     if (*points == tmpPoints) {
02474       *points = (XPoint *)gmalloc(*size * sizeof(XPoint));
02475       memcpy(*points, tmpPoints, *k * sizeof(XPoint));
02476     } else {
02477       *points = (XPoint *)grealloc(*points, *size * sizeof(XPoint));
02478     }
02479   }
02480   (*points)[*k].x = x;
02481   (*points)[*k].y = y;
02482   ++(*k);
02483 }
02484 
02485 void XOutputDev::beginString(GfxState *state, GString *s) {
02486   text->beginString(state, state->getCurX(), state->getCurY());
02487 }
02488 
02489 void XOutputDev::endString(GfxState *state) {
02490   text->endString();
02491 }
02492 
02493 void XOutputDev::drawChar(GfxState *state, double x, double y,
02494               double dx, double dy,
02495               double originX, double originY,
02496               CharCode code, Unicode *u, int uLen) {
02497   int render;
02498   double x1, y1, dx1, dy1;
02499   GfxRGB rgb;
02500   double saveCurX, saveCurY;
02501   double *ctm;
02502   double saveCTM[6];
02503 
02504   text->addChar(state, x, y, dx, dy, u, uLen);
02505 
02506   if (!font) {
02507     return;
02508   }
02509 
02510   // check for invisible text -- this is used by Acrobat Capture
02511   render = state->getRender();
02512   if ((render & 3) == 3) {
02513     return;
02514   }
02515 
02516   x -= originX;
02517   y -= originY;
02518   state->transform(x, y, &x1, &y1);
02519   state->transformDelta(dx, dy, &dx1, &dy1);
02520 
02521   // fill
02522   if (!(render & 1)) {
02523     state->getFillRGB(&rgb);
02524     if (reverseVideo) {
02525       rgb.r = 1 - rgb.r;
02526       rgb.g = 1 - rgb.g;
02527       rgb.b = 1 - rgb.b;
02528     }
02529     font->drawChar(state, pixmap, pixmapW, pixmapH, fillGC, &rgb,
02530            x1, y1, dx1, dy1, code, u, uLen);
02531   }
02532 
02533   // stroke
02534   if ((render & 3) == 1 || (render & 3) == 2) {
02535     if (font->hasGetCharPath()) {
02536       saveCurX = state->getCurX();
02537       saveCurY = state->getCurY();
02538       ctm = state->getCTM();
02539       memcpy(saveCTM, ctm, 6 * sizeof(double));
02540       state->setCTM(1, 0, 0, 1, x1, y1);
02541       font->getCharPath(state, code, u, uLen);
02542       stroke(state);
02543       state->clearPath();
02544       state->setCTM(saveCTM[0], saveCTM[1], saveCTM[2], saveCTM[3],
02545             saveCTM[4], saveCTM[5]);
02546       state->moveTo(saveCurX, saveCurY);
02547     } else {
02548       // can't stroke the outline, so just fill it using the stroke
02549       // color
02550       state->getStrokeRGB(&rgb);
02551       if (reverseVideo) {
02552     rgb.r = 1 - rgb.r;
02553     rgb.g = 1 - rgb.g;
02554     rgb.b = 1 - rgb.b;
02555       }
02556       font->drawChar(state, pixmap, pixmapW, pixmapH, strokeGC, &rgb,
02557              x1, y1, dx1, dy1, code, u, uLen);
02558     }
02559   }
02560 
02561 #if 0 //~ unimplemented: clipping to char path
02562   // clip
02563   if (render & 4) {
02564   }
02565 #endif
02566 }
02567 
02568 GBool XOutputDev::beginType3Char(GfxState *state,
02569                  CharCode code, Unicode *u, int uLen) {
02570   Ref *fontID;
02571   double *ctm, *bbox;
02572   GfxRGB color;
02573   T3FontCache *t3Font;
02574   T3GlyphStack *t3gs;
02575   double x1, y1, xMin, yMin, xMax, yMax, xt, yt;
02576   int i, j;
02577 
02578   if (!gfxFont) {
02579     return gFalse;
02580   }
02581   fontID = gfxFont->getID();
02582   ctm = state->getCTM();
02583   state->transform(0, 0, &xt, &yt);
02584 
02585   // is it the first (MRU) font in the cache?
02586   if (!(nT3Fonts > 0 &&
02587     t3FontCache[0]->matches(fontID, ctm[0], ctm[1], ctm[2], ctm[3]))) {
02588 
02589     // is the font elsewhere in the cache?
02590     for (i = 1; i < nT3Fonts; ++i) {
02591       if (t3FontCache[i]->matches(fontID, ctm[0], ctm[1], ctm[2], ctm[3])) {
02592     t3Font = t3FontCache[i];
02593     for (j = i; j > 0; --j) {
02594       t3FontCache[j] = t3FontCache[j - 1];
02595     }
02596     t3FontCache[0] = t3Font;
02597     break;
02598       }
02599     }
02600     if (i >= nT3Fonts) {
02601 
02602       // create new entry in the font cache
02603       if (nT3Fonts == xOutT3FontCacheSize) {
02604     delete t3FontCache[nT3Fonts - 1];
02605     --nT3Fonts;
02606       }
02607       for (j = nT3Fonts; j > 0; --j) {
02608     t3FontCache[j] = t3FontCache[j - 1];
02609       }
02610       ++nT3Fonts;
02611       bbox = gfxFont->getFontBBox();
02612       if (bbox[0] == 0 && bbox[1] == 0 && bbox[2] == 0 && bbox[3] == 0) {
02613     // broken bounding box -- just take a guess
02614     xMin = xt - 5;
02615     xMax = xMin + 30;
02616     yMax = yt + 15;
02617     yMin = yMax - 45;
02618       } else {
02619     state->transform(bbox[0], bbox[1], &x1, &y1);
02620     xMin = xMax = x1;
02621     yMin = yMax = y1;
02622     state->transform(bbox[0], bbox[3], &x1, &y1);
02623     if (x1 < xMin) {
02624       xMin = x1;
02625     } else if (x1 > xMax) {
02626       xMax = x1;
02627     }
02628     if (y1 < yMin) {
02629       yMin = y1;
02630     } else if (y1 > yMax) {
02631       yMax = y1;
02632     }
02633     state->transform(bbox[2], bbox[1], &x1, &y1);
02634     if (x1 < xMin) {
02635       xMin = x1;
02636     } else if (x1 > xMax) {
02637       xMax = x1;
02638     }
02639     if (y1 < yMin) {
02640       yMin = y1;
02641     } else if (y1 > yMax) {
02642       yMax = y1;
02643     }
02644     state->transform(bbox[2], bbox[3], &x1, &y1);
02645     if (x1 < xMin) {
02646       xMin = x1;
02647     } else if (x1 > xMax) {
02648       xMax = x1;
02649     }
02650     if (y1 < yMin) {
02651       yMin = y1;
02652     } else if (y1 > yMax) {
02653       yMax = y1;
02654     }
02655       }
02656       t3FontCache[0] = new T3FontCache(fontID, ctm[0], ctm[1], ctm[2], ctm[3],
02657                                    (int)floor(xMin - xt),
02658                        (int)floor(yMin - yt),
02659                        (int)ceil(xMax) - (int)floor(xMin) + 3,
02660                        (int)ceil(yMax) - (int)floor(yMin) + 3,
02661                        display, visual, depth, pixmap);
02662     }
02663   }
02664   t3Font = t3FontCache[0];
02665 
02666   // is the glyph in the cache?
02667   i = (code & (t3Font->cacheSets - 1)) * t3Font->cacheAssoc;
02668   for (j = 0; j < t3Font->cacheAssoc; ++j) {
02669     if ((t3Font->cacheTags[i+j].mru & 0x8000) &&
02670     t3Font->cacheTags[i+j].code == code) {
02671       state->getFillRGB(&color);
02672       if (reverseVideo) {
02673     color.r = 1 - color.r;
02674     color.g = 1 - color.g;
02675     color.b = 1 - color.b;
02676       }
02677       text->addChar(state, 0, 0,
02678             t3Font->cacheTags[i+j].wx, t3Font->cacheTags[i+j].wy,
02679             u, uLen);
02680       drawType3Glyph(t3Font, &t3Font->cacheTags[i+j],
02681              t3Font->cacheData + (i+j) * t3Font->glyphSize,
02682              xt, yt, &color);
02683       return gTrue;
02684     }
02685   }
02686 
02687   // push a new Type 3 glyph record
02688   t3gs = new T3GlyphStack();
02689   t3gs->next = t3GlyphStack;
02690   t3GlyphStack = t3gs;
02691   t3GlyphStack->cacheable = gFalse;
02692   t3GlyphStack->code = code;
02693   t3GlyphStack->cache = t3Font;
02694   t3GlyphStack->cacheIdx = i;
02695   t3GlyphStack->x = xt;
02696   t3GlyphStack->y = yt;
02697   t3GlyphStack->u = u;
02698   t3GlyphStack->uLen = uLen;
02699 
02700   return gFalse;
02701 }
02702 
02703 void XOutputDev::endType3Char(GfxState *state) {
02704   XImage *image;
02705   Guchar *p;
02706   int x, y;
02707   Gulong pixel;
02708   double alpha;
02709   T3GlyphStack *t3gs;
02710   double *ctm;
02711 
02712   if (t3GlyphStack->cacheable) {
02713     image = t3GlyphStack->cache->image;
02714     XGetSubImage(display, pixmap, 0, 0,
02715          t3GlyphStack->cache->glyphW, t3GlyphStack->cache->glyphH,
02716          (1 << depth) - 1, ZPixmap, image, 0, 0);
02717     p = t3GlyphStack->cacheData;
02718     for (y = 0; y < t3GlyphStack->cache->glyphH; ++y) {
02719       for (x = 0; x < t3GlyphStack->cache->glyphW; ++x) {
02720     pixel = XGetPixel(image, x, y);
02721     if (trueColor) {
02722       alpha = (double)((pixel >> rShift) & rMul) / (double)rMul;
02723     } else {
02724       alpha = redMap[pixel & 0xff];
02725     }
02726     if (alpha <= 0.2) {
02727       *p++ = 4;
02728     } else if (alpha <= 0.4) {
02729       *p++ = 3;
02730     } else if (alpha <= 0.6) {
02731       *p++ = 2;
02732     } else if (alpha <= 0.8) {
02733       *p++ = 1;
02734     } else {
02735       *p++ = 0;
02736     }
02737       }
02738     }
02739     XDestroyRegion(clipRegion);
02740     XFreeGC(display, strokeGC);
02741     XFreeGC(display, fillGC);
02742     pixmapW = t3GlyphStack->origPixmapW;
02743     pixmapH = t3GlyphStack->origPixmapH;
02744     pixmap = t3GlyphStack->origPixmap;
02745     strokeGC = t3GlyphStack->origStrokeGC;
02746     fillGC = t3GlyphStack->origFillGC;
02747     clipRegion = t3GlyphStack->origClipRegion;
02748     drawType3Glyph(t3GlyphStack->cache,
02749            t3GlyphStack->cacheTag, t3GlyphStack->cacheData,
02750            t3GlyphStack->x, t3GlyphStack->y, &t3GlyphStack->color);
02751     // the CTM must be restored here in order for TextPage::addChar to
02752     // work correctly
02753     ctm = state->getCTM();
02754     state->setCTM(ctm[0], ctm[1], ctm[2], ctm[3],
02755           t3GlyphStack->origCTM4, t3GlyphStack->origCTM5);
02756   }
02757   text->addChar(state, 0, 0, t3GlyphStack->wx, t3GlyphStack->wy,
02758         t3GlyphStack->u, t3GlyphStack->uLen);
02759   t3gs = t3GlyphStack;
02760   t3GlyphStack = t3gs->next;
02761   delete t3gs;
02762 }
02763 
02764 void XOutputDev::drawType3Glyph(T3FontCache *t3Font,
02765                 T3FontCacheTag *tag, Guchar *data,
02766                 double x, double y, GfxRGB *color) {
02767   XImage *image;
02768   XColor xcolor;
02769   GfxRGB bg, rgb;
02770   Gulong map[5];
02771   Gulong pixel;
02772   Guchar *p;
02773   int x0, y0, w0, h0, x1, y1;
02774   int ix, iy;
02775 
02776   // compute: (x0,y0) = position in destination pixmap
02777   //          (x1,y1) = position in the XImage
02778   //          (w0,h0) = size of XImage transfer
02779   x0 = xoutRound(x + t3Font->glyphX);
02780   y0 = xoutRound(y + t3Font->glyphY);
02781   x1 = 0;
02782   y1 = 0;
02783   w0 = t3Font->glyphW;
02784   h0 = t3Font->glyphH;
02785   if (x0 < 0) {
02786     x1 = -x0;
02787     w0 += x0;
02788     x0 = 0;
02789   }
02790   if (x0 + w0 > pixmapW) {
02791     w0 = pixmapW - x0;
02792   }
02793   if (w0 <= 0) {
02794     return;
02795   }
02796   if (y0 < 0) {
02797     y1 = -y0;
02798     h0 += y0;
02799     y0 = 0;
02800   }
02801   if (y0 + h0 > pixmapH) {
02802     h0 = pixmapH - y0;
02803   }
02804   if (h0 <= 0) {
02805     return;
02806   }
02807 
02808   image = t3Font->image;
02809   XGetSubImage(display, pixmap, x0, y0, w0, h0,
02810            (1 << depth) - 1, ZPixmap, image, x1, y1);
02811   xcolor.pixel = XGetPixel(image, t3Font->glyphW / 2, t3Font->glyphH / 2);
02812   XQueryColor(display, colormap, &xcolor);
02813   bg.r = xcolor.red / 65535.0;
02814   bg.g = xcolor.green / 65535.0;
02815   bg.b = xcolor.blue / 65535.0;
02816   rgb.r = 0.25 * (color->r + 3 * bg.r);
02817   rgb.g = 0.25 * (color->g + 3 * bg.g);
02818   rgb.b = 0.25 * (color->b + 3 * bg.b);
02819   map[1] = findColor(&rgb);
02820   rgb.r = 0.5 * (color->r + bg.r);
02821   rgb.g = 0.5 * (color->g + bg.g);
02822   rgb.b = 0.5 * (color->b + bg.b);
02823   map[2] = findColor(&rgb);
02824   rgb.r = 0.25 * (3 * color->r + bg.r);
02825   rgb.g = 0.25 * (3 * color->g + bg.g);
02826   rgb.b = 0.25 * (3 * color->b + bg.b);
02827   map[3] = findColor(&rgb);
02828   map[4] = findColor(color);
02829   p = data;
02830   for (iy = 0; iy < t3Font->glyphH; ++iy) {
02831     for (ix = 0; ix < t3Font->glyphW; ++ix) {
02832       pixel = *p++;
02833       if (pixel > 0) {
02834     XPutPixel(image, ix, iy, map[pixel]);
02835       }
02836     }
02837   }
02838   XPutImage(display, pixmap, fillGC, image, x1, y1, x0, y0, w0, h0);
02839 }
02840 
02841 void XOutputDev::type3D0(GfxState *state, double wx, double wy) {
02842   t3GlyphStack->wx = wx;
02843   t3GlyphStack->wy = wy;
02844 }
02845 
02846 void XOutputDev::type3D1(GfxState *state, double wx, double wy,
02847              double llx, double lly, double urx, double ury) {
02848   GfxColor fgColor;
02849   XGCValues gcValues;
02850   XRectangle rect;
02851   double *ctm;
02852   T3FontCache *t3Font;
02853   int i, j;
02854 
02855   // allocate a cache entry
02856   t3GlyphStack->cacheable = gTrue;
02857   t3Font = t3GlyphStack->cache;
02858   i = t3GlyphStack->cacheIdx;
02859   for (j = 0; j < t3Font->cacheAssoc; ++j) {
02860     if ((t3Font->cacheTags[i+j].mru & 0x7fff) == t3Font->cacheAssoc - 1) {
02861       t3Font->cacheTags[i+j].mru = 0x8000;
02862       t3Font->cacheTags[i+j].code = t3GlyphStack->code;
02863       t3GlyphStack->cacheTag = &t3Font->cacheTags[i+j];
02864       t3GlyphStack->cacheData = t3Font->cacheData + (i+j) * t3Font->glyphSize;
02865     } else {
02866       ++t3Font->cacheTags[i+j].mru;
02867     }
02868   }
02869   t3GlyphStack->wx = wx;
02870   t3GlyphStack->wy = wy;
02871   t3GlyphStack->cacheTag->wx = wx;
02872   t3GlyphStack->cacheTag->wy = wy;
02873 
02874   // prepare to rasterize the glyph
02875   //~ do we need to handle both fill and stroke color?
02876   state->getFillRGB(&t3GlyphStack->color);
02877   if (reverseVideo) {
02878     t3GlyphStack->color.r = 1 - t3GlyphStack->color.r;
02879     t3GlyphStack->color.g = 1 - t3GlyphStack->color.g;
02880     t3GlyphStack->color.b = 1 - t3GlyphStack->color.b;
02881   }
02882   fgColor.c[0] = reverseVideo ? 1 : 0;
02883   state->setFillColorSpace(new GfxDeviceGrayColorSpace());
02884   state->setFillColor(&fgColor);
02885   state->setStrokeColorSpace(new GfxDeviceGrayColorSpace());
02886   state->setStrokeColor(&fgColor);
02887   t3GlyphStack->origPixmapW = pixmapW;
02888   t3GlyphStack->origPixmapH = pixmapH;
02889   t3GlyphStack->origPixmap = pixmap;
02890   t3GlyphStack->origStrokeGC = strokeGC;
02891   t3GlyphStack->origFillGC = fillGC;
02892   t3GlyphStack->origClipRegion = clipRegion;
02893   pixmapW = t3GlyphStack->cache->glyphW;
02894   pixmapH = t3GlyphStack->cache->glyphH;
02895   pixmap = t3GlyphStack->cache->pixmap;
02896   gcValues.foreground = BlackPixel(display, screenNum);
02897   gcValues.background = WhitePixel(display, screenNum);
02898   gcValues.line_width = 0;
02899   gcValues.line_style = LineSolid;
02900   strokeGC = XCreateGC(display, pixmap,
02901                GCForeground | GCBackground | GCLineWidth | GCLineStyle,
02902                &gcValues);
02903   updateLineAttrs(state, gTrue);
02904   gcValues.foreground = WhitePixel(display, screenNum);
02905   fillGC = XCreateGC(display, pixmap,
02906              GCForeground | GCBackground | GCLineWidth | GCLineStyle,
02907              &gcValues);
02908   XFillRectangle(display, pixmap, fillGC, 0, 0, pixmapW, pixmapH);
02909   XSetForeground(display, fillGC, BlackPixel(display, screenNum));
02910   clipRegion = XCreateRegion();
02911   rect.x = rect.y = 0;
02912   rect.width = pixmapW;
02913   rect.height = pixmapH;
02914   XUnionRectWithRegion(&rect, clipRegion, clipRegion);
02915   XSetRegion(display, strokeGC, clipRegion);
02916   XSetRegion(display, fillGC, clipRegion);
02917   ctm = state->getCTM();
02918   t3GlyphStack->origCTM4 = ctm[4];
02919   t3GlyphStack->origCTM5 = ctm[5];
02920   state->setCTM(ctm[0], ctm[1], ctm[2], ctm[3], 
02921         -t3GlyphStack->cache->glyphX, -t3GlyphStack->cache->glyphY);
02922 }
02923 
02924 inline Gulong XOutputDev::findColor(GfxRGB *x, GfxRGB *err) {
02925   double gray;
02926   int r, g, b;
02927   Gulong pixel;
02928 
02929   if (trueColor) {
02930     r = xoutRound(x->r * rMul);
02931     g = xoutRound(x->g * gMul);
02932     b = xoutRound(x->b * bMul);
02933     pixel = ((Gulong)r << rShift) +
02934             ((Gulong)g << gShift) +
02935             ((Gulong)b << bShift);
02936     err->r = x->r - (double)r / rMul;
02937     err->g = x->g - (double)g / gMul;
02938     err->b = x->b - (double)b / bMul;
02939   } else if (numColors == 1) {
02940     gray = 0.299 * x->r + 0.587 * x->g + 0.114 * x->b;
02941     if (gray < 0.5) {
02942       pixel = colors[0];
02943       err->r = x->r;
02944       err->g = x->g;
02945       err->b = x->b;
02946     } else {
02947       pixel = colors[1];
02948       err->r = x->r - 1;
02949       err->g = x->g - 1;
02950       err->b = x->b - 1;
02951     }
02952   } else {
02953     r = xoutRound(x->r * (numColors - 1));
02954     g = xoutRound(x->g * (numColors - 1));
02955     b = xoutRound(x->b * (numColors - 1));
02956     pixel = colors[(r * numColors + g) * numColors + b];
02957     err->r = x->r - (double)r / (numColors - 1);
02958     err->g = x->g - (double)g / (numColors - 1); 
02959     err->b = x->b - (double)b / (numColors - 1);
02960   }
02961   return pixel;
02962 }
02963 
02964 Gulong XOutputDev::findColor(GfxRGB *rgb) {
02965   int r, g, b;
02966   double gray;
02967   Gulong pixel;
02968 
02969   if (trueColor) {
02970     r = xoutRound(rgb->r * rMul);
02971     g = xoutRound(rgb->g * gMul);
02972     b = xoutRound(rgb->b * bMul);
02973     pixel = ((Gulong)r << rShift) +
02974             ((Gulong)g << gShift) +
02975             ((Gulong)b << bShift);
02976   } else if (numColors == 1) {
02977     gray = 0.299 * rgb->r + 0.587 * rgb->g + 0.114 * rgb->b;
02978     if (gray < 0.5)
02979       pixel = colors[0];
02980     else
02981       pixel = colors[1];
02982   } else {
02983     r = xoutRound(rgb->r * (numColors - 1));
02984     g = xoutRound(rgb->g * (numColors - 1));
02985     b = xoutRound(rgb->b * (numColors - 1));
02986 #if 0 // this makes things worse as often as better
02987     // even a very light color shouldn't map to white
02988     if (r == numColors - 1 && g == numColors - 1 && b == numColors - 1) {
02989       if (color->getR() < 0.95)
02990     --r;
02991       if (color->getG() < 0.95)
02992     --g;
02993       if (color->getB() < 0.95)
02994     --b;
02995     }
02996 #endif
02997     pixel = colors[(r * numColors + g) * numColors + b];
02998   }
02999   return pixel;
03000 }
03001 
03002 void XOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
03003                    int width, int height, GBool invert,
03004                    GBool inlineImg) {
03005   ImageStream *imgStr;
03006   XImage *image;
03007   double *ctm;
03008   GBool rot;
03009   double xScale, yScale, xShear, yShear;
03010   int tx, ty, scaledWidth, scaledHeight, xSign, ySign;
03011   int ulx, uly, llx, lly, urx, ury, lrx, lry;
03012   int ulx1, uly1, llx1, lly1, urx1, ury1, lrx1, lry1;
03013   int bx0, by0, bx1, by1, bw, bh;
03014   int cx0, cy0, cx1, cy1, cw, ch;
03015   int yp, yq, yt, yStep, lastYStep;
03016   int xp, xq, xt, xStep, xSrc;
03017   GfxRGB rgb;
03018   Guchar *pixBuf;
03019   int imgPix;
03020   double alpha;
03021   XColor xcolor;
03022   Gulong lastPixel;
03023   GfxRGB rgb2;
03024   double r0, g0, b0, r1, g1, b1;
03025   Gulong pix;
03026   Guchar *p;
03027   int x, y, x1, y1, x2, y2;
03028   int n, m, i, j;
03029 
03030   // get CTM, check for singular matrix
03031   ctm = state->getCTM();
03032   if (fabs(ctm[0] * ctm[3] - ctm[1] * ctm[2]) < 0.000001) {
03033     error(-1, "Singular CTM in drawImage");
03034     if (inlineImg) {
03035       j = height * ((width + 7) / 8);
03036       str->reset();
03037       for (i = 0; i < j; ++i) {
03038     str->getChar();
03039       }
03040       str->close();
03041     }
03042     return;
03043   }
03044 
03045   // compute scale, shear, rotation, translation parameters
03046   rot = fabs(ctm[1]) > fabs(ctm[0]);
03047   if (rot) {
03048     xScale = -ctm[1];
03049     yScale = -ctm[2] + (ctm[0] * ctm[3]) / ctm[1];
03050     xShear = ctm[3] / yScale;
03051     yShear = -ctm[0] / ctm[1];
03052   } else {
03053     xScale = ctm[0];
03054     yScale = -ctm[3] + (ctm[1] * ctm[2]) / ctm[0];
03055     xShear = -ctm[2] / yScale;
03056     yShear = ctm[1] / ctm[0];
03057   }
03058   tx = xoutRound(ctm[2] + ctm[4]);
03059   ty = xoutRound(ctm[3] + ctm[5]);
03060   // use ceil() to avoid gaps between "striped" images
03061   scaledWidth = (int)ceil(fabs(xScale));
03062   xSign = (xScale < 0) ? -1 : 1;
03063   scaledHeight = (int)ceil(fabs(yScale));
03064   ySign = (yScale < 0) ? -1 : 1;
03065 
03066   // compute corners in device space
03067   ulx1 = 0;
03068   uly1 = 0;
03069   urx1 = xSign * (scaledWidth - 1);
03070   ury1 = xoutRound(yShear * urx1);
03071   llx1 = xoutRound(xShear * ySign * (scaledHeight - 1));
03072   lly1 = ySign * (scaledHeight - 1) + xoutRound(yShear * llx1);
03073   lrx1 = xSign * (scaledWidth - 1) +
03074            xoutRound(xShear * ySign * (scaledHeight - 1));
03075   lry1 = ySign * (scaledHeight - 1) + xoutRound(yShear * lrx1);
03076   if (rot) {
03077     ulx = tx + uly1;    uly = ty - ulx1;
03078     urx = tx + ury1;    ury = ty - urx1;
03079     llx = tx + lly1;    lly = ty - llx1;
03080     lrx = tx + lry1;    lry = ty - lrx1;
03081   } else {
03082     ulx = tx + ulx1;    uly = ty + uly1;
03083     urx = tx + urx1;    ury = ty + ury1;
03084     llx = tx + llx1;    lly = ty + lly1;
03085     lrx = tx + lrx1;    lry = ty + lry1;
03086   }
03087 
03088   // bounding box:
03089   //   (bx0, by0) = upper-left corner
03090   //   (bx1, by1) = lower-right corner
03091   //   (bw, bh) = size
03092   bx0 = (ulx < urx) ? (ulx < llx) ? (ulx < lrx) ? ulx : lrx
03093                                   : (llx < lrx) ? llx : lrx
03094             : (urx < llx) ? (urx < lrx) ? urx : lrx
03095                                   : (llx < lrx) ? llx : lrx;
03096   bx1 = (ulx > urx) ? (ulx > llx) ? (ulx > lrx) ? ulx : lrx
03097                                   : (llx > lrx) ? llx : lrx
03098             : (urx > llx) ? (urx > lrx) ? urx : lrx
03099                                   : (llx > lrx) ? llx : lrx;
03100   by0 = (uly < ury) ? (uly < lly) ? (uly < lry) ? uly : lry
03101                                   : (lly < lry) ? lly : lry
03102             : (ury < lly) ? (ury < lry) ? ury : lry
03103                                   : (lly < lry) ? lly : lry;
03104   by1 = (uly > ury) ? (uly > lly) ? (uly > lry) ? uly : lry
03105                                   : (lly > lry) ? lly : lry
03106             : (ury > lly) ? (ury > lry) ? ury : lry
03107                                   : (lly > lry) ? lly : lry;
03108   bw = bx1 - bx0 + 1;
03109   bh = by1 - by0 + 1;
03110 
03111   // Bounding box clipped to pixmap, i.e., "valid" rectangle:
03112   //   (cx0, cy0) = upper-left corner of valid rectangle in Pixmap
03113   //   (cx1, cy1) = upper-left corner of valid rectangle in XImage
03114   //   (cw, ch) = size of valid rectangle
03115   // These values will be used to transfer the XImage from/to the
03116   // Pixmap.
03117   cw = (bx1 >= pixmapW) ? pixmapW - bx0 : bw;
03118   if (bx0 < 0) {
03119     cx0 = 0;
03120     cx1 = -bx0;
03121     cw += bx0;
03122   } else {
03123     cx0 = bx0;
03124     cx1 = 0;
03125   }
03126   ch = (by1 >= pixmapH) ? pixmapH - by0 : bh;
03127   if (by0 < 0) {
03128     cy0 = 0;
03129     cy1 = -by0;
03130     ch += by0;
03131   } else {
03132     cy0 = by0;
03133     cy1 = 0;
03134   }
03135 
03136   // check for tiny (zero width or height) images
03137   // and off-page images
03138   if (scaledWidth <= 0 || scaledHeight <= 0 || cw <= 0 || ch <= 0) {
03139     if (inlineImg) {
03140       j = height * ((width + 7) / 8);
03141       str->reset();
03142       for (i = 0; i < j; ++i) {
03143     str->getChar();
03144       }
03145       str->close();
03146     }
03147     return;
03148   }
03149 
03150   // compute Bresenham parameters for x and y scaling
03151   yp = height / scaledHeight;
03152   yq = height % scaledHeight;
03153   xp = width / scaledWidth;
03154   xq = width % scaledWidth;
03155 
03156   // allocate pixel buffer
03157   pixBuf = (Guchar *)gmalloc((yp + 1) * width * sizeof(Guchar));
03158 
03159   // allocate XImage and read from page pixmap
03160   image = XCreateImage(display, visual, depth, ZPixmap, 0, NULL, bw, bh, 8, 0);
03161   image->data = (char *)gmalloc(bh * image->bytes_per_line);
03162   XGetSubImage(display, pixmap, cx0, cy0, cw, ch, (1 << depth) - 1, ZPixmap,
03163            image, cx1, cy1);
03164 
03165   // get mask color
03166   state->getFillRGB(&rgb);
03167   if (reverseVideo) {
03168     rgb.r = 1 - rgb.r;
03169     rgb.g = 1 - rgb.g;
03170     rgb.b = 1 - rgb.b;
03171   }
03172   r0 = rgb.r;
03173   g0 = rgb.g;
03174   b0 = rgb.b;
03175 
03176   // initialize background color
03177   // (the specific pixel value doesn't matter here, as long as
03178   // r1,g1,b1 correspond correctly to lastPixel)
03179   xcolor.pixel = lastPixel = 0;
03180   XQueryColor(display, colormap, &xcolor);
03181   r1 = (double)xcolor.red / 65535.0;
03182   g1 = (double)xcolor.green / 65535.0;
03183   b1 = (double)xcolor.blue / 65535.0;
03184 
03185   // initialize the image stream
03186   imgStr = new ImageStream(str, width, 1, 1);
03187   imgStr->reset();
03188 
03189   // init y scale Bresenham
03190   yt = 0;
03191   lastYStep = 1;
03192 
03193   for (y = 0; y < scaledHeight; ++y) {
03194 
03195     // y scale Bresenham
03196     yStep = yp;
03197     yt += yq;
03198     if (yt >= scaledHeight) {
03199       yt -= scaledHeight;
03200       ++yStep;
03201     }
03202 
03203     // read row(s) from image
03204     n = (yp > 0) ? yStep : lastYStep;
03205     if (n > 0) {
03206       p = pixBuf;
03207       for (i = 0; i < n; ++i) {
03208     memcpy(p, imgStr->getLine(), width);
03209     if (invert) {
03210       for (j = 0; j < width; ++j) {
03211         p[j] ^= 1;
03212       }
03213     }
03214     p += width;
03215       }
03216     }
03217     lastYStep = yStep;
03218 
03219     // init x scale Bresenham
03220     xt = 0;
03221     xSrc = 0;
03222 
03223     for (x = 0; x < scaledWidth; ++x) {
03224 
03225       // x scale Bresenham
03226       xStep = xp;
03227       xt += xq;
03228       if (xt >= scaledWidth) {
03229     xt -= scaledWidth;
03230     ++xStep;
03231       }
03232 
03233       // x shear
03234       x1 = xSign * x + xoutRound(xShear * ySign * y);
03235 
03236       // y shear
03237       y1 = ySign * y + xoutRound(yShear * x1);
03238 
03239       // rotation
03240       if (rot) {
03241     x2 = y1;
03242     y2 = -x1;
03243       } else {
03244     x2 = x1;
03245     y2 = y1;
03246       }
03247 
03248       // compute the filtered pixel at (x,y) after the
03249       // x and y scaling operations
03250       n = yStep > 0 ? yStep : 1;
03251       m = xStep > 0 ? xStep : 1;
03252       p = pixBuf + xSrc;
03253       imgPix = 0;
03254       for (i = 0; i < n; ++i) {
03255     for (j = 0; j < m; ++j) {
03256       imgPix += *p++;
03257     }
03258     p += width - m;
03259       }
03260 
03261       // x scale Bresenham
03262       xSrc += xStep;
03263 
03264       // blend image pixel with background
03265       alpha = (double)imgPix / (double)(n * m);
03266       xcolor.pixel = XGetPixel(image, tx + x2 - bx0, ty + y2 - by0);
03267       if (xcolor.pixel != lastPixel) {
03268     XQueryColor(display, colormap, &xcolor);
03269     r1 = (double)xcolor.red / 65535.0;
03270     g1 = (double)xcolor.green / 65535.0;
03271     b1 = (double)xcolor.blue / 65535.0;
03272     lastPixel = xcolor.pixel;
03273       }
03274       rgb2.r = r0 * (1 - alpha) + r1 * alpha;
03275       rgb2.g = g0 * (1 - alpha) + g1 * alpha;
03276       rgb2.b = b0 * (1 - alpha) + b1 * alpha;
03277       pix = findColor(&rgb2);
03278 
03279       // set pixel
03280       XPutPixel(image, tx + x2 - bx0, ty + y2 - by0, pix);
03281     }
03282   }
03283 
03284   // blit the image into the pixmap
03285   XPutImage(display, pixmap, fillGC, image, cx1, cy1, cx0, cy0, cw, ch);
03286 
03287   // free memory
03288   delete imgStr;
03289   gfree(pixBuf);
03290   gfree(image->data);
03291   image->data = NULL;
03292   XDestroyImage(image);
03293 }
03294 
03295 void XOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
03296                int width, int height, GfxImageColorMap *colorMap,
03297                int *maskColors, GBool inlineImg) {
03298   ImageStream *imgStr;
03299   XImage *image;
03300   int nComps, nVals, nBits;
03301   GBool dither;
03302   double *ctm;
03303   GBool rot;
03304   double xScale, yScale, xShear, yShear;
03305   int tx, ty, scaledWidth, scaledHeight, xSign, ySign;
03306   int ulx, uly, llx, lly, urx, ury, lrx, lry;
03307   int ulx1, uly1, llx1, lly1, urx1, ury1, lrx1, lry1;
03308   int bx0, by0, bx1, by1, bw, bh;
03309   int cx0, cy0, cx1, cy1, cw, ch;
03310   int yp, yq, yt, yStep, lastYStep;
03311   int xp, xq, xt, xStep, xSrc;
03312   GfxRGB *pixBuf;
03313   Guchar *alphaBuf;
03314   Guchar pixBuf2[gfxColorMaxComps];
03315   GfxRGB color2, err, errRight;
03316   GfxRGB *errDown;
03317   double r0, g0, b0, alpha, mul;
03318   Gulong pix;
03319   GfxRGB *p;
03320   Guchar *q, *p2;
03321   GBool oneBitMode;
03322   GfxRGB oneBitRGB[2];
03323   int x, y, x1, y1, x2, y2;
03324   int n, m, i, j, k;
03325 
03326   // image parameters
03327   nComps = colorMap->getNumPixelComps();
03328   nVals = width * nComps;
03329   nBits = colorMap->getBits();
03330   dither = nComps > 1 || nBits > 1;
03331 
03332   // get CTM, check for singular matrix
03333   ctm = state->getCTM();
03334   if (fabs(ctm[0] * ctm[3] - ctm[1] * ctm[2]) < 0.000001) {
03335     error(-1, "Singular CTM in drawImage");
03336     if (inlineImg) {
03337       str->reset();
03338       j = height * ((nVals * nBits + 7) / 8);
03339       for (i = 0; i < j; ++i) {
03340     str->getChar();
03341       }
03342       str->close();
03343     }
03344     return;
03345   }
03346 
03347   // compute scale, shear, rotation, translation parameters
03348   rot = fabs(ctm[1]) > fabs(ctm[0]);
03349   if (rot) {
03350     xScale = -ctm[1];
03351     yScale = -ctm[2] + (ctm[0] * ctm[3]) / ctm[1];
03352     xShear = ctm[3] / yScale;
03353     yShear = -ctm[0] / ctm[1];
03354   } else {
03355     xScale = ctm[0];
03356     yScale = -ctm[3] + (ctm[1] * ctm[2]) / ctm[0];
03357     xShear = -ctm[2] / yScale;
03358     yShear = ctm[1] / ctm[0];
03359   }
03360   tx = xoutRound(ctm[2] + ctm[4]);
03361   ty = xoutRound(ctm[3] + ctm[5]);
03362   if (xScale < 0) {
03363     // this is the right edge which needs to be (left + width - 1)
03364     --tx;
03365   }
03366   if (yScale < 0) {
03367     // this is the bottom edge which needs to be (top + height - 1)
03368     --ty;
03369   }
03370   // use ceil() to avoid gaps between "striped" images
03371   scaledWidth = (int)ceil(fabs(xScale));
03372   xSign = (xScale < 0) ? -1 : 1;
03373   scaledHeight = (int)ceil(fabs(yScale));
03374   ySign = (yScale < 0) ? -1 : 1;
03375 
03376   // compute corners in device space
03377   ulx1 = 0;
03378   uly1 = 0;
03379   urx1 = xSign * (scaledWidth - 1);
03380   ury1 = xoutRound(yShear * urx1);
03381   llx1 = xoutRound(xShear * ySign * (scaledHeight - 1));
03382   lly1 = ySign * (scaledHeight - 1) + xoutRound(yShear * llx1);
03383   lrx1 = xSign * (scaledWidth - 1) +
03384            xoutRound(xShear * ySign * (scaledHeight - 1));
03385   lry1 = ySign * (scaledHeight - 1) + xoutRound(yShear * lrx1);
03386   if (rot) {
03387     ulx = tx + uly1;    uly = ty - ulx1;
03388     urx = tx + ury1;    ury = ty - urx1;
03389     llx = tx + lly1;    lly = ty - llx1;
03390     lrx = tx + lry1;    lry = ty - lrx1;
03391   } else {
03392     ulx = tx + ulx1;    uly = ty + uly1;
03393     urx = tx + urx1;    ury = ty + ury1;
03394     llx = tx + llx1;    lly = ty + lly1;
03395     lrx = tx + lrx1;    lry = ty + lry1;
03396   }
03397 
03398   // bounding box:
03399   //   (bx0, by0) = upper-left corner
03400   //   (bx1, by1) = lower-right corner
03401   //   (bw, bh) = size
03402   bx0 = (ulx < urx) ? (ulx < llx) ? (ulx < lrx) ? ulx : lrx
03403                                   : (llx < lrx) ? llx : lrx
03404             : (urx < llx) ? (urx < lrx) ? urx : lrx
03405                                   : (llx < lrx) ? llx : lrx;
03406   bx1 = (ulx > urx) ? (ulx > llx) ? (ulx > lrx) ? ulx : lrx
03407                                   : (llx > lrx) ? llx : lrx
03408             : (urx > llx) ? (urx > lrx) ? urx : lrx
03409                                   : (llx > lrx) ? llx : lrx;
03410   by0 = (uly < ury) ? (uly < lly) ? (uly < lry) ? uly : lry
03411                                   : (lly < lry) ? lly : lry
03412             : (ury < lly) ? (ury < lry) ? ury : lry
03413                                   : (lly < lry) ? lly : lry;
03414   by1 = (uly > ury) ? (uly > lly) ? (uly > lry) ? uly : lry
03415                                   : (lly > lry) ? lly : lry
03416             : (ury > lly) ? (ury > lry) ? ury : lry
03417                                   : (lly > lry) ? lly : lry;
03418   bw = bx1 - bx0 + 1;
03419   bh = by1 - by0 + 1;
03420 
03421   // Bounding box clipped to pixmap, i.e., "valid" rectangle:
03422   //   (cx0, cy0) = upper-left corner of valid rectangle in Pixmap
03423   //   (cx1, cy1) = upper-left corner of valid rectangle in XImage
03424   //   (cw, ch) = size of valid rectangle
03425   // These values will be used to transfer the XImage from/to the
03426   // Pixmap.
03427   cw = (bx1 >= pixmapW) ? pixmapW - bx0 : bw;
03428   if (bx0 < 0) {
03429     cx0 = 0;
03430     cx1 = -bx0;
03431     cw += bx0;
03432   } else {
03433     cx0 = bx0;
03434     cx1 = 0;
03435   }
03436   ch = (by1 >= pixmapH) ? pixmapH - by0 : bh;
03437   if (by0 < 0) {
03438     cy0 = 0;
03439     cy1 = -by0;
03440     ch += by0;
03441   } else {
03442     cy0 = by0;
03443     cy1 = 0;
03444   }
03445 
03446   // check for tiny (zero width or height) images
03447   // and off-page images
03448   if (scaledWidth <= 0 || scaledHeight <= 0 || cw <= 0 || ch <= 0) {
03449     if (inlineImg) {
03450       str->reset();
03451       j = height * ((nVals * nBits + 7) / 8);
03452       for (i = 0; i < j; ++i)
03453     str->getChar();
03454       str->close();
03455     }
03456     return;
03457   }
03458 
03459   // compute Bresenham parameters for x and y scaling
03460   yp = height / scaledHeight;
03461   yq = height % scaledHeight;
03462   xp = width / scaledWidth;
03463   xq = width % scaledWidth;
03464 
03465   // allocate pixel buffer
03466   pixBuf = (GfxRGB *)gmalloc((yp + 1) * width * sizeof(GfxRGB));
03467   if (maskColors) {
03468     alphaBuf = (Guchar *)gmalloc((yp + 1) * width * sizeof(Guchar));
03469   } else {
03470     alphaBuf = NULL;
03471   }
03472 
03473   // allocate XImage
03474   image = XCreateImage(display, visual, depth, ZPixmap, 0, NULL, bw, bh, 8, 0);
03475   image->data = (char *)gmalloc(bh * image->bytes_per_line);
03476 
03477   // if the transform is anything other than a 0/90/180/270 degree
03478   // rotation/flip, or if there is color key masking, read the
03479   // backgound pixmap to fill in the corners
03480   if (!((ulx == llx && uly == ury) ||
03481     (uly == lly && ulx == urx)) ||
03482       maskColors) {
03483     XGetSubImage(display, pixmap, cx0, cy0, cw, ch, (1 << depth) - 1, ZPixmap,
03484          image, cx1, cy1);
03485   }
03486 
03487   // allocate error diffusion accumulators
03488   if (dither) {
03489     errDown = (GfxRGB *)gmalloc(bw * sizeof(GfxRGB));
03490     for (j = 0; j < bw; ++j) {
03491       errDown[j].r = errDown[j].g = errDown[j].b = 0;
03492     }
03493   } else {
03494     errDown = NULL;
03495   }
03496 
03497   // optimize the one-bit-deep image case
03498   if ((oneBitMode = nComps == 1 && nBits == 1)) {
03499     pixBuf2[0] = 0;
03500     colorMap->getRGB(pixBuf2, &oneBitRGB[0]);
03501     pixBuf2[0] = 1;
03502     colorMap->getRGB(pixBuf2, &oneBitRGB[1]);
03503   }
03504 
03505   // initialize the image stream
03506   imgStr = new ImageStream(str, width, nComps, nBits);
03507   imgStr->reset();
03508 
03509   // init y scale Bresenham
03510   yt = 0;
03511   lastYStep = 1;
03512 
03513   for (y = 0; y < scaledHeight; ++y) {
03514 
03515     // initialize error diffusion accumulator
03516     errRight.r = errRight.g = errRight.b = 0;
03517 
03518     // y scale Bresenham
03519     yStep = yp;
03520     yt += yq;
03521     if (yt >= scaledHeight) {
03522       yt -= scaledHeight;
03523       ++yStep;
03524     }
03525 
03526     // read row(s) from image
03527     n = (yp > 0) ? yStep : lastYStep;
03528     if (n > 0) {
03529       p = pixBuf;
03530       q = alphaBuf;
03531       for (i = 0; i < n; ++i) {
03532         p2 = imgStr->getLine();
03533         for (j = 0; j < width; ++j) {
03534           if (oneBitMode) {
03535             *p = oneBitRGB[*p2];
03536           } else {
03537             colorMap->getRGB(p2, p);
03538           }
03539           ++p;
03540           if (q) {
03541             *q = 1;
03542             for (k = 0; k < nComps; ++k) {
03543               if (p2[k] < maskColors[2*k] ||
03544                   p2[k] > maskColors[2*k]) {
03545                 *q = 0;
03546                 break;
03547               }
03548             }
03549             ++q;
03550           }
03551           p2 += nComps;
03552         }
03553       }
03554     }
03555     lastYStep = yStep;
03556 
03557     // init x scale Bresenham
03558     xt = 0;
03559     xSrc = 0;
03560 
03561     for (x = 0; x < scaledWidth; ++x) {
03562 
03563       // x scale Bresenham
03564       xStep = xp;
03565       xt += xq;
03566       if (xt >= scaledWidth) {
03567     xt -= scaledWidth;
03568     ++xStep;
03569       }
03570 
03571       // x shear
03572       x1 = xSign * x + xoutRound(xShear * ySign * y);
03573 
03574       // y shear
03575       y1 = ySign * y + xoutRound(yShear * x1);
03576 
03577       // rotation
03578       if (rot) {
03579     x2 = y1;
03580     y2 = -x1;
03581       } else {
03582     x2 = x1;
03583     y2 = y1;
03584       }
03585 
03586       // compute the filtered pixel at (x,y) after the
03587       // x and y scaling operations
03588       n = yStep > 0 ? yStep : 1;
03589       m = xStep > 0 ? xStep : 1;
03590       p = pixBuf + xSrc;
03591       r0 = g0 = b0 = 0;
03592       q = alphaBuf ? alphaBuf + xSrc : (Guchar *)NULL;
03593       alpha = 0;
03594       for (i = 0; i < n; ++i) {
03595     for (j = 0; j < m; ++j) {
03596       r0 += p->r;
03597       g0 += p->g;
03598       b0 += p->b;
03599       ++p;
03600       if (q) {
03601         alpha += *q++;
03602       }
03603     }
03604     p += width - m;
03605       }
03606       mul = 1 / (double)(n * m);
03607       r0 *= mul;
03608       g0 *= mul;
03609       b0 *= mul;
03610       alpha *= mul;
03611 
03612       // x scale Bresenham
03613       xSrc += xStep;
03614 
03615       // compute pixel
03616       if (dither) {
03617     color2.r = r0 + errRight.r + errDown[tx + x2 - bx0].r;
03618     if (color2.r > 1) {
03619       color2.r = 1;
03620     } else if (color2.r < 0) {
03621       color2.r = 0;
03622     }
03623     color2.g = g0 + errRight.g + errDown[tx + x2 - bx0].g;
03624     if (color2.g > 1) {
03625       color2.g = 1;
03626     } else if (color2.g < 0) {
03627       color2.g = 0;
03628     }
03629     color2.b = b0 + errRight.b + errDown[tx + x2 - bx0].b;
03630     if (color2.b > 1) {
03631       color2.b = 1;
03632     } else if (color2.b < 0) {
03633       color2.b = 0;
03634     }
03635     pix = findColor(&color2, &err);
03636     errRight.r = errDown[tx + x2 - bx0].r = err.r / 2;
03637     errRight.g = errDown[tx + x2 - bx0].g = err.g / 2;
03638     errRight.b = errDown[tx + x2 - bx0].b = err.b / 2;
03639       } else {
03640     color2.r = r0;
03641     color2.g = g0;
03642     color2.b = b0;
03643     pix = findColor(&color2, &err);
03644       }
03645 
03646       // set pixel
03647       //~ this should do a blend when 0 < alpha < 1
03648       if (alpha < 0.75) {
03649     XPutPixel(image, tx + x2 - bx0, ty + y2 - by0, pix);
03650       }
03651     }
03652   }
03653 
03654   // blit the image into the pixmap
03655   XPutImage(display, pixmap, fillGC, image, cx1, cy1, cx0, cy0, cw, ch);
03656 
03657   // free memory
03658   delete imgStr;
03659   gfree(pixBuf);
03660   if (maskColors) {
03661     gfree(alphaBuf);
03662   }
03663   gfree(image->data);
03664   image->data = NULL;
03665   XDestroyImage(image);
03666   gfree(errDown);
03667 }
03668 
03669 GBool XOutputDev::findText(Unicode *s, int len, GBool top, GBool bottom,
03670                int *xMin, int *yMin, int *xMax, int *yMax) {
03671   double xMin1, yMin1, xMax1, yMax1;
03672   
03673   xMin1 = (double)*xMin;
03674   yMin1 = (double)*yMin;
03675   xMax1 = (double)*xMax;
03676   yMax1 = (double)*yMax;
03677   if (text->findText(s, len, top, bottom, &xMin1, &yMin1, &xMax1, &yMax1)) {
03678     *xMin = xoutRound(xMin1);
03679     *xMax = xoutRound(xMax1);
03680     *yMin = xoutRound(yMin1);
03681     *yMax = xoutRound(yMax1);
03682     return gTrue;
03683   }
03684   return gFalse;
03685 }
03686 
03687 GString *XOutputDev::getText(int xMin, int yMin, int xMax, int yMax) {
03688   return text->getText((double)xMin, (double)yMin,
03689                (double)xMax, (double)yMax);
03690 }
KDE Home | KDE Accessibility Home | Description of Access Keys