[poppler] Branch 'xpdf303merge' - poppler/CairoOutputDev.cc poppler/CairoOutputDev.h poppler/Gfx.cc poppler/Gfx.h poppler/GfxState.cc poppler/GfxState.h poppler/GlobalParams.cc poppler/GlobalParams.h poppler/OutputDev.cc poppler/OutputDev.h poppler/PreScanOutputDev.cc poppler/PSOutputDev.cc poppler/PSOutputDev.h poppler/SplashOutputDev.cc poppler/SplashOutputDev.h poppler/TextOutputDev.cc poppler/TextOutputDev.h splash/Splash.cc splash/Splash.h splash/SplashPattern.cc splash/SplashPattern.h utils/pdftoppm.cc

Albert Astals Cid aacid at kemper.freedesktop.org
Sun Jan 15 14:31:26 PST 2012


 poppler/CairoOutputDev.cc   |  101 ++-
 poppler/CairoOutputDev.h    |   21 
 poppler/Gfx.cc              |  705 ++++++++++++++------------
 poppler/Gfx.h               |   22 
 poppler/GfxState.cc         |  208 ++++---
 poppler/GfxState.h          |   37 -
 poppler/GlobalParams.cc     |   17 
 poppler/GlobalParams.h      |    3 
 poppler/OutputDev.cc        |   13 
 poppler/OutputDev.h         |   25 
 poppler/PSOutputDev.cc      |   87 +--
 poppler/PSOutputDev.h       |   21 
 poppler/PreScanOutputDev.cc |    2 
 poppler/SplashOutputDev.cc  | 1179 +++++++++++++++++++++-----------------------
 poppler/SplashOutputDev.h   |   80 --
 poppler/TextOutputDev.cc    |    8 
 poppler/TextOutputDev.h     |    8 
 splash/Splash.cc            |   11 
 splash/Splash.h             |  122 ++--
 splash/SplashPattern.cc     |   14 
 splash/SplashPattern.h      |    7 
 utils/pdftoppm.cc           |    1 
 22 files changed, 1390 insertions(+), 1302 deletions(-)

New commits:
commit 9c092e17e8f0cf0335b431a223e6e44bddc27e64
Author: Albert Astals Cid <aacid at kde.org>
Date:   Sun Jan 15 23:28:51 2012 +0100

    [xpdf] More Splash and Gfx changes from Thomas
    
    1. merge of blend changes
    Here I had not only merged the changed in blend modes, I made also a few
    changes in the SPLASH_CMYK area, so that the already sent PDF now also
    be rendered correctly with the -jpegcmyk option
    2. merge of font handling in SplashOutputDev.cc
    There were a few changes left in font handling, I took them over
    3. merge of getcolor-changes
    The getcolor changes win a price for well defined C++ code. I wouldn't
    have merged them, if there were not a lot of other things to merge.
    4. merge of image handling in SplashOutputDev.cc
    I merged the left changes in image handling including colorizing masks
    in pattern colorspace
    5. cleanup of overprint
    I tested the overprint implementation of Derek. They succeed only in 70
    % percent of the PDF where my solution had success, but Derek's solution
    is much cleaner, and I'm sure that I could also fix the rest in it. BUT:
    as I already considered, when I implemented overprint, there are some
    overprint situations, which can not be solved in a CMYK colorspace, we
    have to implement a DeviceN colorpace when also overprint from CMYK
    colors over spot colors should work. Therefore I decided to remove my
    overprint implementation completely from the code and let Derek's
    solution in, even if there could be done some enhancements in it.
    6. colorizing text in pattern colorspace
    When I saw Derek's implementation with a clean interface only at one
    place in Gfx.cc, I first was very surprised. My solution had a lot of
    places in Gfx.cc, where I looked if the current colorspace is a pattern
    colorspace. Therefore I first had a look into the PDF specification
    again, and really, it can be done in the way of Derek. Therefore I
    merged it and removed the fragments of my code.
    
    On this step I started a regtest against the version after the fourth
    patch. There were a lot of enhancements, especially in texts with
    symbolic chars like mathematical and so on, but there was one (and ONLY
    one) regression, shown in bug-poppler27482.pdf
    I examined that (that is also the reason for the delay) and encountered
    that on merging I removed my solution for this bug, therefore
    
    7. insert enhancements for colorizing masks in pattern colorspace
    I adapt the bug fix from bug 27482 to the merge.

diff --git a/poppler/CairoOutputDev.cc b/poppler/CairoOutputDev.cc
index 295eb94..dc5698e 100644
--- a/poppler/CairoOutputDev.cc
+++ b/poppler/CairoOutputDev.cc
@@ -133,7 +133,6 @@ CairoOutputDev::CairoOutputDev() {
   fill_opacity = 1.0;
   textClipPath = NULL;
   strokePathClip = NULL;
-  haveCSPattern = gFalse;
   cairo = NULL;
   currentFont = NULL;
   prescaleImages = gTrue;
@@ -1167,7 +1166,7 @@ void CairoOutputDev::endString(GfxState *state)
     return;
   }
 
