]> www.fi.muni.cz Git - evince.git/blobdiff - pdf/xpdf/XOutputDev.cc
Imported Xpdf 2.03 and fixed build.
[evince.git] / pdf / xpdf / XOutputDev.cc
index 6f207d8e45e0fe976c16bbb384bf1fef68fe709d..a156b5584dfcb2fcf554ee8ae4e918bae51488f2 100644 (file)
@@ -2,15 +2,16 @@
 //
 // XOutputDev.cc
 //
-// Copyright 1996-2002 Glyph & Cog, LLC
+// Copyright 1996-2003 Glyph & Cog, LLC
 //
 //========================================================================
 
-#ifdef __GNUC__
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
 #pragma implementation
 #endif
 
-#include <aconf.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <stddef.h>
@@ -95,6 +96,12 @@ static XOutFontSubst xOutSubstFonts[16] = {
   {"Symbol",                0.576}
 };
 
+//------------------------------------------------------------------------
+
+static void outputToFile(void *stream, char *data, int len) {
+  fwrite(data, 1, len, (FILE *)stream);
+}
+
 //------------------------------------------------------------------------
 // XOutputFont
 //------------------------------------------------------------------------
@@ -581,18 +588,30 @@ void XOutputServer16BitFont::drawChar(GfxState *state, Pixmap pixmap,
 #if HAVE_T1LIB_H
 XOutputT1FontFile::~XOutputT1FontFile() {
   delete fontFile;
+  if (tmpFileName) {
+    unlink(tmpFileName->getCString());
+    delete tmpFileName;
+  }
 }
 #endif
 
 #if FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H)
 XOutputFTFontFile::~XOutputFTFontFile() {
   delete fontFile;
+  if (tmpFileName) {
+    unlink(tmpFileName->getCString());
+    delete tmpFileName;
+  }
 }
 #endif
 
 #if !FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H)
 XOutputTTFontFile::~XOutputTTFontFile() {
   delete fontFile;
+  if (tmpFileName) {
+    unlink(tmpFileName->getCString());
+    delete tmpFileName;
+  }
 }
 #endif
 
@@ -605,18 +624,20 @@ XOutputFontCache::XOutputFontCache(Display *displayA, Guint depthA,
   xOut = xOutA;
 
 #if HAVE_T1LIB_H
-  t1Engine = NULL;
   t1libControl = t1libControlA;
+  t1Engine = NULL;
+  t1FontFiles = NULL;
 #endif
 
 #if FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H)
+  freetypeControl = freetypeControlA;
   ftEngine = NULL;
+  ftFontFiles = NULL;
 #endif
 #if !FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H)
-  ttEngine = NULL;
-#endif
-#if HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H
   freetypeControl = freetypeControlA;
+  ttEngine = NULL;
+  ttFontFiles = NULL;
 #endif
 
   clear();
@@ -626,8 +647,8 @@ XOutputFontCache::~XOutputFontCache() {
   delFonts();
 }
 
-void XOutputFontCache::startDoc(int screenNum, Colormap colormap,
-                               GBool trueColor,
+void XOutputFontCache::startDoc(int screenNum, Visual *visual,
+                               Colormap colormap, GBool trueColor,
                                int rMul, int gMul, int bMul,
                                int rShift, int gShift, int bShift,
                                Gulong *colors, int numColors) {
@@ -636,8 +657,7 @@ void XOutputFontCache::startDoc(int screenNum, Colormap colormap,
 
 #if HAVE_T1LIB_H
   if (t1libControl != fontRastNone) {
-    t1Engine = new T1FontEngine(display, DefaultVisual(display, screenNum),
-                               depth, colormap,
+    t1Engine = new T1FontEngine(display, visual, depth, colormap,
                                t1libControl == fontRastAALow ||
                                  t1libControl == fontRastAAHigh,
                                t1libControl == fontRastAAHigh);
@@ -656,8 +676,7 @@ void XOutputFontCache::startDoc(int screenNum, Colormap colormap,
 
 #if FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H)
   if (freetypeControl != fontRastNone) {
-    ftEngine = new FTFontEngine(display, DefaultVisual(display, screenNum),
-                               depth, colormap,
+    ftEngine = new FTFontEngine(display, visual, depth, colormap,
                                freetypeControl == fontRastAALow ||
                                  freetypeControl == fontRastAAHigh);
     if (ftEngine->isOk()) {
@@ -675,8 +694,7 @@ void XOutputFontCache::startDoc(int screenNum, Colormap colormap,
 
 #if !FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H)
   if (freetypeControl != fontRastNone) {
-    ttEngine = new TTFontEngine(display, DefaultVisual(display, screenNum),
-                               depth, colormap,
+    ttEngine = new TTFontEngine(display, visual, depth, colormap,
                                freetypeControl == fontRastAALow ||
                                  freetypeControl == fontRastAAHigh);
     if (ttEngine->isOk()) {
@@ -702,25 +720,37 @@ void XOutputFontCache::delFonts() {
 
 #if HAVE_T1LIB_H
   // delete Type 1 font files
-  deleteGList(t1FontFiles, XOutputT1FontFile);
+  if (t1FontFiles) {
+    deleteGList(t1FontFiles, XOutputT1FontFile);
+    t1FontFiles = NULL;
+  }
   if (t1Engine) {
     delete t1Engine;
+    t1Engine = NULL;
   }
 #endif
 
 #if FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H)
   // delete FreeType font files
-  deleteGList(ftFontFiles, XOutputFTFontFile);
+  if (ftFontFiles) {
+    deleteGList(ftFontFiles, XOutputFTFontFile);
+    ftFontFiles = NULL;
+  }
   if (ftEngine) {
     delete ftEngine;
+    ftEngine = NULL;
   }
 #endif
 
 #if !FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H)
   // delete TrueType fonts
-  deleteGList(ttFontFiles, XOutputTTFontFile);
+  if (ttFontFiles) {
+    deleteGList(ttFontFiles, XOutputTTFontFile);
+    ttFontFiles = NULL;
+  }
   if (ttEngine) {
     delete ttEngine;
+    ttEngine = NULL;
   }
 #endif
 }
@@ -812,6 +842,7 @@ XOutputFont *XOutputFontCache::getFont(XRef *xref, GfxFont *gfxFont,
     }
 #endif
     break;
+  case fontCIDType0:
   case fontCIDType0C:
 #if FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H)
     if (freetypeControl != fontRastNone) {
@@ -977,8 +1008,8 @@ XOutputFont *XOutputFontCache::tryGetFont(XRef *xref, DisplayFontParam *dfp,
 
   case displayFontT1:
 #if HAVE_T1LIB_H
-    if (t1libControl != fontRastNone) {
-      font = tryGetT1FontFromFile(xref, dfp->t1.fileName, gfxFont,
+    if (t1libControl != fontRastNone && !gfxFont->isCIDFont()) {
+      font = tryGetT1FontFromFile(xref, dfp->t1.fileName, gFalse, gfxFont,
                                  m11Orig, m12Orig, m21Orig, m22Orig,
                                  m11, m12, m21, m22, subst);
     }
@@ -986,9 +1017,9 @@ XOutputFont *XOutputFontCache::tryGetFont(XRef *xref, DisplayFontParam *dfp,
 #if FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H)
     if (!font) {
       if (freetypeControl != fontRastNone) {
-       font = tryGetFTFontFromFile(xref, dfp->t1.fileName, gfxFont,
+       font = tryGetFTFontFromFile(xref, dfp->t1.fileName, gFalse, gfxFont,
                                    m11Orig, m12Orig, m21Orig, m22Orig,
-                                   m11, m12, m21, m22, subst);
+                                   m11, m12, m21, m22, gFalse, subst);
       }
     }
 #endif
@@ -1001,14 +1032,14 @@ XOutputFont *XOutputFontCache::tryGetFont(XRef *xref, DisplayFontParam *dfp,
   case displayFontTT:
 #if FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H)
     if (freetypeControl != fontRastNone) {
-      font = tryGetFTFontFromFile(xref, dfp->tt.fileName, gfxFont,
+      font = tryGetFTFontFromFile(xref, dfp->tt.fileName, gFalse, gfxFont,
                                  m11Orig, m12Orig, m21Orig, m22Orig,
-                                 m11, m12, m21, m22, subst);
+                                 m11, m12, m21, m22, gFalse, subst);
     }
 #endif
 #if !FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H)
     if (freetypeControl != fontRastNone) {
-      font = tryGetTTFontFromFile(xref, dfp->tt.fileName, gfxFont,
+      font = tryGetTTFontFromFile(xref, dfp->tt.fileName, gFalse, gfxFont,
                                  m11Orig, m12Orig, m21Orig, m22Orig,
                                  m11, m12, m21, m22, subst);
     }
@@ -1071,10 +1102,19 @@ XOutputFont *XOutputFontCache::tryGetT1Font(XRef *xref,
     if (gfxFont->getType() == fontType1C) {
       if (!(fontBuf = gfxFont->readEmbFontFile(xref, &fontLen))) {
        fclose(f);
+       unlink(fileName->getCString());
+       delete fileName;
        return NULL;
       }
       ff = new Type1CFontFile(fontBuf, fontLen);
-      ff->convertToType1(f);
+      if (!ff->isOk()) {
+       delete ff;
+       gfree(fontBuf);
+       unlink(fileName->getCString());
+       delete fileName;
+       return NULL;
+      }
+      ff->convertToType1(outputToFile, f);
       delete ff;
       gfree(fontBuf);
     } else { // fontType1
@@ -1091,17 +1131,19 @@ XOutputFont *XOutputFontCache::tryGetT1Font(XRef *xref,
     fclose(f);
 
     // create the Font
-    font = tryGetT1FontFromFile(xref, fileName, gfxFont,
+    font = tryGetT1FontFromFile(xref, fileName, gTrue, gfxFont,
                                m11, m12, m21, m22,
                                m11, m12, m21, m22, gFalse);
 
-    // remove the temporary file
+    // on systems with Unix hard link semantics, this will remove the
+    // last link to the temp file
     unlink(fileName->getCString());
+
     delete fileName;
 
   // check for an external font file
   } else if ((fileName = gfxFont->getExtFontFile())) {
-    font = tryGetT1FontFromFile(xref, fileName, gfxFont,
+    font = tryGetT1FontFromFile(xref, fileName, gFalse, gfxFont,
                                m11, m12, m21, m22,
                                m11, m12, m21, m22, gFalse);
 
@@ -1114,6 +1156,7 @@ XOutputFont *XOutputFontCache::tryGetT1Font(XRef *xref,
 
 XOutputFont *XOutputFontCache::tryGetT1FontFromFile(XRef *xref,
                                                    GString *fileName,
+                                                   GBool deleteFile,
                                                    GfxFont *gfxFont,
                                                    double m11Orig,
                                                    double m12Orig,
@@ -1134,13 +1177,18 @@ XOutputFont *XOutputFontCache::tryGetT1FontFromFile(XRef *xref,
     error(-1, "Couldn't create t1lib font from '%s'",
          fileName->getCString());
     delete fontFile;
+    if (deleteFile) {
+      unlink(fileName->getCString());
+    }
     return NULL;
   }
 
   // add to list
   id = gfxFont->getID();
   t1FontFiles->append(new XOutputT1FontFile(id->num, id->gen,
-                                           subst, fontFile));
+                                           subst, fontFile,
+                                           deleteFile ? fileName->copy()
+                                                      : (GString *)NULL));
 
   // create the Font
   font = new XOutputT1Font(gfxFont->getID(), fontFile,
@@ -1205,6 +1253,8 @@ XOutputFont *XOutputFontCache::tryGetFTFont(XRef *xref,
        gfxFont->getType() == fontCIDType2) {
       if (!(fontBuf = gfxFont->readEmbFontFile(xref, &fontLen))) {
        fclose(f);
+       unlink(fileName->getCString());
+       delete fileName;
        return NULL;
       }
       ff = new TrueTypeFontFile(fontBuf, fontLen);
@@ -1236,19 +1286,21 @@ XOutputFont *XOutputFontCache::tryGetFTFont(XRef *xref,
     fclose(f);
 
     // create the Font
-    font = tryGetFTFontFromFile(xref, fileName, gfxFont,
+    font = tryGetFTFontFromFile(xref, fileName, gTrue, gfxFont,
                                m11, m12, m21, m22,
-                               m11, m12, m21, m22, gFalse);
+                               m11, m12, m21, m22, gTrue, gFalse);
 
-    // remove the temporary file
+    // on systems with Unix hard link semantics, this will remove the
+    // last link to the temp file
     unlink(fileName->getCString());
+
     delete fileName;
 
   // check for an external font file
   } else if ((fileName = gfxFont->getExtFontFile())) {
-    font = tryGetFTFontFromFile(xref, fileName, gfxFont,
+    font = tryGetFTFontFromFile(xref, fileName, gFalse, gfxFont,
                                m11, m12, m21, m22,
-                               m11, m12, m21, m22, gFalse);
+                               m11, m12, m21, m22, gFalse, gFalse);
 
   } else {
     font = NULL;
@@ -1259,6 +1311,7 @@ XOutputFont *XOutputFontCache::tryGetFTFont(XRef *xref,
 
 XOutputFont *XOutputFontCache::tryGetFTFontFromFile(XRef *xref,
                                                    GString *fileName,
+                                                   GBool deleteFile,
                                                    GfxFont *gfxFont,
                                                    double m11Orig,
                                                    double m12Orig,
@@ -1266,36 +1319,66 @@ XOutputFont *XOutputFontCache::tryGetFTFontFromFile(XRef *xref,
                                                    double m22Orig,
                                                    double m11, double m12,
                                                    double m21, double m22,
+                                                   GBool embedded,
                                                    GBool subst) {
   Ref *id;
   FTFontFile *fontFile;
   XOutputFont *font;
+  char *buf;
+  int len;
+  FILE *f;
+  TrueTypeFontFile *ff;
+  Gushort *codeToGID;
 
   // create the FreeType font file
   if (gfxFont->isCIDFont()) {
     if (gfxFont->getType() == fontCIDType2) {
       fontFile = new FTFontFile(ftEngine, fileName->getCString(),
                                ((GfxCIDFont *)gfxFont)->getCIDToGID(),
-                               ((GfxCIDFont *)gfxFont)->getCIDToGIDLen());
-    } else { // fontCIDType0C
-      fontFile = new FTFontFile(ftEngine, fileName->getCString());
+                               ((GfxCIDFont *)gfxFont)->getCIDToGIDLen(),
+                               embedded);
+    } else { // fontCIDType0, fontCIDType0C
+      fontFile = new FTFontFile(ftEngine, fileName->getCString(), embedded);
     }
   } else {
+    if (!(f = fopen(fileName->getCString(), "rb"))) {
+      return NULL;
+    }
+    fseek(f, 0, SEEK_END);
+    len = (int)ftell(f);
+    fseek(f, 0, SEEK_SET);
+    buf = (char *)gmalloc(len);
+    if ((int)fread(buf, 1, len, f) != len) {
+      gfree(buf);
+      fclose(f);
+      return NULL;
+    }
+    fclose(f);
+    ff = new TrueTypeFontFile(buf, len);
+    codeToGID = ((Gfx8BitFont *)gfxFont)->getCodeToGIDMap(ff);
     fontFile = new FTFontFile(ftEngine, fileName->getCString(),
                              ((Gfx8BitFont *)gfxFont)->getEncoding(),
-                             ((Gfx8BitFont *)gfxFont)->getHasEncoding());
+                             codeToGID);
+    gfree(codeToGID);
+    delete ff;
+    gfree(buf);
   }
   if (!fontFile->isOk()) {
     error(-1, "Couldn't create FreeType font from '%s'",
          fileName->getCString());
     delete fontFile;
+    if (deleteFile) {
+      unlink(fileName->getCString());
+    }
     return NULL;
   }
 
   // add to list
   id = gfxFont->getID();
   ftFontFiles->append(new XOutputFTFontFile(id->num, id->gen,
-                                           subst, fontFile));
+                                           subst, fontFile,
+                                           deleteFile ? fileName->copy()
+                                                      : (GString *)NULL));
 
   // create the Font
   font = new XOutputFTFont(gfxFont->getID(), fontFile,
@@ -1363,16 +1446,18 @@ XOutputFont *XOutputFontCache::tryGetTTFont(XRef *xref,
     fclose(f);
 
     // create the Font
-    font = tryGetTTFontFromFile(xref, fileName, gfxFont,
+    font = tryGetTTFontFromFile(xref, fileName, gTrue, gfxFont,
                                m11, m12, m21, m22,
                                m11, m12, m21, m22, gFalse);
 
-    // remove the temporary file
+    // on systems with Unix hard link semantics, this will remove the
+    // last link to the temp file
     unlink(fileName->getCString());
+
     delete fileName;
 
   } else if ((fileName = gfxFont->getExtFontFile())) {
-    font = tryGetTTFontFromFile(xref, fileName, gfxFont,
+    font = tryGetTTFontFromFile(xref, fileName, gFalse, gfxFont,
                                m11, m12, m21, m22,
                                m11, m12, m21, m22, gFalse);
 
@@ -1385,6 +1470,7 @@ XOutputFont *XOutputFontCache::tryGetTTFont(XRef *xref,
 
 XOutputFont *XOutputFontCache::tryGetTTFontFromFile(XRef *xref,
                                                    GString *fileName,
+                                                   GBool deleteFile,
                                                    GfxFont *gfxFont,
                                                    double m11Orig,
                                                    double m12Orig,
@@ -1412,13 +1498,18 @@ XOutputFont *XOutputFontCache::tryGetTTFontFromFile(XRef *xref,
     error(-1, "Couldn't create FreeType font from '%s'",
          fileName->getCString());
     delete fontFile;
+    if (deleteFile) {
+      unlink(fileName->getCString());
+    }
     return NULL;
   }
 
   // add to list
   id = gfxFont->getID();
   ttFontFiles->append(new XOutputTTFontFile(id->num, id->gen,
-                                           subst, fontFile));
+                                           subst, fontFile,
+                                           deleteFile ? fileName->copy()
+                                                      : (GString *)NULL));
 
   // create the Font
   font = new XOutputTTFont(gfxFont->getID(), fontFile,
@@ -1570,6 +1661,7 @@ struct T3GlyphStack {
   GC origStrokeGC;
   GC origFillGC;
   Region origClipRegion;
+  double origCTM4, origCTM5;
   double wx, wy;               // untransformed glyph metrics
   T3GlyphStack *next;
 };
@@ -1578,53 +1670,63 @@ struct T3GlyphStack {
 // XOutputDev
 //------------------------------------------------------------------------
 
-XOutputDev::XOutputDev(Display *displayA, Pixmap pixmapA, Guint depthA,
-                      Colormap colormapA, GBool reverseVideoA,
-                      unsigned long paperColor, GBool installCmap,
-                      int rgbCubeSize) {
+XOutputDev::XOutputDev(Display *displayA, int screenNumA,
+                      Visual *visualA, Colormap colormapA,
+                      GBool reverseVideoA, unsigned long paperColorA,
+                      GBool installCmap, int rgbCubeSize,
+                      int forceDepth) {
   XVisualInfo visualTempl;
   XVisualInfo *visualList;
   int nVisuals;
   Gulong mask;
-  XGCValues gcValues;
   XColor xcolor;
   XColor *xcolors;
-  int r, g, b, n, m, i;
+  int r, g, b, n, m;
   GBool ok;
 
+  // no document yet
   xref = NULL;
 
-  // get display/pixmap info
+  // display / screen / visual / colormap
   display = displayA;
-  screenNum = DefaultScreen(display);
-  pixmap = pixmapA;
-  depth = depthA;
+  screenNum = screenNumA;
+  visual = visualA;
   colormap = colormapA;
 
+  // no pixmap yet
+  pixmapW = pixmapH = 0;
+
   // check for TrueColor visual
-  trueColor = gFalse;
-  if (depth == 0) {
-    depth = DefaultDepth(display, screenNum);
-    visualList = XGetVisualInfo(display, 0, &visualTempl, &nVisuals);
-    for (i = 0; i < nVisuals; ++i) {
-      if (visualList[i].visual == DefaultVisual(display, screenNum)) {
-       if (visualList[i].c_class == TrueColor) {
-         trueColor = gTrue;
-         for (mask = visualList[i].red_mask, rShift = 0;
-              mask && !(mask & 1);
-              mask >>= 1, ++rShift) ;
-         rMul = (int)mask;
-         for (mask = visualList[i].green_mask, gShift = 0;
-              mask && !(mask & 1);
-              mask >>= 1, ++gShift) ;
-         gMul = (int)mask;
-         for (mask = visualList[i].blue_mask, bShift = 0;
-              mask && !(mask & 1);
-              mask >>= 1, ++bShift) ;
-         bMul = (int)mask;
-       }
-       break;
-      }
+  if (forceDepth != 0) {
+    depth = forceDepth;
+    trueColor = depth >= 16;
+  } else {
+    visualTempl.visualid = XVisualIDFromVisual(visual);
+    visualList = XGetVisualInfo(display, VisualIDMask,
+                               &visualTempl, &nVisuals);
+    if (nVisuals < 1) {
+      // this shouldn't happen
+      XFree((XPointer)visualList);
+      visualList = XGetVisualInfo(display, VisualNoMask, &visualTempl,
+                                 &nVisuals);
+    }
+    depth = visualList->depth;
+    if (visualList->c_class == TrueColor) {
+      trueColor = gTrue;
+      for (mask = visualList->red_mask, rShift = 0;
+          mask && !(mask & 1);
+          mask >>= 1, ++rShift) ;
+      rMul = (int)mask;
+      for (mask = visualList->green_mask, gShift = 0;
+          mask && !(mask & 1);
+          mask >>= 1, ++gShift) ;
+      gMul = (int)mask;
+      for (mask = visualList->blue_mask, bShift = 0;
+          mask && !(mask & 1);
+          mask >>= 1, ++bShift) ;
+      bMul = (int)mask;
+    } else {
+      trueColor = gFalse;
     }
     XFree((XPointer)visualList);
   }
@@ -1710,37 +1812,23 @@ XOutputDev::XOutputDev(Display *displayA, Pixmap pixmapA, Guint depthA,
     }
   }
 
-  // reverse video mode
+  // misc parameters
   reverseVideo = reverseVideoA;
-
-  // allocate GCs
-  gcValues.foreground = BlackPixel(display, screenNum);
-  gcValues.background = WhitePixel(display, screenNum);
-  gcValues.line_width = 0;
-  gcValues.line_style = LineSolid;
-  strokeGC = XCreateGC(display, pixmap,
-                      GCForeground | GCBackground | GCLineWidth | GCLineStyle,
-                       &gcValues);
-  fillGC = XCreateGC(display, pixmap,
-                    GCForeground | GCBackground | GCLineWidth | GCLineStyle,
-                    &gcValues);
-  gcValues.foreground = paperColor;
-  paperGC = XCreateGC(display, pixmap,
-                     GCForeground | GCBackground | GCLineWidth | GCLineStyle,
-                     &gcValues);
-
-  // no clip region yet
-  clipRegion = NULL;
+  paperColor = paperColorA;
 
   // set up the font cache and fonts
   gfxFont = NULL;
   font = NULL;
+  needFontUpdate = gFalse;
   fontCache = new XOutputFontCache(display, depth, this,
                                   globalParams->getT1libControl(),
                                   globalParams->getFreeTypeControl());
   nT3Fonts = 0;
   t3GlyphStack = NULL;
 
+  // no text outline clipping path
+  textClipPath = NULL;
+
   // empty state stack
   save = NULL;
 
@@ -1755,12 +1843,6 @@ XOutputDev::~XOutputDev() {
   for (i = 0; i < nT3Fonts; ++i) {
     delete t3FontCache[i];
   }
-  XFreeGC(display, strokeGC);
-  XFreeGC(display, fillGC);
-  XFreeGC(display, paperGC);
-  if (clipRegion) {
-    XDestroyRegion(clipRegion);
-  }
   delete text;
 }
 
@@ -1768,7 +1850,7 @@ void XOutputDev::startDoc(XRef *xrefA) {
   int i;
 
   xref = xrefA;
-  fontCache->startDoc(screenNum, colormap, trueColor, rMul, gMul, bMul,
+  fontCache->startDoc(screenNum, visual, colormap, trueColor, rMul, gMul, bMul,
                      rShift, gShift, bShift, colors, numColors);
   for (i = 0; i < nT3Fonts; ++i) {
     delete t3FontCache[i];
@@ -1777,39 +1859,29 @@ void XOutputDev::startDoc(XRef *xrefA) {
 }
 
 void XOutputDev::startPage(int pageNum, GfxState *state) {
-  XOutputState *s;
   XGCValues gcValues;
   XRectangle rect;
 
-  // clear state stack
-  while (save) {
-    s = save;
-    save = save->next;
-    XFreeGC(display, s->strokeGC);
-    XFreeGC(display, s->fillGC);
-    XDestroyRegion(s->clipRegion);
-    delete s;
-  }
-  save = NULL;
-
   // default line flatness
   flatness = 0;
 
-  // reset GCs
+  // allocate GCs
   gcValues.foreground = BlackPixel(display, screenNum);
   gcValues.background = WhitePixel(display, screenNum);
   gcValues.line_width = 0;
   gcValues.line_style = LineSolid;
-  XChangeGC(display, strokeGC,
-           GCForeground | GCBackground | GCLineWidth | GCLineStyle,
-           &gcValues);
-  XChangeGC(display, fillGC,
-           GCForeground | GCBackground | GCLineWidth | GCLineStyle,
-           &gcValues);
-
-  // clear clipping region
-  if (clipRegion)
-    XDestroyRegion(clipRegion);
+  strokeGC = XCreateGC(display, pixmap,
+                      GCForeground | GCBackground | GCLineWidth | GCLineStyle,
+                       &gcValues);
+  fillGC = XCreateGC(display, pixmap,
+                    GCForeground | GCBackground | GCLineWidth | GCLineStyle,
+                    &gcValues);
+  gcValues.foreground = paperColor;
+  paperGC = XCreateGC(display, pixmap,
+                     GCForeground | GCBackground | GCLineWidth | GCLineStyle,
+                     &gcValues);
+
+  // initialize clip region
   clipRegion = XCreateRegion();
   rect.x = rect.y = 0;
   rect.width = pixmapW;
@@ -1826,40 +1898,85 @@ void XOutputDev::startPage(int pageNum, GfxState *state) {
   XFillRectangle(display, pixmap, paperGC, 0, 0, pixmapW, pixmapH);
 
   // clear text object
-  text->clear();
+  text->startPage(state);
 }
 
 void XOutputDev::endPage() {
-  text->coalesce();
+  XOutputState *s;
+
+  text->coalesce(gTrue);
+
+  // clear state stack, free all GCs, free the clip region
+  while (save) {
+    s = save;
+    save = save->next;
+    XFreeGC(display, s->strokeGC);
+    XFreeGC(display, s->fillGC);
+    XDestroyRegion(s->clipRegion);
+    delete s;
+  }
+  XFreeGC(display, strokeGC);
+  XFreeGC(display, fillGC);
+  XFreeGC(display, paperGC);
+  XDestroyRegion(clipRegion);
 }
 
 void XOutputDev::drawLink(Link *link, Catalog *catalog) {
-  double x1, y1, x2, y2, w;
+  double x1, y1, x2, y2;
+  LinkBorderStyle *borderStyle;
   GfxRGB rgb;
+  double *dash;
+  char dashList[20];
+  int dashLength;
   XPoint points[5];
-  int x, y;
+  int x, y, i;
 
-  link->getBorder(&x1, &y1, &x2, &y2, &w);
-  if (w > 0) {
-    rgb.r = 0;
-    rgb.g = 0;
-    rgb.b = 1;
+  link->getRect(&x1, &y1, &x2, &y2);
+  borderStyle = link->getBorderStyle();
+  if (borderStyle->getWidth() > 0) {
+    borderStyle->getColor(&rgb.r, &rgb.g, &rgb.b);
     XSetForeground(display, strokeGC, findColor(&rgb));
-    XSetLineAttributes(display, strokeGC, xoutRound(w),
-                      LineSolid, CapRound, JoinRound);
-    cvtUserToDev(x1, y1, &x, &y);
-    points[0].x = points[4].x = x;
-    points[0].y = points[4].y = y;
-    cvtUserToDev(x2, y1, &x, &y);
-    points[1].x = x;
-    points[1].y = y;
-    cvtUserToDev(x2, y2, &x, &y);
-    points[2].x = x;
-    points[2].y = y;
-    cvtUserToDev(x1, y2, &x, &y);
-    points[3].x = x;
-    points[3].y = y;
-    XDrawLines(display, pixmap, strokeGC, points, 5, CoordModeOrigin);
+    borderStyle->getDash(&dash, &dashLength);
+    if (borderStyle->getType() == linkBorderDashed && dashLength > 0) {
+      if (dashLength > 20) {
+       dashLength = 20;
+      }
+      for (i = 0; i < dashLength; ++i) {
+       if ((dashList[i] = xoutRound(dash[i])) == 0) {
+         dashList[i] = 1;
+       }
+      }
+      XSetLineAttributes(display, strokeGC, xoutRound(borderStyle->getWidth()),
+                        LineOnOffDash, CapButt, JoinMiter);
+      XSetDashes(display, strokeGC, 0, dashList, dashLength);
+    } else {
+      XSetLineAttributes(display, strokeGC, xoutRound(borderStyle->getWidth()),
+                        LineSolid, CapButt, JoinMiter);
+    }
+    if (borderStyle->getType() == linkBorderUnderlined) {
+      cvtUserToDev(x1, y1, &x, &y);
+      points[0].x = x;
+      points[0].y = y;
+      cvtUserToDev(x2, y1, &x, &y);
+      points[1].x = x;
+      points[1].y = y;
+      XDrawLine(display, pixmap, strokeGC, points[0].x, points[0].y,
+               points[1].x, points[1].y);
+    } else {
+      cvtUserToDev(x1, y1, &x, &y);
+      points[0].x = points[4].x = x;
+      points[0].y = points[4].y = y;
+      cvtUserToDev(x2, y1, &x, &y);
+      points[1].x = x;
+      points[1].y = y;
+      cvtUserToDev(x2, y2, &x, &y);
+      points[2].x = x;
+      points[2].y = y;
+      cvtUserToDev(x1, y2, &x, &y);
+      points[3].x = x;
+      points[3].y = y;
+      XDrawLines(display, pixmap, strokeGC, points, 5, CoordModeOrigin);
+    }
   }
 }
 
@@ -1909,6 +2026,9 @@ void XOutputDev::restoreState(GfxState *state) {
     s = save;
     save = save->next;
     delete s;
+
+    // we'll need to restore the font
+    needFontUpdate = gTrue;
   }
 }
 
@@ -1918,7 +2038,7 @@ void XOutputDev::updateAll(GfxState *state) {
   updateMiterLimit(state);
   updateFillColor(state);
   updateStrokeColor(state);
-  updateFont(state);
+  needFontUpdate = gTrue;
 }
 
 void XOutputDev::updateCTM(GfxState *state, double m11, double m12,
@@ -2026,6 +2146,10 @@ void XOutputDev::updateStrokeColor(GfxState *state) {
 void XOutputDev::updateFont(GfxState *state) {
   double m11, m12, m21, m22;
 
+  needFontUpdate = gFalse;
+
+  text->updateFont(state);
+
   if (!(gfxFont = state->getFont())) {
     font = NULL;
     return;
@@ -2042,8 +2166,6 @@ void XOutputDev::updateFont(GfxState *state) {
     font->updateGC(fillGC);
     font->updateGC(strokeGC);
   }
-
-  text->updateFont(state);
 }
 
 void XOutputDev::stroke(GfxState *state) {
@@ -2052,7 +2174,8 @@ void XOutputDev::stroke(GfxState *state) {
   int n, size, numPoints, i, j;
 
   // transform points
-  n = convertPath(state, &points, &size, &numPoints, &lengths, gFalse);
+  n = convertPath(state, state->getPath(),
+                 &points, &size, &numPoints, &lengths, gFalse);
 
   // draw each subpath
   j = 0;
@@ -2095,7 +2218,8 @@ void XOutputDev::doFill(GfxState *state, int rule) {
   XSetFillRule(display, fillGC, rule);
 
   // transform points, build separate polygons
-  n = convertPath(state, &points, &size, &numPoints, &lengths, gTrue);
+  n = convertPath(state, state->getPath(),
+                 &points, &size, &numPoints, &lengths, gTrue);
 
   // fill them
   j = 0;
@@ -2117,41 +2241,109 @@ void XOutputDev::doFill(GfxState *state, int rule) {
 }
 
 void XOutputDev::clip(GfxState *state) {
-  doClip(state, WindingRule);
+  doClip(state, state->getPath(), WindingRule);
 }
 
 void XOutputDev::eoClip(GfxState *state) {
-  doClip(state, EvenOddRule);
+  doClip(state, state->getPath(), EvenOddRule);
 }
 
-void XOutputDev::doClip(GfxState *state, int rule) {
+void XOutputDev::doClip(GfxState *state, GfxPath *path, int rule) {
+  GfxSubpath *subpath;
   Region region, region2;
+  XPoint rect[5];
   XPoint *points;
   int *lengths;
+  double x0, y0, x1, y1, x2, y2, x3, y3;
+  GBool gotRect;
   int n, size, numPoints, i, j;
 
-  // transform points, build separate polygons
-  n = convertPath(state, &points, &size, &numPoints, &lengths, gTrue);
-
-  // construct union of subpath regions
-  // (XPolygonRegion chokes if there aren't at least three points --
-  // this happens if the PDF file does moveto/closepath/clip, which
-  // sets an empty clipping region)
-  if (lengths[0] > 2) {
-    region = XPolygonRegion(points, lengths[0], rule);
-  } else {
-    region = XCreateRegion();
+  // special case for rectangular clipping paths -- this is a common
+  // case, and we want to make sure not to clip an extra pixel on the
+  // right and bottom edges due to the difference between the PDF and
+  // X rendering models
+  gotRect = gFalse;
+  if (path->getNumSubpaths() == 1) {
+    subpath = path->getSubpath(0);
+    if ((subpath->isClosed() && subpath->getNumPoints() == 5) ||
+       (!subpath->isClosed() && subpath->getNumPoints() == 4)) {
+      state->transform(subpath->getX(0), subpath->getY(0), &x0, &y0);
+      state->transform(subpath->getX(1), subpath->getY(1), &x1, &y1);
+      state->transform(subpath->getX(2), subpath->getY(2), &x2, &y2);
+      state->transform(subpath->getX(3), subpath->getY(3), &x3, &y3);
+      if (fabs(x0-x1) < 1 && fabs(x2-x3) < 1 &&
+         fabs(y0-y3) < 1 && fabs(y1-y2) < 1) {
+       if (x0 < x2) {
+         rect[0].x = rect[1].x = rect[4].x = (int)floor(x0);
+         rect[2].x = rect[3].x = (int)floor(x2) + 1;
+       } else {
+         rect[0].x = rect[1].x = rect[4].x = (int)floor(x0) + 1;
+         rect[2].x = rect[3].x = (int)floor(x2);
+       }
+       if (y0 < y1) {
+         rect[0].y = rect[3].y = rect[4].y = (int)floor(y0);
+         rect[1].y = rect[2].y = (int)floor(y1) + 1;
+       } else {
+         rect[0].y = rect[3].y = rect[4].y = (int)floor(y0) + 1;
+         rect[1].y = rect[2].y = (int)floor(y1);
+       }
+       gotRect = gTrue;
+      } else if (fabs(x0-x3) < 1 && fabs(x1-x2) < 1 &&
+                fabs(y0-y1) < 1 && fabs(y2-y3) < 1) {
+       if (x0 < x1) {
+         rect[0].x = rect[3].x = rect[4].x = (int)floor(x0);
+         rect[1].x = rect[2].x = (int)floor(x1) + 1;
+       } else {
+         rect[0].x = rect[3].x = rect[4].x = (int)floor(x0) + 1;
+         rect[1].x = rect[2].x = (int)floor(x1);
+       }
+       if (y0 < y2) {
+         rect[0].y = rect[1].y = rect[4].y = (int)floor(y0);
+         rect[2].y = rect[3].y = (int)floor(y2) + 1;
+       } else {
+         rect[0].y = rect[1].y = rect[4].y = (int)floor(y0) + 1;
+         rect[2].y = rect[3].y = (int)floor(y2);
+       }
+       gotRect = gTrue;
+      }
+    }
   }
-  j = lengths[0] + 1;
-  for (i = 1; i < n; ++i) {
-    if (lengths[i] > 2) {
-      region2 = XPolygonRegion(points + j, lengths[i], rule);
+
+  if (gotRect) {
+    region = XPolygonRegion(rect, 5, EvenOddRule);
+
+  } else {
+    // transform points, build separate polygons
+    n = convertPath(state, path, &points, &size, &numPoints, &lengths, gTrue);
+
+    // construct union of subpath regions
+    // (XPolygonRegion chokes if there aren't at least three points --
+    // this happens if the PDF file does moveto/closepath/clip, which
+    // sets an empty clipping region)
+    if (lengths[0] > 2) {
+      region = XPolygonRegion(points, lengths[0], rule);
     } else {
-      region2 = XCreateRegion();
+      region = XCreateRegion();
+    }
+    j = lengths[0] + 1;
+    for (i = 1; i < n; ++i) {
+      if (lengths[i] > 2) {
+       region2 = XPolygonRegion(points + j, lengths[i], rule);
+      } else {
+       region2 = XCreateRegion();
+      }
+      XUnionRegion(region2, region, region);
+      XDestroyRegion(region2);
+      j += lengths[i] + 1;
+    }
+
+    // free points and lengths arrays
+    if (points != tmpPoints) {
+      gfree(points);
+    }
+    if (lengths != tmpLengths) {
+      gfree(lengths);
     }
-    XUnionRegion(region2, region, region);
-    XDestroyRegion(region2);
-    j += lengths[i] + 1;
   }
 
   // intersect region with clipping region
@@ -2159,12 +2351,6 @@ void XOutputDev::doClip(GfxState *state, int rule) {
   XDestroyRegion(region);
   XSetRegion(display, strokeGC, clipRegion);
   XSetRegion(display, fillGC, clipRegion);
-
-  // free points and lengths arrays
-  if (points != tmpPoints)
-    gfree(points);
-  if (lengths != tmpLengths)
-    gfree(lengths);
 }
 
 //
@@ -2176,15 +2362,14 @@ void XOutputDev::doClip(GfxState *state, int rule) {
 // Then it connects subaths within a single compound polygon to a single
 // point so that X can fill the polygon (sort of).
 //
-int XOutputDev::convertPath(GfxState *state, XPoint **points, int *size,
+int XOutputDev::convertPath(GfxState *state, GfxPath *path,
+                           XPoint **points, int *size,
                            int *numPoints, int **lengths, GBool fillHack) {
-  GfxPath *path;
   BoundingRect *rects;
   BoundingRect rect;
   int n, i, ii, j, k, k0;
 
   // get path and number of subpaths
-  path = state->getPath();
   n = path->getNumSubpaths();
 
   // allocate lengths array
@@ -2246,15 +2431,15 @@ int XOutputDev::convertPath(GfxState *state, XPoint **points, int *size,
   // kludge: munge any points that are *way* out of bounds - these can
   // crash certain (buggy) X servers
   for (i = 0; i < *numPoints; ++i) {
-    if ((*points)[i].x < -pixmapW) {
-      (*points)[i].x = -pixmapW;
-    } else if ((*points)[i].x > 2 * pixmapW) {
-      (*points)[i].x = 2 * pixmapW;
+    if ((*points)[i].x < -4 * pixmapW) {
+      (*points)[i].x = -4 * pixmapW;
+    } else if ((*points)[i].x > 4 * pixmapW) {
+      (*points)[i].x = 4 * pixmapW;
     }
     if ((*points)[i].y < -pixmapH) {
-      (*points)[i].y = -pixmapH;
-    } else if ((*points)[i].y > 2 * pixmapH) {
-      (*points)[i].y = 2 * pixmapH;
+      (*points)[i].y = -4 * pixmapH;
+    } else if ((*points)[i].y > 4 * pixmapH) {
+      (*points)[i].y = 4 * pixmapH;
     }
   }
 
@@ -2450,11 +2635,11 @@ void XOutputDev::addPoint(XPoint **points, int *size, int *k, int x, int y) {
 }
 
 void XOutputDev::beginString(GfxState *state, GString *s) {
-  text->beginString(state);
+  text->beginWord(state, state->getCurX(), state->getCurY());
 }
 
 void XOutputDev::endString(GfxState *state) {
-  text->endString();
+  text->endWord();
 }
 
 void XOutputDev::drawChar(GfxState *state, double x, double y,
@@ -2468,7 +2653,11 @@ void XOutputDev::drawChar(GfxState *state, double x, double y,
   double *ctm;
   double saveCTM[6];
 
-  text->addChar(state, x, y, dx, dy, u, uLen);
+  if (needFontUpdate) {
+    updateFont(state);
+  }
+
+  text->addChar(state, x, y, dx, dy, code, u, uLen);
 
   if (!font) {
     return;
@@ -2476,7 +2665,7 @@ void XOutputDev::drawChar(GfxState *state, double x, double y,
 
   // check for invisible text -- this is used by Acrobat Capture
   render = state->getRender();
-  if ((render & 3) == 3) {
+  if (render == 3) {
     return;
   }
 
@@ -2525,11 +2714,22 @@ void XOutputDev::drawChar(GfxState *state, double x, double y,
     }
   }
 
-#if 0 //~ unimplemented: clipping to char path
   // clip
   if (render & 4) {
+    if (font->hasGetCharPath()) {
+      saveCurX = state->getCurX();
+      saveCurY = state->getCurY();
+      font->getCharPath(state, code, u, uLen);
+      state->getPath()->offset(x1, y1);
+      if (textClipPath) {
+       textClipPath->append(state->getPath());
+      } else {
+       textClipPath = state->getPath()->copy();
+      }
+      state->clearPath();
+      state->moveTo(saveCurX, saveCurY);
+    }
   }
-#endif
 }
 
 GBool XOutputDev::beginType3Char(GfxState *state,
@@ -2542,6 +2742,9 @@ GBool XOutputDev::beginType3Char(GfxState *state,
   double x1, y1, xMin, yMin, xMax, yMax, xt, yt;
   int i, j;
 
+  if (needFontUpdate) {
+    updateFont(state);
+  }
   if (!gfxFont) {
     return gFalse;
   }
@@ -2625,9 +2828,7 @@ GBool XOutputDev::beginType3Char(GfxState *state,
                                       (int)floor(yMin - yt),
                                       (int)ceil(xMax) - (int)floor(xMin) + 3,
                                       (int)ceil(yMax) - (int)floor(yMin) + 3,
-                                      display,
-                                      DefaultVisual(display, screenNum),
-                                      depth, pixmap);
+                                      display, visual, depth, pixmap);
     }
   }
   t3Font = t3FontCache[0];
@@ -2645,7 +2846,7 @@ GBool XOutputDev::beginType3Char(GfxState *state,
       }
       text->addChar(state, 0, 0,
                    t3Font->cacheTags[i+j].wx, t3Font->cacheTags[i+j].wy,
-                   u, uLen);
+                   code, u, uLen);
       drawType3Glyph(t3Font, &t3Font->cacheTags[i+j],
                     t3Font->cacheData + (i+j) * t3Font->glyphSize,
                     xt, yt, &color);
@@ -2676,6 +2877,7 @@ void XOutputDev::endType3Char(GfxState *state) {
   Gulong pixel;
   double alpha;
   T3GlyphStack *t3gs;
+  double *ctm;
 
   if (t3GlyphStack->cacheable) {
     image = t3GlyphStack->cache->image;
@@ -2716,9 +2918,14 @@ void XOutputDev::endType3Char(GfxState *state) {
     drawType3Glyph(t3GlyphStack->cache,
                   t3GlyphStack->cacheTag, t3GlyphStack->cacheData,
                   t3GlyphStack->x, t3GlyphStack->y, &t3GlyphStack->color);
+    // the CTM must be restored here in order for TextPage::addChar to
+    // work correctly
+    ctm = state->getCTM();
+    state->setCTM(ctm[0], ctm[1], ctm[2], ctm[3],
+                 t3GlyphStack->origCTM4, t3GlyphStack->origCTM5);
   }
   text->addChar(state, 0, 0, t3GlyphStack->wx, t3GlyphStack->wy,
-               t3GlyphStack->u, t3GlyphStack->uLen);
+               t3GlyphStack->code, t3GlyphStack->u, t3GlyphStack->uLen);
   t3gs = t3GlyphStack;
   t3GlyphStack = t3gs->next;
   delete t3gs;
@@ -2813,11 +3020,61 @@ void XOutputDev::type3D1(GfxState *state, double wx, double wy,
   XRectangle rect;
   double *ctm;
   T3FontCache *t3Font;
+  double xt, yt, xMin, xMax, yMin, yMax, x1, y1;
   int i, j;
 
+  t3Font = t3GlyphStack->cache;
+  t3GlyphStack->wx = wx;
+  t3GlyphStack->wy = wy;
+
+  // check for a valid bbox
+  state->transform(0, 0, &xt, &yt);
+  state->transform(llx, lly, &x1, &y1);
+  xMin = xMax = x1;
+  yMin = yMax = y1;
+  state->transform(llx, ury, &x1, &y1);
+  if (x1 < xMin) {
+    xMin = x1;
+  } else if (x1 > xMax) {
+    xMax = x1;
+  }
+  if (y1 < yMin) {
+    yMin = y1;
+  } else if (y1 > yMax) {
+    yMax = y1;
+  }
+  state->transform(urx, lly, &x1, &y1);
+  if (x1 < xMin) {
+    xMin = x1;
+  } else if (x1 > xMax) {
+    xMax = x1;
+  }
+  if (y1 < yMin) {
+    yMin = y1;
+  } else if (y1 > yMax) {
+    yMax = y1;
+  }
+  state->transform(urx, ury, &x1, &y1);
+  if (x1 < xMin) {
+    xMin = x1;
+  } else if (x1 > xMax) {
+    xMax = x1;
+  }
+  if (y1 < yMin) {
+    yMin = y1;
+  } else if (y1 > yMax) {
+    yMax = y1;
+  }
+  if (xMin - xt < t3Font->glyphX ||
+      yMin - yt < t3Font->glyphY ||
+      xMax - xt > t3Font->glyphX + t3Font->glyphW ||
+      yMax - yt > t3Font->glyphY + t3Font->glyphH) {
+    error(-1, "Bad bounding box in Type 3 glyph");
+    return;
+  }
+
   // allocate a cache entry
   t3GlyphStack->cacheable = gTrue;
-  t3Font = t3GlyphStack->cache;
   i = t3GlyphStack->cacheIdx;
   for (j = 0; j < t3Font->cacheAssoc; ++j) {
     if ((t3Font->cacheTags[i+j].mru & 0x7fff) == t3Font->cacheAssoc - 1) {
@@ -2829,8 +3086,6 @@ void XOutputDev::type3D1(GfxState *state, double wx, double wy,
       ++t3Font->cacheTags[i+j].mru;
     }
   }
-  t3GlyphStack->wx = wx;
-  t3GlyphStack->wy = wy;
   t3GlyphStack->cacheTag->wx = wx;
   t3GlyphStack->cacheTag->wy = wy;
 
@@ -2878,11 +3133,29 @@ void XOutputDev::type3D1(GfxState *state, double wx, double wy,
   XSetRegion(display, strokeGC, clipRegion);
   XSetRegion(display, fillGC, clipRegion);
   ctm = state->getCTM();
+  t3GlyphStack->origCTM4 = ctm[4];
+  t3GlyphStack->origCTM5 = ctm[5];
   state->setCTM(ctm[0], ctm[1], ctm[2], ctm[3], 
                -t3GlyphStack->cache->glyphX, -t3GlyphStack->cache->glyphY);
 }
 
-inline Gulong XOutputDev::findColor(GfxRGB *x, GfxRGB *err) {
+void XOutputDev::endTextObject(GfxState *state) {
+  double *ctm;
+  double saveCTM[6];
+
+  if (textClipPath) {
+    ctm = state->getCTM();
+    memcpy(saveCTM, ctm, 6 * sizeof(double));
+    state->setCTM(1, 0, 0, 1, 0, 0);
+    doClip(state, textClipPath, WindingRule);
+    state->setCTM(saveCTM[0], saveCTM[1], saveCTM[2], saveCTM[3],
+                 saveCTM[4], saveCTM[5]);
+    delete textClipPath;
+    textClipPath = NULL;
+  }
+}
+
+inline Gulong XOutputDev::findColor(GfxRGB *x, GfxRGB *actual) {
   double gray;
   int r, g, b;
   Gulong pixel;
@@ -2894,30 +3167,26 @@ inline Gulong XOutputDev::findColor(GfxRGB *x, GfxRGB *err) {
     pixel = ((Gulong)r << rShift) +
             ((Gulong)g << gShift) +
             ((Gulong)b << bShift);
-    err->r = x->r - (double)r / rMul;
-    err->g = x->g - (double)g / gMul;
-    err->b = x->b - (double)b / bMul;
+    actual->r = (double)r / rMul;
+    actual->g = (double)g / gMul;
+    actual->b = (double)b / bMul;
   } else if (numColors == 1) {
     gray = 0.299 * x->r + 0.587 * x->g + 0.114 * x->b;
     if (gray < 0.5) {
       pixel = colors[0];
-      err->r = x->r;
-      err->g = x->g;
-      err->b = x->b;
+      actual->r = actual->g = actual->b = 0;
     } else {
       pixel = colors[1];
-      err->r = x->r - 1;
-      err->g = x->g - 1;
-      err->b = x->b - 1;
+      actual->r = actual->g = actual->b = 1;
     }
   } else {
     r = xoutRound(x->r * (numColors - 1));
     g = xoutRound(x->g * (numColors - 1));
     b = xoutRound(x->b * (numColors - 1));
     pixel = colors[(r * numColors + g) * numColors + b];
-    err->r = x->r - (double)r / (numColors - 1);
-    err->g = x->g - (double)g / (numColors - 1); 
-    err->b = x->b - (double)b / (numColors - 1);
+    actual->r = (double)r / (numColors - 1);
+    actual->g = (double)g / (numColors - 1); 
+    actual->b = (double)b / (numColors - 1);
   }
   return pixel;
 }
@@ -3118,8 +3387,7 @@ void XOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
   pixBuf = (Guchar *)gmalloc((yp + 1) * width * sizeof(Guchar));
 
   // allocate XImage and read from page pixmap
-  image = XCreateImage(display, DefaultVisual(display, screenNum),
-                      depth, ZPixmap, 0, NULL, bw, bh, 8, 0);
+  image = XCreateImage(display, visual, depth, ZPixmap, 0, NULL, bw, bh, 8, 0);
   image->data = (char *)gmalloc(bh * image->bytes_per_line);
   XGetSubImage(display, pixmap, cx0, cy0, cw, ch, (1 << depth) - 1, ZPixmap,
               image, cx1, cy1);
@@ -3167,13 +3435,13 @@ void XOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
     if (n > 0) {
       p = pixBuf;
       for (i = 0; i < n; ++i) {
-       for (j = 0; j < width; ++j) {
-         imgStr->getPixel(p);
-         if (invert) {
-           *p ^= 1;
+       memcpy(p, imgStr->getLine(), width);
+       if (invert) {
+         for (j = 0; j < width; ++j) {
+           p[j] ^= 1;
          }
-         ++p;
        }
+       p += width;
       }
     }
     lastYStep = yStep;
@@ -3272,14 +3540,14 @@ void XOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
   int yp, yq, yt, yStep, lastYStep;
   int xp, xq, xt, xStep, xSrc;
   GfxRGB *pixBuf;
-  Guchar *alphaBuf;
+  Guchar *pixBuf1, *alphaBuf;
   Guchar pixBuf2[gfxColorMaxComps];
-  GfxRGB color2, err, errRight;
-  GfxRGB *errDown;
+  GfxRGB color2, color3, actual, err, errRight;
+  GfxRGB *errDown0, *errDown1, *errDownTmp;
   double r0, g0, b0, alpha, mul;
   Gulong pix;
   GfxRGB *p;
-  Guchar *q;
+  Guchar *q, *p1, *p2;
   GBool oneBitMode;
   GfxRGB oneBitRGB[2];
   int x, y, x1, y1, x2, y2;
@@ -3289,6 +3557,7 @@ void XOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
   nComps = colorMap->getNumPixelComps();
   nVals = width * nComps;
   nBits = colorMap->getBits();
+  oneBitMode = nComps == 1 && nBits == 1 && !maskColors;
   dither = nComps > 1 || nBits > 1;
 
   // get CTM, check for singular matrix
@@ -3321,6 +3590,14 @@ void XOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
   }
   tx = xoutRound(ctm[2] + ctm[4]);
   ty = xoutRound(ctm[3] + ctm[5]);
+  if (xScale < 0) {
+    // this is the right edge which needs to be (left + width - 1)
+    --tx;
+  }
+  if (yScale < 0) {
+    // this is the bottom edge which needs to be (top + height - 1)
+    --ty;
+  }
   // use ceil() to avoid gaps between "striped" images
   scaledWidth = (int)ceil(fabs(xScale));
   xSign = (xScale < 0) ? -1 : 1;
@@ -3417,7 +3694,13 @@ void XOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
   xq = width % scaledWidth;
 
   // allocate pixel buffer
-  pixBuf = (GfxRGB *)gmalloc((yp + 1) * width * sizeof(GfxRGB));
+  if (oneBitMode) {
+    pixBuf1 = (Guchar *)gmalloc((yp + 1) * width * sizeof(Guchar));
+    pixBuf = NULL;
+  } else {
+    pixBuf = (GfxRGB *)gmalloc((yp + 1) * width * sizeof(GfxRGB));
+    pixBuf1 = NULL;
+  }
   if (maskColors) {
     alphaBuf = (Guchar *)gmalloc((yp + 1) * width * sizeof(Guchar));
   } else {
@@ -3425,8 +3708,7 @@ void XOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
   }
 
   // allocate XImage
-  image = XCreateImage(display, DefaultVisual(display, screenNum),
-                      depth, ZPixmap, 0, NULL, bw, bh, 8, 0);
+  image = XCreateImage(display, visual, depth, ZPixmap, 0, NULL, bw, bh, 8, 0);
   image->data = (char *)gmalloc(bh * image->bytes_per_line);
 
   // if the transform is anything other than a 0/90/180/270 degree
@@ -3441,16 +3723,18 @@ void XOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
 
   // allocate error diffusion accumulators
   if (dither) {
-    errDown = (GfxRGB *)gmalloc(bw * sizeof(GfxRGB));
-    for (j = 0; j < bw; ++j) {
-      errDown[j].r = errDown[j].g = errDown[j].b = 0;
+    errDown0 = (GfxRGB *)gmalloc((scaledWidth + 2) * sizeof(GfxRGB));
+    errDown1 = (GfxRGB *)gmalloc((scaledWidth + 2) * sizeof(GfxRGB));
+    for (j = 0; j < scaledWidth + 2; ++j) {
+      errDown0[j].r = errDown0[j].g = errDown0[j].b = 0;
+      errDown1[j].r = errDown1[j].g = errDown1[j].b = 0;
     }
   } else {
-    errDown = NULL;
+    errDown0 = errDown1 = NULL;
   }
 
   // optimize the one-bit-deep image case
-  if ((oneBitMode = nComps == 1 && nBits == 1)) {
+  if (oneBitMode) {
     pixBuf2[0] = 0;
     colorMap->getRGB(pixBuf2, &oneBitRGB[0]);
     pixBuf2[0] = 1;
@@ -3467,8 +3751,16 @@ void XOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
 
   for (y = 0; y < scaledHeight; ++y) {
 
-    // initialize error diffusion accumulator
-    errRight.r = errRight.g = errRight.b = 0;
+    // initialize error diffusion accumulators
+    if (dither) {
+      errDownTmp = errDown0;
+      errDown0 = errDown1;
+      errDown1 = errDownTmp;
+      for (j = 0; j < scaledWidth + 2; ++j) {
+       errDown1[j].r = errDown1[j].g = errDown1[j].b = 0;
+      }
+      errRight.r = errRight.g = errRight.b = 0;
+    }
 
     // y scale Bresenham
     yStep = yp;
@@ -3481,27 +3773,33 @@ void XOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
     // read row(s) from image
     n = (yp > 0) ? yStep : lastYStep;
     if (n > 0) {
-      p = pixBuf;
-      q = alphaBuf;
-      for (i = 0; i < n; ++i) {
-       for (j = 0; j < width; ++j) {
-         imgStr->getPixel(pixBuf2);
-         if (oneBitMode) {
-           *p++ = oneBitRGB[pixBuf2[0]];
-         } else {
-           colorMap->getRGB(pixBuf2, p);
+      if (oneBitMode) {
+       p1 = pixBuf1;
+       for (i = 0; i < n; ++i) {
+         p2 = imgStr->getLine();
+         memcpy(p1, p2, width);
+         p1 += width;
+       }
+      } else {
+       p = pixBuf;
+       q = alphaBuf;
+       for (i = 0; i < n; ++i) {
+         p2 = imgStr->getLine();
+         for (j = 0; j < width; ++j) {
+            colorMap->getRGB(p2, p);
            ++p;
-         }
-         if (q) {
-           *q = 1;
-           for (k = 0; k < nComps; ++k) {
-             if (pixBuf2[k] < maskColors[2*k] ||
-                 pixBuf2[k] > maskColors[2*k]) {
-               *q = 0;
-               break;
+           if (q) {
+             *q = 1;
+             for (k = 0; k < nComps; ++k) {
+               if (p2[k] < maskColors[2*k] ||
+                   p2[k] > maskColors[2*k+1]) {
+                 *q = 0;
+                 break;
+               }
              }
+             ++q;
            }
-           ++q;
+           p2 += nComps;
          }
        }
       }
@@ -3541,60 +3839,94 @@ void XOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
       // x and y scaling operations
       n = yStep > 0 ? yStep : 1;
       m = xStep > 0 ? xStep : 1;
-      p = pixBuf + xSrc;
-      r0 = g0 = b0 = 0;
-      q = alphaBuf ? alphaBuf + xSrc : (Guchar *)NULL;
-      alpha = 0;
-      for (i = 0; i < n; ++i) {
-       for (j = 0; j < m; ++j) {
-         r0 += p->r;
-         g0 += p->g;
-         b0 += p->b;
-         ++p;
-         if (q) {
-           alpha += *q++;
+      if (oneBitMode) {
+       p1 = pixBuf1 + xSrc;
+       k = 0;
+       for (i = 0; i < n; ++i) {
+         for (j = 0; j < m; ++j) {
+           k += *p1++;
          }
+         p1 += width - m;
        }
-       p += width - m;
+       mul = (double)k / (double)(n * m);
+       r0 = mul * oneBitRGB[1].r + (1 - mul) * oneBitRGB[0].r;
+       g0 = mul * oneBitRGB[1].g + (1 - mul) * oneBitRGB[0].g;
+       b0 = mul * oneBitRGB[1].b + (1 - mul) * oneBitRGB[0].b;
+       alpha = 0;
+      } else {
+       p = pixBuf + xSrc;
+       q = alphaBuf ? alphaBuf + xSrc : (Guchar *)NULL;
+       alpha = 0;
+       r0 = g0 = b0 = 0;
+       for (i = 0; i < n; ++i) {
+         for (j = 0; j < m; ++j) {
+           r0 += p->r;
+           g0 += p->g;
+           b0 += p->b;
+           ++p;
+           if (q) {
+             alpha += *q++;
+           }
+         }
+         p += width - m;
+       }
+       mul = 1 / (double)(n * m);
+       r0 *= mul;
+       g0 *= mul;
+       b0 *= mul;
+       alpha *= mul;
       }
-      mul = 1 / (double)(n * m);
-      r0 *= mul;
-      g0 *= mul;
-      b0 *= mul;
-      alpha *= mul;
 
       // x scale Bresenham
       xSrc += xStep;
 
       // compute pixel
       if (dither) {
-       color2.r = r0 + errRight.r + errDown[tx + x2 - bx0].r;
+       color2.r = r0 + errRight.r + errDown0[x + 1].r;
        if (color2.r > 1) {
-         color2.r = 1;
+         color3.r = 1;
        } else if (color2.r < 0) {
-         color2.r = 0;
+         color3.r = 0;
+       } else {
+         color3.r = color2.r;
        }
-       color2.g = g0 + errRight.g + errDown[tx + x2 - bx0].g;
+       color2.g = g0 + errRight.g + errDown0[x + 1].g;
        if (color2.g > 1) {
-         color2.g = 1;
+         color3.g = 1;
        } else if (color2.g < 0) {
-         color2.g = 0;
+         color3.g = 0;
+       } else {
+         color3.g = color2.g;
        }
-       color2.b = b0 + errRight.b + errDown[tx + x2 - bx0].b;
+       color2.b = b0 + errRight.b + errDown0[x + 1].b;
        if (color2.b > 1) {
-         color2.b = 1;
+         color3.b = 1;
        } else if (color2.b < 0) {
-         color2.b = 0;
+         color3.b = 0;
+       } else {
+         color3.b = color2.b;
        }
-       pix = findColor(&color2, &err);
-       errRight.r = errDown[tx + x2 - bx0].r = err.r / 2;
-       errRight.g = errDown[tx + x2 - bx0].g = err.g / 2;
-       errRight.b = errDown[tx + x2 - bx0].b = err.b / 2;
+       pix = findColor(&color3, &actual);
+       err.r = (color2.r - actual.r) / 16;
+       err.g = (color2.g - actual.g) / 16;
+       err.b = (color2.b - actual.b) / 16;
+       errRight.r = 7 * err.r;
+       errRight.g = 7 * err.g;
+       errRight.b = 7 * err.b;
+       errDown1[x].r += 3 * err.r;
+       errDown1[x].g += 3 * err.g;
+       errDown1[x].b += 3 * err.b;
+       errDown1[x + 1].r += 5 * err.r;
+       errDown1[x + 1].g += 5 * err.g;
+       errDown1[x + 1].b += 5 * err.b;
+       errDown1[x + 2].r = err.r;
+       errDown1[x + 2].g = err.g;
+       errDown1[x + 2].b = err.b;
       } else {
        color2.r = r0;
        color2.g = g0;
        color2.b = b0;
-       pix = findColor(&color2, &err);
+       pix = findColor(&color2, &actual);
       }
 
       // set pixel
@@ -3610,25 +3942,35 @@ void XOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
 
   // free memory
   delete imgStr;
-  gfree(pixBuf);
+  if (oneBitMode) {
+    gfree(pixBuf1);
+  } else {
+    gfree(pixBuf);
+  }
   if (maskColors) {
     gfree(alphaBuf);
   }
   gfree(image->data);
   image->data = NULL;
   XDestroyImage(image);
-  gfree(errDown);
+  gfree(errDown0);
+  gfree(errDown1);
 }
 
-GBool XOutputDev::findText(Unicode *s, int len, GBool top, GBool bottom,
-                          int *xMin, int *yMin, int *xMax, int *yMax) {
+GBool XOutputDev::findText(Unicode *s, int len,
+                          GBool startAtTop, GBool stopAtBottom,
+                          GBool startAtLast, GBool stopAtLast,
+                          int *xMin, int *yMin,
+                          int *xMax, int *yMax) {
   double xMin1, yMin1, xMax1, yMax1;
   
   xMin1 = (double)*xMin;
   yMin1 = (double)*yMin;
   xMax1 = (double)*xMax;
   yMax1 = (double)*yMax;
-  if (text->findText(s, len, top, bottom, &xMin1, &yMin1, &xMax1, &yMax1)) {
+  if (text->findText(s, len, startAtTop, stopAtBottom,
+                    startAtLast, stopAtLast,
+                    &xMin1, &yMin1, &xMax1, &yMax1)) {
     *xMin = xoutRound(xMin1);
     *xMax = xoutRound(xMax1);
     *yMin = xoutRound(yMin1);