-  if (!(render & 1) && !haveCSPattern) {
+  if (!(render & 1)) {
     LOG (printf ("fill string\n"));
     cairo_set_source (cairo, fill_pattern);
     if (use_show_text_glyphs)
@@ -1191,7 +1190,7 @@ void CairoOutputDev::endString(GfxState *state)
   }
 
   // clip
-  if (haveCSPattern || (render & 4)) {
+  if ((render & 4)) {
     LOG (printf ("clip string\n"));
     // append the glyph path to textClipPath.
 
@@ -1281,36 +1280,9 @@ void CairoOutputDev::type3D1(GfxState *state, double wx, double wy,
 }
 
 void CairoOutputDev::beginTextObject(GfxState *state) {
-  if (!(state->getRender() & 4) && state->getFillColorSpace()->getMode() == csPattern) {
-    haveCSPattern = gTrue;
-    saveState(state);
-  }
 }
 
 void CairoOutputDev::endTextObject(GfxState *state) {
-  if (haveCSPattern) {
-    haveCSPattern = gFalse;
-    if (state->getFillColorSpace()->getMode() != csPattern) {
-      if (textClipPath) {
-	cairo_new_path (cairo);
-	cairo_append_path (cairo, textClipPath);
-	cairo_set_fill_rule (cairo, CAIRO_FILL_RULE_WINDING);
-	cairo_set_source (cairo, fill_pattern);
-	cairo_fill (cairo);
-	if (cairo_shape) {
-	  cairo_new_path (cairo_shape);
-	  cairo_append_path (cairo_shape, textClipPath);
-	  cairo_set_fill_rule (cairo_shape, CAIRO_FILL_RULE_WINDING);
-	  cairo_fill (cairo_shape);
-	}
-	cairo_path_destroy (textClipPath);
-	textClipPath = NULL;
-      }
-      restoreState(state);
-      updateFillColor(state);
-    }
-  }
-
   if (textClipPath) {
     // clip the accumulated text path
     cairo_append_path (cairo, textClipPath);
@@ -1626,10 +1598,6 @@ void CairoOutputDev::clearSoftMask(GfxState * /*state*/) {
   mask = NULL;
 }
 
-void CairoOutputDev::endMaskClip(GfxState *state) {
-  clearSoftMask(state);
-}
-
 /* Taken from cairo/doc/tutorial/src/singular.c */
 static void
 get_singular_values (const cairo_matrix_t *matrix,
@@ -1797,9 +1765,6 @@ void CairoOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
     return;
   }
 
-  if (state->getFillColorSpace()->getMode() == csPattern)
-    cairo_push_group_with_content (cairo, CAIRO_CONTENT_ALPHA);
-
   /* shape is 1.0 for painted areas, 0.0 for unpainted ones */
 
   cairo_matrix_t matrix;
@@ -1812,11 +1777,65 @@ void CairoOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
     drawImageMaskRegular(state, ref, str, width, height, invert, interpolate, inlineImg);
   }
 
-  if (state->getFillColorSpace()->getMode() == csPattern) {
-    if (mask)
-      cairo_pattern_destroy (mask);
-    mask = cairo_pop_group (cairo);
+}
+
+void CairoOutputDev::setSoftMaskFromImageMask(GfxState *state, Object *ref, Stream *str,
+				   int width, int height, GBool invert,
+				   GBool inlineImg) {
+
+  /* FIXME: Doesn't the image mask support any colorspace? */
+  cairo_set_source (cairo, fill_pattern);
+
+  /* work around a cairo bug when scaling 1x1 surfaces */
+  if (width == 1 && height == 1) {
+    ImageStream *imgStr;
+    Guchar pix;
+    int invert_bit;
+
+    imgStr = new ImageStream(str, width, 1, 1);
+    imgStr->reset();
+    imgStr->getPixel(&pix);
+    imgStr->close();
+    delete imgStr;
+
+    invert_bit = invert ? 1 : 0;
+    if (pix ^ invert_bit)
+      return;
+
+    cairo_save (cairo);
+    cairo_rectangle (cairo, 0., 0., width, height);
+    cairo_fill (cairo);
+    cairo_restore (cairo);
+    if (cairo_shape) {
+      cairo_save (cairo_shape);
+      cairo_rectangle (cairo_shape, 0., 0., width, height);
+      cairo_fill (cairo_shape);
+      cairo_restore (cairo_shape);
+    }
+    return;
+  }
+
+  cairo_push_group_with_content (cairo, CAIRO_CONTENT_ALPHA);
+
+  /* shape is 1.0 for painted areas, 0.0 for unpainted ones */
+
+  cairo_matrix_t matrix;
+  cairo_get_matrix (cairo, &matrix);
+  //XXX: it is possible that we should only do sub pixel positioning if 
+  // we are rendering fonts */
+  if (!printing && prescaleImages && matrix.xy == 0.0 && matrix.yx == 0.0) {
+    drawImageMaskPrescaled(state, ref, str, width, height, invert, gFalse, inlineImg);
+  } else {
+    drawImageMaskRegular(state, ref, str, width, height, invert, gFalse, inlineImg);
   }
+
+  if (mask)
+    cairo_pattern_destroy (mask);
+  mask = cairo_pop_group (cairo);
+}
+
+void CairoOutputDev::unsetSoftMaskFromImageMask(GfxState *state) {
+  clearSoftMask(state);
 }
 
 void CairoOutputDev::drawImageMaskRegular(GfxState *state, Object *ref, Stream *str,
diff --git a/poppler/CairoOutputDev.h b/poppler/CairoOutputDev.h
index b00ab96..6e3c1b9 100644
--- a/poppler/CairoOutputDev.h
+++ b/poppler/CairoOutputDev.h
@@ -192,25 +192,18 @@ public:
 			       CharCode code, Unicode *u, int uLen);
   virtual void endType3Char(GfxState *state);
   virtual void beginTextObject(GfxState *state);
-  virtual GBool deviceHasTextClip(GfxState *state) { return textClipPath && haveCSPattern; }
+  virtual GBool deviceHasTextClip(GfxState *state) { return textClipPath; }
   virtual void endTextObject(GfxState *state);
 
-  // If current colorspace is pattern,
-  // does this device support text in pattern colorspace?
-  virtual GBool supportTextCSPattern(GfxState *state) {
-      return state->getFillColorSpace()->getMode() == csPattern; }
-
-  // If current colorspace is pattern,
-  // need this device special handling for masks in pattern colorspace?
-  virtual GBool fillMaskCSPattern(GfxState * state) {
-      return state->getFillColorSpace()->getMode() == csPattern; }
-
-  virtual void endMaskClip(GfxState *state);
-
   //----- image drawing
   virtual void drawImageMask(GfxState *state, Object *ref, Stream *str,
 			     int width, int height, GBool invert, GBool interpolate,
 			     GBool inlineImg);
+  virtual void setSoftMaskFromImageMask(GfxState *state,
+					Object *ref, Stream *str,
+					int width, int height, GBool invert,
+					GBool inlineImg);
+  virtual void unsetSoftMaskFromImageMask(GfxState *state);
   void drawImageMaskPrescaled(GfxState *state, Object *ref, Stream *str,
 			      int width, int height, GBool invert, GBool interpolate,
 			      GBool inlineImg);
@@ -353,8 +346,6 @@ protected:
       struct MaskStack *next;
   } *maskStack;
 
-  GBool haveCSPattern;	// set if text has been drawn with a
-                        //   clipping render mode because of pattern colorspace
 };
 
 //------------------------------------------------------------------------
diff --git a/poppler/Gfx.cc b/poppler/Gfx.cc
index 9ac8fa9..fc49da7 100644
--- a/poppler/Gfx.cc
+++ b/poppler/Gfx.cc
@@ -543,9 +543,6 @@ Gfx::Gfx(PDFDoc *docA, OutputDev *outA, int pageNum, Dict *resDict,
   subPage = gFalse;
   printCommands = globalParams->getPrintCommands();
   profileCommands = globalParams->getProfileCommands();
-  textHaveCSPattern = gFalse;
-  drawText = gFalse;
-  maskHaveCSPattern = gFalse;
   mcStack = NULL;
   parser = NULL;
 
@@ -567,6 +564,9 @@ Gfx::Gfx(PDFDoc *docA, OutputDev *outA, int pageNum, Dict *resDict,
     baseMatrix[i] = state->getCTM()[i];
   }
   formDepth = 0;
+  textClipBBoxEmpty = gTrue;
+  ocState = gTrue;
+  parser = NULL;
   abortCheckCbk = abortCheckCbkA;
   abortCheckCbkData = abortCheckCbkDataA;
 
@@ -599,9 +599,6 @@ Gfx::Gfx(PDFDoc *docA, OutputDev *outA, Dict *resDict,
   subPage = gTrue;
   printCommands = globalParams->getPrintCommands();
   profileCommands = globalParams->getProfileCommands();
-  textHaveCSPattern = gFalse;
-  drawText = gFalse;
-  maskHaveCSPattern = gFalse;
   mcStack = NULL;
   parser = NULL;
 
@@ -620,6 +617,9 @@ Gfx::Gfx(PDFDoc *docA, OutputDev *outA, Dict *resDict,
     baseMatrix[i] = state->getCTM()[i];
   }
   formDepth = 0;
+  textClipBBoxEmpty = gTrue;
+  ocState = gTrue;
+  parser = NULL;
   abortCheckCbk = abortCheckCbkA;
   abortCheckCbkData = abortCheckCbkDataA;
 
@@ -640,20 +640,18 @@ Gfx::~Gfx() {
   while (stateGuards.size()) {
     popStateGuard();
   }
+  if (!subPage) {
+    out->endPage();
+  }
   // There shouldn't be more saves, but pop them if there were any
   while (state->hasSaves()) {
     error(errSyntaxError, -1, "Found state under last state guard. Popping.");
     restoreState();
   }
-  if (!subPage) {
-    out->endPage();
-  }
+  delete state;
   while (res) {
     popResources();
   }
-  if (state) {
-    delete state;
-  }
   while (mcStack) {
     popMarkedContent();
   }
@@ -1303,7 +1301,7 @@ void Gfx::doSoftMask(Object *str, GBool alpha,
 
   // draw it
   ++formDepth;
-  doForm1(str, resDict, m, bbox, gTrue, gTrue,
+  drawForm(str, resDict, m, bbox, gTrue, gTrue,
 	  blendingColorSpace, isolated, knockout,
 	  alpha, transferFunc, backdropColor);
   --formDepth;
@@ -1326,14 +1324,6 @@ void Gfx::opSetFillGray(Object args[], int numArgs) {
   GfxColorSpace *colorSpace;
   Object obj;
 
-  if (textHaveCSPattern && drawText) {
-    GBool needFill = out->deviceHasTextClip(state);
-    out->endTextObject(state);
-    if (needFill) {
-      doPatternFill(gTrue);
-    }
-    out->restoreState(state);
-  }
   state->setFillPattern(NULL);
   res->lookupColorSpace("DefaultGray", &obj);
   if (obj.isNull()) {
@@ -1347,13 +1337,6 @@ void Gfx::opSetFillGray(Object args[], int numArgs) {
   color.c[0] = dblToCol(args[0].getNum());
   state->setFillColor(&color);
   out->updateFillColor(state);
-  if (textHaveCSPattern) {
-    out->beginTextObject(state);
-    out->updateRender(state);
-    out->updateTextMat(state);
-    out->updateTextPos(state);
-    textHaveCSPattern = gFalse;
-  }
 }
 
 void Gfx::opSetStrokeGray(Object args[], int numArgs) {
@@ -1382,14 +1365,6 @@ void Gfx::opSetFillCMYKColor(Object args[], int numArgs) {
   Object obj;
   int i;
 
-  if (textHaveCSPattern && drawText) {
-    GBool needFill = out->deviceHasTextClip(state);
-    out->endTextObject(state);
-    if (needFill) {
-      doPatternFill(gTrue);
-    }
-    out->restoreState(state);
-  }
   res->lookupColorSpace("DefaultCMYK", &obj);
   if (obj.isNull()) {
     colorSpace = new GfxDeviceCMYKColorSpace();
@@ -1405,13 +1380,6 @@ void Gfx::opSetFillCMYKColor(Object args[], int numArgs) {
   }
   state->setFillColor(&color);
   out->updateFillColor(state);
-  if (textHaveCSPattern) {
-    out->beginTextObject(state);
-    out->updateRender(state);
-    out->updateTextMat(state);
-    out->updateTextPos(state);
-    textHaveCSPattern = gFalse;
-  }
 }
 
 void Gfx::opSetStrokeCMYKColor(Object args[], int numArgs) {
@@ -1443,14 +1411,6 @@ void Gfx::opSetFillRGBColor(Object args[], int numArgs) {
   GfxColor color;
   int i;
 
-  if (textHaveCSPattern && drawText) {
-    GBool needFill = out->deviceHasTextClip(state);
-    out->endTextObject(state);
-    if (needFill) {
-      doPatternFill(gTrue);
-    }
-    out->restoreState(state);
-  }
   state->setFillPattern(NULL);
   res->lookupColorSpace("DefaultRGB", &obj);
   if (obj.isNull()) {
@@ -1466,13 +1426,6 @@ void Gfx::opSetFillRGBColor(Object args[], int numArgs) {
   }
   state->setFillColor(&color);
   out->updateFillColor(state);
-  if (textHaveCSPattern) {
-    out->beginTextObject(state);
-    out->updateRender(state);
-    out->updateTextMat(state);
-    out->updateTextPos(state);
-    textHaveCSPattern = gFalse;
-  }
 }
 
 void Gfx::opSetStrokeRGBColor(Object args[], int numArgs) {
@@ -1511,30 +1464,12 @@ void Gfx::opSetFillColorSpace(Object args[], int numArgs) {
   }
   obj.free();
   if (colorSpace) {
-    if (textHaveCSPattern && drawText) {
-      GBool needFill = out->deviceHasTextClip(state);
-      out->endTextObject(state);
-      if (needFill) {
-        doPatternFill(gTrue);
-      }
-      out->restoreState(state);
-    }
     state->setFillPattern(NULL);
     state->setFillColorSpace(colorSpace);
     out->updateFillColorSpace(state);
     colorSpace->getDefaultColor(&color);
     state->setFillColor(&color);
     out->updateFillColor(state);
-    if (textHaveCSPattern) {
-      out->beginTextObject(state);
-      out->updateRender(state);
-      out->updateTextMat(state);
-      out->updateTextPos(state);
-      textHaveCSPattern = colorSpace->getMode() == csPattern;
-    } else if (drawText && out->supportTextCSPattern(state)) {
-      out->beginTextObject(state);
-      textHaveCSPattern = gTrue;
-    }
   } else {
     error(errSyntaxError, getPos(), "Bad color space (fill)");
   }
@@ -1941,13 +1876,13 @@ void Gfx::doPatternFill(GBool eoFill) {
   }
   switch (pattern->getType()) {
   case 1:
-    doTilingPatternFill((GfxTilingPattern *)pattern, gFalse, eoFill);
+    doTilingPatternFill((GfxTilingPattern *)pattern, gFalse, eoFill, gFalse);
     break;
   case 2:
-    doShadingPatternFill((GfxShadingPattern *)pattern, gFalse, eoFill);
+    doShadingPatternFill((GfxShadingPattern *)pattern, gFalse, eoFill, gFalse);
     break;
   default:
-    error(errSyntaxError, getPos(), "Unimplemented pattern type ({0:d}) in fill",
+    error(errSyntaxError, getPos(), "Unknown pattern type ({0:d}) in fill",
 	  pattern->getType());
     break;
   }
@@ -1968,20 +1903,66 @@ void Gfx::doPatternStroke() {
   }
   switch (pattern->getType()) {
   case 1:
-    doTilingPatternFill((GfxTilingPattern *)pattern, gTrue, gFalse);
+    doTilingPatternFill((GfxTilingPattern *)pattern, gTrue, gFalse, gFalse);
+    break;
+  case 2:
+    doShadingPatternFill((GfxShadingPattern *)pattern, gTrue, gFalse, gFalse);
+    break;
+  default:
+    error(errSyntaxError, getPos(), "Unknown pattern type ({0:d}) in stroke",
+	  pattern->getType());
+    break;
+  }
+}
+
+void Gfx::doPatternText() {
+  GfxPattern *pattern;
+
+  // this is a bit of a kludge -- patterns can be really slow, so we
+  // skip them if we're only doing text extraction, since they almost
+  // certainly don't contain any text
+  if (!out->needNonText()) {
+    return;
+  }
+
+  if (!(pattern = state->getFillPattern())) {
+    return;
+  }
+  switch (pattern->getType()) {
+  case 1:
+    doTilingPatternFill((GfxTilingPattern *)pattern, gFalse, gFalse, gTrue);
     break;
   case 2:
-    doShadingPatternFill((GfxShadingPattern *)pattern, gTrue, gFalse);
+    doShadingPatternFill((GfxShadingPattern *)pattern, gFalse, gFalse, gTrue);
     break;
   default:
-    error(errSyntaxError, getPos(), "Unimplemented pattern type ({0:d}) in stroke",
+    error(errSyntaxError, getPos(), "Unknown pattern type ({0:d}) in fill",
 	  pattern->getType());
     break;
   }
 }
 
+void Gfx::doPatternImageMask(Object *ref, Stream *str, int width, int height,
+			     GBool invert, GBool inlineImg) {
+  saveState();
+
+  out->setSoftMaskFromImageMask(state, ref, str,
+				width, height, invert, inlineImg);
+
+  state->clearPath();
+  state->moveTo(0, 0);
+  state->lineTo(1, 0);
+  state->lineTo(1, 1);
+  state->lineTo(0, 1);
+  state->closePath();
+  doPatternText();
+
+  out->unsetSoftMaskFromImageMask(state);
+  restoreState();
+}
+
 void Gfx::doTilingPatternFill(GfxTilingPattern *tPat,
-			      GBool stroke, GBool eoFill) {
+			      GBool stroke, GBool eoFill, GBool text) {
   GfxPatternColorSpace *patCS;
   GfxColorSpace *cs;
   GfxColor color;
@@ -2074,7 +2055,7 @@ void Gfx::doTilingPatternFill(GfxTilingPattern *tPat,
   if (stroke) {
     state->clipToStrokePath();
     out->clipToStrokePath(state);
-  } else if (!textHaveCSPattern && !maskHaveCSPattern) {
+  } else if (!text) {
     state->clip();
     if (eoFill) {
       out->eoClip(state);
@@ -2158,7 +2139,7 @@ void Gfx::doTilingPatternFill(GfxTilingPattern *tPat,
 	  y = yi * ystep;
 	  m1[4] = x * m[0] + y * m[2] + m[4];
 	  m1[5] = x * m[1] + y * m[3] + m[5];
-	  doForm1(tPat->getContentStream(), tPat->getResDict(),
+	  drawForm(tPat->getContentStream(), tPat->getResDict(),
 		  m1, tPat->getBBox());
 	}
       }
@@ -2171,9 +2152,9 @@ void Gfx::doTilingPatternFill(GfxTilingPattern *tPat,
 }
 
 void Gfx::doShadingPatternFill(GfxShadingPattern *sPat,
-			       GBool stroke, GBool eoFill) {
+			       GBool stroke, GBool eoFill, GBool text) {
   GfxShading *shading;
-  GfxPath *savedPath;
+  GfxState *savedState;
   double *ctm, *btm, *ptm;
   double m[6], ictm[6], m1[6];
   double xMin, yMin, xMax, yMax;
@@ -2182,28 +2163,13 @@ void Gfx::doShadingPatternFill(GfxShadingPattern *sPat,
   shading = sPat->getShading();
 
   // save current graphics state
-  savedPath = state->getPath()->copy();
-  saveState();
-
-  // clip to bbox
-  if (shading->getHasBBox()) {
-    shading->getBBox(&xMin, &yMin, &xMax, &yMax);
-    state->moveTo(xMin, yMin);
-    state->lineTo(xMax, yMin);
-    state->lineTo(xMax, yMax);
-    state->lineTo(xMin, yMax);
-    state->closePath();
-    state->clip();
-    if (!textHaveCSPattern && !maskHaveCSPattern)
-      out->clip(state);
-    state->setPath(savedPath->copy());
-  }
+  savedState = saveStateStack();
 
   // clip to current path
   if (stroke) {
     state->clipToStrokePath();
     out->clipToStrokePath(state);
-  } else if (!textHaveCSPattern && !maskHaveCSPattern) {
+  } else if (!text) {
     state->clip();
     if (eoFill) {
       out->eoClip(state);
@@ -2211,18 +2177,6 @@ void Gfx::doShadingPatternFill(GfxShadingPattern *sPat,
       out->clip(state);
     }
   }
-
-  // set the color space
-  state->setFillColorSpace(shading->getColorSpace()->copy());
-  out->updateFillColorSpace(state);
-
-  // background color fill
-  if (shading->getHasBackground()) {
-    state->setFillColor(shading->getBackground());
-    out->updateFillColor(state);
-    if (!contentIsHidden())
-      out->fill(state);
-  }
   state->clearPath();
 
   // construct a (pattern space) -> (current space) transform matrix
@@ -2256,6 +2210,37 @@ void Gfx::doShadingPatternFill(GfxShadingPattern *sPat,
   state->concatCTM(m[0], m[1], m[2], m[3], m[4], m[5]);
   out->updateCTM(state, m[0], m[1], m[2], m[3], m[4], m[5]);
 
+  // clip to bbox
+  if (shading->getHasBBox()) {
+    shading->getBBox(&xMin, &yMin, &xMax, &yMax);
+    state->moveTo(xMin, yMin);
+    state->lineTo(xMax, yMin);
+    state->lineTo(xMax, yMax);
+    state->lineTo(xMin, yMax);
+    state->closePath();
+    state->clip();
+    out->clip(state);
+    state->clearPath();
+  }
+
+  // set the color space
+  state->setFillColorSpace(shading->getColorSpace()->copy());
+  out->updateFillColorSpace(state);
+
+  // background color fill
+  if (shading->getHasBackground()) {
+    state->setFillColor(shading->getBackground());
+    out->updateFillColor(state);
+    state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax);
+    state->moveTo(xMin, yMin);
+    state->lineTo(xMax, yMin);
+    state->lineTo(xMax, yMax);
+    state->lineTo(xMin, yMax);
+    state->closePath();
+    out->fill(state);
+    state->clearPath();
+  }
+
 #if 1 //~tmp: turn off anti-aliasing temporarily
   GBool vaa = out->getVectorAntialias();
   if (vaa) {
@@ -2291,22 +2276,24 @@ void Gfx::doShadingPatternFill(GfxShadingPattern *sPat,
 #endif
 
   // restore graphics state
-  restoreState();
-  state->setPath(savedPath);
+  restoreStateStack(savedState);
 }
 
 void Gfx::opShFill(Object args[], int numArgs) {
   GfxShading *shading;
-  GfxPath *savedPath;
+  GfxState *savedState;
   double xMin, yMin, xMax, yMax;
 
+  if (!ocState) {
+    return;
+  }
+
   if (!(shading = res->lookupShading(args[0].getName(), this))) {
     return;
   }
 
   // save current graphics state
-  savedPath = state->getPath()->copy();
-  saveState();
+  savedState = saveStateStack();
 
   // clip to bbox
   if (shading->getHasBBox()) {
@@ -2360,8 +2347,7 @@ void Gfx::opShFill(Object args[], int numArgs) {
 #endif
 
   // restore graphics state
-  restoreState();
-  state->setPath(savedPath);
+  restoreStateStack(savedState);
 
   delete shading;
 }
@@ -2511,10 +2497,10 @@ void Gfx::doAxialShFill(GfxAxialShading *shading) {
   double xMin, yMin, xMax, yMax;
   double x0, y0, x1, y1;
   double dx, dy, mul;
-  GBool dxZero, dyZero;
+  GBool dxdyZero, horiz;
   double bboxIntersections[4];
   double tMin, tMax, tx, ty;
-  double s[4], sMin, sMax, tmp;
+  double sMin, sMax, tmp;
   double ux0, uy0, ux1, uy1, vx0, vy0, vx1, vy1;
   double t0, t1, tt;
   double ta[axialMaxSplits + 1];
@@ -2532,9 +2518,9 @@ void Gfx::doAxialShFill(GfxAxialShading *shading) {
   shading->getCoords(&x0, &y0, &x1, &y1);
   dx = x1 - x0;
   dy = y1 - y0;
-  dxZero = fabs(dx) < 0.01;
-  dyZero = fabs(dy) < 0.01;
-  if (dxZero && dyZero) {
+  dxdyZero = fabs(dx) < 0.01 && fabs(dy) < 0.01;
+  horiz = fabs(dy) < fabs(dx);
+  if (dxdyZero) {
     tMin = tMax = 0;
   } else {
     mul = 1 / (dx * dx + dy * dy);
@@ -2571,18 +2557,16 @@ void Gfx::doAxialShFill(GfxAxialShading *shading) {
   //     y(s) = ty + s * dx    -->   s = (y - ty) / dx
   //
   // Then look at the intersection of this line with the bounding box
-  // (xMin, yMin, xMax, yMax).  In the general case, there are four
-  // intersection points:
+  // (xMin, yMin, xMax, yMax).  For -1 < |dy/dx| < 1, look at the
+  // intersection with yMin, yMax:
   //
-  //     s0 = (xMin - tx) / -dy
-  //     s1 = (xMax - tx) / -dy
-  //     s2 = (yMin - ty) / dx
-  //     s3 = (yMax - ty) / dx
+  //     s0 = (yMin - ty) / dx
+  //     s1 = (yMax - ty) / dx
   //
-  // and we want the middle two s values.
+  // else look at the intersection with xMin, xMax:
   //
-  // In the case where dx = 0, take s0 and s1; in the case where dy =
-  // 0, take s2 and s3.
+  //     s0 = (xMin - tx) / -dy
+  //     s1 = (xMax - tx) / -dy
   //
   // Each filled polygon is bounded by two of these line segments
   // perpdendicular to the t axis.
@@ -2591,13 +2575,10 @@ void Gfx::doAxialShFill(GfxAxialShading *shading) {
   // difference across a region is small enough, and then the region
   // is painted with a single color.
 
-  // set up: require at least one split to avoid problems when the two
-  // ends of the t axis have the same color
+  // set up
   nComps = shading->getColorSpace()->getNComps();
   ta[0] = tMin;
-  next[0] = axialMaxSplits / 2;
-  ta[axialMaxSplits / 2] = 0.5 * (tMin + tMax);
-  next[axialMaxSplits / 2] = axialMaxSplits;
+  next[0] = axialMaxSplits;
   ta[axialMaxSplits] = tMax;
 
   // compute the color at t = tMin
@@ -2621,24 +2602,19 @@ void Gfx::doAxialShFill(GfxAxialShading *shading) {
   // bounding box
   tx = x0 + tMin * dx;
   ty = y0 + tMin * dy;
-  if (dxZero && dyZero) {
+  if (dxdyZero) {
     sMin = sMax = 0;
-  } else if (dxZero) {
-    sMin = (xMin - tx) / -dy;
-    sMax = (xMax - tx) / -dy;
-    if (sMin > sMax) { tmp = sMin; sMin = sMax; sMax = tmp; }
-  } else if (dyZero) {
-    sMin = (yMin - ty) / dx;
-    sMax = (yMax - ty) / dx;
-    if (sMin > sMax) { tmp = sMin; sMin = sMax; sMax = tmp; }
   } else {
-    s[0] = (yMin - ty) / dx;
-    s[1] = (yMax - ty) / dx;
-    s[2] = (xMin - tx) / -dy;
-    s[3] = (xMax - tx) / -dy;
-    bubbleSort(s);
-    sMin = s[1];
-    sMax = s[2];
+    if (horiz) {
+      sMin = (yMin - ty) / dx;
+      sMax = (yMax - ty) / dx;
+    } else {
+      sMin = (xMin - tx) / -dy;
+      sMax = (xMax - tx) / -dy;
+    }
+    if (sMin > sMax) {
+      tmp = sMin; sMin = sMax; sMax = tmp;
+    }
   }
   ux0 = tx - sMin * dy;
   uy0 = ty + sMin * dx;
@@ -2647,7 +2623,7 @@ void Gfx::doAxialShFill(GfxAxialShading *shading) {
 
   i = 0;
   bool doneBBox1, doneBBox2;
-  if (dxZero && dyZero) {
+  if (dxdyZero) {
     doneBBox1 = doneBBox2 = true;
   } else {
     doneBBox1 = bboxIntersections[1] < tMin;
@@ -2727,24 +2703,21 @@ void Gfx::doAxialShFill(GfxAxialShading *shading) {
     // bounding box
     tx = x0 + ta[j] * dx;
     ty = y0 + ta[j] * dy;
-    if (dxZero && dyZero) {
+    tx = x0 + ta[j] * dx;
+    ty = y0 + ta[j] * dy;
+    if (dxdyZero) {
       sMin = sMax = 0;
-    } else if (dxZero) {
-      sMin = (xMin - tx) / -dy;
-      sMax = (xMax - tx) / -dy;
-      if (sMin > sMax) { tmp = sMin; sMin = sMax; sMax = tmp; }
-    } else if (dyZero) {
-      sMin = (yMin - ty) / dx;
-      sMax = (yMax - ty) / dx;
-      if (sMin > sMax) { tmp = sMin; sMin = sMax; sMax = tmp; }
     } else {
-      s[0] = (yMin - ty) / dx;
-      s[1] = (yMax - ty) / dx;
-      s[2] = (xMin - tx) / -dy;
-      s[3] = (xMax - tx) / -dy;
-      bubbleSort(s);
-      sMin = s[1];
-      sMax = s[2];
+      if (horiz) {
+	sMin = (yMin - ty) / dx;
+	sMax = (yMax - ty) / dx;
+      } else {
+	sMin = (xMin - tx) / -dy;
+	sMax = (xMax - tx) / -dy;
+      }
+      if (sMin > sMax) {
+	tmp = sMin; sMin = sMax; sMax = tmp;
+      }
     }
     ux1 = tx - sMin * dy;
     uy1 = ty + sMin * dx;
@@ -2824,7 +2797,10 @@ void Gfx::doRadialShFill(GfxRadialShading *shading) {
   GfxColor colorA, colorB;
   double xa, ya, xb, yb, ra, rb;
   double ta, tb, sa, sb;
-  double sz, xz, yz, sMin, sMax;
+  double sz, sMin, sMax, h;
+  double sLeft, sRight, sTop, sBottom, sZero, sDiag;
+  GBool haveSLeft, haveSRight, haveSTop, haveSBottom, haveSZero;
+  GBool haveSMin, haveSMax;
   GBool enclosed;
   int ia, ib, k, n;
   double *ctm;
@@ -2839,24 +2815,25 @@ void Gfx::doRadialShFill(GfxRadialShading *shading) {
 
   // Compute the point at which r(s) = 0; check for the enclosed
   // circles case; and compute the angles for the tangent lines.
-  if (x0 == x1 && y0 == y1) {
+  h = sqrt((x1 - x0) * (x1 - x0) + (y1 - y0) * (y1 - y0));
+  if (h == 0) {
     enclosed = gTrue;
     theta = 0; // make gcc happy
     sz = 0; // make gcc happy
-  } else if (r0 == r1) {
+  } else if (r1 - r0 == 0) {
     enclosed = gFalse;
     theta = 0;
     sz = 0; // make gcc happy
+  } else if (fabs(r1 - r0) >= h) {
+    enclosed = gTrue;
+    theta = 0; // make gcc happy
+    sz = 0; // make gcc happy
   } else {
-    sz = (r1 > r0) ? -r0 / (r1 - r0) : -r1 / (r0 - r1);
-    xz = x0 + sz * (x1 - x0);
-    yz = y0 + sz * (y1 - y0);
-    enclosed = (xz - x0) * (xz - x0) + (yz - y0) * (yz - y0) <= r0 * r0;
-    theta = asin(r0 / sqrt((x0 - xz) * (x0 - xz) + (y0 - yz) * (y0 - yz)));
-    if (r0 > r1) {
-      theta = -theta;
-    }
+    enclosed = gFalse;
+    sz = -r0 / (r1 - r0);
+    theta = asin((r1 - r0) / h);
   }
+
   if (enclosed) {
     alpha = 0;
   } else {
@@ -2869,59 +2846,101 @@ void Gfx::doRadialShFill(GfxRadialShading *shading) {
     sMin = 0;
     sMax = 1;
   } else {
-    sMin = 1;
-    sMax = 0;
-    // solve for x(s) + r(s) = xMin
-    if ((x1 + r1) - (x0 + r0) != 0) {
-      sa = (xMin - (x0 + r0)) / ((x1 + r1) - (x0 + r0));
-      if (sa < sMin) {
-	sMin = sa;
-      } else if (sa > sMax) {
-	sMax = sa;
-      }
+    // solve x(sLeft) + r(sLeft) = xMin
+    if ((haveSLeft = fabs((x1 + r1) - (x0 + r0)) > 0.000001)) {
+      sLeft = (xMin - (x0 + r0)) / ((x1 + r1) - (x0 + r0));
+    } else {
+      sLeft = 0; // make gcc happy
     }
-    // solve for x(s) - r(s) = xMax
-    if ((x1 - r1) - (x0 - r0) != 0) {
-      sa = (xMax - (x0 - r0)) / ((x1 - r1) - (x0 - r0));
-      if (sa < sMin) {
-	sMin = sa;
-      } else if (sa > sMax) {
-	sMax = sa;
-      }
+    // solve x(sRight) - r(sRight) = xMax
+    if ((haveSRight = fabs((x1 - r1) - (x0 - r0)) > 0.000001)) {
+      sRight = (xMax - (x0 - r0)) / ((x1 - r1) - (x0 - r0));
+    } else {
+      sRight = 0; // make gcc happy
     }
-    // solve for y(s) + r(s) = yMin
-    if ((y1 + r1) - (y0 + r0) != 0) {
-      sa = (yMin - (y0 + r0)) / ((y1 + r1) - (y0 + r0));
-      if (sa < sMin) {
-	sMin = sa;
-      } else if (sa > sMax) {
-	sMax = sa;
-      }
+    // solve y(sBottom) + r(sBottom) = yMin
+    if ((haveSBottom = fabs((y1 + r1) - (y0 + r0)) > 0.000001)) {
+      sBottom = (yMin - (y0 + r0)) / ((y1 + r1) - (y0 + r0));
+    } else {
+      sBottom = 0; // make gcc happy
     }
-    // solve for y(s) - r(s) = yMax
-    if ((y1 - r1) - (y0 - r0) != 0) {
-      sa = (yMax - (y0 - r0)) / ((y1 - r1) - (y0 - r0));
-      if (sa < sMin) {
-	sMin = sa;
-      } else if (sa > sMax) {
-	sMax = sa;
-      }
+    // solve y(sTop) - r(sTop) = yMax
+    if ((haveSTop = fabs((y1 - r1) - (y0 - r0)) > 0.000001)) {
+      sTop = (yMax - (y0 - r0)) / ((y1 - r1) - (y0 - r0));
+    } else {
+      sTop = 0; // make gcc happy
     }
-    // check against sz
-    if (r0 < r1) {
-      if (sMin < sz) {
-	sMin = sz;
+    // solve r(sZero) = 0
+    if ((haveSZero = fabs(r1 - r0) > 0.000001)) {
+      sZero = -r0 / (r1 - r0);
+    } else {
+      sZero = 0; // make gcc happy
+    }
+    // solve r(sDiag) = sqrt((xMax-xMin)^2 + (yMax-yMin)^2)
+    if (haveSZero) {
+      sDiag = (sqrt((xMax - xMin) * (xMax - xMin) +
+		    (yMax - yMin) * (yMax - yMin)) - r0) / (r1 - r0);
+    } else {
+      sDiag = 0; // make gcc happy
+    }
+    // compute sMin
+    if (shading->getExtend0()) {
+      sMin = 0;
+      haveSMin = gFalse;
+      if (x0 < x1 && haveSLeft && sLeft < 0) {
+	sMin = sLeft;
+	haveSMin = gTrue;
+      } else if (x0 > x1 && haveSRight && sRight < 0) {
+	sMin = sRight;
+	haveSMin = gTrue;
       }
-    } else if (r0 > r1) {
-      if (sMax > sz) {
-	sMax = sz;
+      if (y0 < y1 && haveSBottom && sBottom < 0) {
+	if (!haveSMin || sBottom > sMin) {
+	  sMin = sBottom;
+	  haveSMin = gTrue;
+	}
+      } else if (y0 > y1 && haveSTop && sTop < 0) {
+	if (!haveSMin || sTop > sMin) {
+	  sMin = sTop;
+	  haveSMin = gTrue;
+	}
       }
-    }
-    // check the 'extend' flags
-    if (!shading->getExtend0() && sMin < 0) {
+      if (haveSZero && sZero < 0) {
+	if (!haveSMin || sZero > sMin) {
+	  sMin = sZero;
+	}
+      }
+    } else {
       sMin = 0;
     }
-    if (!shading->getExtend1() && sMax > 1) {
+    // compute sMax
+    if (shading->getExtend1()) {
+      sMax = 1;
+      haveSMax = gFalse;
+      if (x1 < x0 && haveSLeft && sLeft > 1) {
+	sMax = sLeft;
+	haveSMax = gTrue;
+      } else if (x1 > x0 && haveSRight && sRight > 1) {
+	sMax = sRight;
+	haveSMax = gTrue;
+      }
+      if (y1 < y0 && haveSBottom && sBottom > 1) {
+	if (!haveSMax || sBottom < sMax) {
+	  sMax = sBottom;
+	  haveSMax = gTrue;
+	}
+      } else if (y1 > y0 && haveSTop && sTop > 1) {
+	if (!haveSMax || sTop < sMax) {
+	  sMax = sTop;
+	  haveSMax = gTrue;
+	}
+      }
+      if (haveSZero && sDiag > 1) {
+	if (!haveSMax || sDiag < sMax) {
+	  sMax = sDiag;
+	}
+      }
+    } else {
       sMax = 1;
     }
   }
@@ -3548,28 +3567,16 @@ void Gfx::opEOClip(Object args[], int numArgs) {
 
 void Gfx::opBeginText(Object args[], int numArgs) {
   out->beginTextObject(state);
-  drawText = gTrue;
   state->setTextMat(1, 0, 0, 1, 0, 0);
   state->textMoveTo(0, 0);
   out->updateTextMat(state);
   out->updateTextPos(state);
   fontChanged = gTrue;
-  if (!(state->getRender() & 4) && out->supportTextCSPattern(state)) {
-    textHaveCSPattern = gTrue;
-  }
+  textClipBBoxEmpty = gTrue;
 }
 
 void Gfx::opEndText(Object args[], int numArgs) {
-  GBool needFill = out->deviceHasTextClip(state);
   out->endTextObject(state);
-  drawText = gFalse;
-  if (textHaveCSPattern) {
-    if (needFill) {
-      doPatternFill(gTrue);
-    }
-    out->restoreState(state);
-  }
-  textHaveCSPattern = gFalse;
 }
 
 //------------------------------------------------------------------------
@@ -3611,21 +3618,6 @@ void Gfx::opSetTextLeading(Object args[], int numArgs) {
 void Gfx::opSetTextRender(Object args[], int numArgs) {
   int rm = state->getRender();
   state->setRender(args[0].getInt());
-  if ((args[0].getInt() & 4) && textHaveCSPattern && drawText) {
-    GBool needFill = out->deviceHasTextClip(state);
-    out->endTextObject(state);
-    if (needFill) {
-      doPatternFill(gTrue);
-    }
-    out->restoreState(state);
-    out->beginTextObject(state);
-    out->updateTextMat(state);
-    out->updateTextPos(state);
-    textHaveCSPattern = gFalse;
-  } else if ((rm & 4) && !(args[0].getInt() & 4) && out->supportTextCSPattern(state) && drawText) {
-	out->beginTextObject(state);
-    textHaveCSPattern = gTrue;
-  }
   out->updateRender(state);
 }
 
@@ -3701,9 +3693,13 @@ void Gfx::opShowText(Object args[], int numArgs) {
     out->updateFont(state);
     fontChanged = gFalse;
   }
-  out->beginStringOp(state);
-  doShowText(args[0].getString());
-  out->endStringOp(state);
+  if (ocState) {
+    out->beginStringOp(state);
+    doShowText(args[0].getString());
+    out->endStringOp(state);
+  } else {
+    doIncCharCount(args[0].getString());
+  }
 }
 
 void Gfx::opMoveShowText(Object args[], int numArgs) {
@@ -3721,9 +3717,13 @@ void Gfx::opMoveShowText(Object args[], int numArgs) {
   ty = state->getLineY() - state->getLeading();
   state->textMoveTo(tx, ty);
   out->updateTextPos(state);
-  out->beginStringOp(state);
-  doShowText(args[0].getString());
-  out->endStringOp(state);
+  if (ocState) {
+    out->beginStringOp(state);
+    doShowText(args[0].getString());
+    out->endStringOp(state);
+  } else {
+    doIncCharCount(args[0].getString());
+  }
 }
 
 void Gfx::opMoveSetShowText(Object args[], int numArgs) {
@@ -3745,9 +3745,13 @@ void Gfx::opMoveSetShowText(Object args[], int numArgs) {
   out->updateWordSpace(state);
   out->updateCharSpace(state);
   out->updateTextPos(state);
-  out->beginStringOp(state);
-  doShowText(args[2].getString());
-  out->endStringOp(state);
+  if (ocState) {
+    out->beginStringOp(state);
+    doShowText(args[2].getString());
+    out->endStringOp(state);
+  } else {
+    doIncCharCount(args[2].getString());
+  }
 }
 
 void Gfx::opShowSpaceText(Object args[], int numArgs) {
@@ -3764,30 +3768,43 @@ void Gfx::opShowSpaceText(Object args[], int numArgs) {
     out->updateFont(state);
     fontChanged = gFalse;
   }
-  out->beginStringOp(state);
-  wMode = state->getFont()->getWMode();
-  a = args[0].getArray();
-  for (i = 0; i < a->getLength(); ++i) {
-    a->get(i, &obj);
-    if (obj.isNum()) {
+  if (ocState) {
+    out->beginStringOp(state);
+    wMode = state->getFont()->getWMode();
+    a = args[0].getArray();
+    for (i = 0; i < a->getLength(); ++i) {
+      a->get(i, &obj);
+      if (obj.isNum()) {
       // this uses the absolute value of the font size to match
       // Acrobat's behavior
-      if (wMode) {
-	state->textShift(0, -obj.getNum() * 0.001 *
-			    fabs(state->getFontSize()));
+	if (wMode) {
+	  state->textShift(0, -obj.getNum() * 0.001 *
+			   state->getFontSize());
+	} else {
+	  state->textShift(-obj.getNum() * 0.001 *
+			   state->getFontSize() *
+			   state->getHorizScaling(), 0);
+	}
+	out->updateTextShift(state, obj.getNum());
+      } else if (obj.isString()) {
+	doShowText(obj.getString());
       } else {
-	state->textShift(-obj.getNum() * 0.001 *
-			 fabs(state->getFontSize()), 0);
+	error(errSyntaxError, getPos(),
+	      "Element of show/space array must be number or string");
       }
-      out->updateTextShift(state, obj.getNum());
-    } else if (obj.isString()) {
-      doShowText(obj.getString());
-    } else {
-      error(errSyntaxError, getPos(), "Element of show/space array must be number or string");
+      obj.free();
+    }
+    out->endStringOp(state);
+  } else {
+    a = args[0].getArray();
+    for (i = 0; i < a->getLength(); ++i) {
+      a->get(i, &obj);
+      if (obj.isString()) {
+	doIncCharCount(obj.getString());
+      }
+      obj.free();
     }
-    obj.free();
   }
-  out->endStringOp(state);
 }
 
 void Gfx::doShowText(GooString *s) {
@@ -3796,14 +3813,18 @@ void Gfx::doShowText(GooString *s) {
   double riseX, riseY;
   CharCode code;
   Unicode *u = NULL;
-  double x, y, dx, dy, dx2, dy2, curX, curY, tdx, tdy, lineX, lineY;
+  double x, y, dx, dy, dx2, dy2, curX, curY, tdx, tdy, ddx, ddy;
   double originX, originY, tOriginX, tOriginY;
+  double x0, y0, x1, y1;
   double oldCTM[6], newCTM[6];
   double *mat;
   Object charProc;
   Dict *resDict;
   Parser *oldParser;
+  GfxState *savedState;
   char *p;
+  int render;
+  GBool patternFill;
   int len, n, uLen, nChars, nSpaces, i;
 
   font = state->getFont();
@@ -3813,6 +3834,28 @@ void Gfx::doShowText(GooString *s) {
     out->beginString(state, s);
   }
 
+  // if we're doing a pattern fill, set up clipping
+  render = state->getRender();
+  if (!(render & 1) &&
+      state->getFillColorSpace()->getMode() == csPattern) {
+    patternFill = gTrue;
+    saveState();
+    // disable fill, enable clipping, leave stroke unchanged
+    if ((render ^ (render >> 1)) & 1) {
+      render = 5;
+    } else {
+      render = 7;
+    }
+    state->setRender(render);
+    out->updateRender(state);
+  } else {
+    patternFill = gFalse;
+  }
+
+  state->textTransformDelta(0, state->getRise(), &riseX, &riseY);
+  x0 = state->getCurX() + riseX;
+  y0 = state->getCurY() + riseY;
+
   // handle a Type 3 char
   if (font->getType() == fontType3 && out->interpretType3Chars()) {
     mat = state->getCTM();
@@ -3835,11 +3878,8 @@ void Gfx::doShowText(GooString *s) {
     newCTM[3] *= state->getFontSize();
     newCTM[0] *= state->getHorizScaling();
     newCTM[2] *= state->getHorizScaling();
-    state->textTransformDelta(0, state->getRise(), &riseX, &riseY);
     curX = state->getCurX();
     curY = state->getCurY();
-    lineX = state->getLineX();
-    lineY = state->getLineY();
     oldParser = parser;
     p = s->getCString();
     len = s->getLength();
@@ -3855,11 +3895,12 @@ void Gfx::doShowText(GooString *s) {
       dy *= state->getFontSize();
       state->textTransformDelta(dx, dy, &tdx, &tdy);
       state->transform(curX + riseX, curY + riseY, &x, &y);
-      saveState();
+      savedState = saveStateStack();
       state->setCTM(newCTM[0], newCTM[1], newCTM[2], newCTM[3], x, y);
       //~ the CTM concat values here are wrong (but never used)
       out->updateCTM(state, 1, 0, 0, 1, 0, 0);
-      if (!out->beginType3Char(state, curX + riseX, curY + riseY, tdx, tdy,
+      state->transformDelta(dx, dy, &ddx, &ddy);
+      if (!out->beginType3Char(state, curX + riseX, curY + riseY, ddx, ddy,
 			       code, u, uLen)) {
 	((Gfx8BitFont *)font)->getCharProc(code, &charProc);
 	if ((resDict = ((Gfx8BitFont *)font)->getResources())) {
@@ -3876,20 +3917,18 @@ void Gfx::doShowText(GooString *s) {
 	}
 	charProc.free();
       }
-      restoreState();
+      restoreStateStack(savedState);
       // GfxState::restore() does *not* restore the current position,
       // so we deal with it here using (curX, curY) and (lineX, lineY)
       curX += tdx;
       curY += tdy;
       state->moveTo(curX, curY);
-      state->textSetPos(lineX, lineY);
       p += n;
       len -= n;
     }
     parser = oldParser;
 
   } else if (out->useDrawChar()) {
-    state->textTransformDelta(0, state->getRise(), &riseX, &riseY);
     p = s->getCString();
     len = s->getLength();
     while (len > 0) {
@@ -3963,9 +4002,52 @@ void Gfx::doShowText(GooString *s) {
     out->endString(state);
   }
 
+  if (patternFill) {
+    out->saveTextPos(state);
+    // tell the OutputDev to do the clipping
+    out->endTextObject(state);
+    // set up a clipping bbox so doPatternText will work -- assume
+    // that the text bounding box does not extend past the baseline in
+    // any direction by more than twice the font size
+    x1 = state->getCurX() + riseX;
+    y1 = state->getCurY() + riseY;
+    if (x0 > x1) {
+      x = x0; x0 = x1; x1 = x;
+    }
+    if (y0 > y1) {
+      y = y0; y0 = y1; y1 = y;
+    }
+    state->textTransformDelta(0, state->getFontSize(), &dx, &dy);
+    state->textTransformDelta(state->getFontSize(), 0, &dx2, &dy2);
+    dx = fabs(dx);
+    dx2 = fabs(dx2);
+    if (dx2 > dx) {
+      dx = dx2;
+    }
+    dy = fabs(dy);
+    dy2 = fabs(dy2);
+    if (dy2 > dy) {
+      dy = dy2;
+    }
+    state->clipToRect(x0 - 2 * dx, y0 - 2 * dy, x1 + 2 * dx, y1 + 2 * dy);
+    // set render mode to fill-only
+    state->setRender(0);
+    out->updateRender(state);
+    doPatternText();
+    restoreState();
+    out->restoreTextPos(state);
+  }
+
   updateLevel += 10 * s->getLength();
 }
 
+// NB: this is only called when ocState is false.
+void Gfx::doIncCharCount(GooString *s) {
+  if (out->needCharCount()) {
+    out->incCharCount(s->getLength());
+  }
+}
+
 //------------------------------------------------------------------------
 // XObject operators
 //------------------------------------------------------------------------
@@ -3977,6 +4059,9 @@ void Gfx::opXObject(Object args[], int numArgs) {
   Object opiDict;
 #endif
 
+  if (!ocState && !out->needCharCount()) {
+    return;
+  }
   name = args[0].getName();
   if (!res->lookupXObject(name, &obj1)) {
     return;
@@ -4163,12 +4248,10 @@ void Gfx::doImage(Object *ref, Stream *str, GBool inlineImg) {
 
     // draw it
     if (!contentIsHidden()) {
+      if (state->getFillColorSpace()->getMode() == csPattern) {
+	doPatternImageMask(ref, str, width, height, invert, inlineImg);
+      } else {
 	out->drawImageMask(state, ref, str, width, height, invert, interpolate, inlineImg);
-      if (out->fillMaskCSPattern(state)) {
-        maskHaveCSPattern = gTrue;
-        doPatternFill(gTrue);
-        out->endMaskClip(state);
-        maskHaveCSPattern = gFalse;
       }
     }
   } else {
@@ -4450,6 +4533,7 @@ void Gfx::doForm(Object *str) {
   double m[6], bbox[4];
   Object resObj;
   Dict *resDict;
+  GBool ocSaved;
   Object obj1, obj2, obj3;
   int i;
 
@@ -4469,10 +4553,15 @@ void Gfx::doForm(Object *str) {
   obj1.free();
 
   // check for optional content key
+  ocSaved = ocState;
   dict->lookupNF("OC", &obj1);
   if (catalog->getOptContentConfig() && !catalog->getOptContentConfig()->optContentIsVisible(&obj1)) {
     obj1.free();
-    return;
+    if (out->needCharCount()) {
+      ocState = gFalse;
+    } else {
+      return;
+    }
   }
   obj1.free();
 
@@ -4481,6 +4570,7 @@ void Gfx::doForm(Object *str) {
   if (!bboxObj.isArray()) {
     bboxObj.free();
     error(errSyntaxError, getPos(), "Bad form bounding box");
+    ocState = ocSaved;
     return;
   }
   for (i = 0; i < 4; ++i) {
@@ -4541,7 +4631,7 @@ void Gfx::doForm(Object *str) {
 
   // draw it
   ++formDepth;
-  doForm1(str, resDict, m, bbox,
+  drawForm(str, resDict, m, bbox,
 	  transpGroup, gFalse, blendingColorSpace, isolated, knockout);
   --formDepth;
 
@@ -4549,15 +4639,18 @@ void Gfx::doForm(Object *str) {
     delete blendingColorSpace;
   }
   resObj.free();
+
+  ocState = ocSaved;
 }
 
-void Gfx::doForm1(Object *str, Dict *resDict, double *matrix, double *bbox,
+void Gfx::drawForm(Object *str, Dict *resDict, double *matrix, double *bbox,
 		  GBool transpGroup, GBool softMask,
 		  GfxColorSpace *blendingColorSpace,
 		  GBool isolated, GBool knockout,
 		  GBool alpha, Function *transferFunc,
 		  GfxColor *backdropColor) {
   Parser *oldParser;
+  GfxState *savedState;
   double oldBaseMatrix[6];
   int i;
 
@@ -4565,7 +4658,7 @@ void Gfx::doForm1(Object *str, Dict *resDict, double *matrix, double *bbox,
   pushResources(resDict);
 
   // save current graphics state
-  saveState();
+  savedState = saveStateStack();
 
   // kill any pre-existing path
   state->clearPath();
@@ -4642,7 +4735,7 @@ void Gfx::doForm1(Object *str, Dict *resDict, double *matrix, double *bbox,
   parser = oldParser;
 
   // restore graphics state
-  restoreState();
+  restoreStateStack(savedState);
 
   // pop resource stack
   popResources();
@@ -5021,7 +5114,7 @@ void Gfx::drawAnnot(Object *str, AnnotBorder *border, AnnotColor *aColor,
     resDict = resObj.isDict() ? resObj.getDict() : (Dict *)NULL;
 
     // draw it
-    doForm1(str, resDict, m, bbox);
+    drawForm(str, resDict, m, bbox);
 
     resObj.free();
   }
diff --git a/poppler/Gfx.h b/poppler/Gfx.h
index 32fe9cc..689d362 100644
--- a/poppler/Gfx.h
+++ b/poppler/Gfx.h
@@ -181,7 +181,7 @@ public:
   // Get the current graphics state object.
   GfxState *getState() { return state; }
 
-  void doForm1(Object *str, Dict *resDict, double *matrix, double *bbox,
+  void drawForm(Object *str, Dict *resDict, double *matrix, double *bbox,
 	       GBool transpGroup = gFalse, GBool softMask = gFalse,
 	       GfxColorSpace *blendingColorSpace = NULL,
 	       GBool isolated = gFalse, GBool knockout = gFalse,
@@ -190,7 +190,7 @@ public:
 
   void pushResources(Dict *resDict);
   void popResources();
-  
+ 
 #ifdef USE_CMS
   PopplerCache *getIccColorSpaceCache();
 #endif
@@ -204,9 +204,6 @@ private:
   GBool subPage;		// is this a sub-page object?
   GBool printCommands;		// print the drawing commands (for debugging)
   GBool profileCommands;	// profile the drawing commands (for debugging)
-  GBool textHaveCSPattern;	// in text drawing and text has pattern colorspace
-  GBool drawText;		// in text drawing
-  GBool maskHaveCSPattern;	// in mask drawing and mask has pattern colorspace
   GBool commandAborted;         // did the previous command abort the drawing?
   GfxResources *res;		// resource stack
   int updateLevel;
@@ -220,11 +217,16 @@ private:
   double baseMatrix[6];		// default matrix for most recent
 				//   page/form/pattern
   int formDepth;
+  double textClipBBox[4];	// text clipping bounding box
+  GBool textClipBBoxEmpty;	// true if textClipBBox has not been
+				//   initialized yet
+  GBool ocState;		// true if drawing is enabled, false if
+				//   disabled
 
   MarkedContentStack *mcStack;	// current BMC/EMC stack
 
   Parser *parser;		// parser for page content stream(s)
- 
+
 #ifdef USE_CMS
   PopplerCache iccColorSpaceCache;
 #endif
@@ -295,10 +297,13 @@ private:
   void opCloseEOFillStroke(Object args[], int numArgs);
   void doPatternFill(GBool eoFill);
   void doPatternStroke();
+  void doPatternText();
+  void doPatternImageMask(Object *ref, Stream *str, int width, int height,
+			  GBool invert, GBool inlineImg);
   void doTilingPatternFill(GfxTilingPattern *tPat,
-			   GBool stroke, GBool eoFill);
+			   GBool stroke, GBool eoFill, GBool text);
   void doShadingPatternFill(GfxShadingPattern *sPat,
-			    GBool stroke, GBool eoFill);
+			    GBool stroke, GBool eoFill, GBool text);
   void opShFill(Object args[], int numArgs);
   void doFunctionShFill(GfxFunctionShading *shading);
   void doFunctionShFill1(GfxFunctionShading *shading,
@@ -349,6 +354,7 @@ private:
   void opMoveSetShowText(Object args[], int numArgs);
   void opShowSpaceText(Object args[], int numArgs);
   void doShowText(GooString *s);
+  void doIncCharCount(GooString *s);
 
   // XObject operators
   void opXObject(Object args[], int numArgs);
diff --git a/poppler/GfxState.cc b/poppler/GfxState.cc
index 7475ab4..de6f17b 100644
--- a/poppler/GfxState.cc
+++ b/poppler/GfxState.cc
@@ -213,7 +213,7 @@ cmsHPROFILE GfxColorSpace::getDisplayProfile() {
 //------------------------------------------------------------------------
 
 GfxColorSpace::GfxColorSpace() {
-  overprintMask = 0x0f;
+  overprintMask = 0x0f;
 }
 
 GfxColorSpace::~GfxColorSpace() {
@@ -1858,7 +1858,7 @@ GfxIndexedColorSpace::GfxIndexedColorSpace(GfxColorSpace *baseA,
   indexHigh = indexHighA;
   lookup = (Guchar *)gmallocn((indexHigh + 1) * base->getNComps(),
 			      sizeof(Guchar));
-  overprintMask = base->getOverprintMask();
+  overprintMask = base->getOverprintMask();
 }
 
 GfxIndexedColorSpace::~GfxIndexedColorSpace() {
@@ -2051,29 +2051,29 @@ GfxSeparationColorSpace::GfxSeparationColorSpace(GooString *nameA,
   alt = altA;
   func = funcA;
   nonMarking = !name->cmp("None");
-  if (!name->cmp("Cyan")) {
-    overprintMask = 0x01;
-  } else if (!name->cmp("Magenta")) {
-    overprintMask = 0x02;
-  } else if (!name->cmp("Yellow")) {
-    overprintMask = 0x04;
-  } else if (!name->cmp("Black")) {
-    overprintMask = 0x08;
-  }
-}
-
-GfxSeparationColorSpace::GfxSeparationColorSpace(GooString *nameA,
-						 GfxColorSpace *altA,
-						 Function *funcA,
-						 GBool nonMarkingA,
-						 Guint overprintMaskA) {
-  name = nameA;
-  alt = altA;
-  func = funcA;
-  nonMarking = nonMarkingA;
-  overprintMask = overprintMaskA;
-}
-
+  if (!name->cmp("Cyan")) {
+    overprintMask = 0x01;
+  } else if (!name->cmp("Magenta")) {
+    overprintMask = 0x02;
+  } else if (!name->cmp("Yellow")) {
+    overprintMask = 0x04;
+  } else if (!name->cmp("Black")) {
+    overprintMask = 0x08;
+  }
+}
+
+GfxSeparationColorSpace::GfxSeparationColorSpace(GooString *nameA,
+						 GfxColorSpace *altA,
+						 Function *funcA,
+						 GBool nonMarkingA,
+						 Guint overprintMaskA) {
+  name = nameA;
+  alt = altA;
+  func = funcA;
+  nonMarking = nonMarkingA;
+  overprintMask = overprintMaskA;
+}
+
 GfxSeparationColorSpace::~GfxSeparationColorSpace() {
   delete name;
   delete alt;
@@ -2081,8 +2081,8 @@ GfxSeparationColorSpace::~GfxSeparationColorSpace() {
 }
 
 GfxColorSpace *GfxSeparationColorSpace::copy() {
-  return new GfxSeparationColorSpace(name->copy(), alt->copy(), func->copy(),
-				     nonMarking, overprintMask);
+  return new GfxSeparationColorSpace(name->copy(), alt->copy(), func->copy(),
+				     nonMarking, overprintMask);
 }
 
 //~ handle the 'All' and 'None' colorants
@@ -2177,54 +2177,54 @@ void GfxSeparationColorSpace::getDefaultColor(GfxColor *color) {
 // GfxDeviceNColorSpace
 //------------------------------------------------------------------------
 
-GfxDeviceNColorSpace::GfxDeviceNColorSpace(int nCompsA,
-					   GooString **namesA,
-					   GfxColorSpace *altA,
-					   Function *funcA) {
-  int i;
+GfxDeviceNColorSpace::GfxDeviceNColorSpace(int nCompsA,
+					   GooString **namesA,
+					   GfxColorSpace *altA,
+					   Function *funcA) {
+  int i;
 
   nComps = nCompsA;
   alt = altA;
   func = funcA;
   nonMarking = gTrue;
-  overprintMask = 0;
-  for (i = 0; i < nComps; ++i) {
-    names[i] = namesA[i];
-    if (names[i]->cmp("None")) {
-      nonMarking = gFalse;
-    }
-    if (!names[i]->cmp("Cyan")) {
-      overprintMask |= 0x01;
-    } else if (!names[i]->cmp("Magenta")) {
-      overprintMask |= 0x02;
-    } else if (!names[i]->cmp("Yellow")) {
-      overprintMask |= 0x04;
-    } else if (!names[i]->cmp("Black")) {
-      overprintMask |= 0x08;
-    } else {
-      overprintMask = 0x0f;
-    }
-  }
-}
-
-GfxDeviceNColorSpace::GfxDeviceNColorSpace(int nCompsA,
-					   GooString **namesA,
-					   GfxColorSpace *altA,
-					   Function *funcA,
-					   GBool nonMarkingA,
-					   Guint overprintMaskA) {
-  int i;
-
-  nComps = nCompsA;
-  alt = altA;
-  func = funcA;
-  nonMarking = nonMarkingA;
-  overprintMask = overprintMaskA;
-  for (i = 0; i < nComps; ++i) {
-    names[i] = namesA[i]->copy();
-  }
-}
-
+  overprintMask = 0;
+  for (i = 0; i < nComps; ++i) {
+    names[i] = namesA[i];
+    if (names[i]->cmp("None")) {
+      nonMarking = gFalse;
+    }
+    if (!names[i]->cmp("Cyan")) {
+      overprintMask |= 0x01;
+    } else if (!names[i]->cmp("Magenta")) {
+      overprintMask |= 0x02;
+    } else if (!names[i]->cmp("Yellow")) {
+      overprintMask |= 0x04;
+    } else if (!names[i]->cmp("Black")) {
+      overprintMask |= 0x08;
+    } else {
+      overprintMask = 0x0f;
+    }
+  }
+}
+
+GfxDeviceNColorSpace::GfxDeviceNColorSpace(int nCompsA,
+					   GooString **namesA,
+					   GfxColorSpace *altA,
+					   Function *funcA,
+					   GBool nonMarkingA,
+					   Guint overprintMaskA) {
+  int i;
+
+  nComps = nCompsA;
+  alt = altA;
+  func = funcA;
+  nonMarking = nonMarkingA;
+  overprintMask = overprintMaskA;
+  for (i = 0; i < nComps; ++i) {
+    names[i] = namesA[i]->copy();
+  }
+}
+
 GfxDeviceNColorSpace::~GfxDeviceNColorSpace() {
   int i;
 
@@ -2236,8 +2236,8 @@ GfxDeviceNColorSpace::~GfxDeviceNColorSpace() {
 }
 
 GfxColorSpace *GfxDeviceNColorSpace::copy() {
-  return new GfxDeviceNColorSpace(nComps, names, alt->copy(), func->copy(),
-				  nonMarking, overprintMask);
+  return new GfxDeviceNColorSpace(nComps, names, alt->copy(), func->copy(),
+				  nonMarking, overprintMask);
 }
 
 //~ handle the 'None' colorant
@@ -2285,7 +2285,7 @@ GfxColorSpace *GfxDeviceNColorSpace::parse(Array *arr, Gfx *gfx, int recursion)
     goto err4;
   }
   obj1.free();
-  cs = new GfxDeviceNColorSpace(nCompsA, namesA, altA, funcA);
+  cs = new GfxDeviceNColorSpace(nCompsA, namesA, altA, funcA);
   cs->nonMarking = gTrue;
   for (i = 0; i < nCompsA; ++i) {
     cs->names[i] = namesA[i];
@@ -5580,7 +5580,7 @@ GfxState::~GfxState() {
 }
 
 // Used for copy();
-GfxState::GfxState(GfxState *state, GBool copyPath) {
+GfxState::GfxState(GfxState *state, GBool copyPath) {
   int i;
 
   memcpy(this, state, sizeof(GfxState));
@@ -5608,9 +5608,9 @@ GfxState::GfxState(GfxState *state, GBool copyPath) {
   if (font)
     font->incRefCnt();
 
-  if (copyPath) {
-    path = state->path->copy();
-  }
+  if (copyPath) {
+    path = state->path->copy();
+  }
   saved = NULL;
 }
 
@@ -5904,6 +5904,60 @@ void GfxState::clipToStrokePath() {
   }
 }
 
+void GfxState::clipToRect(double xMin, double yMin, double xMax, double yMax) {
+  double x, y, xMin1, yMin1, xMax1, yMax1;
+
+  transform(xMin, yMin, &x, &y);
+  xMin1 = xMax1 = x;
+  yMin1 = yMax1 = y;
+  transform(xMax, yMin, &x, &y);
+  if (x < xMin1) {
+    xMin1 = x;
+  } else if (x > xMax1) {
+    xMax1 = x;
+  }
+  if (y < yMin1) {
+    yMin1 = y;
+  } else if (y > yMax1) {
+    yMax1 = y;
+  }
+  transform(xMax, yMax, &x, &y);
+  if (x < xMin1) {
+    xMin1 = x;
+  } else if (x > xMax1) {
+    xMax1 = x;
+  }
+  if (y < yMin1) {
+    yMin1 = y;
+  } else if (y > yMax1) {
+    yMax1 = y;
+  }
+  transform(xMin, yMax, &x, &y);
+  if (x < xMin1) {
+    xMin1 = x;
+  } else if (x > xMax1) {
+    xMax1 = x;
+  }
+  if (y < yMin1) {
+    yMin1 = y;
+  } else if (y > yMax1) {
+    yMax1 = y;
+  }
+
+  if (xMin1 > clipXMin) {
+    clipXMin = xMin1;
+  }
+  if (yMin1 > clipYMin) {
+    clipYMin = yMin1;
+  }
+  if (xMax1 < clipXMax) {
+    clipXMax = xMax1;
+  }
+  if (yMax1 < clipYMax) {
+    clipYMax = yMax1;
+  }
+}
+
 void GfxState::textShift(double tx, double ty) {
   double dx, dy;
 
diff --git a/poppler/GfxState.h b/poppler/GfxState.h
index c8995e3..6edab64 100644
--- a/poppler/GfxState.h
+++ b/poppler/GfxState.h
@@ -227,9 +227,9 @@ public:
   // mark the page (e.g., the "None" colorant).
   virtual GBool isNonMarking() { return gFalse; }
 
-  // Return the color space's overprint mask.
-  Guint getOverprintMask() { return overprintMask; }
-
+  // Return the color space's overprint mask.
+  Guint getOverprintMask() { return overprintMask; }
+
   // Return the number of color space modes
   static int getNumColorSpaceModes();
 
@@ -246,9 +246,9 @@ public:
   // result will be a cmsHPROFILE 
   static void *getDisplayProfile();
 #endif
-protected:
-
-  Guint overprintMask;
+protected:
+
+  Guint overprintMask;
 };
 
 //------------------------------------------------------------------------
@@ -586,9 +586,9 @@ public:
 
 private:
 
-  GfxSeparationColorSpace(GooString *nameA, GfxColorSpace *altA,
-			  Function *funcA, GBool nonMarkingA,
-			  Guint overprintMaskA);
+  GfxSeparationColorSpace(GooString *nameA, GfxColorSpace *altA,
+			  Function *funcA, GBool nonMarkingA,
+			  Guint overprintMaskA);
 
   GooString *name;		// colorant name
   GfxColorSpace *alt;		// alternate color space
@@ -603,8 +603,8 @@ private:
 class GfxDeviceNColorSpace: public GfxColorSpace {
 public:
 
-  GfxDeviceNColorSpace(int nCompsA, GooString **namesA,
-		       GfxColorSpace *alt, Function *func);
+  GfxDeviceNColorSpace(int nCompsA, GooString **namesA,
+		       GfxColorSpace *alt, Function *func);
   virtual ~GfxDeviceNColorSpace();
   virtual GfxColorSpace *copy();
   virtual GfxColorSpaceMode getMode() { return csDeviceN; }
@@ -628,9 +628,9 @@ public:
 
 private:
 
-  GfxDeviceNColorSpace(int nCompsA, GooString **namesA,
-		       GfxColorSpace *alt, Function *func,
-		       GBool nonMarkingA, Guint overprintMaskA);
+  GfxDeviceNColorSpace(int nCompsA, GooString **namesA,
+		       GfxColorSpace *alt, Function *func,
+		       GBool nonMarkingA, Guint overprintMaskA);
 
   int nComps;			// number of components
   GooString			// colorant names
@@ -1312,9 +1312,9 @@ public:
   // Destructor.
   ~GfxState();
 
-  // Copy.
-  GfxState *copy(GBool copyPath = gFalse)
-    { return new GfxState(this, copyPath); }
+  // Copy.
+  GfxState *copy(GBool copyPath = gFalse)
+    { return new GfxState(this, copyPath); }
 
   // Accessors.
   double getHDPI() { return hDPI; }
@@ -1466,6 +1466,7 @@ public:
   // Update clip region.
   void clip();
   void clipToStrokePath();
+  void clipToRect(double xMin, double yMin, double xMax, double yMax);
 
   // Text position.
   void textSetPos(double tx, double ty) { lineX = tx; lineY = ty; }
@@ -1540,7 +1541,7 @@ private:
 
   GfxState *saved;		// next GfxState on stack
 
-  GfxState(GfxState *state, GBool copyPath);
+  GfxState(GfxState *state, GBool copyPath);
 };
 
 #endif
diff --git a/poppler/GlobalParams.cc b/poppler/GlobalParams.cc
index a6c6ace..d2c007f 100644
--- a/poppler/GlobalParams.cc
+++ b/poppler/GlobalParams.cc
@@ -695,6 +695,7 @@ GlobalParams::GlobalParams(const char *customPopplerDataDir)
   screenGamma = 1.0;
   screenBlackThreshold = 0.0;
   screenWhiteThreshold = 1.0;
+  minLineWidth = 0.0;
   overprintPreview = gFalse;
   mapNumericCharNames = gTrue;
   mapUnknownCharNames = gFalse;
@@ -1600,6 +1601,15 @@ double GlobalParams::getScreenWhiteThreshold() {
   return thresh;
 }
 
+double GlobalParams::getMinLineWidth() {
+  double minLineWidthA;
+
+  lockGlobalParams;
+  minLineWidthA = minLineWidth;
+  unlockGlobalParams;
+  return minLineWidthA;
+}
+
 GBool GlobalParams::getMapNumericCharNames() {
   GBool map;
 
@@ -1940,6 +1950,13 @@ void GlobalParams::setScreenWhiteThreshold(double whiteThreshold)
   unlockGlobalParams;
 }
 
+void GlobalParams::setMinLineWidth(double minLineWidthA)
+{
+  lockGlobalParams;
+  minLineWidth = minLineWidthA;
+  unlockGlobalParams;
+}
+
 void GlobalParams::setOverprintPreview(GBool overprintPreviewA) {
   lockGlobalParams;
   overprintPreview = overprintPreviewA;
diff --git a/poppler/GlobalParams.h b/poppler/GlobalParams.h
index 57b18f1..bcaeda3 100644
--- a/poppler/GlobalParams.h
+++ b/poppler/GlobalParams.h
@@ -214,6 +214,7 @@ public:
   double getScreenGamma();
   double getScreenBlackThreshold();
   double getScreenWhiteThreshold();
+  double getMinLineWidth();
   GBool getOverprintPreview() { return overprintPreview; }
   GBool getMapNumericCharNames();
   GBool getMapUnknownCharNames();
@@ -262,6 +263,7 @@ public:
   void setScreenGamma(double gamma);
   void setScreenBlackThreshold(double blackThreshold);
   void setScreenWhiteThreshold(double whiteThreshold);
+  void setMinLineWidth(double minLineWidth);
   void setOverprintPreview(GBool overprintPreviewA);
   void setMapNumericCharNames(GBool map);
   void setMapUnknownCharNames(GBool map);
@@ -350,6 +352,7 @@ private:
   double screenGamma;		// screen gamma correction
   double screenBlackThreshold;	// screen black clamping threshold
   double screenWhiteThreshold;	// screen white clamping threshold
+  double minLineWidth;		// minimum line width
   GBool overprintPreview;	// enable overprint preview
   GBool mapNumericCharNames;	// map numeric char names (from font subsets)?
   GBool mapUnknownCharNames;	// map unknown char names?
diff --git a/poppler/OutputDev.cc b/poppler/OutputDev.cc
index 6adaea5..43b7f5b 100644
--- a/poppler/OutputDev.cc
+++ b/poppler/OutputDev.cc
@@ -108,6 +108,17 @@ void OutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
   }
 }
 
+void OutputDev::setSoftMaskFromImageMask(GfxState *state,
+					 Object *ref, Stream *str,
+					 int width, int height, GBool invert,
+					 GBool inlineImg) {
+  drawImageMask(state, ref, str, width, height, invert, gFalse, inlineImg);
+}
+
+void OutputDev::unsetSoftMaskFromImageMask(GfxState *state) {
+  return;
+}
+
 void OutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
 			  int width, int height, GfxImageColorMap *colorMap,
 			  GBool interpolate, int *maskColors, GBool inlineImg) {
@@ -166,7 +177,7 @@ void OutputDev::startProfile() {
 
   profileHash = new GooHash (true);
 }
- 
+
 GooHash *OutputDev::endProfile() {
   GooHash *profile = profileHash;
 
diff --git a/poppler/OutputDev.h b/poppler/OutputDev.h
index 03e1613..7e51b9d 100644
--- a/poppler/OutputDev.h
+++ b/poppler/OutputDev.h
@@ -106,16 +106,9 @@ public:
   // Does this device need non-text content?
   virtual GBool needNonText() { return gTrue; }
 
-  // If current colorspace ist pattern,
-  // does this device support text in pattern colorspace?
-  // Default is false
-  virtual GBool supportTextCSPattern(GfxState * /*state*/) { return gFalse; }
-
-  // If current colorspace ist pattern,
-  // need this device special handling for masks in pattern colorspace?
-  // Default is false
-  virtual GBool fillMaskCSPattern(GfxState * /*state*/) { return gFalse; }
-  virtual void endMaskClip(GfxState * /*state*/) {}
+  // Does this device require incCharCount to be called for text on
+  // non-shown layers?
+  virtual GBool needCharCount() { return gFalse; }
 
   //----- initialization and control
 
@@ -192,6 +185,8 @@ public:
   virtual void updateHorizScaling(GfxState * /*state*/) {}
   virtual void updateTextPos(GfxState * /*state*/) {}
   virtual void updateTextShift(GfxState * /*state*/, double /*shift*/) {}
+  virtual void saveTextPos(GfxState * /*state*/) {}
+  virtual void restoreTextPos(GfxState * /*state*/) {}
 
   //----- path painting
   virtual void stroke(GfxState * /*state*/) {}
@@ -241,6 +236,7 @@ public:
   virtual void beginTextObject(GfxState * /*state*/) {}
   virtual GBool deviceHasTextClip(GfxState * /*state*/) { return gFalse; }
   virtual void endTextObject(GfxState * /*state*/) {}
+  virtual void incCharCount(int /*nChars*/) {}
   virtual void beginActualText(GfxState * /*state*/, GooString * /*text*/ ) {}
   virtual void endActualText(GfxState * /*state*/) {}
 
@@ -248,6 +244,11 @@ public:
   virtual void drawImageMask(GfxState *state, Object *ref, Stream *str,
 			     int width, int height, GBool invert, GBool interpolate,
 			     GBool inlineImg);
+  virtual void setSoftMaskFromImageMask(GfxState *state,
+					Object *ref, Stream *str,
+					int width, int height, GBool invert,
+					GBool inlineImg);
+  virtual void unsetSoftMaskFromImageMask(GfxState *state);
   virtual void drawImage(GfxState *state, Object *ref, Stream *str,
 			 int width, int height, GfxImageColorMap *colorMap,
 			 GBool interpolate, int *maskColors, GBool inlineImg);
@@ -269,8 +270,8 @@ public:
 
   virtual void markPoint(char *name);
   virtual void markPoint(char *name, Dict *properties);
-  
-  
+
+
 
 #if OPI_SUPPORT
   //----- OPI functions
diff --git a/poppler/PSOutputDev.cc b/poppler/PSOutputDev.cc
index 5db5a31..4a6537d 100644
--- a/poppler/PSOutputDev.cc
+++ b/poppler/PSOutputDev.cc
@@ -1020,7 +1020,6 @@ PSOutputDev::PSOutputDev(const char *fileName, PDFDoc *doc,
   embFontList = NULL;
   customColors = NULL;
   haveTextClip = gFalse;
-  haveCSPattern = gFalse;
   t3String = NULL;
 
   forceRasterize = forceRasterizeA;
@@ -1084,7 +1083,6 @@ PSOutputDev::PSOutputDev(PSOutputFunc outputFuncA, void *outputStreamA,
   embFontList = NULL;
   customColors = NULL;
   haveTextClip = gFalse;
-  haveCSPattern = gFalse;
   t3String = NULL;
 
   forceRasterize = forceRasterizeA;
@@ -4001,6 +3999,14 @@ void PSOutputDev::updateTextShift(GfxState *state, double shift) {
   }
 }
 
+void PSOutputDev::saveTextPos(GfxState *state) {
+  writePS("currentpoint\n");
+}
+
+void PSOutputDev::restoreTextPos(GfxState *state) {
+  writePS("m\n");
+}
+
 void PSOutputDev::stroke(GfxState *state) {
   doPath(state->getPath());
   if (t3String) {
@@ -4664,72 +4670,57 @@ void PSOutputDev::drawString(GfxState *state, GooString *s) {
   }
   delete s2;
 
-  if (state->getRender() & 4 || haveCSPattern) {
+  if (state->getRender() & 4) {
     haveTextClip = gTrue;
   }
 }
 
 void PSOutputDev::beginTextObject(GfxState *state) {
-  if (state->getFillColorSpace()->getMode() == csPattern) {
-    saveState(state);
-    haveCSPattern = gTrue;
-    writePS("true Tp\n");
-  }
 }
 
 void PSOutputDev::endTextObject(GfxState *state) {
-  if (haveCSPattern) {
-    if (haveTextClip) {
-      writePS("Tclip*\n");
-      haveTextClip = gFalse;
-      if (state->getFillColorSpace()->getMode() != csPattern) {
-        double cxMin, cyMin, cxMax, cyMax;
-        state->getClipBBox(&cxMin, &cyMin, &cxMax, &cyMax);
-        writePSFmt("{0:.6g} {1:.6g} {2:.6g} {3:.6g} re\n",
-                   cxMin, cyMin,
-                   cxMax, cyMax);
-        writePS("f*\n");
-        restoreState(state);
-        updateFillColor(state);
-      }
-    }
-    haveCSPattern = gFalse;
-  } else if (haveTextClip) {
+  if (haveTextClip) {
     writePS("Tclip\n");
     haveTextClip = gFalse;
   }
 }
 
-void PSOutputDev::endMaskClip(GfxState * state) {
-  writePS("pdfImClipEnd\n");
-}
-
 void PSOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
 				int width, int height, GBool invert,
 				GBool interpolate, GBool inlineImg) {
   int len;
 
   len = height * ((width + 7) / 8);
-  if (state->getFillColorSpace()->getMode() == csPattern && (level != psLevel1 && level != psLevel1Sep)) {
+  switch (level) {
+    case psLevel1:
+    case psLevel1Sep:
+      doImageL1(ref, NULL, invert, inlineImg, str, width, height, len,
+                NULL, NULL, 0, 0, gFalse);
+    break;
+    case psLevel2:
+    case psLevel2Sep:
+      doImageL2(ref, NULL, invert, inlineImg, str, width, height, len,
+                NULL, NULL, 0, 0, gFalse);
+    break;
+    case psLevel3:
+    case psLevel3Sep:
+      doImageL3(ref, NULL, invert, inlineImg, str, width, height, len,
+                NULL, NULL, 0, 0, gFalse);
+    break;
+  }
+}
+
+void PSOutputDev::setSoftMaskFromImageMask(GfxState *state, Object *ref, Stream *str,
+				int width, int height, GBool invert,
+				GBool inlineImg) {
+  if (level != psLevel1 && level != psLevel1Sep) {
     maskToClippingPath(str, width, height, invert);
-  } else {
-    switch (level) {
-      case psLevel1:
-      case psLevel1Sep:
-        doImageL1(ref, NULL, invert, inlineImg, str, width, height, len,
-                  NULL, NULL, 0, 0, gFalse);
-      break;
-      case psLevel2:
-      case psLevel2Sep:
-        doImageL2(ref, NULL, invert, inlineImg, str, width, height, len,
-                  NULL, NULL, 0, 0, gFalse);
-      break;
-      case psLevel3:
-      case psLevel3Sep:
-        doImageL3(ref, NULL, invert, inlineImg, str, width, height, len,
-                  NULL, NULL, 0, 0, gFalse);
-      break;
-    }
+  }
+}
+
+void PSOutputDev::unsetSoftMaskFromImageMask(GfxState * state) {
+  if (level != psLevel1 && level != psLevel1Sep) {
+    writePS("pdfImClipEnd\n");
   }
 }
 
diff --git a/poppler/PSOutputDev.h b/poppler/PSOutputDev.h
index 11ebbb8..69725d4 100644
--- a/poppler/PSOutputDev.h
+++ b/poppler/PSOutputDev.h
@@ -134,10 +134,6 @@ public:
   // text in Type 3 fonts will be drawn with drawChar/drawString.
   virtual GBool interpretType3Chars() { return gFalse; }
 
-  // This device now supports text in pattern colorspace!
-  virtual GBool supportTextCSPattern(GfxState *state)
-  	{ return state->getFillColorSpace()->getMode() == csPattern; }
-
   //----- header/trailer (used only if manualCtrl is true)
 
   // Write the document-level header.
@@ -204,6 +200,8 @@ public:
   virtual void updateHorizScaling(GfxState *state);
   virtual void updateTextPos(GfxState *state);
   virtual void updateTextShift(GfxState *state, double shift);
+  virtual void saveTextPos(GfxState *state);
+  virtual void restoreTextPos(GfxState *state);
 
   //----- path painting
   virtual void stroke(GfxState *state);
@@ -227,13 +225,18 @@ public:
   //----- text drawing
   virtual void drawString(GfxState *state, GooString *s);
   virtual void beginTextObject(GfxState *state);
-  virtual GBool deviceHasTextClip(GfxState *state) { return haveTextClip && haveCSPattern; }
+  virtual GBool deviceHasTextClip(GfxState *state) { return haveTextClip; }
   virtual void endTextObject(GfxState *state);
 
   //----- image drawing
   virtual void drawImageMask(GfxState *state, Object *ref, Stream *str,
 			     int width, int height, GBool invert,
 			     GBool interpolate, GBool inlineImg);
+  virtual void setSoftMaskFromImageMask(GfxState *state,
+					Object *ref, Stream *str,
+					int width, int height, GBool invert,
+					GBool inlineImg);
+  virtual void unsetSoftMaskFromImageMask(GfxState *state);
   virtual void drawImage(GfxState *state, Object *ref, Stream *str,
 			 int width, int height, GfxImageColorMap *colorMap,
 			 GBool interpolate, int *maskColors, GBool inlineImg);
@@ -243,12 +246,6 @@ public:
 			       GBool interpolate,
 			       Stream *maskStr, int maskWidth, int maskHeight,
 			       GBool maskInvert, GBool maskInterpolate);
-  // If current colorspace ist pattern,
-  // need this device special handling for masks in pattern colorspace?
-  // Default is false
-  virtual GBool fillMaskCSPattern(GfxState * state)
-  	{ return state->getFillColorSpace()->getMode() == csPattern && (level != psLevel1 && level != psLevel1Sep); }
-  virtual void endMaskClip(GfxState * /*state*/);
 
 #if OPI_SUPPORT
   //----- OPI functions
@@ -445,8 +442,6 @@ private:
 
   GBool haveTextClip;		// set if text has been drawn with a
 				//   clipping render mode
-  GBool haveCSPattern;		// set if text has been drawn with a
-				//   clipping render mode because of pattern colorspace
 
   GBool inType3Char;		// inside a Type 3 CharProc
   GooString *t3String;		// Type 3 content string
diff --git a/poppler/PreScanOutputDev.cc b/poppler/PreScanOutputDev.cc
index 1c5d09a..7be6abb 100644
--- a/poppler/PreScanOutputDev.cc
+++ b/poppler/PreScanOutputDev.cc
@@ -87,7 +87,7 @@ GBool PreScanOutputDev::tilingPatternFill(GfxState *state, Gfx *gfx, Catalog *ca
 					int x0, int y0, int x1, int y1,
 					double xStep, double yStep) {
   if (paintType == 1) {
-    gfx->doForm1(str, resDict, mat, bbox);
+    gfx->drawForm(str, resDict, mat, bbox);
   } else {
     check(state->getFillColorSpace(), state->getFillColor(),
 	  state->getFillOpacity(), state->getBlendMode());
diff --git a/poppler/SplashOutputDev.cc b/poppler/SplashOutputDev.cc
index 58382ca..fe99fd4 100644
--- a/poppler/SplashOutputDev.cc
+++ b/poppler/SplashOutputDev.cc
@@ -130,182 +130,6 @@ static inline void convertGfxColor(SplashColorPtr dest,
   splashColorCopy(dest, color);
 }
 
-static inline SplashPattern *createOverprintPattern(GfxColorSpace *colorSpace, SplashColorPtr color, GBool image, Guchar tolerance = 0x01) {
-  switch (colorSpace->getMode()) {
-    case csDeviceCMYK:
-      if (image) {
-        return new SplashSolidColor(color);
-      }
-    case csSeparation:
-    case csDeviceN:
-      if (image && colorSpace->getMode() == csDeviceN) {
-        GfxDeviceNColorSpace *deviceNSpace = (GfxDeviceNColorSpace *) colorSpace;
-        GBool hasSpot = gFalse;
-        GBool hasProcess = gFalse;
-        for (int i = 0; i < deviceNSpace->getNComps(); i++) {
-          GooString *name = deviceNSpace->getColorantName(i)->upperCase();
-          if (name->cmp("CYAN") != 0 && 
-              name->cmp("MAGENTA") != 0 && 
-              name->cmp("YELLOW") != 0 && 
-              name->cmp("BLACK") != 0 && 
-              name->cmp("NONE") != 0) {
-            hasSpot = gTrue;
-          } else {
-            hasProcess = gTrue;
-          }
-        }
-        if (hasSpot && hasProcess)
-          return new SplashSolidColor(color);
-      }
-      return new SplashOverprintColor(colorSpace, color, tolerance);
-    case csIndexed:
-      return createOverprintPattern(((GfxIndexedColorSpace *) colorSpace)->getBase(), color, image);          
-    case csICCBased:
-      if (image) {
-        return createOverprintPattern(((GfxICCBasedColorSpace *) colorSpace)->getAlt(), color, gFalse, 0x05);          
-      }
-    case csDeviceGray:
-      if (image) {
-        return new SplashOverprintColor(colorSpace, color, tolerance);
-      }
-    default: // knockout
-      return new SplashSolidColor(color);
-  }
-}
-
-
-//------------------------------------------------------------------------
-// SplashOverprintColor
-//------------------------------------------------------------------------
-
-SplashOverprintColor::SplashOverprintColor(GfxColorSpace *colorSpaceA, SplashColorPtr colorA, Guchar toleranceA) {
-  splashColorCopy(color, colorA);
-  colorSpace = colorSpaceA;
-  tolerance = toleranceA;
-}
-
-SplashOverprintColor::~SplashOverprintColor() {
-}
-
-GBool SplashOverprintColor::getColor(int x, int y, SplashColorPtr c) {
-  splashColorCopy(c, color);
-  return gTrue;
-}
-
-void SplashOverprintColor::overprint(GBool op, Guchar aSrc, SplashColorPtr cSrc,
-                                     Guchar aDest, SplashColorPtr cDest,
-                                     SplashColorPtr colorResult) {
-  switch(colorSpace->getMode()) {
-    case csDeviceGray: // only in case of grayscale images
-      colorResult[0] = cDest[0];
-      colorResult[1] = cDest[1];
-      colorResult[2] = cDest[2];
-      colorResult[3] = (Guchar)(((aDest - aSrc) * cDest[3] +
-                       aSrc * cSrc[3]) / aDest);
-    break;
-    case csDeviceCMYK:
-      colorResult[0] = (cSrc[0] < tolerance && op) ?
-                       cDest[0] :
-                       (Guchar)(((aDest - aSrc) * cDest[0] + aSrc * cSrc[0]) / aDest);
-      colorResult[1] = (cSrc[1] < tolerance && op) ?
-                       cDest[1] :
-                       (Guchar)(((aDest - aSrc) * cDest[1] + aSrc * cSrc[1]) / aDest);
-      colorResult[2] = (cSrc[2] < tolerance && op) ?
-                       cDest[2] :
-                       (Guchar)(((aDest - aSrc) * cDest[2] + aSrc * cSrc[2]) / aDest);
-      colorResult[3] = (cSrc[3] < tolerance && op) ?
-                       cDest[3] :
-                       (Guchar)(((aDest - aSrc) * cDest[3] + aSrc * cSrc[3]) / aDest);
-    break;
-
-    case csSeparation:
-    {
-      GfxSeparationColorSpace *sepSpace = (GfxSeparationColorSpace *) colorSpace;
-      GooString *name = sepSpace->getName()->upperCase();
-      if (name->cmp("CYAN") == 0) {
-        colorResult[0] = (Guchar)(((aDest - aSrc) * cDest[0] +
-                         aSrc * cSrc[0]) / aDest);
-                         colorResult[1] = cDest[1];
-                         colorResult[2] = cDest[2];
-                         colorResult[3] = cDest[3];
-      } else if (name->cmp("MAGENTA") == 0) {
-        colorResult[0] = cDest[0];
-        colorResult[1] = (Guchar)(((aDest - aSrc) * cDest[1] +
-                         aSrc * cSrc[1]) / aDest);
-                         colorResult[2] = cDest[2];
-                         colorResult[3] = cDest[3];
-      } else if (name->cmp("YELLOW") == 0) {
-        colorResult[0] = cDest[0];
-        colorResult[1] = cDest[1];
-        colorResult[2] = (Guchar)(((aDest - aSrc) * cDest[2] +
-                         aSrc * cSrc[2]) / aDest);
-        colorResult[3] = cDest[3];
-      } else if (name->cmp("BLACK") == 0) {
-        colorResult[0] = cDest[0];
-        colorResult[1] = cDest[1];
-        colorResult[2] = cDest[2];
-        colorResult[3] = (Guchar)(((aDest - aSrc) * cDest[3] +
-                         aSrc * cSrc[3]) / aDest);
-      } else {
-        colorResult[0] = ((int) cDest[0] + cSrc[0]) > 0xff ? 0xff : cDest[0] + cSrc[0];
-        colorResult[1] = ((int) cDest[1] + cSrc[1]) > 0xff ? 0xff : cDest[1] + cSrc[1];
-        colorResult[2] = ((int) cDest[2] + cSrc[2]) > 0xff ? 0xff : cDest[2] + cSrc[2];
-        colorResult[3] = ((int) cDest[3] + cSrc[3]) > 0xff ? 0xff : cDest[3] + cSrc[3];
-      }
-    }
-    break;
-    case csDeviceN:
-    {
-      GfxDeviceNColorSpace *deviceNSpace = (GfxDeviceNColorSpace *) colorSpace;
-      colorResult[0] = cDest[0];
-      colorResult[1] = cDest[1];
-      colorResult[2] = cDest[2];
-      colorResult[3] = cDest[3];
-      for (int i = 0; i < deviceNSpace->getNComps(); i++) {
-        GooString *name = deviceNSpace->getColorantName(i)->upperCase();
-        if (name->cmp("CYAN") == 0) {
-          colorResult[0] = (Guchar)(((aDest - aSrc) * cDest[0] +
-                           aSrc * cSrc[0]) / aDest);
-        } else if (name->cmp("MAGENTA") == 0) {
-          colorResult[1] = (Guchar)(((aDest - aSrc) * cDest[1] +
-                           aSrc * cSrc[1]) / aDest);
-        } else if (name->cmp("YELLOW") == 0) {
-          colorResult[2] = (Guchar)(((aDest - aSrc) * cDest[2] +
-                           aSrc * cSrc[2]) / aDest);
-        } else if (name->cmp("BLACK") == 0) {
-          colorResult[3] = (Guchar)(((aDest - aSrc) * cDest[3] +
-                           aSrc * cSrc[3]) / aDest);
-        } else if (name->cmp("NONE") != 0) { 
-          colorResult[0] = (cSrc[0] < tolerance && op) ?
-                           cDest[0] :
-                           (Guchar)(((aDest - aSrc) * cDest[0] + aSrc * cSrc[0]) / aDest);
-          colorResult[1] = (cSrc[1] < tolerance && op) ?
-                           cDest[1] :
-                           (Guchar)(((aDest - aSrc) * cDest[1] + aSrc * cSrc[1]) / aDest);
-          colorResult[2] = (cSrc[2] < tolerance && op) ?
-                           cDest[2] :
-                           (Guchar)(((aDest - aSrc) * cDest[2] + aSrc * cSrc[2]) / aDest);
-          colorResult[3] = (cSrc[3] < tolerance && op) ?
-                           cDest[3] :
-                           (Guchar)(((aDest - aSrc) * cDest[3] + aSrc * cSrc[3]) / aDest);
-          break;
-        }
-      }
-    }
-    break;
-    default:
-        // default for overprint is knockout:
-        colorResult[0] = (Guchar)(((aDest - aSrc) * cDest[0] +
-                         aSrc * cSrc[0]) / aDest);
-        colorResult[1] = (Guchar)(((aDest - aSrc) * cDest[1] +
-                         aSrc * cSrc[1]) / aDest);
-        colorResult[2] = (Guchar)(((aDest - aSrc) * cDest[2] +
-                         aSrc * cSrc[2]) / aDest);
-        colorResult[3] = (Guchar)(((aDest - aSrc) * cDest[3] +
-                         aSrc * cSrc[3]) / aDest);
-  }
-}
-
 //------------------------------------------------------------------------
 // SplashGouraudPattern
 //------------------------------------------------------------------------
@@ -319,11 +143,9 @@ SplashGouraudPattern::SplashGouraudPattern(GBool bDirectColorTranslationA,
   bDirectColorTranslation = bDirectColorTranslationA;
   shadingA->getColorSpace()->getDefaultColor(&srcColor);
   convertGfxColor(defaultColor, mode, shadingA->getColorSpace(), &srcColor);
-  opPattern = new SplashOverprintColor(shadingA->getColorSpace(), defaultColor, 0x01);
 }
 
 SplashGouraudPattern::~SplashGouraudPattern() {
-  delete opPattern;
 }
 
 void SplashGouraudPattern::getParameterizedColor(double colorinterp, SplashColorMode mode, SplashColorPtr dest) {
@@ -345,11 +167,6 @@ void SplashGouraudPattern::getParameterizedColor(double colorinterp, SplashColor
   }
 }
 
-void SplashGouraudPattern::overprint(GBool op, Guchar aSrc, SplashColorPtr cSrc,
-                                     Guchar aDest, SplashColorPtr cDest,
-                                     SplashColorPtr colorResult) {
-  opPattern->overprint(op, aSrc, cSrc, aDest, cDest, colorResult);
-}
 //------------------------------------------------------------------------
 // SplashUnivariatePattern
 //------------------------------------------------------------------------
@@ -420,11 +237,9 @@ SplashRadialPattern::SplashRadialPattern(SplashColorMode colorModeA, GfxState *s
     inva = 1.0 / a;
   shadingA->getColorSpace()->getDefaultColor(&srcColor);
   convertGfxColor(defaultColor, colorModeA, shadingA->getColorSpace(), &srcColor);
-  opPattern = new SplashOverprintColor(shadingA->getColorSpace(), defaultColor, 0x01);
 }
 
 SplashRadialPattern::~SplashRadialPattern() {
-  delete opPattern;
 }
 
 GBool SplashRadialPattern::getParameter(double xs, double ys, double *t) {
@@ -510,12 +325,6 @@ GBool SplashRadialPattern::getParameter(double xs, double ys, double *t) {
   return gFalse;
 }
 
-void SplashRadialPattern::overprint(GBool op, Guchar aSrc, SplashColorPtr cSrc,
-                                    Guchar aDest, SplashColorPtr cDest,
-                                    SplashColorPtr colorResult) {
-  opPattern->overprint(op, aSrc, cSrc, aDest, cDest, colorResult);
-}
-
 #undef RADIAL_EPSILON
 
 //------------------------------------------------------------------------
@@ -534,11 +343,9 @@ SplashAxialPattern::SplashAxialPattern(SplashColorMode colorModeA, GfxState *sta
   mul = 1 / (dx * dx + dy * dy);
   shadingA->getColorSpace()->getDefaultColor(&srcColor);
   convertGfxColor(defaultColor, colorModeA, shadingA->getColorSpace(), &srcColor);
-  opPattern = new SplashOverprintColor(shadingA->getColorSpace(), defaultColor, 0x01);
 }
 
 SplashAxialPattern::~SplashAxialPattern() {
-  delete opPattern;
 }
 
 GBool SplashAxialPattern::getParameter(double xc, double yc, double *t) {
@@ -561,14 +368,13 @@ GBool SplashAxialPattern::getParameter(double xc, double yc, double *t) {
   return gTrue;
 }
 
-void SplashAxialPattern::overprint(GBool op, Guchar aSrc, SplashColorPtr cSrc,
-                                   Guchar aDest, SplashColorPtr cDest,
-                                   SplashColorPtr colorResult) {
-  opPattern->overprint(op, aSrc, cSrc, aDest, cDest, colorResult);
-}
-
 //------------------------------------------------------------------------
+// Type 3 font cache size parameters
+#define type3FontCacheAssoc   8
+#define type3FontCacheMaxSets 8
+#define type3FontCacheSize    (128*1024)
 
+//------------------------------------------------------------------------
 // Divide a 16-bit value (in [0, 255*255]) by 255, returning an 8-bit result.
 static inline Guchar div255(int x) {
   return (Guchar)((x + (x >> 8) + 0x80) >> 8);
@@ -894,15 +700,21 @@ static void splashOutBlendDifference(SplashColorPtr src, SplashColorPtr dest,
 				     SplashColorMode cm) {
   int i;
 
-  for (i = 0; i < splashColorModeNComps[cm]; ++i) {
 #if SPLASH_CMYK
-    if (cm == splashModeCMYK8)
-    {
-      blend[i] = dest[i] < src[i] ? 255 - (src[i] - dest[i]) : 255 - (dest[i] - src[i]);
+  if (cm == splashModeCMYK8) {
+    SplashColor rgbSrc;
+    SplashColor rgbDest;
+    SplashColor rgbBlend;
+    cmykToRGB(src, rgbSrc);
+    cmykToRGB(dest, rgbDest);
+    for (i = 0; i < 3; ++i) {
+      rgbBlend[i] = rgbDest[i] < rgbSrc[i] ? rgbSrc[i] - rgbDest[i] : rgbDest[i] - rgbSrc[i];
     }
-    else
+    rgbToCMYK(rgbBlend, blend);
+  } else
 #endif
-    {
+  {
+    for (i = 0; i < splashColorModeNComps[cm]; ++i) {
       blend[i] = dest[i] < src[i] ? src[i] - dest[i] : dest[i] - src[i];
     }
   }
@@ -932,64 +744,100 @@ static void splashOutBlendExclusion(SplashColorPtr src, SplashColorPtr dest,
   }
 }
 
-static void cvtRGBToHSV(Guchar r, Guchar g, Guchar b, int *h, int *s, int *v) {
-  int cmax, cmid, cmin, x;
+static int getLum(int r, int g, int b) {
+  return (int)(0.3 * r + 0.59 * g + 0.11 * b);
+}
 
-  if (r >= g) {
-    if (g >= b)      { x = 0; cmax = r; cmid = g; cmin = b; }
-    else if (b >= r) { x = 4; cmax = b; cmid = r; cmin = g; }
-    else             { x = 5; cmax = r; cmid = b; cmin = g; }
-  } else {
-    if (r >= b)      { x = 1; cmax = g; cmid = r; cmin = b; }
-    else if (g >= b) { x = 2; cmax = g; cmid = b; cmin = r; }
-    else             { x = 3; cmax = b; cmid = g; cmin = r; }
+static int getSat(int r, int g, int b) {
+  int rgbMin, rgbMax;
+
+  rgbMin = rgbMax = r;
+  if (g < rgbMin) {
+    rgbMin = g;
+  } else if (g > rgbMax) {
+    rgbMax = g;
+  }
+  if (b < rgbMin) {
+    rgbMin = b;
+  } else if (b > rgbMax) {
+    rgbMax = b;
   }
-  if (cmax == cmin) {
-    *h = *s = 0;
+  return rgbMax - rgbMin;
+}
+
+static void clipColor(int rIn, int gIn, int bIn,
+		      Guchar *rOut, Guchar *gOut, Guchar *bOut) {
+  int lum, rgbMin, rgbMax;
+
+  lum = getLum(rIn, gIn, bIn);
+  rgbMin = rgbMax = rIn;
+  if (gIn < rgbMin) {
+    rgbMin = gIn;
+  } else if (gIn > rgbMax) {
+    rgbMax = gIn;
+  }
+  if (bIn < rgbMin) {
+    rgbMin = bIn;
+  } else if (bIn > rgbMax) {
+    rgbMax = bIn;
+  }
+  if (rgbMin < 0) {
+    *rOut = (Guchar)(lum + ((rIn - lum) * lum) / (lum - rgbMin));
+    *gOut = (Guchar)(lum + ((gIn - lum) * lum) / (lum - rgbMin));
+    *bOut = (Guchar)(lum + ((bIn - lum) * lum) / (lum - rgbMin));
+  } else if (rgbMax > 255) {
+    *rOut = (Guchar)(lum + ((rIn - lum) * (255 - lum)) / (rgbMax - lum));
+    *gOut = (Guchar)(lum + ((gIn - lum) * (255 - lum)) / (rgbMax - lum));
+    *bOut = (Guchar)(lum + ((bIn - lum) * (255 - lum)) / (rgbMax - lum));
   } else {
-    *h = x * 60;
-    if (x & 1) {
-      *h += ((cmax - cmid) * 60) / (cmax - cmin);
-    } else {
-      *h += ((cmid - cmin) * 60) / (cmax - cmin);
-    }
-    *s = (255 * (cmax - cmin)) / cmax;
+    *rOut = rIn;
+    *gOut = gIn;
+    *bOut = bIn;
   }
-  *v = cmax;
 }
 
-static void cvtHSVToRGB(int h, int s, int v, Guchar *r, Guchar *g, Guchar *b) {
-  int x, f, cmax, cmid, cmin;
+static void setLum(Guchar rIn, Guchar gIn, Guchar bIn, int lum,
+		   Guchar *rOut, Guchar *gOut, Guchar *bOut) {
+  int d;
+
+  d = lum - getLum(rIn, gIn, bIn);
+  clipColor(rIn + d, gIn + d, bIn + d, rOut, gOut, bOut);
+}
 
-  if (s == 0) {
-    *r = *g = *b = v;
+static void setSat(Guchar rIn, Guchar gIn, Guchar bIn, int sat,
+		   Guchar *rOut, Guchar *gOut, Guchar *bOut) {
+  int rgbMin, rgbMid, rgbMax;
+  Guchar *minOut, *midOut, *maxOut;
+
+  if (rIn < gIn) {
+    rgbMin = rIn;  minOut = rOut;
+    rgbMid = gIn;  midOut = gOut;
   } else {
-    x = h / 60;
-    f = h % 60;
-    cmax = v;
-    if (x & 1) {
-      cmid = div255(v * 255 - ((s * f) / 60));
-    } else {
-      cmid = div255(v * (255 - ((s * (60 - f)) / 60)));
-    }
-    cmin = div255(v * (255 - s));
-    switch (x) {
-    case 0: *r = cmax; *g = cmid; *b = cmin; break;
-    case 1: *g = cmax; *r = cmid; *b = cmin; break;
-    case 2: *g = cmax; *b = cmid; *r = cmin; break;
-    case 3: *b = cmax; *g = cmid; *r = cmin; break;
-    case 4: *b = cmax; *r = cmid; *g = cmin; break;
-    case 5: *r = cmax; *b = cmid; *g = cmin; break;
-    }
+    rgbMin = gIn;  minOut = gOut;
+    rgbMid = rIn;  midOut = rOut;
+  }
+  if (bIn > rgbMid) {
+    rgbMax = bIn;  maxOut = bOut;
+  } else if (bIn > rgbMin) {
+    rgbMax = rgbMid;  maxOut = midOut;
+    rgbMid = bIn;     midOut = bOut;
+  } else {
+    rgbMax = rgbMid;  maxOut = midOut;
+    rgbMid = rgbMin;  midOut = minOut;
+    rgbMin = bIn;     minOut = bOut;
   }
+  if (rgbMax > rgbMin) {
+    *midOut = (Guchar)((rgbMid - rgbMin) * sat) / (rgbMax - rgbMin);
+    *maxOut = (Guchar)sat;
+  } else {
+    *midOut = *maxOut = 0;
+  }
+  *minOut = 0;
 }
 
 static void splashOutBlendHue(SplashColorPtr src, SplashColorPtr dest,
 			      SplashColorPtr blend, SplashColorMode cm) {
-  int hs, ss, vs, hd, sd, vd;
-#if SPLASH_CMYK
-  Guchar r, g, b;
-#endif
+  Guchar r0, g0, b0, r1, g1, b1;
 
   switch (cm) {
   case splashModeMono1:
@@ -1000,25 +848,22 @@ static void splashOutBlendHue(SplashColorPtr src, SplashColorPtr dest,
     src[3] = 255;
   case splashModeRGB8:
   case splashModeBGR8:
-    cvtRGBToHSV(src[0], src[1], src[2], &hs, &ss, &vs);
-    cvtRGBToHSV(dest[0], dest[1], dest[2], &hd, &sd, &vd);
-    cvtHSVToRGB(hs, sd, vd, &blend[0], &blend[1], &blend[2]);
+    setSat(src[0], src[1], src[2], getSat(dest[0], dest[1], dest[2]),
+	   &r0, &g0, &b0);
+    setLum(r0, g0, b0, getLum(dest[0], dest[1], dest[2]),
+	   &blend[0], &blend[1], &blend[2]);
     break;
 #if SPLASH_CMYK
   case splashModeCMYK8:
-    //~ (0xff - ...) should be clipped
-    cvtRGBToHSV(0xff - (src[0] + src[3]),
-		0xff - (src[1] + src[3]),
-		0xff - (src[2] + src[3]), &hs, &ss, &vs);
-    cvtRGBToHSV(0xff - (dest[0] + dest[3]),
-		0xff - (dest[1] + dest[3]),
-		0xff - (dest[2] + dest[3]), &hd, &sd, &vd);
-    cvtHSVToRGB(hs, sd, vd, &r, &g, &b);
-    //~ should do black generation
-    blend[0] = 0xff - r;
-    blend[1] = 0xff - g;
-    blend[2] = 0xff - b;
-    blend[3] = 0;
+    // NB: inputs have already been converted to additive mode
+    setSat(src[0], src[1], src[2], getSat(dest[0], dest[1], dest[2]),
+	   &r0, &g0, &b0);
+    setLum(r0, g0, b0, getLum(dest[0], dest[1], dest[2]),
+	   &r1, &g1, &b1);
+    blend[0] = r1;
+    blend[1] = g1;
+    blend[2] = b1;
+    blend[3] = dest[3];
     break;
 #endif
   }
@@ -1027,10 +872,7 @@ static void splashOutBlendHue(SplashColorPtr src, SplashColorPtr dest,
 static void splashOutBlendSaturation(SplashColorPtr src, SplashColorPtr dest,
 				     SplashColorPtr blend,
 				     SplashColorMode cm) {
-  int hs, ss, vs, hd, sd, vd;
-#if SPLASH_CMYK
-  Guchar r, g, b;
-#endif
+  Guchar r0, g0, b0, r1, g1, b1;
 
   switch (cm) {
   case splashModeMono1:
@@ -1041,25 +883,22 @@ static void splashOutBlendSaturation(SplashColorPtr src, SplashColorPtr dest,
     src[3] = 255;
   case splashModeRGB8:
   case splashModeBGR8:
-    cvtRGBToHSV(src[0], src[1], src[2], &hs, &ss, &vs);
-    cvtRGBToHSV(dest[0], dest[1], dest[2], &hd, &sd, &vd);
-    cvtHSVToRGB(hd, ss, vd, &blend[0], &blend[1], &blend[2]);
+    setSat(dest[0], dest[1], dest[2], getSat(src[0], src[1], src[2]),
+	   &r0, &g0, &b0);
+    setLum(r0, g0, b0, getLum(dest[0], dest[1], dest[2]),
+	   &blend[0], &blend[1], &blend[2]);
     break;
 #if SPLASH_CMYK
   case splashModeCMYK8:
-    //~ (0xff - ...) should be clipped
-    cvtRGBToHSV(0xff - (src[0] + src[3]),
-		0xff - (src[1] + src[3]),
-		0xff - (src[2] + src[3]), &hs, &ss, &vs);
-    cvtRGBToHSV(0xff - (dest[0] + dest[3]),
-		0xff - (dest[1] + dest[3]),
-		0xff - (dest[2] + dest[3]), &hd, &sd, &vd);
-    cvtHSVToRGB(hd, ss, vd, &r, &g, &b);
-    //~ should do black generation
-    blend[0] = 0xff - r;
-    blend[1] = 0xff - g;
-    blend[2] = 0xff - b;
-    blend[3] = 0;
+    // NB: inputs have already been converted to additive mode
+    setSat(dest[0], dest[1], dest[2], getSat(src[0], src[1], src[2]),
+	   &r0, &g0, &b0);
+    setLum(r0, g0, b0, getLum(dest[0], dest[1], dest[2]),
+	   &r1, &g1, &b1);
+    blend[0] = r1;
+    blend[1] = g1;
+    blend[2] = b1;
+    blend[3] = dest[3];
     break;
 #endif
   }
@@ -1067,7 +906,9 @@ static void splashOutBlendSaturation(SplashColorPtr src, SplashColorPtr dest,
 
 static void splashOutBlendColor(SplashColorPtr src, SplashColorPtr dest,
 				SplashColorPtr blend, SplashColorMode cm) {
-  int hs, ss, vs, hd, sd, vd;
+#if SPLASH_CMYK
+  Guchar r, g, b;
+#endif
 
   switch (cm) {
   case splashModeMono1:
@@ -1078,21 +919,18 @@ static void splashOutBlendColor(SplashColorPtr src, SplashColorPtr dest,
     src[3] = 255;
   case splashModeRGB8:
   case splashModeBGR8:
-    cvtRGBToHSV(src[0], src[1], src[2], &hs, &ss, &vs);
-    cvtRGBToHSV(dest[0], dest[1], dest[2], &hd, &sd, &vd);
-    cvtHSVToRGB(hs, ss, vd, &blend[0], &blend[1], &blend[2]);
+    setLum(src[0], src[1], src[2], getLum(dest[0], dest[1], dest[2]),
+	   &blend[0], &blend[1], &blend[2]);
     break;
 #if SPLASH_CMYK
   case splashModeCMYK8:
-    SplashColor rgbSrc;
-    SplashColor rgbDest;
-    SplashColor rgbBlend;
-    cmykToRGB(src, rgbSrc);
-    cmykToRGB(dest, rgbDest);
-    cvtRGBToHSV(rgbSrc[0], rgbSrc[1], rgbSrc[2], &hs, &ss, &vs);
-    cvtRGBToHSV(rgbDest[0], rgbDest[1], rgbDest[2], &hd, &sd, &vd);
-    cvtHSVToRGB(hs, ss, vd, &rgbBlend[0], &rgbBlend[1], &rgbBlend[2]);
-	rgbToCMYK(rgbBlend, blend);
+    // NB: inputs have already been converted to additive mode
+    setLum(src[0], src[1], src[2], getLum(dest[0], dest[1], dest[2]),
+	   &r, &g, &b);
+    blend[0] = r;
+    blend[1] = g;
+    blend[2] = b;
+    blend[3] = dest[3];
     break;
 #endif
   }
@@ -1101,7 +939,6 @@ static void splashOutBlendColor(SplashColorPtr src, SplashColorPtr dest,
 static void splashOutBlendLuminosity(SplashColorPtr src, SplashColorPtr dest,
 				     SplashColorPtr blend,
 				     SplashColorMode cm) {
-  int hs, ss, vs, hd, sd, vd;
 #if SPLASH_CMYK
   Guchar r, g, b;
 #endif
@@ -1115,25 +952,18 @@ static void splashOutBlendLuminosity(SplashColorPtr src, SplashColorPtr dest,
     src[3] = 255;
   case splashModeRGB8:
   case splashModeBGR8:
-    cvtRGBToHSV(src[0], src[1], src[2], &hs, &ss, &vs);
-    cvtRGBToHSV(dest[0], dest[1], dest[2], &hd, &sd, &vd);
-    cvtHSVToRGB(hd, sd, vs, &blend[0], &blend[1], &blend[2]);
+    setLum(dest[0], dest[1], dest[2], getLum(src[0], src[1], src[2]),
+	   &blend[0], &blend[1], &blend[2]);
     break;
 #if SPLASH_CMYK
   case splashModeCMYK8:
-    //~ (0xff - ...) should be clipped
-    cvtRGBToHSV(0xff - (src[0] + src[3]),
-		0xff - (src[1] + src[3]),
-		0xff - (src[2] + src[3]), &hs, &ss, &vs);
-    cvtRGBToHSV(0xff - (dest[0] + dest[3]),
-		0xff - (dest[1] + dest[3]),
-		0xff - (dest[2] + dest[3]), &hd, &sd, &vd);
-    cvtHSVToRGB(hd, sd, vs, &r, &g, &b);
-    //~ should do black generation
-    blend[0] = 0xff - r;
-    blend[1] = 0xff - g;
-    blend[2] = 0xff - b;
-    blend[3] = 0;
+    // NB: inputs have already been converted to additive mode
+    setLum(dest[0], dest[1], dest[2], getLum(src[0], src[1], src[2]),
+	   &r, &g, &b);
+    blend[0] = r;
+    blend[1] = g;
+    blend[2] = b;
+    blend[3] = src[3];
     break;
 #endif
   }
@@ -1230,28 +1060,23 @@ T3FontCache::T3FontCache(Ref *fontIDA, double m11A, double m12A,
   glyphW = glyphWA;
   glyphH = glyphHA;
   validBBox = validBBoxA;
+  // sanity check for excessively large glyphs (which most likely
+  // indicate an incorrect BBox)
+  i = glyphW * glyphH;
+  if (i > 100000 || glyphW > INT_MAX / glyphH || glyphW <= 0 || glyphH <= 0) {
+    glyphW = glyphH = 100;
+    validBBox = gFalse;
+  }
   if (aa) {
     glyphSize = glyphW * glyphH;
   } else {
     glyphSize = ((glyphW + 7) >> 3) * glyphH;
   }
-  cacheAssoc = 8;
-  if (glyphSize <= 256) {
-    cacheSets = 8;
-  } else if (glyphSize <= 512) {
-    cacheSets = 4;
-  } else if (glyphSize <= 1024) {
-    cacheSets = 2;
-  } else if (glyphSize <= 2048) {
-    cacheSets = 1;
-    cacheAssoc = 4;
-  } else if (glyphSize <= 4096) {
-    cacheSets = 1;
-    cacheAssoc = 2;
-  } else {
-    cacheSets = 1;
-    cacheAssoc = 1;
-  }
+  cacheAssoc = type3FontCacheAssoc;
+  for (cacheSets = type3FontCacheMaxSets;
+       cacheSets > 1 &&
+	 cacheSets * cacheAssoc * glyphSize > type3FontCacheSize;
+       cacheSets >>= 1) ;
   if (glyphSize < 10485760 / cacheAssoc / cacheSets) {
     cacheData = (Guchar *)gmallocn_checkoverflow(cacheSets * cacheAssoc, glyphSize);
   } else {
@@ -1326,6 +1151,7 @@ SplashOutputDev::SplashOutputDev(SplashColorMode colorModeA,
   colorMode = colorModeA;
   bitmapRowPad = bitmapRowPadA;
   bitmapTopDown = bitmapTopDownA;
+  bitmapUpsideDown = gFalse;
   allowAntialias = allowAntialiasA;
   vectorAntialias = allowAntialias &&
 		      globalParams->getVectorAntialias() &&
@@ -1340,6 +1166,8 @@ SplashOutputDev::SplashOutputDev(SplashColorMode colorModeA,
   } else {
     splashClearColor(paperColor);
   }
+  skipHorizText = gFalse;
+  skipRotatedText = gFalse;
   keepAlphaChannel = paperColorA == NULL;
 
   doc = NULL;
@@ -1347,6 +1175,7 @@ SplashOutputDev::SplashOutputDev(SplashColorMode colorModeA,
   bitmap = new SplashBitmap(1, 1, bitmapRowPad, colorMode,
 			    colorMode != splashModeMono1, bitmapTopDown);
   splash = new Splash(bitmap, vectorAntialias, &screenParams);
+  splash->setMinLineWidth(globalParams->getMinLineWidth());
   splash->clear(paperColor, 0);
 
   fontEngine = NULL;
@@ -1357,7 +1186,6 @@ SplashOutputDev::SplashOutputDev(SplashColorMode colorModeA,
   font = NULL;
   needFontUpdate = gFalse;
   textClipPath = NULL;
-  haveCSPattern = gFalse;
   transpGroupStack = NULL;
   nestCount = 0;
 }
@@ -1488,6 +1316,7 @@ void SplashOutputDev::startPage(int pageNum, GfxState *state) {
 			      colorMode != splashModeMono1, bitmapTopDown);
   }
   splash = new Splash(bitmap, vectorAntialias, &screenParams);
+  splash->setMinLineWidth(globalParams->getMinLineWidth());
   if (state) {
     ctm = state->getCTM();
     mat[0] = (SplashCoord)ctm[0];
@@ -1593,7 +1422,10 @@ void SplashOutputDev::updateLineDash(GfxState *state) {
 }
 
 void SplashOutputDev::updateFlatness(GfxState *state) {
-  splash->setFlatness(state->getFlatness());
+#if 0 // Acrobat ignores the flatness setting, and always renders curves
+      // with a fairly small flatness value
+   splash->setFlatness(state->getFlatness());
+#endif
 }
 
 void SplashOutputDev::updateLineJoin(GfxState *state) {
@@ -1626,14 +1458,25 @@ void SplashOutputDev::updateFillColor(GfxState *state) {
   GfxCMYK cmyk;
 #endif
 
-  state->getFillGray(&gray);
-  state->getFillRGB(&rgb);
+  switch (colorMode) {
+  case splashModeMono1:
+  case splashModeMono8:
+    state->getFillGray(&gray);
+    splash->setFillPattern(getColor(gray));
+    break;
+  case splashModeXBGR8:
+  case splashModeRGB8:
+  case splashModeBGR8:
+    state->getFillRGB(&rgb);
+    splash->setFillPattern(getColor(&rgb));
+    break;
 #if SPLASH_CMYK
-  state->getFillCMYK(&cmyk);
-  splash->setFillPattern(getColor(state->getFillColorSpace(), gray, &rgb, &cmyk));
-#else
-  splash->setFillPattern(getColor(gray, &rgb));
+  case splashModeCMYK8:
+    state->getFillCMYK(&cmyk);
+    splash->setFillPattern(getColor(&cmyk));
+    break;
 #endif
+  }
 }
 
 void SplashOutputDev::updateStrokeColor(GfxState *state) {
@@ -1643,28 +1486,42 @@ void SplashOutputDev::updateStrokeColor(GfxState *state) {
   GfxCMYK cmyk;
 #endif
 
-  state->getStrokeGray(&gray);
-  state->getStrokeRGB(&rgb);
+  switch (colorMode) {
+  case splashModeMono1:
+  case splashModeMono8:
+    state->getStrokeGray(&gray);
+    splash->setStrokePattern(getColor(gray));
+    break;
+  case splashModeXBGR8:
+  case splashModeRGB8:
+  case splashModeBGR8:
+    state->getStrokeRGB(&rgb);
+    splash->setStrokePattern(getColor(&rgb));
+    break;
 #if SPLASH_CMYK
-  state->getStrokeCMYK(&cmyk);
-  splash->setStrokePattern(getColor(state->getStrokeColorSpace(), gray, &rgb, &cmyk));
-#else
-  splash->setStrokePattern(getColor(gray, &rgb));
+  case splashModeCMYK8:
+    state->getStrokeCMYK(&cmyk);
+    splash->setStrokePattern(getColor(&cmyk));
+    break;
 #endif
+  }
 }
 
-#if SPLASH_CMYK
-SplashPattern *SplashOutputDev::getColor(GfxColorSpace *colorSpace, GfxGray gray, GfxRGB *rgb,
-					 GfxCMYK *cmyk) {
-#else
-SplashPattern *SplashOutputDev::getColor(GfxGray gray, GfxRGB *rgb) {
-#endif
-  SplashPattern *pattern;
+SplashPattern *SplashOutputDev::getColor(GfxGray gray) {
   SplashColor color;
-  GfxColorComp r, g, b;
 
   if (reverseVideo) {
     gray = gfxColorComp1 - gray;
+  }
+  color[0] = colToByte(gray);
+  return new SplashSolidColor(color);
+}
+
+SplashPattern *SplashOutputDev::getColor(GfxRGB *rgb) {
+  GfxColorComp r, g, b;
+  SplashColor color;
+
+  if (reverseVideo) {
     r = gfxColorComp1 - rgb->r;
     g = gfxColorComp1 - rgb->g;
     b = gfxColorComp1 - rgb->b;
@@ -1673,36 +1530,25 @@ SplashPattern *SplashOutputDev::getColor(GfxGray gray, GfxRGB *rgb) {
     g = rgb->g;
     b = rgb->b;
   }
+  color[0] = colToByte(r);
+  color[1] = colToByte(g);
+  color[2] = colToByte(b);
+  if (colorMode == splashModeXBGR8) color[3] = 255;
+  return new SplashSolidColor(color);
+}
 
-  pattern = NULL; // make gcc happy
-  switch (colorMode) {
-  case splashModeMono1:
-  case splashModeMono8:
-    color[0] = colToByte(gray);
-    pattern = new SplashSolidColor(color);
-    break;
-  case splashModeXBGR8:
-    color[3] = 255;
-  case splashModeRGB8:
-  case splashModeBGR8:
-    color[0] = colToByte(r);
-    color[1] = colToByte(g);
-    color[2] = colToByte(b);
-    pattern = new SplashSolidColor(color);
-    break;
 #if SPLASH_CMYK
-  case splashModeCMYK8:
-    color[0] = colToByte(cmyk->c);
-    color[1] = colToByte(cmyk->m);
-    color[2] = colToByte(cmyk->y);
-    color[3] = colToByte(cmyk->k);
-    pattern = createOverprintPattern(colorSpace, color, gFalse);
-    break;
-#endif
-  }
+SplashPattern *SplashOutputDev::getColor(GfxCMYK *cmyk) {
+  SplashColor color;
 
-  return pattern;
+  color[0] = colToByte(cmyk->c);
+  color[1] = colToByte(cmyk->m);
+  color[2] = colToByte(cmyk->y);
+  color[3] = colToByte(cmyk->k);
+  // return createOverprintPattern(colorSpace, color, gFalse);
+  return new SplashSolidColor(color);
 }
+#endif
 
 void SplashOutputDev::setOverprintMask(GfxColorSpace *colorSpace,
 				       GBool overprintFlag,
@@ -1828,9 +1674,9 @@ void SplashOutputDev::doUpdateFont(GfxState *state) {
   DisplayFontParam *dfp;
   double *textMat;
   double m11, m12, m21, m22, fontSize;
-  SplashCoord mat[4];
-  int n;
   int faceIndex = 0;
+  SplashCoord mat[4];
+  int n, i;
   GBool recreateFont = gFalse;
   GBool doAdjustFontMatrix = gFalse;
 
@@ -1848,6 +1694,13 @@ void SplashOutputDev::doUpdateFont(GfxState *state) {
     goto err1;
   }
 
+  // sanity-check the font size - skip anything larger than 10 inches
+  // (this avoids problems allocating memory for the font cache)
+  if (state->getTransformedFontSize()
+        > 10 * (state->getHDPI() + state->getVDPI())) {
+    goto err1;
+  }
+
   // check the font file cache
   id = new SplashOutFontFileID(gfxFont->getID());
   if ((fontFile = fontEngine->getFontFile(id))) {
@@ -1940,6 +1793,17 @@ void SplashOutputDev::doUpdateFont(GfxState *state) {
 	codeToGID = ((Gfx8BitFont *)gfxFont)->getCodeToGIDMap(ff);
 	n = 256;
 	delete ff;
+	// if we're substituting for a non-TrueType font, we need to mark
+	// all notdef codes as "do not draw" (rather than drawing TrueType
+	// notdef glyphs)
+	if (gfxFont->getType() != fontTrueType &&
+	    gfxFont->getType() != fontTrueTypeOT) {
+	  for (i = 0; i < 256; ++i) {
+	    if (codeToGID[i] == 0) {
+	      codeToGID[i] = -1;
+	    }
+	  }
+	}
       } else {
 	codeToGID = NULL;
 	n = 0;
@@ -2197,6 +2061,18 @@ void SplashOutputDev::drawChar(GfxState *state, double x, double y,
 			       Unicode *u, int uLen) {
   SplashPath *path;
   int render;
+  GBool doFill, doStroke, doClip, strokeAdjust;
+  double m[4];
+  GBool horiz;
+
+  if (skipHorizText || skipRotatedText) {
+    state->getFontTransMat(&m[0], &m[1], &m[2], &m[3]);
+    horiz = m[0] > 0 && fabs(m[1]) < 0.001 &&
+            fabs(m[2]) < 0.001 && m[3] < 0;
+    if ((skipHorizText && horiz) || (skipRotatedText && !horiz)) {
+      return;
+    }
+  }
 
   // check for invisible text -- this is used by Acrobat Capture
   render = state->getRender();
@@ -2214,42 +2090,76 @@ void SplashOutputDev::drawChar(GfxState *state, double x, double y,
   x -= originX;
   y -= originY;
 
-  // fill
-  if (!(render & 1)) {
-    if (!haveCSPattern && !state->getFillColorSpace()->isNonMarking()) {
-      setOverprintMask(state->getFillColorSpace(), state->getFillOverprint(),
-	       state->getOverprintMode(), state->getFillColor());
-      splash->fillChar((SplashCoord)x, (SplashCoord)y, code, font);
+  doFill = !(render & 1) && !state->getFillColorSpace()->isNonMarking();
+  doStroke = ((render & 3) == 1 || (render & 3) == 2) &&
+             !state->getStrokeColorSpace()->isNonMarking();
+  doClip = render & 4;
+
+  path = NULL;
+  if (doStroke || doClip) {
+    if ((path = font->getGlyphPath(code))) {
+      path->offset((SplashCoord)x, (SplashCoord)y);
     }
   }
 
+  // don't use stroke adjustment when stroking text -- the results
+  // tend to be ugly (because characters with horizontal upper or
+  // lower edges get misaligned relative to the other characters)
+  strokeAdjust = gFalse; // make gcc happy
+  if (doStroke) {
+    strokeAdjust = splash->getStrokeAdjust();
+    splash->setStrokeAdjust(gFalse);
+  }
+
+  // fill and stroke
+  if (doFill && doStroke) {
+    if (path) {
+      setOverprintMask(state->getFillColorSpace(), state->getFillOverprint(),
+		       state->getOverprintMode(), state->getFillColor());
+      splash->fill(path, gFalse);
+      setOverprintMask(state->getStrokeColorSpace(),
+		       state->getStrokeOverprint(),
+		       state->getOverprintMode(),
+		       state->getStrokeColor());
+      splash->stroke(path);
+    }
+
+  // fill
+  } else if (doFill) {
+    setOverprintMask(state->getFillColorSpace(), state->getFillOverprint(),
+		     state->getOverprintMode(), state->getFillColor());
+    splash->fillChar((SplashCoord)x, (SplashCoord)y, code, font);
+
   // stroke
-  if ((render & 3) == 1 || (render & 3) == 2) {
-    if (!state->getStrokeColorSpace()->isNonMarking()) {
-      if ((path = font->getGlyphPath(code))) {
-	path->offset((SplashCoord)x, (SplashCoord)y);
-    setOverprintMask(state->getStrokeColorSpace(),
+  } else if (doStroke) {
+    if (path) {
+      setOverprintMask(state->getStrokeColorSpace(),
 		       state->getStrokeOverprint(),
 		       state->getOverprintMode(),
 		       state->getStrokeColor());
-	splash->stroke(path);
-	delete path;
-      }
+      splash->stroke(path);
     }
   }
 
   // clip
-  if (haveCSPattern || (render & 4)) {
-    if ((path = font->getGlyphPath(code))) {
-      path->offset((SplashCoord)x, (SplashCoord)y);
+  if (doClip) {
+    if (path) {
       if (textClipPath) {
 	textClipPath->append(path);
-	delete path;
       } else {
 	textClipPath = path;
+	path = NULL;
       }
     }
   }
+
+  if (doStroke) {
+    splash->setStrokeAdjust(strokeAdjust);
+  }
+
+  if (path) {
+    delete path;
+  }
 }
 
 GBool SplashOutputDev::beginType3Char(GfxState *state, double x, double y,
@@ -2261,9 +2171,20 @@ GBool SplashOutputDev::beginType3Char(GfxState *state, double x, double y,
   T3FontCache *t3Font;
   T3GlyphStack *t3gs;
   GBool validBBox;
+  double m[4];
+  GBool horiz;
   double x1, y1, xMin, yMin, xMax, yMax, xt, yt;
   int i, j;
 
+  if (skipHorizText || skipRotatedText) {
+    state->getFontTransMat(&m[0], &m[1], &m[2], &m[3]);
+    horiz = m[0] > 0 && fabs(m[1]) < 0.001 &&
+            fabs(m[2]) < 0.001 && m[3] < 0;
+    if ((skipHorizText && horiz) || (skipRotatedText && !horiz)) {
+      return gTrue;
+    }
+  }
+
   if (!(gfxFont = state->getFont())) {
     return gFalse;
   }
@@ -2345,10 +2266,10 @@ GBool SplashOutputDev::beginType3Char(GfxState *state, double x, double y,
 	validBBox = gTrue;
       }
       t3FontCache[0] = new T3FontCache(fontID, ctm[0], ctm[1], ctm[2], ctm[3],
-	                               (int)floor(xMin - xt),
-				       (int)floor(yMin - yt),
-				       (int)ceil(xMax) - (int)floor(xMin) + 3,
-				       (int)ceil(yMax) - (int)floor(yMin) + 3,
+	                               (int)floor(xMin - xt) - 2,
+				       (int)floor(yMin - yt) - 2,
+				       (int)ceil(xMax) - (int)floor(xMin) + 4,
+				       (int)ceil(yMax) - (int)floor(yMin) + 4,
 				       validBBox,
 				       colorMode != splashModeMono1);
     }
@@ -2377,6 +2298,8 @@ GBool SplashOutputDev::beginType3Char(GfxState *state, double x, double y,
   t3GlyphStack->cacheTag = NULL;
   t3GlyphStack->cacheData = NULL;
 
+  haveT3Dx = gFalse;
+
   return gFalse;
 }
 
@@ -2405,6 +2328,7 @@ void SplashOutputDev::endType3Char(GfxState *state) {
 }
 
 void SplashOutputDev::type3D0(GfxState *state, double wx, double wy) {
+  haveT3Dx = gTrue;
 }
 
 void SplashOutputDev::type3D1(GfxState *state, double wx, double wy,
@@ -2415,6 +2339,12 @@ void SplashOutputDev::type3D1(GfxState *state, double wx, double wy,
   double xt, yt, xMin, xMax, yMin, yMax, x1, y1;
   int i, j;
 
+  // ignore multiple d0/d1 operators
+  if (haveT3Dx) {
+    return;
+  }
+  haveT3Dx = gTrue;
+
   if (unlikely(t3GlyphStack == NULL)) {
     error(errSyntaxWarning, -1, "t3GlyphStack was null in SplashOutputDev::type3D1");
     return;
@@ -2511,7 +2441,7 @@ void SplashOutputDev::type3D1(GfxState *state, double wx, double wy,
 			t3GlyphStack->origSplash->getScreen());
     color[0] = 0;
     splash->clear(color);
-    color[0] = 1;
+    color[0] = 0xff;
   } else {
     bitmap = new SplashBitmap(t3Font->glyphW, t3Font->glyphH, 1,
 			      splashModeMono8, gFalse);
@@ -2521,6 +2451,7 @@ void SplashOutputDev::type3D1(GfxState *state, double wx, double wy,
     splash->clear(color);
     color[0] = 0xff;
   }
+  splash->setMinLineWidth(globalParams->getMinLineWidth());
   splash->setFillPattern(new SplashSolidColor(color));
   splash->setStrokePattern(new SplashSolidColor(color));
   //~ this should copy other state from t3GlyphStack->origSplash?
@@ -2547,25 +2478,9 @@ void SplashOutputDev::drawType3Glyph(GfxState *state, T3FontCache *t3Font,
 }
 
 void SplashOutputDev::beginTextObject(GfxState *state) {
-  if (!(state->getRender() & 4) && state->getFillColorSpace()->getMode() == csPattern) {
-    haveCSPattern = gTrue;
-    saveState(state);
-  }
 }
 
 void SplashOutputDev::endTextObject(GfxState *state) {
-  if (haveCSPattern) {
-    haveCSPattern = gFalse;
-    if (state->getFillColorSpace()->getMode() != csPattern) {
-      if (textClipPath) {
-        splash->fill(textClipPath, gTrue);
-        delete textClipPath;
-        textClipPath = NULL;
-      }
-      restoreState(state);
-      updateFillColor(state);
-    }
-  }
   if (textClipPath) {
     splash->clipToPath(textClipPath, gFalse);
     delete textClipPath;
@@ -2588,32 +2503,16 @@ GBool SplashOutputDev::imageMaskSrc(void *data, SplashColorPtr line) {
   if (imgMaskData->y == imgMaskData->height) {
     return gFalse;
   }
-  for (x = 0, p = imgMaskData->imgStr->getLine(), q = line;
-       x < imgMaskData->width;
-       ++x) {
+  if (!(p = imgMaskData->imgStr->getLine())) {
+    return gFalse;
+  }
+  for (x = 0, q = line; x < imgMaskData->width; ++x) {
     *q++ = *p++ ^ imgMaskData->invert;
   }
   ++imgMaskData->y;
   return gTrue;
 }
 
-void SplashOutputDev::endMaskClip(GfxState * state) {
-  double bbox[4] = {0,0,1,1}; // dummy
-  /* transfer mask to alpha channel! */
-  // memcpy(maskBitmap->getAlphaPtr(), maskBitmap->getDataPtr(), bitmap->getRowSize() * bitmap->getHeight());
-  // memset(maskBitmap->getDataPtr(), 0, bitmap->getRowSize() * bitmap->getHeight());
-  int c;
-  Guchar *dest = bitmap->getAlphaPtr();
-  Guchar *src = maskBitmap->getDataPtr();
-  for (c= 0; c < maskBitmap->getRowSize() * maskBitmap->getHeight(); c++) {
-    dest[c] = src[c];
-  }
-  delete maskBitmap;
-  maskBitmap = NULL;
-  endTransparencyGroup(state);
-  paintTransparencyGroup(state, bbox);
-}
-
 void SplashOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
 				    int width, int height, GBool invert,
 				    GBool interpolate, GBool inlineImg) {
@@ -2645,42 +2544,8 @@ void SplashOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
   imgMaskData.height = height;
   imgMaskData.y = 0;
 
-  if (state->getFillColorSpace()->getMode() == csPattern) {
-    Splash *maskSplash;
-    SplashColor maskColor;
-
-    /* from beginTransparencyGroup: */
-    // push a new stack entry
-    SplashTransparencyGroup *transpGroup = new SplashTransparencyGroup();
-    transpGroup->tx = 0;
-    transpGroup->ty = 0;
-    transpGroup->blendingColorSpace = NULL;
-    transpGroup->isolated = gFalse;
-    transpGroup->next = transpGroupStack;
-    transpGroupStack = transpGroup;
-    // save state
-    transpGroup->origBitmap = bitmap;
-    transpGroup->origSplash = splash;
-    //~ this ignores the blendingColorSpace arg
-    // create the temporary bitmap
-    bitmap = new SplashBitmap(bitmap->getWidth(), bitmap->getHeight(), bitmapRowPad, colorMode, gTrue,
-                              bitmapTopDown);
-    splash = new Splash(bitmap, vectorAntialias,
-                        transpGroup->origSplash->getScreen());
-    splash->blitTransparent(transpGroup->origBitmap, 0, 0, 0, 0, bitmap->getWidth(), bitmap->getHeight());
-    splash->setInNonIsolatedGroup(transpGroup->origBitmap, 0, 0);
-    transpGroup->tBitmap = bitmap;
-
-    maskBitmap = new SplashBitmap(bitmap->getWidth(), bitmap->getHeight(), 1, splashModeMono8, gFalse);
-    maskSplash = new Splash(maskBitmap, vectorAntialias);
-    maskColor[0] = 0;
-    maskSplash->clear(maskColor);
-    maskColor[0] = 0xff;
-    maskSplash->setFillPattern(new SplashSolidColor(maskColor));
-    maskSplash->fillImageMask(&imageMaskSrc, &imgMaskData,  width, height, mat, t3GlyphStack != NULL);
-    delete maskSplash;
-  } else {
-    splash->fillImageMask(&imageMaskSrc, &imgMaskData, width, height, mat, t3GlyphStack != NULL);
+  splash->fillImageMask(&imageMaskSrc, &imgMaskData, width, height, mat, t3GlyphStack != NULL);
+  if (inlineImg) {
     if (inlineImg) {
       while (imgMaskData.y < height) {
         imgMaskData.imgStr->getLine();
@@ -2693,6 +2558,89 @@ void SplashOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
   str->close();
 }
 
+void SplashOutputDev::setSoftMaskFromImageMask(GfxState *state,
+					       Object *ref, Stream *str,
+					       int width, int height,
+					       GBool invert,
+					       GBool inlineImg) {
+  double *ctm;
+  SplashCoord mat[6];
+  SplashOutImageMaskData imgMaskData;
+  Splash *maskSplash;
+  SplashColor maskColor;
+
+  if (state->getFillColorSpace()->isNonMarking()) {
+    return;
+  }
+
+  ctm = state->getCTM();
+  for (int i = 0; i < 6; ++i) {
+    if (!isfinite(ctm[i])) return;
+  }
+  mat[0] = ctm[0];
+  mat[1] = ctm[1];
+  mat[2] = -ctm[2];
+  mat[3] = -ctm[3];
+  mat[4] = ctm[2] + ctm[4];
+  mat[5] = ctm[3] + ctm[5];
+  imgMaskData.imgStr = new ImageStream(str, width, 1, 1);
+  imgMaskData.imgStr->reset();
+  imgMaskData.invert = invert ? 0 : 1;
+  imgMaskData.width = width;
+  imgMaskData.height = height;
+  imgMaskData.y = 0;
+
+  /* from beginTransparencyGroup: */
+  // push a new stack entry
+  SplashTransparencyGroup *transpGroup = new SplashTransparencyGroup();
+  transpGroup->tx = 0;
+  transpGroup->ty = 0;
+  transpGroup->blendingColorSpace = NULL;
+  transpGroup->isolated = gFalse;
+  transpGroup->next = transpGroupStack;
+  transpGroupStack = transpGroup;
+  // save state
+  transpGroup->origBitmap = bitmap;
+  transpGroup->origSplash = splash;
+  //~ this ignores the blendingColorSpace arg
+  // create the temporary bitmap
+  bitmap = new SplashBitmap(bitmap->getWidth(), bitmap->getHeight(), bitmapRowPad, colorMode, gTrue,
+                            bitmapTopDown);
+  splash = new Splash(bitmap, vectorAntialias,
+                      transpGroup->origSplash->getScreen());
+  splash->blitTransparent(transpGroup->origBitmap, 0, 0, 0, 0, bitmap->getWidth(), bitmap->getHeight());
+  splash->setInNonIsolatedGroup(transpGroup->origBitmap, 0, 0);
+  transpGroup->tBitmap = bitmap;
+
+  maskBitmap = new SplashBitmap(bitmap->getWidth(), bitmap->getHeight(), 1, splashModeMono8, gFalse);
+  maskSplash = new Splash(maskBitmap, vectorAntialias);
+  maskColor[0] = 0;
+  maskSplash->clear(maskColor);
+  maskColor[0] = 0xff;
+  maskSplash->setFillPattern(new SplashSolidColor(maskColor));
+  maskSplash->fillImageMask(&imageMaskSrc, &imgMaskData,  width, height, mat, t3GlyphStack != NULL);
+  delete maskSplash;
+  delete imgMaskData.imgStr;
+  str->close();
+}
+
+void SplashOutputDev::unsetSoftMaskFromImageMask(GfxState *state) {
+  double bbox[4] = {0,0,1,1}; // dummy
+
+  /* transfer mask to alpha channel! */
+  // memcpy(maskBitmap->getAlphaPtr(), maskBitmap->getDataPtr(), bitmap->getRowSize() * bitmap->getHeight());
+  // memset(maskBitmap->getDataPtr(), 0, bitmap->getRowSize() * bitmap->getHeight());
+  Guchar *dest = bitmap->getAlphaPtr();
+  Guchar *src = maskBitmap->getDataPtr();
+  for (int c= 0; c < maskBitmap->getRowSize() * maskBitmap->getHeight(); c++) {
+    dest[c] = src[c];
+  }
+  delete maskBitmap;
+  maskBitmap = NULL;
+  endTransparencyGroup(state);
+  paintTransparencyGroup(state, bbox);
+}
+
 struct SplashOutImageData {
   ImageStream *imgStr;
   GfxImageColorMap *colorMap;
@@ -2707,6 +2655,7 @@ GBool SplashOutputDev::imageSrc(void *data, SplashColorPtr colorLine,
   SplashOutImageData *imgData = (SplashOutImageData *)data;
   Guchar *p;
   SplashColorPtr q, col;
+  GfxRGB rgb;
   GfxGray gray;
 #if SPLASH_CMYK
   GfxCMYK cmyk;
@@ -2716,34 +2665,31 @@ GBool SplashOutputDev::imageSrc(void *data, SplashColorPtr colorLine,
   if (imgData->y == imgData->height) {
     return gFalse;
   }
+  if (!(p = imgData->imgStr->getLine())) {
+    return gFalse;
+  }
 
   nComps = imgData->colorMap->getNumPixelComps();
 
   if (imgData->lookup) {
     switch (imgData->colorMode) {
-  case splashModeMono1:
-  case splashModeMono8:
-      for (x = 0, p = imgData->imgStr->getLine(), q = colorLine;
-	   x < imgData->width;
-	   ++x, ++p) {
+    case splashModeMono1:
+    case splashModeMono8:
+      for (x = 0, q = colorLine; x < imgData->width; ++x, ++p) {
 	*q++ = imgData->lookup[*p];
       }
-    break;
-  case splashModeRGB8:
-  case splashModeBGR8:
-      for (x = 0, p = imgData->imgStr->getLine(), q = colorLine;
-	   x < imgData->width;
-	   ++x, ++p) {
+      break;
+    case splashModeRGB8:
+    case splashModeBGR8:
+      for (x = 0, q = colorLine; x < imgData->width; ++x, ++p) {
 	col = &imgData->lookup[3 * *p];
 	*q++ = col[0];
 	*q++ = col[1];
 	*q++ = col[2];
       }
-    break;
+      break;
   case splashModeXBGR8:
-      for (x = 0, p = imgData->imgStr->getLine(), q = colorLine;
-	   x < imgData->width;
-	   ++x, ++p) {
+      for (x = 0, q = colorLine; x < imgData->width; ++x, ++p) {
 	col = &imgData->lookup[4 * *p];
 	*q++ = col[0];
 	*q++ = col[1];
@@ -2753,9 +2699,7 @@ GBool SplashOutputDev::imageSrc(void *data, SplashColorPtr colorLine,
       break;
 #if SPLASH_CMYK
     case splashModeCMYK8:
-      for (x = 0, p = imgData->imgStr->getLine(), q = colorLine;
-	   x < imgData->width;
-	   ++x, ++p) {
+      for (x = 0, q = colorLine; x < imgData->width; ++x, ++p) {
 	col = &imgData->lookup[4 * *p];
 	*q++ = col[0];
 	*q++ = col[1];
@@ -2764,36 +2708,37 @@ GBool SplashOutputDev::imageSrc(void *data, SplashColorPtr colorLine,
       }
       break;
 #endif
-  }
+    }
   } else {
     switch (imgData->colorMode) {
     case splashModeMono1:
     case splashModeMono8:
-      for (x = 0, p = imgData->imgStr->getLine(), q = colorLine;
-	   x < imgData->width;
-	   ++x, p += nComps) {
+      for (x = 0, q = colorLine; x < imgData->width; ++x, p += nComps) {
 	imgData->colorMap->getGray(p, &gray);
 	*q++ = colToByte(gray);
       }
       break;
     case splashModeRGB8:
     case splashModeBGR8:
-      p = imgData->imgStr->getLine();
-      q = colorLine;
-
-      imgData->colorMap->getRGBLine(p, colorLine, imgData->width);
+      for (x = 0, q = colorLine; x < imgData->width; ++x, p += nComps) {
+	imgData->colorMap->getRGB(p, &rgb);
+	*q++ = colToByte(rgb.r);
+	*q++ = colToByte(rgb.g);
+	*q++ = colToByte(rgb.b);
+      }
       break;
     case splashModeXBGR8:
-      p = imgData->imgStr->getLine();
-      q = colorLine;
-
-      imgData->colorMap->getRGBXLine(p, colorLine, imgData->width);
+      for (x = 0, q = colorLine; x < imgData->width; ++x, p += nComps) {
+	imgData->colorMap->getRGB(p, &rgb);
+	*q++ = colToByte(rgb.r);
+	*q++ = colToByte(rgb.g);
+	*q++ = colToByte(rgb.b);
+	*q++ = 255;
+      }
       break;
 #if SPLASH_CMYK
     case splashModeCMYK8:
-      for (x = 0, p = imgData->imgStr->getLine(), q = colorLine;
-	   x < imgData->width;
-	   ++x, p += nComps) {
+      for (x = 0, q = colorLine; x < imgData->width; ++x, p += nComps) {
 	imgData->colorMap->getCMYK(p, &cmyk);
 	*q++ = colToByte(cmyk.c);
 	*q++ = colToByte(cmyk.m);
@@ -2825,10 +2770,13 @@ GBool SplashOutputDev::alphaImageSrc(void *data, SplashColorPtr colorLine,
   if (imgData->y == imgData->height) {
     return gFalse;
   }
+  if (!(p = imgData->imgStr->getLine())) {
+    return gFalse;
+  }
 
   nComps = imgData->colorMap->getNumPixelComps();
 
-  for (x = 0, p = imgData->imgStr->getLine(), q = colorLine, aq = alphaLine;
+  for (x = 0, q = colorLine, aq = alphaLine;
        x < imgData->width;
        ++x, p += nComps) {
     alpha = 0;
@@ -2844,7 +2792,6 @@ GBool SplashOutputDev::alphaImageSrc(void *data, SplashColorPtr colorLine,
       case splashModeMono1:
       case splashModeMono8:
 	*q++ = imgData->lookup[*p];
-	*aq++ = alpha;
 	break;
       case splashModeRGB8:
       case splashModeBGR8:
@@ -2852,7 +2799,6 @@ GBool SplashOutputDev::alphaImageSrc(void *data, SplashColorPtr colorLine,
 	*q++ = col[0];
 	*q++ = col[1];
 	*q++ = col[2];
-	*aq++ = alpha;
 	break;
       case splashModeXBGR8:
 	col = &imgData->lookup[4 * *p];
@@ -2860,7 +2806,6 @@ GBool SplashOutputDev::alphaImageSrc(void *data, SplashColorPtr colorLine,
 	*q++ = col[1];
 	*q++ = col[2];
 	*q++ = 255;
-	*aq++ = alpha;
 	break;
 #if SPLASH_CMYK
       case splashModeCMYK8:
@@ -2869,17 +2814,16 @@ GBool SplashOutputDev::alphaImageSrc(void *data, SplashColorPtr colorLine,
 	*q++ = col[1];
 	*q++ = col[2];
 	*q++ = col[3];
-	*aq++ = alpha;
 	break;
 #endif
       }
+      *aq++ = alpha;
     } else {
       switch (imgData->colorMode) {
       case splashModeMono1:
       case splashModeMono8:
 	imgData->colorMap->getGray(p, &gray);
 	*q++ = colToByte(gray);
-	*aq++ = alpha;
 	break;
       case splashModeXBGR8:
       case splashModeRGB8:
@@ -2889,7 +2833,6 @@ GBool SplashOutputDev::alphaImageSrc(void *data, SplashColorPtr colorLine,
 	*q++ = colToByte(rgb.g);
 	*q++ = colToByte(rgb.b);
 	if (imgData->colorMode == splashModeXBGR8) *q++ = 255;
-	*aq++ = alpha;
 	break;
 #if SPLASH_CMYK
       case splashModeCMYK8:
@@ -2898,10 +2841,10 @@ GBool SplashOutputDev::alphaImageSrc(void *data, SplashColorPtr colorLine,
 	*q++ = colToByte(cmyk.m);
 	*q++ = colToByte(cmyk.y);
 	*q++ = colToByte(cmyk.k);
-	*aq++ = alpha;
 	break;
 #endif
       }
+      *aq++ = alpha;
     }
   }
 
@@ -2939,13 +2882,13 @@ GBool SplashOutputDev::tilingBitmapSrc(void *data, SplashColorPtr colorLine,
       for (int m = 0; m < imgData->repeatX; m++) {
         for (int x = 0; x < imgData->bitmap->getWidth(); x++) {
           imgData->bitmap->getPixel(x, imgData->y, q);
-          q += splashColorModeNComps[cMode]; 
+          q += splashColorModeNComps[cMode];
         }
       }
     } else {
       const int n = imgData->bitmap->getRowSize();
       SplashColorPtr p;
-      for (int m = 0; m < imgData->repeatX; m++) {   
+      for (int m = 0; m < imgData->repeatX; m++) {
         p = imgData->bitmap->getDataPtr() + imgData->y * imgData->bitmap->getRowSize();
         for (int x = 0; x < n; ++x) {
           *q++ = *p++;
@@ -2972,7 +2915,7 @@ GBool SplashOutputDev::tilingBitmapSrc(void *data, SplashColorPtr colorLine,
     for (int m = 0; m < imgData->repeatX; m++) {
       for (int x = 0; x < imgData->bitmap->getWidth(); x++) {
         imgData->bitmap->getPixel(x, imgData->y, col);
-        imgData->pattern->getColor(x, imgData->y, pat); 
+        imgData->pattern->getColor(x, imgData->y, pat);
         for (int i = 0; i < splashColorModeNComps[imgData->colorMode]; ++i) {
 #if SPLASH_CMYK
           if (imgData->colorMode == splashModeCMYK8)
@@ -2981,11 +2924,11 @@ GBool SplashOutputDev::tilingBitmapSrc(void *data, SplashColorPtr colorLine,
 #endif
             dest[i] = 255 - div255((255 - pat[i]) * (255 - col[0]));
         }
-        dest += splashColorModeNComps[imgData->colorMode]; 
+        dest += splashColorModeNComps[imgData->colorMode];
       }
     }
     if (alphaLine != NULL) {
-      const int y = (imgData->y == imgData->bitmap->getHeight() - 1 && imgData->y > 50) ? imgData->y - 1 : imgData->y; 
+      const int y = (imgData->y == imgData->bitmap->getHeight() - 1 && imgData->y > 50) ? imgData->y - 1 : imgData->y;
       SplashColorPtr aq = alphaLine;
       SplashColorPtr p;
       const int n = imgData->bitmap->getWidth();
@@ -3010,8 +2953,6 @@ void SplashOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
   SplashCoord mat[6];
   SplashOutImageData imgData;
   SplashColorMode srcMode;
-  SplashColor defaultColor;
-  GfxColor srcColor;
   SplashImageSource src;
   GfxGray gray;
   GfxRGB rgb;
@@ -3024,6 +2965,7 @@ void SplashOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
 
   setOverprintMask(colorMap->getColorSpace(), state->getFillOverprint(),
 		   state->getOverprintMode(), NULL);
+
   ctm = state->getCTM();
   for (i = 0; i < 6; ++i) {
     if (!isfinite(ctm[i])) return;
@@ -3106,7 +3048,6 @@ void SplashOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
 #endif
       break;
 #endif
-      break;
     }
   }
 
@@ -3116,12 +3057,8 @@ void SplashOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
     srcMode = colorMode;
   }
   src = maskColors ? &alphaImageSrc : &imageSrc;
-  colorMap->getColorSpace()->getDefaultColor(&srcColor);
-  convertGfxColor(defaultColor, srcMode, colorMap->getColorSpace(), &srcColor);
-  SplashPattern *pattern = createOverprintPattern(colorMap->getColorSpace(), defaultColor, !grayIndexed);
   splash->drawImage(src, &imgData, srcMode, maskColors ? gTrue : gFalse,
-	  width, height, mat, pattern);
-  delete pattern;
+		    width, height, mat);
   if (inlineImg) {
     while (imgData.y < height) {
       imgData.imgStr->getLine();
@@ -3147,7 +3084,6 @@ GBool SplashOutputDev::maskedImageSrc(void *data, SplashColorPtr colorLine,
 				      Guchar *alphaLine) {
   SplashOutMaskedImageData *imgData = (SplashOutMaskedImageData *)data;
   Guchar *p, *aq;
-  SplashColor maskColor;
   SplashColorPtr q, col;
   GfxRGB rgb;
   GfxGray gray;
@@ -3155,25 +3091,35 @@ GBool SplashOutputDev::maskedImageSrc(void *data, SplashColorPtr colorLine,
   GfxCMYK cmyk;
 #endif
   Guchar alpha;
+  Guchar *maskPtr;
+  int maskBit;
   int nComps, x;
 
   if (imgData->y == imgData->height) {
     return gFalse;
   }
+  if (!(p = imgData->imgStr->getLine())) {
+    return gFalse;
+  }
 
   nComps = imgData->colorMap->getNumPixelComps();
 
-  for (x = 0, p = imgData->imgStr->getLine(), q = colorLine, aq = alphaLine;
+  maskPtr = imgData->mask->getDataPtr() +
+              imgData->y * imgData->mask->getRowSize();
+  maskBit = 0x80;
+  for (x = 0, q = colorLine, aq = alphaLine;
        x < imgData->width;
        ++x, p += nComps) {
-    imgData->mask->getPixel(x, imgData->y, maskColor);
-    alpha = maskColor[0] ? 0xff : 0x00;
+    alpha = (*maskPtr & maskBit) ? 0xff : 0x00;
+    if (!(maskBit >>= 1)) {
+      ++maskPtr;
+      maskBit = 0x80;
+    }
     if (imgData->lookup) {
       switch (imgData->colorMode) {
       case splashModeMono1:
       case splashModeMono8:
 	*q++ = imgData->lookup[*p];
-	*aq++ = alpha;
 	break;
       case splashModeRGB8:
       case splashModeBGR8:
@@ -3181,7 +3127,6 @@ GBool SplashOutputDev::maskedImageSrc(void *data, SplashColorPtr colorLine,
 	*q++ = col[0];
 	*q++ = col[1];
 	*q++ = col[2];
-	*aq++ = alpha;
 	break;
       case splashModeXBGR8:
 	col = &imgData->lookup[4 * *p];
@@ -3189,7 +3134,6 @@ GBool SplashOutputDev::maskedImageSrc(void *data, SplashColorPtr colorLine,
 	*q++ = col[1];
 	*q++ = col[2];
 	*q++ = 255;
-	*aq++ = alpha;
 	break;
 #if SPLASH_CMYK
       case splashModeCMYK8:
@@ -3198,17 +3142,16 @@ GBool SplashOutputDev::maskedImageSrc(void *data, SplashColorPtr colorLine,
 	*q++ = col[1];
 	*q++ = col[2];
 	*q++ = col[3];
-	*aq++ = alpha;
 	break;
 #endif
       }
+      *aq++ = alpha;
     } else {
       switch (imgData->colorMode) {
       case splashModeMono1:
       case splashModeMono8:
 	imgData->colorMap->getGray(p, &gray);
 	*q++ = colToByte(gray);
-	*aq++ = alpha;
 	break;
       case splashModeXBGR8:
       case splashModeRGB8:
@@ -3218,7 +3161,6 @@ GBool SplashOutputDev::maskedImageSrc(void *data, SplashColorPtr colorLine,
 	*q++ = colToByte(rgb.g);
 	*q++ = colToByte(rgb.b);
 	if (imgData->colorMode == splashModeXBGR8) *q++ = 255;
-	*aq++ = alpha;
 	break;
 #if SPLASH_CMYK
       case splashModeCMYK8:
@@ -3227,10 +3169,10 @@ GBool SplashOutputDev::maskedImageSrc(void *data, SplashColorPtr colorLine,
 	*q++ = colToByte(cmyk.m);
 	*q++ = colToByte(cmyk.y);
 	*q++ = colToByte(cmyk.k);
-	*aq++ = alpha;
 	break;
 #endif
       }
+      *aq++ = alpha;
     }
   }
 
@@ -3265,6 +3207,7 @@ void SplashOutputDev::drawMaskedImage(GfxState *state, Object *ref,
 
   setOverprintMask(colorMap->getColorSpace(), state->getFillOverprint(),
 		   state->getOverprintMode(), NULL);
+
   // If the mask is higher resolution than the image, use
   // drawSoftMaskedImage() instead.
   if (maskWidth > width || maskHeight > height) {
@@ -3281,9 +3224,6 @@ void SplashOutputDev::drawMaskedImage(GfxState *state, Object *ref,
     delete maskColorMap;
 
   } else {
-    SplashColor defaultColor;
-    GfxColor srcColor;
-
     //----- scale the mask image to the same size as the source image
 
     mat[0] = (SplashCoord)width;
@@ -3395,12 +3335,8 @@ void SplashOutputDev::drawMaskedImage(GfxState *state, Object *ref,
     } else {
       srcMode = colorMode;
     }
-    colorMap->getColorSpace()->getDefaultColor(&srcColor);
-    convertGfxColor(defaultColor, srcMode, colorMap->getColorSpace(), &srcColor);
-    SplashPattern *pattern = createOverprintPattern(colorMap->getColorSpace(), defaultColor, gTrue);
     splash->drawImage(&maskedImageSrc, &imgData, srcMode, gTrue,
-		      width, height, mat, pattern);
-    delete pattern;
+		      width, height, mat);
     delete maskBitmap;
     gfree(imgData.lookup);
     delete imgData.imgStr;
@@ -3424,8 +3360,6 @@ void SplashOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref,
   SplashBitmap *maskBitmap;
   Splash *maskSplash;
   SplashColor maskColor;
-  SplashColor defaultColor;
-  GfxColor srcColor;
   GfxGray gray;
   GfxRGB rgb;
 #if SPLASH_CMYK
@@ -3436,6 +3370,7 @@ void SplashOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref,
 
   setOverprintMask(colorMap->getColorSpace(), state->getFillOverprint(),
 		   state->getOverprintMode(), NULL);
+
   ctm = state->getCTM();
   for (i = 0; i < 6; ++i) {
     if (!isfinite(ctm[i])) return;
@@ -3550,12 +3485,7 @@ void SplashOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref,
   } else {
     srcMode = colorMode;
   }
-
-  colorMap->getColorSpace()->getDefaultColor(&srcColor);
-  convertGfxColor(defaultColor, srcMode, colorMap->getColorSpace(), &srcColor);
-  SplashPattern *pattern = createOverprintPattern(colorMap->getColorSpace(), defaultColor, gTrue);
-  splash->drawImage(&imageSrc, &imgData, srcMode, gFalse, width, height, mat, pattern);
-  delete pattern;
+  splash->drawImage(&imageSrc, &imgData, srcMode, gFalse, width, height, mat);
   splash->setSoftMask(NULL);
   gfree(imgData.lookup);
   delete imgData.imgStr;
@@ -3564,12 +3494,12 @@ void SplashOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref,
 
 void SplashOutputDev::beginTransparencyGroup(GfxState *state, double *bbox,
 					     GfxColorSpace *blendingColorSpace,
-					     GBool isolated, GBool /*knockout*/,
-					     GBool /*forSoftMask*/) {
+					     GBool isolated, GBool knockout,
+					     GBool forSoftMask) {
   SplashTransparencyGroup *transpGroup;
   SplashColor color;
   double xMin, yMin, xMax, yMax, x, y;
-  int tx, ty, w, h;
+  int tx, ty, w, h, i;
 
   // transform the bbox
   state->transform(bbox[0], bbox[1], &x, &y);
@@ -3648,34 +3578,49 @@ void SplashOutputDev::beginTransparencyGroup(GfxState *state, double *bbox,
   transpGroup->origBitmap = bitmap;
   transpGroup->origSplash = splash;
 
-  //~ this ignores the blendingColorSpace arg
+  //~ this handles the blendingColorSpace arg for soft masks, but
+  //~   not yet for transparency groups
+
+  // switch to the blending color space
+  if (forSoftMask && isolated && blendingColorSpace) {
+    if (blendingColorSpace->getMode() == csDeviceGray ||
+	blendingColorSpace->getMode() == csCalGray ||
+	(blendingColorSpace->getMode() == csICCBased &&
+	 blendingColorSpace->getNComps() == 1)) {
+      colorMode = splashModeMono8;
+    } else if (blendingColorSpace->getMode() == csDeviceRGB ||
+	       blendingColorSpace->getMode() == csCalRGB ||
+	       (blendingColorSpace->getMode() == csICCBased &&
+		blendingColorSpace->getNComps() == 3)) {
+      //~ does this need to use BGR8?
+      colorMode = splashModeRGB8;
+#if SPLASH_CMYK
+    } else if (blendingColorSpace->getMode() == csDeviceCMYK ||
+	       (blendingColorSpace->getMode() == csICCBased &&
+		blendingColorSpace->getNComps() == 4)) {
+      colorMode = splashModeCMYK8;
+#endif
+    }
+  }
 
   // create the temporary bitmap
   bitmap = new SplashBitmap(w, h, bitmapRowPad, colorMode, gTrue,
 			    bitmapTopDown);
   splash = new Splash(bitmap, vectorAntialias,
 		      transpGroup->origSplash->getScreen());
+  splash->setMinLineWidth(globalParams->getMinLineWidth());
+  //~ Acrobat apparently copies at least the fill and stroke colors, and
+  //~ maybe other state(?) -- but not the clipping path (and not sure
+  //~ what else)
+  //~ [this is likely the same situation as in type3D1()]
+  splash->setFillPattern(transpGroup->origSplash->getFillPattern()->copy());
+  splash->setStrokePattern(
+		         transpGroup->origSplash->getStrokePattern()->copy());
   if (isolated) {
-    switch (colorMode) {
-    case splashModeMono1:
-    case splashModeMono8:
-      color[0] = 0;
-      break;
-    case splashModeXBGR8:
-      color[3] = 255;
-    case splashModeRGB8:
-    case splashModeBGR8:
-      color[0] = color[1] = color[2] = 0;
-      break;
-#if SPLASH_CMYK
-    case splashModeCMYK8:
-      color[0] = color[1] = color[2] = color[3] = 0;
-      break;
-#endif
-    default:
-      // make gcc happy
-      break;
+    for (i = 0; i < splashMaxColorComps; ++i) {
+      color[i] = 0;
     }
+    if (colorMode == splashModeXBGR8) color[3] = 255;
     splash->clear(color, 0);
   } else {
     splash->blitTransparent(transpGroup->origBitmap, tx, ty, 0, 0, w, h);
@@ -3692,12 +3637,13 @@ void SplashOutputDev::endTransparencyGroup(GfxState *state) {
   --nestCount;
   delete splash;
   bitmap = transpGroupStack->origBitmap;
+  colorMode = bitmap->getMode();
   splash = transpGroupStack->origSplash;
   state->shiftCTM(transpGroupStack->tx, transpGroupStack->ty);
   updateCTM(state, 0, 0, 0, 0, 0, 0);
 }
 
-void SplashOutputDev::paintTransparencyGroup(GfxState * /*state*/, double * /*bbox*/) {
+void SplashOutputDev::paintTransparencyGroup(GfxState *state, double *bbox) {
   SplashBitmap *tBitmap;
   SplashTransparencyGroup *transpGroup;
   GBool isolated;
@@ -3710,10 +3656,12 @@ void SplashOutputDev::paintTransparencyGroup(GfxState * /*state*/, double * /*bb
 
   // paint the transparency group onto the parent bitmap
   // - the clip path was set in the parent's state)
-  splash->setOverprintMask(0xffffffff);
-  splash->composite(tBitmap, 0, 0, tx, ty,
-		    tBitmap->getWidth(), tBitmap->getHeight(),
-		    gFalse, !isolated);
+  if (tx < bitmap->getWidth() && ty < bitmap->getHeight()) {
+    splash->setOverprintMask(0xffffffff);
+    splash->composite(tBitmap, 0, 0, tx, ty,
+		      tBitmap->getWidth(), tBitmap->getHeight(),
+		      gFalse, !isolated);
+  }
 
   // pop the stack
   transpGroup = transpGroupStack;
@@ -3723,7 +3671,7 @@ void SplashOutputDev::paintTransparencyGroup(GfxState * /*state*/, double * /*bb
   delete tBitmap;
 }
 
-void SplashOutputDev::setSoftMask(GfxState * /*state*/, double * /*bbox*/,
+void SplashOutputDev::setSoftMask(GfxState *state, double *bbox,
 				  GBool alpha, Function *transferFunc,
 				  GfxColor *backdropColor) {
   SplashBitmap *softMask, *tBitmap;
@@ -3744,13 +3692,13 @@ void SplashOutputDev::setSoftMask(GfxState * /*state*/, double * /*bbox*/,
   tBitmap = transpGroupStack->tBitmap;
 
   // composite with backdrop color
-  if (!alpha && colorMode != splashModeMono1) {
+  if (!alpha && tBitmap->getMode() != splashModeMono1) {
     //~ need to correctly handle the case where no blending color
     //~ space is given
     if (transpGroupStack->blendingColorSpace) {
       tSplash = new Splash(tBitmap, vectorAntialias,
 			   transpGroupStack->origSplash->getScreen());
-      switch (colorMode) {
+      switch (tBitmap->getMode()) {
       case splashModeMono1:
 	// transparency is not supported in mono1 mode
 	break;
@@ -3803,30 +3751,30 @@ void SplashOutputDev::setSoftMask(GfxState * /*state*/, double * /*bbox*/,
       if (alpha) {
 	p[x] = tBitmap->getAlpha(x, y);
       } else {
-	tBitmap->getPixel(x, y, color);
-	// convert to luminosity
-	switch (colorMode) {
-	case splashModeMono1:
-	case splashModeMono8:
-	  lum = color[0] / 255.0;
-	  break;
-	case splashModeXBGR8:
-	case splashModeRGB8:
-	case splashModeBGR8:
-	  lum = (0.3 / 255.0) * color[0] +
-	        (0.59 / 255.0) * color[1] +
-	        (0.11 / 255.0) * color[2];
-	  break;
+	  tBitmap->getPixel(x, y, color);
+	  // convert to luminosity
+	  switch (tBitmap->getMode()) {
+	  case splashModeMono1:
+	  case splashModeMono8:
+	    lum = color[0] / 255.0;
+	    break;
+	  case splashModeXBGR8:
+	  case splashModeRGB8:
+	  case splashModeBGR8:
+	    lum = (0.3 / 255.0) * color[0] +
+	          (0.59 / 255.0) * color[1] +
+	          (0.11 / 255.0) * color[2];
+	    break;
 #if SPLASH_CMYK
-	case splashModeCMYK8:
-	  lum = (1 - color[3] / 255.0)
-	        - (0.3 / 255.0) * color[0]
-	        - (0.59 / 255.0) * color[1]
-	        - (0.11 / 255.0) * color[2];
-	  if (lum < 0) {
-	    lum = 0;
-	  }
-	  break;
+	  case splashModeCMYK8:
+	    lum = (1 - color[3] / 255.0)
+	          - (0.3 / 255.0) * color[0]
+	          - (0.59 / 255.0) * color[1]
+	          - (0.11 / 255.0) * color[2];
+	    if (lum < 0) {
+	      lum = 0;
+	    }
+	    break;
 #endif
 	}
 	if (transferFunc) {
@@ -3837,7 +3785,7 @@ void SplashOutputDev::setSoftMask(GfxState * /*state*/, double * /*bbox*/,
 	p[x] = (int)(lum2 * 255.0 + 0.5);
       }
     }
-    p += softMask->getRowSize();
+	p += softMask->getRowSize();
   }
   splash->setSoftMask(softMask);
 
@@ -3849,7 +3797,7 @@ void SplashOutputDev::setSoftMask(GfxState * /*state*/, double * /*bbox*/,
   delete tBitmap;
 }
 
-void SplashOutputDev::clearSoftMask(GfxState * /*state*/) {
+void SplashOutputDev::clearSoftMask(GfxState *state) {
   splash->setSoftMask(NULL);
 }
 
@@ -4024,6 +3972,7 @@ GBool SplashOutputDev::tilingPatternFill(GfxState *state, Gfx *gfx1, Catalog *ca
 #endif
   }
   splash = new Splash(bitmap, gTrue);
+  splash->setMinLineWidth(globalParams->getMinLineWidth());
 
   box.x1 = bbox[0]; box.y1 = bbox[1];
   box.x2 = bbox[2]; box.y2 = bbox[3];
diff --git a/poppler/SplashOutputDev.h b/poppler/SplashOutputDev.h
index 8975c65..c190203 100644
--- a/poppler/SplashOutputDev.h
+++ b/poppler/SplashOutputDev.h
@@ -53,33 +53,6 @@ struct T3GlyphStack;
 struct SplashTransparencyGroup;
 
 //------------------------------------------------------------------------
-// SplashOverprintColor
-//------------------------------------------------------------------------
-
-class SplashOverprintColor: public SplashPattern {
-public:
-  SplashOverprintColor(GfxColorSpace *colorSpace, SplashColorPtr colorA, Guchar tolerance);
-
-  virtual SplashPattern *copy() { return new SplashOverprintColor(colorSpace, color, tolerance); }
-
-  virtual ~SplashOverprintColor();
-
-  virtual GBool getColor(int x, int y, SplashColorPtr c);
-
-  virtual GBool testPosition(int x, int y) { return gFalse; }
-
-  virtual GBool isStatic() { return gTrue; }
-
-  virtual void overprint(GBool op, Guchar alphaSrc, SplashColorPtr colorSrc, 
-                         Guchar alphaDest, SplashColorPtr colorDest, SplashColorPtr colorResult);
-
-private:
-  GfxColorSpace *colorSpace;
-  SplashColor color;
-  Guchar tolerance;
-};
-
-//------------------------------------------------------------------------
 // Splash dynamic pattern
 //------------------------------------------------------------------------
 
@@ -119,13 +92,9 @@ public:
 
   virtual GBool getParameter(double xs, double ys, double *t);
 
-  virtual void overprint(GBool op, Guchar alphaSrc, SplashColorPtr colorSrc, 
-                         Guchar alphaDest, SplashColorPtr colorDest, SplashColorPtr colorResult);
-
 private:
   double x0, y0, x1, y1;
   double dx, dy, mul;
-  SplashOverprintColor *opPattern;
 };
 
 // see GfxState.h, GfxGouraudTriangleShading
@@ -153,13 +122,10 @@ public:
 
   virtual void getParameterizedColor(double t, SplashColorMode mode, SplashColorPtr c);
 
-  virtual void overprint(GBool op, Guchar alphaSrc, SplashColorPtr colorSrc, 
-                         Guchar alphaDest, SplashColorPtr colorDest, SplashColorPtr colorResult);
 private:
   GfxGouraudTriangleShading *shading;
   GfxState *state;
   GBool bDirectColorTranslation;
-  SplashOverprintColor *opPattern;
   SplashColorMode mode;
 };
 
@@ -175,13 +141,9 @@ public:
 
   virtual GBool getParameter(double xs, double ys, double *t);
 
-  virtual void overprint(GBool op, Guchar alphaSrc, SplashColorPtr colorSrc, 
-                         Guchar alphaDest, SplashColorPtr colorDest, SplashColorPtr colorResult);
-
 private:
   double x0, y0, r0, dx, dy, dr;
   double a, inva;
-  SplashOverprintColor *opPattern;
 };
 
 //------------------------------------------------------------------------
@@ -220,7 +182,7 @@ public:
 
   // Does this device use upside-down coordinates?
   // (Upside-down means (0,0) is the top left corner of the page.)
-  virtual GBool upsideDown() { return gTrue; }
+  virtual GBool upsideDown() { return bitmapTopDown ^ bitmapUpsideDown; }
 
   // Does this device use drawChar() or drawString()?
   virtual GBool useDrawChar() { return gTrue; }
@@ -229,10 +191,6 @@ public:
   // text in Type 3 fonts will be drawn with drawChar/drawString.
   virtual GBool interpretType3Chars() { return gTrue; }
 
-  // This device now supports text in pattern colorspace!
-  virtual GBool supportTextCSPattern(GfxState *state)
-  	{ return state->getFillColorSpace()->getMode() == csPattern; }
-
   //----- initialization and control
 
   // Start a page.
@@ -297,13 +255,18 @@ public:
 			       CharCode code, Unicode *u, int uLen);
   virtual void endType3Char(GfxState *state);
   virtual void beginTextObject(GfxState *state);
-  virtual GBool deviceHasTextClip(GfxState *state) { return textClipPath && haveCSPattern; }
+  virtual GBool deviceHasTextClip(GfxState *state) { return textClipPath; }
   virtual void endTextObject(GfxState *state);
 
   //----- image drawing
   virtual void drawImageMask(GfxState *state, Object *ref, Stream *str,
 			     int width, int height, GBool invert,
 			     GBool interpolate, GBool inlineImg);
+  virtual void setSoftMaskFromImageMask(GfxState *state,
+					Object *ref, Stream *str,
+					int width, int height, GBool invert,
+					GBool inlineImg);
+  virtual void unsetSoftMaskFromImageMask(GfxState *state);
   virtual void drawImage(GfxState *state, Object *ref, Stream *str,
 			 int width, int height, GfxImageColorMap *colorMap,
 			 GBool interpolate, int *maskColors, GBool inlineImg);
@@ -321,12 +284,6 @@ public:
 				   int maskWidth, int maskHeight,
 				   GfxImageColorMap *maskColorMap,
 				   GBool maskInterpolate);
-  // If current colorspace ist pattern,
-  // need this device special handling for masks in pattern colorspace?
-  // Default is false
-  virtual GBool fillMaskCSPattern(GfxState * state)
-  	{ return state->getFillColorSpace()->getMode() == csPattern; }
-  virtual void endMaskClip(GfxState * /*state*/);
 
   //----- Type 3 font operators
   virtual void type3D0(GfxState *state, double wx, double wy);
@@ -363,6 +320,10 @@ public:
   // caller.
   SplashBitmap *takeBitmap();
 
+  // Set this flag to true to generate an upside-down bitmap (useful
+  // for Windows BMP files).
+  void setBitmapUpsideDown(GBool f) { bitmapUpsideDown = f; }
+
   // Get the Splash object.
   Splash *getSplash() { return splash; }
 
@@ -374,6 +335,13 @@ public:
 
   SplashFont *getCurrentFont() { return font; }
 
+  // If <skipTextA> is true, don't draw horizontal text.
+  // If <skipRotatedTextA> is true, don't draw rotated (non-horizontal) text.
+  void setSkipText(GBool skipHorizTextA, GBool skipRotatedTextA)
+    { skipHorizText = skipHorizTextA; skipRotatedText = skipRotatedTextA; }
+
+  int getNestCount() { return nestCount; }
+
 #if 1 //~tmp: turn off anti-aliasing temporarily
   virtual GBool getVectorAntialias();
   virtual void setVectorAntialias(GBool vaa);
@@ -385,10 +353,10 @@ private:
   GBool univariateShadedFill(GfxState *state, SplashUnivariatePattern *pattern, double tMin, double tMax);
 
   void setupScreenParams(double hDPI, double vDPI);
+  SplashPattern *getColor(GfxGray gray);
+  SplashPattern *getColor(GfxRGB *rgb);
 #if SPLASH_CMYK
-  SplashPattern *getColor(GfxColorSpace *colorSpace, GfxGray gray, GfxRGB *rgb, GfxCMYK *cmyk);
-#else
-  SplashPattern *getColor(GfxGray gray, GfxRGB *rgb);
+  SplashPattern *getColor(GfxCMYK *cmyk);
 #endif
   void setOverprintMask(GfxColorSpace *colorSpace, GBool overprintFlag,
 			int overprintMode, GfxColor *singleColor);
@@ -407,13 +375,12 @@ private:
   static GBool tilingBitmapSrc(void *data, SplashColorPtr line,
 			     Guchar *alphaLine);
 
-  GBool haveCSPattern;		// set if text has been drawn with a
-							//   clipping render mode because of pattern colorspace
   GBool keepAlphaChannel;	// don't fill with paper color, keep alpha channel
 
   SplashColorMode colorMode;
   int bitmapRowPad;
   GBool bitmapTopDown;
+  GBool bitmapUpsideDown;
   GBool allowAntialias;
   GBool vectorAntialias;
   GBool enableAutoHinting;
@@ -422,6 +389,8 @@ private:
   GBool reverseVideo;		// reverse video mode
   SplashColor paperColor;	// paper color
   SplashScreenParams screenParams;
+  GBool skipHorizText;
+  GBool skipRotatedText;
 
   PDFDoc *doc;			// the current document
 
@@ -433,6 +402,7 @@ private:
     t3FontCache[splashOutT3FontCacheSize];
   int nT3Fonts;			// number of valid entries in t3FontCache
   T3GlyphStack *t3GlyphStack;	// Type 3 glyph context stack
+  GBool haveT3Dx;		// set after seeing a d0/d1 operator
 
   SplashFont *font;		// current font
   GBool needFontUpdate;		// set when the font needs to be updated
diff --git a/poppler/TextOutputDev.cc b/poppler/TextOutputDev.cc
index 15fdf99..7a0b8d9 100644
--- a/poppler/TextOutputDev.cc
+++ b/poppler/TextOutputDev.cc
@@ -2379,6 +2379,10 @@ void TextPage::addChar(GfxState *state, double x, double y,
   charPos += nBytes;
 }
 
+void TextPage::incCharCount(int nChars) {
+  charPos += nChars;
+}
+
 void TextPage::endWord() {
   // This check is needed because Type 3 characters can contain
   // text-drawing operations (when TextPage is being used via
@@ -5296,6 +5300,10 @@ void TextOutputDev::drawChar(GfxState *state, double x, double y,
   actualText->addChar(state, x, y, dx, dy, c, nBytes, u, uLen);
 }
 
+void TextOutputDev::incCharCount(int nChars) {
+  text->incCharCount(nChars);
+}
+
 void TextOutputDev::beginActualText(GfxState *state, GooString *text)
 {
   actualText->begin(state, text);
diff --git a/poppler/TextOutputDev.h b/poppler/TextOutputDev.h
index 2dd78cd..fd34c8b 100644
--- a/poppler/TextOutputDev.h
+++ b/poppler/TextOutputDev.h
@@ -505,6 +505,9 @@ public:
 	       double dx, double dy,
 	       CharCode c, int nBytes, Unicode *u, int uLen);
 
+  // Add <nChars> invisible characters.
+  void incCharCount(int nChars);
+
   // End the current word, sorting it into the list of words.
   void endWord();
 
@@ -704,6 +707,10 @@ public:
   // Does this device need non-text content?
   virtual GBool needNonText() { return gFalse; }
 
+  // Does this device require incCharCount to be called for text on
+  // non-shown layers?
+  virtual GBool needCharCount() { return gTrue; }
+
   //----- initialization and control
 
   // Start a page.
@@ -722,6 +729,7 @@ public:
 			double dx, double dy,
 			double originX, double originY,
 			CharCode c, int nBytes, Unicode *u, int uLen);
+  virtual void incCharCount(int nChars);
   virtual void beginActualText(GfxState *state, GooString *text);
   virtual void endActualText(GfxState *state);
 
diff --git a/splash/Splash.cc b/splash/Splash.cc
index a188f34..fcab28c 100644
--- a/splash/Splash.cc
+++ b/splash/Splash.cc
@@ -3328,7 +3328,7 @@ void Splash::blitMask(SplashBitmap *src, int xDest, int yDest,
 
 SplashError Splash::drawImage(SplashImageSource src, void *srcData,
 			      SplashColorMode srcMode, GBool srcAlpha,
-			      int w, int h, SplashCoord *mat, SplashPattern *opImagePattern) {
+			      int w, int h, SplashCoord *mat) {
   GBool ok;
   SplashBitmap *scaledImg;
   SplashClipResult clipRes;
@@ -4506,9 +4506,6 @@ void Splash::blitImage(SplashBitmap *src, GBool srcAlpha, int xDest, int yDest,
   if (x0 < w && y0 < h && x0 < x1 && y0 < y1) {
     pipeInit(&pipe, xDest + x0, yDest + y0, NULL, pixel,
 	     (Guchar)splashRound(state->fillAlpha * 255), srcAlpha, gFalse);
-//  pipeInit(&pipe, xDest + x0, yDest + y0, NULL, pixel, state->fillAlpha,
-//	   srcAlpha,
-//	   gFalse, opImagePattern);
     if (srcAlpha) {
       for (y = y0; y < y1; ++y) {
 	pipeSetXY(&pipe, xDest + x0, yDest + y);
@@ -4561,9 +4558,6 @@ void Splash::blitImageClipped(SplashBitmap *src, GBool srcAlpha,
   if (vectorAntialias) {
     pipeInit(&pipe, xDest, yDest, NULL, pixel,
 	     (Guchar)splashRound(state->fillAlpha * 255), gTrue, gFalse);
-//  pipeInit(&pipe, xDest, yDest, NULL, pixel, state->fillAlpha,
-//	   gTrue,
-//	   gFalse, opImagePattern);
     drawAAPixelInit();
     if (srcAlpha) {
       for (y = 0; y < h; ++y) {
@@ -4586,9 +4580,6 @@ void Splash::blitImageClipped(SplashBitmap *src, GBool srcAlpha,
   } else {
     pipeInit(&pipe, xDest, yDest, NULL, pixel,
 	     (Guchar)splashRound(state->fillAlpha * 255), srcAlpha, gFalse);
-//  pipeInit(&pipe, xDest, yDest, NULL, pixel, state->fillAlpha,
-//	   srcAlpha,
-//	   gFalse, opImagePattern);
     if (srcAlpha) {
       for (y = 0; y < h; ++y) {
 	ap = src->getAlphaPtr() + (ySrc + y) * src->getWidth() + xSrc;
diff --git a/splash/Splash.h b/splash/Splash.h
index ce59497..3c2bf9f 100644
--- a/splash/Splash.h
+++ b/splash/Splash.h
@@ -209,7 +209,7 @@ public:
   // The matrix behaves as for fillImageMask.
   SplashError drawImage(SplashImageSource src, void *srcData,
 			SplashColorMode srcMode, GBool srcAlpha,
-			int w, int h, SplashCoord *mat, SplashPattern *overprintPattern = NULL);
+			int w, int h, SplashCoord *mat);
 
   // Composite a rectangular region from <src> onto this Splash
   // object.
@@ -318,66 +318,66 @@ private:
 			      SplashPattern *pattern, SplashCoord alpha);
   GBool pathAllOutside(SplashPath *path);
   void fillGlyph2(int x0, int y0, SplashGlyphBitmap *glyph, GBool noclip);
-  void arbitraryTransformMask(SplashImageMaskSource src, void *srcData,
-			      int srcWidth, int srcHeight,
-			      SplashCoord *mat, GBool glyphMode);
-  SplashBitmap *scaleMask(SplashImageMaskSource src, void *srcData,
-			  int srcWidth, int srcHeight,
-			  int scaledWidth, int scaledHeight);
-  void scaleMaskYdXd(SplashImageMaskSource src, void *srcData,
-		     int srcWidth, int srcHeight,
-		     int scaledWidth, int scaledHeight,
-		     SplashBitmap *dest);
-  void scaleMaskYdXu(SplashImageMaskSource src, void *srcData,
-		     int srcWidth, int srcHeight,
-		     int scaledWidth, int scaledHeight,
-		     SplashBitmap *dest);
-  void scaleMaskYuXd(SplashImageMaskSource src, void *srcData,
-		     int srcWidth, int srcHeight,
-		     int scaledWidth, int scaledHeight,
-		     SplashBitmap *dest);
-  void scaleMaskYuXu(SplashImageMaskSource src, void *srcData,
-		     int srcWidth, int srcHeight,
-		     int scaledWidth, int scaledHeight,
-		     SplashBitmap *dest);
-  void blitMask(SplashBitmap *src, int xDest, int yDest,
-		SplashClipResult clipRes);
-  SplashError arbitraryTransformImage(SplashImageSource src, void *srcData,
-			       SplashColorMode srcMode, int nComps,
-			       GBool srcAlpha,
-			       int srcWidth, int srcHeight,
-			       SplashCoord *mat);
-  SplashBitmap *scaleImage(SplashImageSource src, void *srcData,
-			   SplashColorMode srcMode, int nComps,
-			   GBool srcAlpha, int srcWidth, int srcHeight,
-			   int scaledWidth, int scaledHeight);
-  void scaleImageYdXd(SplashImageSource src, void *srcData,
-		      SplashColorMode srcMode, int nComps,
-		      GBool srcAlpha, int srcWidth, int srcHeight,
-		      int scaledWidth, int scaledHeight,
-		      SplashBitmap *dest);
-  void scaleImageYdXu(SplashImageSource src, void *srcData,
-		      SplashColorMode srcMode, int nComps,
-		      GBool srcAlpha, int srcWidth, int srcHeight,
-		      int scaledWidth, int scaledHeight,
-		      SplashBitmap *dest);
-  void scaleImageYuXd(SplashImageSource src, void *srcData,
-		      SplashColorMode srcMode, int nComps,
-		      GBool srcAlpha, int srcWidth, int srcHeight,
-		      int scaledWidth, int scaledHeight,
-		      SplashBitmap *dest);
-  void scaleImageYuXu(SplashImageSource src, void *srcData,
-		      SplashColorMode srcMode, int nComps,
-		      GBool srcAlpha, int srcWidth, int srcHeight,
-		      int scaledWidth, int scaledHeight,
-		      SplashBitmap *dest);
-  void vertFlipImage(SplashBitmap *img, int width, int height,
-		     int nComps);
-  void blitImage(SplashBitmap *src, GBool srcAlpha, int xDest, int yDest,
-		 SplashClipResult clipRes);
-  void blitImageClipped(SplashBitmap *src, GBool srcAlpha,
-			int xSrc, int ySrc, int xDest, int yDest,
-			int w, int h);
+  void arbitraryTransformMask(SplashImageMaskSource src, void *srcData,
+			      int srcWidth, int srcHeight,
+			      SplashCoord *mat, GBool glyphMode);
+  SplashBitmap *scaleMask(SplashImageMaskSource src, void *srcData,
+			  int srcWidth, int srcHeight,
+			  int scaledWidth, int scaledHeight);
+  void scaleMaskYdXd(SplashImageMaskSource src, void *srcData,
+		     int srcWidth, int srcHeight,
+		     int scaledWidth, int scaledHeight,
+		     SplashBitmap *dest);
+  void scaleMaskYdXu(SplashImageMaskSource src, void *srcData,
+		     int srcWidth, int srcHeight,
+		     int scaledWidth, int scaledHeight,
+		     SplashBitmap *dest);
+  void scaleMaskYuXd(SplashImageMaskSource src, void *srcData,
+		     int srcWidth, int srcHeight,
+		     int scaledWidth, int scaledHeight,
+		     SplashBitmap *dest);
+  void scaleMaskYuXu(SplashImageMaskSource src, void *srcData,
+		     int srcWidth, int srcHeight,
+		     int scaledWidth, int scaledHeight,
+		     SplashBitmap *dest);
+  void blitMask(SplashBitmap *src, int xDest, int yDest,
+		SplashClipResult clipRes);
+  SplashError arbitraryTransformImage(SplashImageSource src, void *srcData,
+			       SplashColorMode srcMode, int nComps,
+			       GBool srcAlpha,
+			       int srcWidth, int srcHeight,
+			       SplashCoord *mat);
+  SplashBitmap *scaleImage(SplashImageSource src, void *srcData,
+			   SplashColorMode srcMode, int nComps,
+			   GBool srcAlpha, int srcWidth, int srcHeight,
+			   int scaledWidth, int scaledHeight);
+  void scaleImageYdXd(SplashImageSource src, void *srcData,
+		      SplashColorMode srcMode, int nComps,
+		      GBool srcAlpha, int srcWidth, int srcHeight,
+		      int scaledWidth, int scaledHeight,
+		      SplashBitmap *dest);
+  void scaleImageYdXu(SplashImageSource src, void *srcData,
+		      SplashColorMode srcMode, int nComps,
+		      GBool srcAlpha, int srcWidth, int srcHeight,
+		      int scaledWidth, int scaledHeight,
+		      SplashBitmap *dest);
+  void scaleImageYuXd(SplashImageSource src, void *srcData,
+		      SplashColorMode srcMode, int nComps,
+		      GBool srcAlpha, int srcWidth, int srcHeight,
+		      int scaledWidth, int scaledHeight,
+		      SplashBitmap *dest);
+  void scaleImageYuXu(SplashImageSource src, void *srcData,
+		      SplashColorMode srcMode, int nComps,
+		      GBool srcAlpha, int srcWidth, int srcHeight,
+		      int scaledWidth, int scaledHeight,
+		      SplashBitmap *dest);
+  void vertFlipImage(SplashBitmap *img, int width, int height,
+		     int nComps);
+  void blitImage(SplashBitmap *src, GBool srcAlpha, int xDest, int yDest,
+		 SplashClipResult clipRes);
+  void blitImageClipped(SplashBitmap *src, GBool srcAlpha,
+			int xSrc, int ySrc, int xDest, int yDest,
+			int w, int h);
   void dumpPath(SplashPath *path);
   void dumpXPath(SplashXPath *path);
 
diff --git a/splash/SplashPattern.cc b/splash/SplashPattern.cc
index 6747973..28ca499 100644
--- a/splash/SplashPattern.cc
+++ b/splash/SplashPattern.cc
@@ -53,17 +53,3 @@ GBool SplashSolidColor::getColor(int x, int y, SplashColorPtr c) {
   splashColorCopy(c, color);
   return gTrue;
 }
-
-void SplashSolidColor::overprint(GBool op, Guchar aSrc, SplashColorPtr cSrc, 
-                                 Guchar aDest, SplashColorPtr cDest, 
-                                 SplashColorPtr colorResult) {
-  // default for overprint is knockout:
-  colorResult[0] = (Guchar)(((aDest - aSrc) * cDest[0] +
-                   aSrc * cSrc[0]) / aDest);
-  colorResult[1] = (Guchar)(((aDest - aSrc) * cDest[1] +
-                   aSrc * cSrc[1]) / aDest);
-  colorResult[2] = (Guchar)(((aDest - aSrc) * cDest[2] +
-                   aSrc * cSrc[2]) / aDest);
-  colorResult[3] = (Guchar)(((aDest - aSrc) * cDest[3] +
-                   aSrc * cSrc[3]) / aDest);
-}
diff --git a/splash/SplashPattern.h b/splash/SplashPattern.h
index de1f887..42c1660 100644
--- a/splash/SplashPattern.h
+++ b/splash/SplashPattern.h
@@ -52,10 +52,6 @@ public:
   // value for all pixels.
   virtual GBool isStatic() = 0;
 
-  // calculate destination color if overprint is enables
-  virtual void overprint(GBool op, Guchar alphaSrc, SplashColorPtr colorSrc, 
-                         Guchar alphaDest, SplashColorPtr colorDest, SplashColorPtr colorResult) = 0;
-
 private:
 };
 
@@ -78,9 +74,6 @@ public:
 
   virtual GBool isStatic() { return gTrue; }
 
-  virtual void overprint(GBool op, Guchar alphaSrc, SplashColorPtr colorSrc, 
-	  Guchar alphaDest, SplashColorPtr colorDest, SplashColorPtr colorResult);
-
 private:
 
   SplashColor color;
diff --git a/utils/pdftoppm.cc b/utils/pdftoppm.cc
index fba9b93..222c89b 100644
--- a/utils/pdftoppm.cc
+++ b/utils/pdftoppm.cc
@@ -352,6 +352,7 @@ int main(int argc, char *argv[]) {
   // write PPM files
 #if SPLASH_CMYK
   if (jpegcmyk || overprint) {
+    globalParams->setOverprintPreview(gTrue);
     paperColor[0] = 0;
     paperColor[1] = 0;
     paperColor[2] = 0;


More information about the poppler mailing list