upgraded to xpdf-3.01pl1
[swftools.git] / pdf2swf / xpdf / GfxState.cc
index b7afdae..051c7b6 100644 (file)
@@ -2,30 +2,85 @@
 //
 // GfxState.cc
 //
-// Copyright 1996 Derek B. Noonburg
+// Copyright 1996-2003 Glyph & Cog, LLC
 //
 //========================================================================
 
-#ifdef __GNUC__
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
 #pragma implementation
 #endif
 
 #include <stddef.h>
 #include <math.h>
-#include <string.h> // for memcpy()
+#include <string.h>
 #include "gmem.h"
 #include "Error.h"
 #include "Object.h"
 #include "Array.h"
+#include "Page.h"
 #include "GfxState.h"
 
 //------------------------------------------------------------------------
 
+static inline GfxColorComp clip01(GfxColorComp x) {
+  return (x < 0) ? 0 : (x > gfxColorComp1) ? gfxColorComp1 : x;
+}
+
 static inline double clip01(double x) {
-  return (x < 0) ? 0 : ((x > 1) ? 1 : x);
+  return (x < 0) ? 0 : (x > 1) ? 1 : x;
 }
 
 //------------------------------------------------------------------------
+
+static struct {
+  char *name;
+  GfxBlendMode mode;
+} gfxBlendModeNames[] = {
+  { "Normal",     gfxBlendNormal },
+  { "Compatible", gfxBlendNormal },
+  { "Multiply",   gfxBlendMultiply },
+  { "Screen",     gfxBlendScreen },
+  { "Overlay",    gfxBlendOverlay },
+  { "Darken",     gfxBlendDarken },
+  { "Lighten",    gfxBlendLighten },
+  { "ColorDodge", gfxBlendColorDodge },
+  { "ColorBurn",  gfxBlendColorBurn },
+  { "HardLight",  gfxBlendHardLight },
+  { "SoftLight",  gfxBlendSoftLight },
+  { "Difference", gfxBlendDifference },
+  { "Exclusion",  gfxBlendExclusion },
+  { "Hue",        gfxBlendHue },
+  { "Saturation", gfxBlendSaturation },
+  { "Color",      gfxBlendColor },
+  { "Luminosity", gfxBlendLuminosity }
+};
+
+#define nGfxBlendModeNames \
+          ((int)((sizeof(gfxBlendModeNames) / sizeof(char *))))
+
+//------------------------------------------------------------------------
+
+// NB: This must match the GfxColorSpaceMode enum defined in
+// GfxState.h
+static char *gfxColorSpaceModeNames[] = {
+  "DeviceGray",
+  "CalGray",
+  "DeviceRGB",
+  "CalRGB",
+  "DeviceCMYK",
+  "Lab",
+  "ICCBased",
+  "Indexed",
+  "Separation",
+  "DeviceN",
+  "Pattern"
+};
+
+#define nGfxColorSpaceModes ((sizeof(gfxColorSpaceModeNames) / sizeof(char *)))
+
+//------------------------------------------------------------------------
 // GfxColorSpace
 //------------------------------------------------------------------------
 
@@ -77,7 +132,7 @@ GfxColorSpace *GfxColorSpace::parse(Object *csObj) {
     } else if (obj1.isName("Pattern")) {
       cs = GfxPatternColorSpace::parse(csObj->getArray());
     } else {
-      error(-1, "Bad color space '%s'", csObj->getName());
+      error(-1, "Bad color space");
     }
     obj1.free();
   } else {
@@ -96,6 +151,14 @@ void GfxColorSpace::getDefaultRanges(double *decodeLow, double *decodeRange,
   }
 }
 
+int GfxColorSpace::getNumColorSpaceModes() {
+  return nGfxColorSpaceModes;
+}
+
+char *GfxColorSpace::getColorSpaceModeName(int idx) {
+  return gfxColorSpaceModeNames[idx];
+}
+
 //------------------------------------------------------------------------
 // GfxDeviceGrayColorSpace
 //------------------------------------------------------------------------
@@ -110,7 +173,7 @@ GfxColorSpace *GfxDeviceGrayColorSpace::copy() {
   return new GfxDeviceGrayColorSpace();
 }
 
-void GfxDeviceGrayColorSpace::getGray(GfxColor *color, double *gray) {
+void GfxDeviceGrayColorSpace::getGray(GfxColor *color, GfxGray *gray) {
   *gray = clip01(color->c[0]);
 }
 
@@ -120,7 +183,7 @@ void GfxDeviceGrayColorSpace::getRGB(GfxColor *color, GfxRGB *rgb) {
 
 void GfxDeviceGrayColorSpace::getCMYK(GfxColor *color, GfxCMYK *cmyk) {
   cmyk->c = cmyk->m = cmyk->y = 0;
-  cmyk->k = clip01(1 - color->c[0]);
+  cmyk->k = clip01(gfxColorComp1 - color->c[0]);
 }
 
 //------------------------------------------------------------------------
@@ -195,7 +258,7 @@ GfxColorSpace *GfxCalGrayColorSpace::parse(Array *arr) {
   return cs;
 }
 
-void GfxCalGrayColorSpace::getGray(GfxColor *color, double *gray) {
+void GfxCalGrayColorSpace::getGray(GfxColor *color, GfxGray *gray) {
   *gray = clip01(color->c[0]);
 }
 
@@ -205,7 +268,7 @@ void GfxCalGrayColorSpace::getRGB(GfxColor *color, GfxRGB *rgb) {
 
 void GfxCalGrayColorSpace::getCMYK(GfxColor *color, GfxCMYK *cmyk) {
   cmyk->c = cmyk->m = cmyk->y = 0;
-  cmyk->k = clip01(1 - color->c[0]);
+  cmyk->k = clip01(gfxColorComp1 - color->c[0]);
 }
 
 //------------------------------------------------------------------------
@@ -222,10 +285,10 @@ GfxColorSpace *GfxDeviceRGBColorSpace::copy() {
   return new GfxDeviceRGBColorSpace();
 }
 
-void GfxDeviceRGBColorSpace::getGray(GfxColor *color, double *gray) {
-  *gray = clip01(0.299 * color->c[0] +
-                0.587 * color->c[1] +
-                0.114 * color->c[2]);
+void GfxDeviceRGBColorSpace::getGray(GfxColor *color, GfxGray *gray) {
+  *gray = clip01((GfxColorComp)(0.3  * color->c[0] +
+                               0.59 * color->c[1] +
+                               0.11 * color->c[2] + 0.5));
 }
 
 void GfxDeviceRGBColorSpace::getRGB(GfxColor *color, GfxRGB *rgb) {
@@ -235,11 +298,11 @@ void GfxDeviceRGBColorSpace::getRGB(GfxColor *color, GfxRGB *rgb) {
 }
 
 void GfxDeviceRGBColorSpace::getCMYK(GfxColor *color, GfxCMYK *cmyk) {
-  double c, m, y, k;
+  GfxColorComp c, m, y, k;
 
-  c = clip01(1 - color->c[0]);
-  m = clip01(1 - color->c[1]);
-  y = clip01(1 - color->c[2]);
+  c = clip01(gfxColorComp1 - color->c[0]);
+  m = clip01(gfxColorComp1 - color->c[1]);
+  y = clip01(gfxColorComp1 - color->c[2]);
   k = c;
   if (m < k) {
     k = m;
@@ -261,9 +324,9 @@ GfxCalRGBColorSpace::GfxCalRGBColorSpace() {
   whiteX = whiteY = whiteZ = 1;
   blackX = blackY = blackZ = 0;
   gammaR = gammaG = gammaB = 1;
-  m[0] = 1; m[1] = 0; m[2] = 0;
-  m[3] = 0; m[4] = 1; m[5] = 0;
-  m[6] = 0; m[7] = 0; m[8] = 1;
+  mat[0] = 1; mat[1] = 0; mat[2] = 0;
+  mat[3] = 0; mat[4] = 1; mat[5] = 0;
+  mat[6] = 0; mat[7] = 0; mat[8] = 1;
 }
 
 GfxCalRGBColorSpace::~GfxCalRGBColorSpace() {
@@ -284,7 +347,7 @@ GfxColorSpace *GfxCalRGBColorSpace::copy() {
   cs->gammaG = gammaG;
   cs->gammaB = gammaB;
   for (i = 0; i < 9; ++i) {
-    cs->m[i] = m[i];
+    cs->mat[i] = mat[i];
   }
   return cs;
 }
@@ -344,7 +407,7 @@ GfxColorSpace *GfxCalRGBColorSpace::parse(Array *arr) {
       obj2.arrayGetLength() == 9) {
     for (i = 0; i < 9; ++i) {
       obj2.arrayGet(i, &obj3);
-      cs->m[i] = obj3.getNum();
+      cs->mat[i] = obj3.getNum();
       obj3.free();
     }
   }
@@ -353,10 +416,10 @@ GfxColorSpace *GfxCalRGBColorSpace::parse(Array *arr) {
   return cs;
 }
 
-void GfxCalRGBColorSpace::getGray(GfxColor *color, double *gray) {
-  *gray = clip01(0.299 * color->c[0] +
-                0.587 * color->c[1] +
-                0.114 * color->c[2]);
+void GfxCalRGBColorSpace::getGray(GfxColor *color, GfxGray *gray) {
+  *gray = clip01((GfxColorComp)(0.299 * color->c[0] +
+                               0.587 * color->c[1] +
+                               0.114 * color->c[2] + 0.5));
 }
 
 void GfxCalRGBColorSpace::getRGB(GfxColor *color, GfxRGB *rgb) {
@@ -366,11 +429,11 @@ void GfxCalRGBColorSpace::getRGB(GfxColor *color, GfxRGB *rgb) {
 }
 
 void GfxCalRGBColorSpace::getCMYK(GfxColor *color, GfxCMYK *cmyk) {
-  double c, m, y, k;
+  GfxColorComp c, m, y, k;
 
-  c = clip01(1 - color->c[0]);
-  m = clip01(1 - color->c[1]);
-  y = clip01(1 - color->c[2]);
+  c = clip01(gfxColorComp1 - color->c[0]);
+  m = clip01(gfxColorComp1 - color->c[1]);
+  y = clip01(gfxColorComp1 - color->c[2]);
   k = c;
   if (m < k) {
     k = m;
@@ -398,17 +461,73 @@ GfxColorSpace *GfxDeviceCMYKColorSpace::copy() {
   return new GfxDeviceCMYKColorSpace();
 }
 
-void GfxDeviceCMYKColorSpace::getGray(GfxColor *color, double *gray) {
-  *gray = clip01(1 - color->c[3]
-                - 0.299 * color->c[0]
-                - 0.587 * color->c[1]
-                - 0.114 * color->c[2]);
+void GfxDeviceCMYKColorSpace::getGray(GfxColor *color, GfxGray *gray) {
+  *gray = clip01((GfxColorComp)(gfxColorComp1 - color->c[3]
+                               - 0.3  * color->c[0]
+                               - 0.59 * color->c[1]
+                               - 0.11 * color->c[2] + 0.5));
 }
 
 void GfxDeviceCMYKColorSpace::getRGB(GfxColor *color, GfxRGB *rgb) {
-  rgb->r = clip01(1 - (color->c[0] + color->c[3]));
-  rgb->g = clip01(1 - (color->c[1] + color->c[3]));
-  rgb->b = clip01(1 - (color->c[2] + color->c[3]));
+  double c, m, y, k, c1, m1, y1, k1, r, g, b, x;
+
+  c = colToDbl(color->c[0]);
+  m = colToDbl(color->c[1]);
+  y = colToDbl(color->c[2]);
+  k = colToDbl(color->c[3]);
+  c1 = 1 - c;
+  m1 = 1 - m;
+  y1 = 1 - y;
+  k1 = 1 - k;
+  // this is a matrix multiplication, unrolled for performance
+  //                        C M Y K
+  x = c1 * m1 * y1 * k1; // 0 0 0 0
+  r = g = b = x;
+  x = c1 * m1 * y1 * k;  // 0 0 0 1
+  r += 0.1373 * x;
+  g += 0.1216 * x;
+  b += 0.1255 * x;
+  x = c1 * m1 * y  * k1; // 0 0 1 0
+  r += x;
+  g += 0.9490 * x;
+  x = c1 * m1 * y  * k;  // 0 0 1 1
+  r += 0.1098 * x;
+  g += 0.1020 * x;
+  x = c1 * m  * y1 * k1; // 0 1 0 0
+  r += 0.9255 * x;
+  b += 0.5490 * x;
+  x = c1 * m  * y1 * k;  // 0 1 0 1
+  r += 0.1412 * x;
+  x = c1 * m  * y  * k1; // 0 1 1 0
+  r += 0.9294 * x;
+  g += 0.1098 * x;
+  b += 0.1412 * x;
+  x = c1 * m  * y  * k;  // 0 1 1 1
+  r += 0.1333 * x;
+  x = c  * m1 * y1 * k1; // 1 0 0 0
+  g += 0.6784 * x;
+  b += 0.9373 * x;
+  x = c  * m1 * y1 * k;  // 1 0 0 1
+  g += 0.0588 * x;
+  b += 0.1412 * x;
+  x = c  * m1 * y  * k1; // 1 0 1 0
+  g += 0.6510 * x;
+  b += 0.3137 * x;
+  x = c  * m1 * y  * k;  // 1 0 1 1
+  g += 0.0745 * x;
+  x = c  * m  * y1 * k1; // 1 1 0 0
+  r += 0.1804 * x;
+  g += 0.1922 * x;
+  b += 0.5725 * x;
+  x = c  * m  * y1 * k;  // 1 1 0 1
+  b += 0.0078 * x;
+  x = c  * m  * y  * k1; // 1 1 1 0
+  r += 0.2118 * x;
+  g += 0.2119 * x;
+  b += 0.2235 * x;
+  rgb->r = clip01(dblToCol(r));
+  rgb->g = clip01(dblToCol(g));
+  rgb->b = clip01(dblToCol(b));
 }
 
 void GfxDeviceCMYKColorSpace::getCMYK(GfxColor *color, GfxCMYK *cmyk) {
@@ -528,13 +647,13 @@ GfxColorSpace *GfxLabColorSpace::parse(Array *arr) {
   return cs;
 }
 
-void GfxLabColorSpace::getGray(GfxColor *color, double *gray) {
+void GfxLabColorSpace::getGray(GfxColor *color, GfxGray *gray) {
   GfxRGB rgb;
 
   getRGB(color, &rgb);
-  *gray = clip01(0.299 * rgb.r +
-                0.587 * rgb.g +
-                0.114 * rgb.b);
+  *gray = clip01((GfxColorComp)(0.299 * rgb.r +
+                               0.587 * rgb.g +
+                               0.114 * rgb.b + 0.5));
 }
 
 void GfxLabColorSpace::getRGB(GfxColor *color, GfxRGB *rgb) {
@@ -543,8 +662,8 @@ void GfxLabColorSpace::getRGB(GfxColor *color, GfxRGB *rgb) {
   double r, g, b;
 
   // convert L*a*b* to CIE 1931 XYZ color space
-  t1 = (color->c[0] + 16) / 116;
-  t2 = t1 + color->c[1] / 500;
+  t1 = (colToDbl(color->c[0]) + 16) / 116;
+  t2 = t1 + colToDbl(color->c[1]) / 500;
   if (t2 >= (6.0 / 29.0)) {
     X = t2 * t2 * t2;
   } else {
@@ -557,7 +676,7 @@ void GfxLabColorSpace::getRGB(GfxColor *color, GfxRGB *rgb) {
     Y = (108.0 / 841.0) * (t1 - (4.0 / 29.0));
   }
   Y *= whiteY;
-  t2 = t1 - color->c[2] / 200;
+  t2 = t1 - colToDbl(color->c[2]) / 200;
   if (t2 >= (6.0 / 29.0)) {
     Z = t2 * t2 * t2;
   } else {
@@ -569,19 +688,19 @@ void GfxLabColorSpace::getRGB(GfxColor *color, GfxRGB *rgb) {
   r = xyzrgb[0][0] * X + xyzrgb[0][1] * Y + xyzrgb[0][2] * Z;
   g = xyzrgb[1][0] * X + xyzrgb[1][1] * Y + xyzrgb[1][2] * Z;
   b = xyzrgb[2][0] * X + xyzrgb[2][1] * Y + xyzrgb[2][2] * Z;
-  rgb->r = pow(clip01(r * kr), 0.5);
-  rgb->g = pow(clip01(g * kg), 0.5);
-  rgb->b = pow(clip01(b * kb), 0.5);
+  rgb->r = dblToCol(pow(clip01(r * kr), 0.5));
+  rgb->g = dblToCol(pow(clip01(g * kg), 0.5));
+  rgb->b = dblToCol(pow(clip01(b * kb), 0.5));
 }
 
 void GfxLabColorSpace::getCMYK(GfxColor *color, GfxCMYK *cmyk) {
   GfxRGB rgb;
-  double c, m, y, k;
+  GfxColorComp c, m, y, k;
 
   getRGB(color, &rgb);
-  c = clip01(1 - rgb.r);
-  m = clip01(1 - rgb.g);
-  y = clip01(1 - rgb.b);
+  c = clip01(gfxColorComp1 - rgb.r);
+  m = clip01(gfxColorComp1 - rgb.g);
+  y = clip01(gfxColorComp1 - rgb.b);
   k = c;
   if (m < k) {
     k = m;
@@ -609,11 +728,11 @@ void GfxLabColorSpace::getDefaultRanges(double *decodeLow, double *decodeRange,
 // GfxICCBasedColorSpace
 //------------------------------------------------------------------------
 
-GfxICCBasedColorSpace::GfxICCBasedColorSpace(int nComps, GfxColorSpace *alt,
-                                            Ref *iccProfileStream) {
-  this->nComps = nComps;
-  this->alt = alt;
-  this->iccProfileStream = *iccProfileStream;
+GfxICCBasedColorSpace::GfxICCBasedColorSpace(int nCompsA, GfxColorSpace *altA,
+                                            Ref *iccProfileStreamA) {
+  nComps = nCompsA;
+  alt = altA;
+  iccProfileStream = *iccProfileStreamA;
   rangeMin[0] = rangeMin[1] = rangeMin[2] = rangeMin[3] = 0;
   rangeMax[0] = rangeMax[1] = rangeMax[2] = rangeMax[3] = 1;
 }
@@ -636,19 +755,19 @@ GfxColorSpace *GfxICCBasedColorSpace::copy() {
 
 GfxColorSpace *GfxICCBasedColorSpace::parse(Array *arr) {
   GfxICCBasedColorSpace *cs;
-  Ref iccProfileStream;
-  int nComps;
-  GfxColorSpace *alt;
+  Ref iccProfileStreamA;
+  int nCompsA;
+  GfxColorSpace *altA;
   Dict *dict;
   Object obj1, obj2, obj3;
   int i;
 
   arr->getNF(1, &obj1);
   if (obj1.isRef()) {
-    iccProfileStream = obj1.getRef();
+    iccProfileStreamA = obj1.getRef();
   } else {
-    iccProfileStream.num = 0;
-    iccProfileStream.gen = 0;
+    iccProfileStreamA.num = 0;
+    iccProfileStreamA.gen = 0;
   }
   obj1.free();
   arr->get(1, &obj1);
@@ -664,19 +783,24 @@ GfxColorSpace *GfxICCBasedColorSpace::parse(Array *arr) {
     obj1.free();
     return NULL;
   }
-  nComps = obj2.getInt();
+  nCompsA = obj2.getInt();
   obj2.free();
+  if (nCompsA > gfxColorMaxComps) {
+    error(-1, "ICCBased color space with too many (%d > %d) components",
+         nCompsA, gfxColorMaxComps);
+    nCompsA = gfxColorMaxComps;
+  }
   if (dict->lookup("Alternate", &obj2)->isNull() ||
-      !(alt = GfxColorSpace::parse(&obj2))) {
-    switch (nComps) {
+      !(altA = GfxColorSpace::parse(&obj2))) {
+    switch (nCompsA) {
     case 1:
-      alt = new GfxDeviceGrayColorSpace();
+      altA = new GfxDeviceGrayColorSpace();
       break;
     case 3:
-      alt = new GfxDeviceRGBColorSpace();
+      altA = new GfxDeviceRGBColorSpace();
       break;
     case 4:
-      alt = new GfxDeviceCMYKColorSpace();
+      altA = new GfxDeviceCMYKColorSpace();
       break;
     default:
       error(-1, "Bad ICCBased color space - invalid N");
@@ -686,10 +810,10 @@ GfxColorSpace *GfxICCBasedColorSpace::parse(Array *arr) {
     }
   }
   obj2.free();
-  cs = new GfxICCBasedColorSpace(nComps, alt, &iccProfileStream);
+  cs = new GfxICCBasedColorSpace(nCompsA, altA, &iccProfileStreamA);
   if (dict->lookup("Range", &obj2)->isArray() &&
-      obj2.arrayGetLength() == 2 * nComps) {
-    for (i = 0; i < nComps; ++i) {
+      obj2.arrayGetLength() == 2 * nCompsA) {
+    for (i = 0; i < nCompsA; ++i) {
       obj2.arrayGet(2*i, &obj3);
       cs->rangeMin[i] = obj3.getNum();
       obj3.free();
@@ -703,7 +827,7 @@ GfxColorSpace *GfxICCBasedColorSpace::parse(Array *arr) {
   return cs;
 }
 
-void GfxICCBasedColorSpace::getGray(GfxColor *color, double *gray) {
+void GfxICCBasedColorSpace::getGray(GfxColor *color, GfxGray *gray) {
   alt->getGray(color, gray);
 }
 
@@ -718,24 +842,30 @@ void GfxICCBasedColorSpace::getCMYK(GfxColor *color, GfxCMYK *cmyk) {
 void GfxICCBasedColorSpace::getDefaultRanges(double *decodeLow,
                                             double *decodeRange,
                                             int maxImgPixel) {
+  alt->getDefaultRanges(decodeLow, decodeRange, maxImgPixel);
+
+#if 0
+  // this is nominally correct, but some PDF files don't set the
+  // correct ranges in the ICCBased dict
   int i;
 
   for (i = 0; i < nComps; ++i) {
     decodeLow[i] = rangeMin[i];
     decodeRange[i] = rangeMax[i] - rangeMin[i];
   }
+#endif
 }
 
 //------------------------------------------------------------------------
 // GfxIndexedColorSpace
 //------------------------------------------------------------------------
 
-GfxIndexedColorSpace::GfxIndexedColorSpace(GfxColorSpace *base,
-                                          int indexHigh) {
-  this->base = base;
-  this->indexHigh = indexHigh;
-  this->lookup = (Guchar *)gmalloc((indexHigh + 1) * base->getNComps() *
-                                  sizeof(Guchar));
+GfxIndexedColorSpace::GfxIndexedColorSpace(GfxColorSpace *baseA,
+                                          int indexHighA) {
+  base = baseA;
+  indexHigh = indexHighA;
+  lookup = (Guchar *)gmallocn((indexHigh + 1) * base->getNComps(),
+                             sizeof(Guchar));
 }
 
 GfxIndexedColorSpace::~GfxIndexedColorSpace() {
@@ -754,8 +884,8 @@ GfxColorSpace *GfxIndexedColorSpace::copy() {
 
 GfxColorSpace *GfxIndexedColorSpace::parse(Array *arr) {
   GfxIndexedColorSpace *cs;
-  GfxColorSpace *base;
-  int indexHigh;
+  GfxColorSpace *baseA;
+  int indexHighA;
   Object obj1;
   int x;
   char *s;
@@ -766,23 +896,33 @@ GfxColorSpace *GfxIndexedColorSpace::parse(Array *arr) {
     goto err1;
   }
   arr->get(1, &obj1);
-  if (!(base = GfxColorSpace::parse(&obj1))) {
+  if (!(baseA = GfxColorSpace::parse(&obj1))) {
     error(-1, "Bad Indexed color space (base color space)");
     goto err2;
   }
   obj1.free();
   if (!arr->get(2, &obj1)->isInt()) {
     error(-1, "Bad Indexed color space (hival)");
+    delete baseA;
+    goto err2;
+  }
+  indexHighA = obj1.getInt();
+  if (indexHighA < 0 || indexHighA > 255) {
+    // the PDF spec requires indexHigh to be in [0,255] -- allowing
+    // values larger than 255 creates a security hole: if nComps *
+    // indexHigh is greater than 2^31, the loop below may overwrite
+    // past the end of the array
+    error(-1, "Bad Indexed color space (invalid indexHigh value)");
+    delete baseA;
     goto err2;
   }
-  indexHigh = obj1.getInt();
   obj1.free();
-  cs = new GfxIndexedColorSpace(base, indexHigh);
+  cs = new GfxIndexedColorSpace(baseA, indexHighA);
   arr->get(3, &obj1);
-  n = base->getNComps();
+  n = baseA->getNComps();
   if (obj1.isStream()) {
     obj1.streamReset();
-    for (i = 0; i <= indexHigh; ++i) {
+    for (i = 0; i <= indexHighA; ++i) {
       for (j = 0; j < n; ++j) {
        if ((x = obj1.streamGetChar()) == EOF) {
          error(-1, "Bad Indexed color space (lookup table stream too short)");
@@ -793,12 +933,12 @@ GfxColorSpace *GfxIndexedColorSpace::parse(Array *arr) {
     }
     obj1.streamClose();
   } else if (obj1.isString()) {
-    if (obj1.getString()->getLength() < (indexHigh + 1) * n) {
+    if (obj1.getString()->getLength() < (indexHighA + 1) * n) {
       error(-1, "Bad Indexed color space (lookup table string too short)");
       goto err3;
     }
     s = obj1.getString()->getCString();
-    for (i = 0; i <= indexHigh; ++i) {
+    for (i = 0; i <= indexHighA; ++i) {
       for (j = 0; j < n; ++j) {
        cs->lookup[i*n + j] = (Guchar)*s++;
       }
@@ -818,43 +958,37 @@ GfxColorSpace *GfxIndexedColorSpace::parse(Array *arr) {
   return NULL;
 }
 
-void GfxIndexedColorSpace::getGray(GfxColor *color, double *gray) {
+GfxColor *GfxIndexedColorSpace::mapColorToBase(GfxColor *color,
+                                              GfxColor *baseColor) {
   Guchar *p;
-  GfxColor color2;
+  double low[gfxColorMaxComps], range[gfxColorMaxComps];
   int n, i;
 
   n = base->getNComps();
-  p = &lookup[(int)(color->c[0] + 0.5) * n];
+  base->getDefaultRanges(low, range, indexHigh);
+  p = &lookup[(int)(colToDbl(color->c[0]) + 0.5) * n];
   for (i = 0; i < n; ++i) {
-    color2.c[i] = p[i] / 255.0;
+    baseColor->c[i] = dblToCol(low[i] + (p[i] / 255.0) * range[i]);
   }
-  base->getGray(&color2, gray);
+  return baseColor;
+}
+
+void GfxIndexedColorSpace::getGray(GfxColor *color, GfxGray *gray) {
+  GfxColor color2;
+
+  base->getGray(mapColorToBase(color, &color2), gray);
 }
 
 void GfxIndexedColorSpace::getRGB(GfxColor *color, GfxRGB *rgb) {
-  Guchar *p;
   GfxColor color2;
-  int n, i;
 
-  n = base->getNComps();
-  p = &lookup[(int)(color->c[0] + 0.5) * n];
-  for (i = 0; i < n; ++i) {
-    color2.c[i] = p[i] / 255.0;
-  }
-  base->getRGB(&color2, rgb);
+  base->getRGB(mapColorToBase(color, &color2), rgb);
 }
 
 void GfxIndexedColorSpace::getCMYK(GfxColor *color, GfxCMYK *cmyk) {
-  Guchar *p;
   GfxColor color2;
-  int n, i;
 
-  n = base->getNComps();
-  p = &lookup[(int)(color->c[0] + 0.5) * n];
-  for (i = 0; i < n; ++i) {
-    color2.c[i] = p[i] / 255.0;
-  }
-  base->getCMYK(&color2, cmyk);
+  base->getCMYK(mapColorToBase(color, &color2), cmyk);
 }
 
 void GfxIndexedColorSpace::getDefaultRanges(double *decodeLow,
@@ -868,12 +1002,12 @@ void GfxIndexedColorSpace::getDefaultRanges(double *decodeLow,
 // GfxSeparationColorSpace
 //------------------------------------------------------------------------
 
-GfxSeparationColorSpace::GfxSeparationColorSpace(GString *name,
-                                                GfxColorSpace *alt,
-                                                Function *func) {
-  this->name = name;
-  this->alt = alt;
-  this->func = func;
+GfxSeparationColorSpace::GfxSeparationColorSpace(GString *nameA,
+                                                GfxColorSpace *altA,
+                                                Function *funcA) {
+  name = nameA;
+  alt = altA;
+  func = funcA;
 }
 
 GfxSeparationColorSpace::~GfxSeparationColorSpace() {
@@ -889,9 +1023,9 @@ GfxColorSpace *GfxSeparationColorSpace::copy() {
 //~ handle the 'All' and 'None' colorants
 GfxColorSpace *GfxSeparationColorSpace::parse(Array *arr) {
   GfxSeparationColorSpace *cs;
-  GString *name;
-  GfxColorSpace *alt;
-  Function *func;
+  GString *nameA;
+  GfxColorSpace *altA;
+  Function *funcA;
   Object obj1;
 
   if (arr->getLength() != 4) {
@@ -902,52 +1036,71 @@ GfxColorSpace *GfxSeparationColorSpace::parse(Array *arr) {
     error(-1, "Bad Separation color space (name)");
     goto err2;
   }
-  name = new GString(obj1.getName());
+  nameA = new GString(obj1.getName());
   obj1.free();
   arr->get(2, &obj1);
-  if (!(alt = GfxColorSpace::parse(&obj1))) {
+  if (!(altA = GfxColorSpace::parse(&obj1))) {
     error(-1, "Bad Separation color space (alternate color space)");
     goto err3;
   }
   obj1.free();
-  func = Function::parse(arr->get(3, &obj1));
-  obj1.free();
-  if (!func || !func->isOk()) {
+  arr->get(3, &obj1);
+  if (!(funcA = Function::parse(&obj1))) {
     goto err4;
   }
-  cs = new GfxSeparationColorSpace(name, alt, func);
+  obj1.free();
+  cs = new GfxSeparationColorSpace(nameA, altA, funcA);
   return cs;
 
  err4:
-  if(func)
-    delete func;
-  delete alt;
+  delete altA;
  err3:
-  delete name;
+  delete nameA;
  err2:
   obj1.free();
  err1:
   return NULL;
 }
 
-void GfxSeparationColorSpace::getGray(GfxColor *color, double *gray) {
+void GfxSeparationColorSpace::getGray(GfxColor *color, GfxGray *gray) {
+  double x;
+  double c[gfxColorMaxComps];
   GfxColor color2;
+  int i;
 
-  func->transform(color->c, color2.c);
+  x = colToDbl(color->c[0]);
+  func->transform(&x, c);
+  for (i = 0; i < alt->getNComps(); ++i) {
+    color2.c[i] = dblToCol(c[i]);
+  }
   alt->getGray(&color2, gray);
 }
 
 void GfxSeparationColorSpace::getRGB(GfxColor *color, GfxRGB *rgb) {
+  double x;
+  double c[gfxColorMaxComps];
   GfxColor color2;
+  int i;
 
-  func->transform(color->c, color2.c);
+  x = colToDbl(color->c[0]);
+  func->transform(&x, c);
+  for (i = 0; i < alt->getNComps(); ++i) {
+    color2.c[i] = dblToCol(c[i]);
+  }
   alt->getRGB(&color2, rgb);
 }
 
 void GfxSeparationColorSpace::getCMYK(GfxColor *color, GfxCMYK *cmyk) {
+  double x;
+  double c[gfxColorMaxComps];
   GfxColor color2;
+  int i;
 
-  func->transform(color->c, color2.c);
+  x = colToDbl(color->c[0]);
+  func->transform(&x, c);
+  for (i = 0; i < alt->getNComps(); ++i) {
+    color2.c[i] = dblToCol(c[i]);
+  }
   alt->getCMYK(&color2, cmyk);
 }
 
@@ -955,12 +1108,12 @@ void GfxSeparationColorSpace::getCMYK(GfxColor *color, GfxCMYK *cmyk) {
 // GfxDeviceNColorSpace
 //------------------------------------------------------------------------
 
-GfxDeviceNColorSpace::GfxDeviceNColorSpace(int nComps,
-                                          GfxColorSpace *alt,
-                                          Function *func) {
-  this->nComps = nComps;
-  this->alt = alt;
-  this->func = func;
+GfxDeviceNColorSpace::GfxDeviceNColorSpace(int nCompsA,
+                                          GfxColorSpace *altA,
+                                          Function *funcA) {
+  nComps = nCompsA;
+  alt = altA;
+  func = funcA;
 }
 
 GfxDeviceNColorSpace::~GfxDeviceNColorSpace() {
@@ -987,10 +1140,10 @@ GfxColorSpace *GfxDeviceNColorSpace::copy() {
 //~ handle the 'None' colorant
 GfxColorSpace *GfxDeviceNColorSpace::parse(Array *arr) {
   GfxDeviceNColorSpace *cs;
-  int nComps;
-  GString *names[gfxColorMaxComps];
-  GfxColorSpace *alt;
-  Function *func;
+  int nCompsA;
+  GString *namesA[gfxColorMaxComps];
+  GfxColorSpace *altA;
+  Function *funcA;
   Object obj1, obj2;
   int i;
 
@@ -1002,40 +1155,44 @@ GfxColorSpace *GfxDeviceNColorSpace::parse(Array *arr) {
     error(-1, "Bad DeviceN color space (names)");
     goto err2;
   }
-  nComps = obj1.arrayGetLength();
-  for (i = 0; i < nComps; ++i) {
+  nCompsA = obj1.arrayGetLength();
+  if (nCompsA > gfxColorMaxComps) {
+    error(-1, "DeviceN color space with too many (%d > %d) components",
+         nCompsA, gfxColorMaxComps);
+    nCompsA = gfxColorMaxComps;
+  }
+  for (i = 0; i < nCompsA; ++i) {
     if (!obj1.arrayGet(i, &obj2)->isName()) {
       error(-1, "Bad DeviceN color space (names)");
       obj2.free();
       goto err2;
     }
-    names[i] = new GString(obj2.getName());
+    namesA[i] = new GString(obj2.getName());
     obj2.free();
   }
   obj1.free();
   arr->get(2, &obj1);
-  if (!(alt = GfxColorSpace::parse(&obj1))) {
+  if (!(altA = GfxColorSpace::parse(&obj1))) {
     error(-1, "Bad DeviceN color space (alternate color space)");
     goto err3;
   }
   obj1.free();
-  func = Function::parse(arr->get(3, &obj1));
-  obj1.free();
-  if (!func->isOk()) {
+  arr->get(3, &obj1);
+  if (!(funcA = Function::parse(&obj1))) {
     goto err4;
   }
-  cs = new GfxDeviceNColorSpace(nComps, alt, func);
-  for (i = 0; i < nComps; ++i) {
-    cs->names[i] = names[i];
+  obj1.free();
+  cs = new GfxDeviceNColorSpace(nCompsA, altA, funcA);
+  for (i = 0; i < nCompsA; ++i) {
+    cs->names[i] = namesA[i];
   }
   return cs;
 
  err4:
-  delete func;
-  delete alt;
+  delete altA;
  err3:
-  for (i = 0; i < nComps; ++i) {
-    delete names[i];
+  for (i = 0; i < nCompsA; ++i) {
+    delete namesA[i];
   }
  err2:
   obj1.free();
@@ -1043,24 +1200,48 @@ GfxColorSpace *GfxDeviceNColorSpace::parse(Array *arr) {
   return NULL;
 }
 
-void GfxDeviceNColorSpace::getGray(GfxColor *color, double *gray) {
+void GfxDeviceNColorSpace::getGray(GfxColor *color, GfxGray *gray) {
+  double x[gfxColorMaxComps], c[gfxColorMaxComps];
   GfxColor color2;
+  int i;
 
-  func->transform(color->c, color2.c);
+  for (i = 0; i < nComps; ++i) {
+    x[i] = colToDbl(color->c[i]);
+  }
+  func->transform(x, c);
+  for (i = 0; i < alt->getNComps(); ++i) {
+    color2.c[i] = dblToCol(c[i]);
+  }
   alt->getGray(&color2, gray);
 }
 
 void GfxDeviceNColorSpace::getRGB(GfxColor *color, GfxRGB *rgb) {
+  double x[gfxColorMaxComps], c[gfxColorMaxComps];
   GfxColor color2;
+  int i;
 
-  func->transform(color->c, color2.c);
+  for (i = 0; i < nComps; ++i) {
+    x[i] = colToDbl(color->c[i]);
+  }
+  func->transform(x, c);
+  for (i = 0; i < alt->getNComps(); ++i) {
+    color2.c[i] = dblToCol(c[i]);
+  }
   alt->getRGB(&color2, rgb);
 }
 
 void GfxDeviceNColorSpace::getCMYK(GfxColor *color, GfxCMYK *cmyk) {
+  double x[gfxColorMaxComps], c[gfxColorMaxComps];
   GfxColor color2;
+  int i;
 
-  func->transform(color->c, color2.c);
+  for (i = 0; i < nComps; ++i) {
+    x[i] = colToDbl(color->c[i]);
+  }
+  func->transform(x, c);
+  for (i = 0; i < alt->getNComps(); ++i) {
+    color2.c[i] = dblToCol(c[i]);
+  }
   alt->getCMYK(&color2, cmyk);
 }
 
@@ -1068,8 +1249,8 @@ void GfxDeviceNColorSpace::getCMYK(GfxColor *color, GfxCMYK *cmyk) {
 // GfxPatternColorSpace
 //------------------------------------------------------------------------
 
-GfxPatternColorSpace::GfxPatternColorSpace(GfxColorSpace *under) {
-  this->under = under;
+GfxPatternColorSpace::GfxPatternColorSpace(GfxColorSpace *underA) {
+  under = underA;
 }
 
 GfxPatternColorSpace::~GfxPatternColorSpace() {
@@ -1085,28 +1266,28 @@ GfxColorSpace *GfxPatternColorSpace::copy() {
 
 GfxColorSpace *GfxPatternColorSpace::parse(Array *arr) {
   GfxPatternColorSpace *cs;
-  GfxColorSpace *under;
+  GfxColorSpace *underA;
   Object obj1;
 
   if (arr->getLength() != 1 && arr->getLength() != 2) {
     error(-1, "Bad Pattern color space");
     return NULL;
   }
-  under = NULL;
+  underA = NULL;
   if (arr->getLength() == 2) {
     arr->get(1, &obj1);
-    if (!(under = GfxColorSpace::parse(&obj1))) {
+    if (!(underA = GfxColorSpace::parse(&obj1))) {
       error(-1, "Bad Pattern color space (underlying color space)");
       obj1.free();
       return NULL;
     }
     obj1.free();
   }
-  cs = new GfxPatternColorSpace(under);
+  cs = new GfxPatternColorSpace(underA);
   return cs;
 }
 
-void GfxPatternColorSpace::getGray(GfxColor *color, double *gray) {
+void GfxPatternColorSpace::getGray(GfxColor *color, GfxGray *gray) {
   *gray = 0;
 }
 
@@ -1123,8 +1304,8 @@ void GfxPatternColorSpace::getCMYK(GfxColor *color, GfxCMYK *cmyk) {
 // Pattern
 //------------------------------------------------------------------------
 
-GfxPattern::GfxPattern(int type) {
-  this->type = type;
+GfxPattern::GfxPattern(int typeA) {
+  type = typeA;
 }
 
 GfxPattern::~GfxPattern() {
@@ -1132,18 +1313,22 @@ GfxPattern::~GfxPattern() {
 
 GfxPattern *GfxPattern::parse(Object *obj) {
   GfxPattern *pattern;
-  Dict *dict;
   Object obj1;
 
+  if (obj->isDict()) {
+    obj->dictLookup("PatternType", &obj1);
+  } else if (obj->isStream()) {
+    obj->streamGetDict()->lookup("PatternType", &obj1);
+  } else {
+    return NULL;
+  }
   pattern = NULL;
-  if (obj->isStream()) {
-    dict = obj->streamGetDict();
-    dict->lookup("PatternType", &obj1);
-    if (obj1.isInt() && obj1.getInt() == 1) {
-      pattern = new GfxTilingPattern(dict, obj);
-    }
-    obj1.free();
+  if (obj1.isInt() && obj1.getInt() == 1) {
+    pattern = GfxTilingPattern::parse(obj);
+  } else if (obj1.isInt() && obj1.getInt() == 2) {
+    pattern = GfxShadingPattern::parse(obj);
   }
+  obj1.free();
   return pattern;
 }
 
@@ -1151,33 +1336,42 @@ GfxPattern *GfxPattern::parse(Object *obj) {
 // GfxTilingPattern
 //------------------------------------------------------------------------
 
-GfxTilingPattern::GfxTilingPattern(Dict *streamDict, Object *stream):
-  GfxPattern(1)
-{
+GfxTilingPattern *GfxTilingPattern::parse(Object *patObj) {
+  GfxTilingPattern *pat;
+  Dict *dict;
+  int paintTypeA, tilingTypeA;
+  double bboxA[4], matrixA[6];
+  double xStepA, yStepA;
+  Object resDictA;
   Object obj1, obj2;
   int i;
 
-  if (streamDict->lookup("PaintType", &obj1)->isInt()) {
-    paintType = obj1.getInt();
+  if (!patObj->isStream()) {
+    return NULL;
+  }
+  dict = patObj->streamGetDict();
+
+  if (dict->lookup("PaintType", &obj1)->isInt()) {
+    paintTypeA = obj1.getInt();
   } else {
-    paintType = 1;
+    paintTypeA = 1;
     error(-1, "Invalid or missing PaintType in pattern");
   }
   obj1.free();
-  if (streamDict->lookup("TilingType", &obj1)->isInt()) {
-    tilingType = obj1.getInt();
+  if (dict->lookup("TilingType", &obj1)->isInt()) {
+    tilingTypeA = obj1.getInt();
   } else {
-    tilingType = 1;
+    tilingTypeA = 1;
     error(-1, "Invalid or missing TilingType in pattern");
   }
   obj1.free();
-  bbox[0] = bbox[1] = 0;
-  bbox[2] = bbox[3] = 1;
-  if (streamDict->lookup("BBox", &obj1)->isArray() &&
+  bboxA[0] = bboxA[1] = 0;
+  bboxA[2] = bboxA[3] = 1;
+  if (dict->lookup("BBox", &obj1)->isArray() &&
       obj1.arrayGetLength() == 4) {
     for (i = 0; i < 4; ++i) {
       if (obj1.arrayGet(i, &obj2)->isNum()) {
-       bbox[i] = obj2.getNum();
+       bboxA[i] = obj2.getNum();
       }
       obj2.free();
     }
@@ -1185,39 +1379,65 @@ GfxTilingPattern::GfxTilingPattern(Dict *streamDict, Object *stream):
     error(-1, "Invalid or missing BBox in pattern");
   }
   obj1.free();
-  if (streamDict->lookup("XStep", &obj1)->isNum()) {
-    xStep = obj1.getNum();
+  if (dict->lookup("XStep", &obj1)->isNum()) {
+    xStepA = obj1.getNum();
   } else {
-    xStep = 1;
+    xStepA = 1;
     error(-1, "Invalid or missing XStep in pattern");
   }
   obj1.free();
-  if (streamDict->lookup("YStep", &obj1)->isNum()) {
-    yStep = obj1.getNum();
+  if (dict->lookup("YStep", &obj1)->isNum()) {
+    yStepA = obj1.getNum();
   } else {
-    yStep = 1;
+    yStepA = 1;
     error(-1, "Invalid or missing YStep in pattern");
   }
   obj1.free();
-  if (!streamDict->lookup("Resources", &resDict)->isDict()) {
-    resDict.free();
-    resDict.initNull();
+  if (!dict->lookup("Resources", &resDictA)->isDict()) {
+    resDictA.free();
+    resDictA.initNull();
     error(-1, "Invalid or missing Resources in pattern");
   }
-  matrix[0] = 1; matrix[1] = 0;
-  matrix[2] = 0; matrix[3] = 1;
-  matrix[4] = 0; matrix[5] = 0;
-  if (streamDict->lookup("Matrix", &obj1)->isArray() &&
+  matrixA[0] = 1; matrixA[1] = 0;
+  matrixA[2] = 0; matrixA[3] = 1;
+  matrixA[4] = 0; matrixA[5] = 0;
+  if (dict->lookup("Matrix", &obj1)->isArray() &&
       obj1.arrayGetLength() == 6) {
     for (i = 0; i < 6; ++i) {
       if (obj1.arrayGet(i, &obj2)->isNum()) {
-       matrix[i] = obj2.getNum();
+       matrixA[i] = obj2.getNum();
       }
       obj2.free();
     }
   }
   obj1.free();
-  stream->copy(&contentStream);
+
+  pat = new GfxTilingPattern(paintTypeA, tilingTypeA, bboxA, xStepA, yStepA,
+                            &resDictA, matrixA, patObj);
+  resDictA.free();
+  return pat;
+}
+
+GfxTilingPattern::GfxTilingPattern(int paintTypeA, int tilingTypeA,
+                                  double *bboxA, double xStepA, double yStepA,
+                                  Object *resDictA, double *matrixA,
+                                  Object *contentStreamA):
+  GfxPattern(1)
+{
+  int i;
+
+  paintType = paintTypeA;
+  tilingType = tilingTypeA;
+  for (i = 0; i < 4; ++i) {
+    bbox[i] = bboxA[i];
+  }
+  xStep = xStepA;
+  yStep = yStepA;
+  resDictA->copy(&resDict);
+  for (i = 0; i < 6; ++i) {
+    matrix[i] = matrixA[i];
+  }
+  contentStreamA->copy(&contentStream);
 }
 
 GfxTilingPattern::~GfxTilingPattern() {
@@ -1226,492 +1446,1672 @@ GfxTilingPattern::~GfxTilingPattern() {
 }
 
 GfxPattern *GfxTilingPattern::copy() {
-  return new GfxTilingPattern(this);
+  return new GfxTilingPattern(paintType, tilingType, bbox, xStep, yStep,
+                             &resDict, matrix, &contentStream);
 }
 
-GfxTilingPattern::GfxTilingPattern(GfxTilingPattern *pat):
-  GfxPattern(1)
+//------------------------------------------------------------------------
+// GfxShadingPattern
+//------------------------------------------------------------------------
+
+GfxShadingPattern *GfxShadingPattern::parse(Object *patObj) {
+  Dict *dict;
+  GfxShading *shadingA;
+  double matrixA[6];
+  Object obj1, obj2;
+  int i;
+
+  if (!patObj->isDict()) {
+    return NULL;
+  }
+  dict = patObj->getDict();
+
+  dict->lookup("Shading", &obj1);
+  shadingA = GfxShading::parse(&obj1);
+  obj1.free();
+  if (!shadingA) {
+    return NULL;
+  }
+
+  matrixA[0] = 1; matrixA[1] = 0;
+  matrixA[2] = 0; matrixA[3] = 1;
+  matrixA[4] = 0; matrixA[5] = 0;
+  if (dict->lookup("Matrix", &obj1)->isArray() &&
+      obj1.arrayGetLength() == 6) {
+    for (i = 0; i < 6; ++i) {
+      if (obj1.arrayGet(i, &obj2)->isNum()) {
+       matrixA[i] = obj2.getNum();
+      }
+      obj2.free();
+    }
+  }
+  obj1.free();
+
+  return new GfxShadingPattern(shadingA, matrixA);
+}
+
+GfxShadingPattern::GfxShadingPattern(GfxShading *shadingA, double *matrixA):
+  GfxPattern(2)
 {
-  memcpy(this, pat, sizeof(GfxTilingPattern));
-  pat->resDict.copy(&resDict);
-  pat->contentStream.copy(&contentStream);
+  int i;
+
+  shading = shadingA;
+  for (i = 0; i < 6; ++i) {
+    matrix[i] = matrixA[i];
+  }
+}
+
+GfxShadingPattern::~GfxShadingPattern() {
+  delete shading;
+}
+
+GfxPattern *GfxShadingPattern::copy() {
+  return new GfxShadingPattern(shading->copy(), matrix);
 }
 
 //------------------------------------------------------------------------
-// Function
+// GfxShading
 //------------------------------------------------------------------------
 
-Function::Function() {
+GfxShading::GfxShading(int typeA) {
+  type = typeA;
+  colorSpace = NULL;
+}
+
+GfxShading::GfxShading(GfxShading *shading) {
+  int i;
+
+  type = shading->type;
+  colorSpace = shading->colorSpace->copy();
+  for (i = 0; i < gfxColorMaxComps; ++i) {
+    background.c[i] = shading->background.c[i];
+  }
+  hasBackground = shading->hasBackground;
+  xMin = shading->xMin;
+  yMin = shading->yMin;
+  xMax = shading->xMax;
+  yMax = shading->yMax;
+  hasBBox = shading->hasBBox;
 }
 
-Function::~Function() {
+GfxShading::~GfxShading() {
+  if (colorSpace) {
+    delete colorSpace;
+  }
 }
 
-Function *Function::parse(Object *funcObj) {
-  Function *func;
+GfxShading *GfxShading::parse(Object *obj) {
+  GfxShading *shading;
   Dict *dict;
-  int funcType;
+  int typeA;
   Object obj1;
 
-  if (funcObj->isStream()) {
-    dict = funcObj->streamGetDict();
-  } else if (funcObj->isDict()) {
-    dict = funcObj->getDict();
+  if (obj->isDict()) {
+    dict = obj->getDict();
+  } else if (obj->isStream()) {
+    dict = obj->streamGetDict();
   } else {
-    error(-1, "Expected function dictionary or stream");
     return NULL;
   }
 
-  if (!dict->lookup("FunctionType", &obj1)->isInt()) {
-    error(-1, "Function type is missing or wrong type");
+  if (!dict->lookup("ShadingType", &obj1)->isInt()) {
+    error(-1, "Invalid ShadingType in shading dictionary");
     obj1.free();
     return NULL;
   }
-  funcType = obj1.getInt();
+  typeA = obj1.getInt();
   obj1.free();
 
-  if (funcType == 0) {
-    func = new SampledFunction(funcObj, dict);
-  } else if (funcType == 2) {
-    func = new ExponentialFunction(funcObj, dict);
-  } else {
-    error(-1, "Unimplemented function type");
-    return NULL;
-  }
-  if (!func->isOk()) {
-    delete func;
-    return NULL;
+  switch (typeA) {
+  case 1:
+    shading = GfxFunctionShading::parse(dict);
+    break;
+  case 2:
+    shading = GfxAxialShading::parse(dict);
+    break;
+  case 3:
+    shading = GfxRadialShading::parse(dict);
+    break;
+  case 4:
+    if (obj->isStream()) {
+      shading = GfxGouraudTriangleShading::parse(4, dict, obj->getStream());
+    } else {
+      error(-1, "Invalid Type 4 shading object");
+      goto err1;
+    }
+    break;
+  case 5:
+    if (obj->isStream()) {
+      shading = GfxGouraudTriangleShading::parse(5, dict, obj->getStream());
+    } else {
+      error(-1, "Invalid Type 5 shading object");
+      goto err1;
+    }
+    break;
+  case 6:
+    if (obj->isStream()) {
+      shading = GfxPatchMeshShading::parse(6, dict, obj->getStream());
+    } else {
+      error(-1, "Invalid Type 6 shading object");
+      goto err1;
+    }
+    break;
+  case 7:
+    if (obj->isStream()) {
+      shading = GfxPatchMeshShading::parse(7, dict, obj->getStream());
+    } else {
+      error(-1, "Invalid Type 7 shading object");
+      goto err1;
+    }
+    break;
+  default:
+    error(-1, "Unimplemented shading type %d", typeA);
+    goto err1;
   }
 
-  return func;
+  return shading;
+
+ err1:
+  return NULL;
 }
 
-GBool Function::init(Dict *dict) {
+GBool GfxShading::init(Dict *dict) {
   Object obj1, obj2;
   int i;
 
-  //----- Domain
-  if (!dict->lookup("Domain", &obj1)->isArray()) {
-    error(-1, "Function is missing domain");
-    goto err2;
+  dict->lookup("ColorSpace", &obj1);
+  if (!(colorSpace = GfxColorSpace::parse(&obj1))) {
+    error(-1, "Bad color space in shading dictionary");
+    obj1.free();
+    return gFalse;
   }
-  m = obj1.arrayGetLength() / 2;
-  if (m > funcMaxInputs) {
-    error(-1, "Functions with more than %d inputs are unsupported",
-         funcMaxInputs);
-    goto err2;
+  obj1.free();
+
+  for (i = 0; i < gfxColorMaxComps; ++i) {
+    background.c[i] = 0;
   }
-  for (i = 0; i < m; ++i) {
-    obj1.arrayGet(2*i, &obj2);
-    if (!obj2.isNum()) {
-      error(-1, "Illegal value in function domain array");
-      goto err1;
-    }
-    domain[i][0] = obj2.getNum();
-    obj2.free();
-    obj1.arrayGet(2*i+1, &obj2);
-    if (!obj2.isNum()) {
-      error(-1, "Illegal value in function domain array");
-      goto err1;
+  hasBackground = gFalse;
+  if (dict->lookup("Background", &obj1)->isArray()) {
+    if (obj1.arrayGetLength() == colorSpace->getNComps()) {
+      hasBackground = gTrue;
+      for (i = 0; i < colorSpace->getNComps(); ++i) {
+       background.c[i] = dblToCol(obj1.arrayGet(i, &obj2)->getNum());
+       obj2.free();
+      }
+    } else {
+      error(-1, "Bad Background in shading dictionary");
     }
-    domain[i][1] = obj2.getNum();
-    obj2.free();
   }
   obj1.free();
 
-  //----- Range
-  hasRange = gFalse;
-  n = 0;
-  if (dict->lookup("Range", &obj1)->isArray()) {
-    hasRange = gTrue;
-    n = obj1.arrayGetLength() / 2;
-    if (n > funcMaxOutputs) {
-      error(-1, "Functions with more than %d outputs are unsupported",
-           funcMaxOutputs);
-      goto err2;
-    }
-    for (i = 0; i < n; ++i) {
-      obj1.arrayGet(2*i, &obj2);
-      if (!obj2.isNum()) {
-       error(-1, "Illegal value in function range array");
-       goto err1;
-      }
-      range[i][0] = obj2.getNum();
+  xMin = yMin = xMax = yMax = 0;
+  hasBBox = gFalse;
+  if (dict->lookup("BBox", &obj1)->isArray()) {
+    if (obj1.arrayGetLength() == 4) {
+      hasBBox = gTrue;
+      xMin = obj1.arrayGet(0, &obj2)->getNum();
       obj2.free();
-      obj1.arrayGet(2*i+1, &obj2);
-      if (!obj2.isNum()) {
-       error(-1, "Illegal value in function range array");
-       goto err1;
-      }
-      range[i][1] = obj2.getNum();
+      yMin = obj1.arrayGet(1, &obj2)->getNum();
+      obj2.free();
+      xMax = obj1.arrayGet(2, &obj2)->getNum();
       obj2.free();
+      yMax = obj1.arrayGet(3, &obj2)->getNum();
+      obj2.free();
+    } else {
+      error(-1, "Bad BBox in shading dictionary");
     }
-    obj1.free();
   }
+  obj1.free();
 
   return gTrue;
-
- err1:
-  obj2.free();
- err2:
-  obj1.free();
-  return gFalse;
 }
 
 //------------------------------------------------------------------------
-// SampledFunction
+// GfxFunctionShading
 //------------------------------------------------------------------------
 
-SampledFunction::SampledFunction(Object *funcObj, Dict *dict) {
-  Stream *str;
-  int nSamples, sampleBits;
-  double sampleMul;
-  Object obj1, obj2;
-  Guint buf, bitMask;
-  int bits;
-  int s;
+GfxFunctionShading::GfxFunctionShading(double x0A, double y0A,
+                                      double x1A, double y1A,
+                                      double *matrixA,
+                                      Function **funcsA, int nFuncsA):
+  GfxShading(1)
+{
   int i;
 
-  samples = NULL;
-  ok = gFalse;
-
-  //----- initialize the generic stuff
-  if (!init(dict)) {
-    goto err1;
+  x0 = x0A;
+  y0 = y0A;
+  x1 = x1A;
+  y1 = y1A;
+  for (i = 0; i < 6; ++i) {
+    matrix[i] = matrixA[i];
   }
-  if (!hasRange) {
-    error(-1, "Type 0 function is missing range");
-    goto err1;
+  nFuncs = nFuncsA;
+  for (i = 0; i < nFuncs; ++i) {
+    funcs[i] = funcsA[i];
   }
+}
 
-  //----- get the stream
-  if (!funcObj->isStream()) {
-    error(-1, "Type 0 function isn't a stream");
-    goto err1;
+GfxFunctionShading::GfxFunctionShading(GfxFunctionShading *shading):
+  GfxShading(shading)
+{
+  int i;
+
+  x0 = shading->x0;
+  y0 = shading->y0;
+  x1 = shading->x1;
+  y1 = shading->y1;
+  for (i = 0; i < 6; ++i) {
+    matrix[i] = shading->matrix[i];
+  }
+  nFuncs = shading->nFuncs;
+  for (i = 0; i < nFuncs; ++i) {
+    funcs[i] = shading->funcs[i]->copy();
   }
-  str = funcObj->getStream();
+}
 
-  //----- Size
-  if (!dict->lookup("Size", &obj1)->isArray() ||
-      obj1.arrayGetLength() != m) {
-    error(-1, "Function has missing or invalid size array");
-    goto err2;
+GfxFunctionShading::~GfxFunctionShading() {
+  int i;
+
+  for (i = 0; i < nFuncs; ++i) {
+    delete funcs[i];
   }
-  for (i = 0; i < m; ++i) {
-    obj1.arrayGet(i, &obj2);
-    if (!obj2.isInt()) {
-      error(-1, "Illegal value in function size array");
-      goto err3;
-    }
-    sampleSize[i] = obj2.getInt();
+}
+
+GfxFunctionShading *GfxFunctionShading::parse(Dict *dict) {
+  GfxFunctionShading *shading;
+  double x0A, y0A, x1A, y1A;
+  double matrixA[6];
+  Function *funcsA[gfxColorMaxComps];
+  int nFuncsA;
+  Object obj1, obj2;
+  int i;
+
+  x0A = y0A = 0;
+  x1A = y1A = 1;
+  if (dict->lookup("Domain", &obj1)->isArray() &&
+      obj1.arrayGetLength() == 4) {
+    x0A = obj1.arrayGet(0, &obj2)->getNum();
+    obj2.free();
+    y0A = obj1.arrayGet(1, &obj2)->getNum();
+    obj2.free();
+    x1A = obj1.arrayGet(2, &obj2)->getNum();
+    obj2.free();
+    y1A = obj1.arrayGet(3, &obj2)->getNum();
     obj2.free();
   }
   obj1.free();
 
-  //----- BitsPerSample
-  if (!dict->lookup("BitsPerSample", &obj1)->isInt()) {
-    error(-1, "Function has missing or invalid BitsPerSample");
-    goto err2;
+  matrixA[0] = 1; matrixA[1] = 0;
+  matrixA[2] = 0; matrixA[3] = 1;
+  matrixA[4] = 0; matrixA[5] = 0;
+  if (dict->lookup("Matrix", &obj1)->isArray() &&
+      obj1.arrayGetLength() == 6) {
+    matrixA[0] = obj1.arrayGet(0, &obj2)->getNum();
+    obj2.free();
+    matrixA[1] = obj1.arrayGet(1, &obj2)->getNum();
+    obj2.free();
+    matrixA[2] = obj1.arrayGet(2, &obj2)->getNum();
+    obj2.free();
+    matrixA[3] = obj1.arrayGet(3, &obj2)->getNum();
+    obj2.free();
+    matrixA[4] = obj1.arrayGet(4, &obj2)->getNum();
+    obj2.free();
+    matrixA[5] = obj1.arrayGet(5, &obj2)->getNum();
+    obj2.free();
   }
-  sampleBits = obj1.getInt();
-  sampleMul = 1.0 / (double)((1 << sampleBits) - 1);
   obj1.free();
 
-  //----- Encode
-  if (dict->lookup("Encode", &obj1)->isArray() &&
-      obj1.arrayGetLength() == 2*m) {
-    for (i = 0; i < m; ++i) {
-      obj1.arrayGet(2*i, &obj2);
-      if (!obj2.isNum()) {
-       error(-1, "Illegal value in function encode array");
-       goto err3;
-      }
-      encode[i][0] = obj2.getNum();
-      obj2.free();
-      obj1.arrayGet(2*i+1, &obj2);
-      if (!obj2.isNum()) {
-       error(-1, "Illegal value in function encode array");
-       goto err3;
-      }
-      encode[i][1] = obj2.getNum();
-      obj2.free();
-    }
-  } else {
-    for (i = 0; i < m; ++i) {
-      encode[i][0] = 0;
-      encode[i][1] = sampleSize[i] - 1;
+  dict->lookup("Function", &obj1);
+  if (obj1.isArray()) {
+    nFuncsA = obj1.arrayGetLength();
+    if (nFuncsA > gfxColorMaxComps) {
+      error(-1, "Invalid Function array in shading dictionary");
+      goto err1;
     }
-  }
-  obj1.free();
-
-  //----- Decode
-  if (dict->lookup("Decode", &obj1)->isArray() &&
-      obj1.arrayGetLength() == 2*n) {
-    for (i = 0; i < n; ++i) {
-      obj1.arrayGet(2*i, &obj2);
-      if (!obj2.isNum()) {
-       error(-1, "Illegal value in function decode array");
-       goto err3;
-      }
-      decode[i][0] = obj2.getNum();
-      obj2.free();
-      obj1.arrayGet(2*i+1, &obj2);
-      if (!obj2.isNum()) {
-       error(-1, "Illegal value in function decode array");
-       goto err3;
+    for (i = 0; i < nFuncsA; ++i) {
+      obj1.arrayGet(i, &obj2);
+      if (!(funcsA[i] = Function::parse(&obj2))) {
+       goto err2;
       }
-      decode[i][1] = obj2.getNum();
       obj2.free();
     }
   } else {
-    for (i = 0; i < n; ++i) {
-      decode[i][0] = range[i][0];
-      decode[i][1] = range[i][1];
+    nFuncsA = 1;
+    if (!(funcsA[0] = Function::parse(&obj1))) {
+      goto err1;
     }
   }
   obj1.free();
 
-  //----- samples
-  nSamples = n;
-  for (i = 0; i < m; ++i)
-    nSamples *= sampleSize[i];
-  samples = (double *)gmalloc(nSamples * sizeof(double));
-  buf = 0;
-  bits = 0;
-  bitMask = (1 << sampleBits) - 1;
-  str->reset();
-  for (i = 0; i < nSamples; ++i) {
-    if (sampleBits == 8) {
-      s = str->getChar();
-    } else if (sampleBits == 16) {
-      s = str->getChar();
-      s = (s << 8) + str->getChar();
-    } else if (sampleBits == 32) {
-      s = str->getChar();
-      s = (s << 8) + str->getChar();
-      s = (s << 8) + str->getChar();
-      s = (s << 8) + str->getChar();
-    } else {
-      while (bits < sampleBits) {
-       buf = (buf << 8) | (str->getChar() & 0xff);
-       bits += 8;
-      }
-      s = (buf >> (bits - sampleBits)) & bitMask;
-      bits -= sampleBits;
-    }
-    samples[i] = (double)s * sampleMul;
+  shading = new GfxFunctionShading(x0A, y0A, x1A, y1A, matrixA,
+                                  funcsA, nFuncsA);
+  if (!shading->init(dict)) {
+    delete shading;
+    return NULL;
   }
-  str->close();
-
-  ok = gTrue;
-  return;
+  return shading;
 
- err3:
-  obj2.free();
  err2:
-  obj1.free();
+  obj2.free();
  err1:
-  return;
-}
+  obj1.free();
+  return NULL;
+}
+
+GfxShading *GfxFunctionShading::copy() {
+  return new GfxFunctionShading(this);
+}
+
+void GfxFunctionShading::getColor(double x, double y, GfxColor *color) {
+  double in[2], out[gfxColorMaxComps];
+  int i;
 
-SampledFunction::~SampledFunction() {
-  if (samples) {
-    gfree(samples);
+  // NB: there can be one function with n outputs or n functions with
+  // one output each (where n = number of color components)
+  for (i = 0; i < gfxColorMaxComps; ++i) {
+    out[i] = 0;
+  }
+  in[0] = x;
+  in[1] = y;
+  for (i = 0; i < nFuncs; ++i) {
+    funcs[i]->transform(in, &out[i]);
+  }
+  for (i = 0; i < gfxColorMaxComps; ++i) {
+    color->c[i] = dblToCol(out[i]);
   }
 }
 
-SampledFunction::SampledFunction(SampledFunction *func) {
-  int nSamples, i;
+//------------------------------------------------------------------------
+// GfxAxialShading
+//------------------------------------------------------------------------
 
-  memcpy(this, func, sizeof(SampledFunction));
+GfxAxialShading::GfxAxialShading(double x0A, double y0A,
+                                double x1A, double y1A,
+                                double t0A, double t1A,
+                                Function **funcsA, int nFuncsA,
+                                GBool extend0A, GBool extend1A):
+  GfxShading(2)
+{
+  int i;
 
-  nSamples = n;
-  for (i = 0; i < m; ++i) {
-    nSamples *= sampleSize[i];
+  x0 = x0A;
+  y0 = y0A;
+  x1 = x1A;
+  y1 = y1A;
+  t0 = t0A;
+  t1 = t1A;
+  nFuncs = nFuncsA;
+  for (i = 0; i < nFuncs; ++i) {
+    funcs[i] = funcsA[i];
   }
-  samples = (double *)gmalloc(nSamples * sizeof(double));
-  memcpy(samples, func->samples, nSamples * sizeof(double));
+  extend0 = extend0A;
+  extend1 = extend1A;
 }
 
-void SampledFunction::transform(double *in, double *out) {
-  double e[4];
-  double s;
-  double x0, x1;
-  int e0, e1;
-  double efrac;
+GfxAxialShading::GfxAxialShading(GfxAxialShading *shading):
+  GfxShading(shading)
+{
   int i;
 
-  // map input values into sample array
-  for (i = 0; i < m; ++i) {
-    e[i] = ((in[i] - domain[i][0]) / (domain[i][1] - domain[i][0])) *
-           (encode[i][1] - encode[i][0]) + encode[i][0];
-    if (e[i] < 0) {
-      e[i] = 0;
-    } else if (e[i] > sampleSize[i] - 1) {
-      e[i] = sampleSize[i] - 1;
-    }
+  x0 = shading->x0;
+  y0 = shading->y0;
+  x1 = shading->x1;
+  y1 = shading->y1;
+  t0 = shading->t0;
+  y1 = shading->t1;
+  nFuncs = shading->nFuncs;
+  for (i = 0; i < nFuncs; ++i) {
+    funcs[i] = shading->funcs[i]->copy();
   }
+  extend0 = shading->extend0;
+  extend1 = shading->extend1;
+}
 
-  for (i = 0; i < n; ++i) {
+GfxAxialShading::~GfxAxialShading() {
+  int i;
+
+  for (i = 0; i < nFuncs; ++i) {
+    delete funcs[i];
+  }
+}
+
+GfxAxialShading *GfxAxialShading::parse(Dict *dict) {
+  GfxAxialShading *shading;
+  double x0A, y0A, x1A, y1A;
+  double t0A, t1A;
+  Function *funcsA[gfxColorMaxComps];
+  int nFuncsA;
+  GBool extend0A, extend1A;
+  Object obj1, obj2;
+  int i;
+
+  x0A = y0A = x1A = y1A = 0;
+  if (dict->lookup("Coords", &obj1)->isArray() &&
+      obj1.arrayGetLength() == 4) {
+    x0A = obj1.arrayGet(0, &obj2)->getNum();
+    obj2.free();
+    y0A = obj1.arrayGet(1, &obj2)->getNum();
+    obj2.free();
+    x1A = obj1.arrayGet(2, &obj2)->getNum();
+    obj2.free();
+    y1A = obj1.arrayGet(3, &obj2)->getNum();
+    obj2.free();
+  } else {
+    error(-1, "Missing or invalid Coords in shading dictionary");
+    goto err1;
+  }
+  obj1.free();
 
-    // m-linear interpolation
-    // (only m=1 is currently supported)
-    e0 = (int)floor(e[0]);
-    e1 = (int)ceil(e[0]);
-    efrac = e[0] - e0;
-    x0 = samples[e0 * n + i];
-    x1 = samples[e1 * n + i];
-    s = (1 - efrac) * x0 + efrac * x1;
+  t0A = 0;
+  t1A = 1;
+  if (dict->lookup("Domain", &obj1)->isArray() &&
+      obj1.arrayGetLength() == 2) {
+    t0A = obj1.arrayGet(0, &obj2)->getNum();
+    obj2.free();
+    t1A = obj1.arrayGet(1, &obj2)->getNum();
+    obj2.free();
+  }
+  obj1.free();
 
-    // map output values to range
-    out[i] = s * (decode[i][1] - decode[i][0]) + decode[i][0];
-    if (out[i] < range[i][0]) {
-      out[i] = range[i][0];
-    } else if (out[i] > range[i][1]) {
-      out[i] = range[i][1];
+  dict->lookup("Function", &obj1);
+  if (obj1.isArray()) {
+    nFuncsA = obj1.arrayGetLength();
+    if (nFuncsA > gfxColorMaxComps) {
+      error(-1, "Invalid Function array in shading dictionary");
+      goto err1;
+    }
+    for (i = 0; i < nFuncsA; ++i) {
+      obj1.arrayGet(i, &obj2);
+      if (!(funcsA[i] = Function::parse(&obj2))) {
+       obj1.free();
+       obj2.free();
+       goto err1;
+      }
+      obj2.free();
     }
+  } else {
+    nFuncsA = 1;
+    if (!(funcsA[0] = Function::parse(&obj1))) {
+      obj1.free();
+      goto err1;
+    }
+  }
+  obj1.free();
+
+  extend0A = extend1A = gFalse;
+  if (dict->lookup("Extend", &obj1)->isArray() &&
+      obj1.arrayGetLength() == 2) {
+    extend0A = obj1.arrayGet(0, &obj2)->getBool();
+    obj2.free();
+    extend1A = obj1.arrayGet(1, &obj2)->getBool();
+    obj2.free();
+  }
+  obj1.free();
+
+  shading = new GfxAxialShading(x0A, y0A, x1A, y1A, t0A, t1A,
+                               funcsA, nFuncsA, extend0A, extend1A);
+  if (!shading->init(dict)) {
+    delete shading;
+    return NULL;
+  }
+  return shading;
+
+ err1:
+  return NULL;
+}
+
+GfxShading *GfxAxialShading::copy() {
+  return new GfxAxialShading(this);
+}
+
+void GfxAxialShading::getColor(double t, GfxColor *color) {
+  double out[gfxColorMaxComps];
+  int i;
+
+  // NB: there can be one function with n outputs or n functions with
+  // one output each (where n = number of color components)
+  for (i = 0; i < gfxColorMaxComps; ++i) {
+    out[i] = 0;
+  }
+  for (i = 0; i < nFuncs; ++i) {
+    funcs[i]->transform(&t, &out[i]);
+  }
+  for (i = 0; i < gfxColorMaxComps; ++i) {
+    color->c[i] = dblToCol(out[i]);
   }
 }
 
 //------------------------------------------------------------------------
-// ExponentialFunction
+// GfxRadialShading
 //------------------------------------------------------------------------
 
-ExponentialFunction::ExponentialFunction(Object *funcObj, Dict *dict) {
-  Object obj1, obj2;
-  GBool hasN;
+GfxRadialShading::GfxRadialShading(double x0A, double y0A, double r0A,
+                                  double x1A, double y1A, double r1A,
+                                  double t0A, double t1A,
+                                  Function **funcsA, int nFuncsA,
+                                  GBool extend0A, GBool extend1A):
+  GfxShading(3)
+{
   int i;
 
-  ok = gFalse;
-  hasN = gFalse;
+  x0 = x0A;
+  y0 = y0A;
+  r0 = r0A;
+  x1 = x1A;
+  y1 = y1A;
+  r1 = r1A;
+  t0 = t0A;
+  t1 = t1A;
+  nFuncs = nFuncsA;
+  for (i = 0; i < nFuncs; ++i) {
+    funcs[i] = funcsA[i];
+  }
+  extend0 = extend0A;
+  extend1 = extend1A;
+}
 
-  //----- initialize the generic stuff
-  if (!init(dict)) {
-    goto err1;
+GfxRadialShading::GfxRadialShading(GfxRadialShading *shading):
+  GfxShading(shading)
+{
+  int i;
+
+  x0 = shading->x0;
+  y0 = shading->y0;
+  r0 = shading->r0;
+  x1 = shading->x1;
+  y1 = shading->y1;
+  r1 = shading->r1;
+  t0 = shading->t0;
+  y1 = shading->t1;
+  nFuncs = shading->nFuncs;
+  for (i = 0; i < nFuncs; ++i) {
+    funcs[i] = shading->funcs[i]->copy();
+  }
+  extend0 = shading->extend0;
+  extend1 = shading->extend1;
+}
+
+GfxRadialShading::~GfxRadialShading() {
+  int i;
+
+  for (i = 0; i < nFuncs; ++i) {
+    delete funcs[i];
   }
-  if (m != 1) {
-    error(-1, "Exponential function with more than one input");
+}
+
+GfxRadialShading *GfxRadialShading::parse(Dict *dict) {
+  GfxRadialShading *shading;
+  double x0A, y0A, r0A, x1A, y1A, r1A;
+  double t0A, t1A;
+  Function *funcsA[gfxColorMaxComps];
+  int nFuncsA;
+  GBool extend0A, extend1A;
+  Object obj1, obj2;
+  int i;
+
+  x0A = y0A = r0A = x1A = y1A = r1A = 0;
+  if (dict->lookup("Coords", &obj1)->isArray() &&
+      obj1.arrayGetLength() == 6) {
+    x0A = obj1.arrayGet(0, &obj2)->getNum();
+    obj2.free();
+    y0A = obj1.arrayGet(1, &obj2)->getNum();
+    obj2.free();
+    r0A = obj1.arrayGet(2, &obj2)->getNum();
+    obj2.free();
+    x1A = obj1.arrayGet(3, &obj2)->getNum();
+    obj2.free();
+    y1A = obj1.arrayGet(4, &obj2)->getNum();
+    obj2.free();
+    r1A = obj1.arrayGet(5, &obj2)->getNum();
+    obj2.free();
+  } else {
+    error(-1, "Missing or invalid Coords in shading dictionary");
     goto err1;
   }
+  obj1.free();
 
-  //----- default values
-  for (i = 0; i < funcMaxOutputs; ++i) {
-    c0[i] = 0;
-    c1[i] = 1;
+  t0A = 0;
+  t1A = 1;
+  if (dict->lookup("Domain", &obj1)->isArray() &&
+      obj1.arrayGetLength() == 2) {
+    t0A = obj1.arrayGet(0, &obj2)->getNum();
+    obj2.free();
+    t1A = obj1.arrayGet(1, &obj2)->getNum();
+    obj2.free();
   }
+  obj1.free();
 
-  //----- C0
-  if (dict->lookup("C0", &obj1)->isArray()) {
-    if (!hasN) {
-      n = obj1.arrayGetLength();
-    } else if (obj1.arrayGetLength() != n) {
-      error(-1, "Function's C0 array is wrong length");
-      goto err2;
+  dict->lookup("Function", &obj1);
+  if (obj1.isArray()) {
+    nFuncsA = obj1.arrayGetLength();
+    if (nFuncsA > gfxColorMaxComps) {
+      error(-1, "Invalid Function array in shading dictionary");
+      goto err1;
     }
-    for (i = 0; i < n; ++i) {
+    for (i = 0; i < nFuncsA; ++i) {
       obj1.arrayGet(i, &obj2);
-      if (!obj2.isNum()) {
-       error(-1, "Illegal value in function C0 array");
-       goto err3;
+      if (!(funcsA[i] = Function::parse(&obj2))) {
+       obj1.free();
+       obj2.free();
+       goto err1;
       }
-      c0[i] = obj2.getNum();
       obj2.free();
     }
-    obj1.free();
+  } else {
+    nFuncsA = 1;
+    if (!(funcsA[0] = Function::parse(&obj1))) {
+      obj1.free();
+      goto err1;
+    }
   }
+  obj1.free();
 
-  //----- C1
-  if (dict->lookup("C1", &obj1)->isArray()) {
-    if (!hasN) {
-      n = obj1.arrayGetLength();
-    } else if (obj1.arrayGetLength() != n) {
-      error(-1, "Function's C1 array is wrong length");
-      goto err2;
+  extend0A = extend1A = gFalse;
+  if (dict->lookup("Extend", &obj1)->isArray() &&
+      obj1.arrayGetLength() == 2) {
+    extend0A = obj1.arrayGet(0, &obj2)->getBool();
+    obj2.free();
+    extend1A = obj1.arrayGet(1, &obj2)->getBool();
+    obj2.free();
+  }
+  obj1.free();
+
+  shading = new GfxRadialShading(x0A, y0A, r0A, x1A, y1A, r1A, t0A, t1A,
+                                funcsA, nFuncsA, extend0A, extend1A);
+  if (!shading->init(dict)) {
+    delete shading;
+    return NULL;
+  }
+  return shading;
+
+ err1:
+  return NULL;
+}
+
+GfxShading *GfxRadialShading::copy() {
+  return new GfxRadialShading(this);
+}
+
+void GfxRadialShading::getColor(double t, GfxColor *color) {
+  double out[gfxColorMaxComps];
+  int i;
+
+  // NB: there can be one function with n outputs or n functions with
+  // one output each (where n = number of color components)
+  for (i = 0; i < gfxColorMaxComps; ++i) {
+    out[i] = 0;
+  }
+  for (i = 0; i < nFuncs; ++i) {
+    funcs[i]->transform(&t, &out[i]);
+  }
+  for (i = 0; i < gfxColorMaxComps; ++i) {
+    color->c[i] = dblToCol(out[i]);
+  }
+}
+
+//------------------------------------------------------------------------
+// GfxShadingBitBuf
+//------------------------------------------------------------------------
+
+class GfxShadingBitBuf {
+public:
+
+  GfxShadingBitBuf(Stream *strA);
+  ~GfxShadingBitBuf();
+  GBool getBits(int n, Guint *val);
+  void flushBits();
+
+private:
+
+  Stream *str;
+  int bitBuf;
+  int nBits;
+};
+
+GfxShadingBitBuf::GfxShadingBitBuf(Stream *strA) {
+  str = strA;
+  str->reset();
+  bitBuf = 0;
+  nBits = 0;
+}
+
+GfxShadingBitBuf::~GfxShadingBitBuf() {
+  str->close();
+}
+
+GBool GfxShadingBitBuf::getBits(int n, Guint *val) {
+  int x;
+
+  if (nBits >= n) {
+    x = (bitBuf >> (nBits - n)) & ((1 << n) - 1);
+    nBits -= n;
+  } else {
+    x = 0;
+    if (nBits > 0) {
+      x = bitBuf & ((1 << nBits) - 1);
+      n -= nBits;
+      nBits = 0;
     }
-    for (i = 0; i < n; ++i) {
-      obj1.arrayGet(i, &obj2);
-      if (!obj2.isNum()) {
-       error(-1, "Illegal value in function C1 array");
-       goto err3;
+    while (n > 0) {
+      if ((bitBuf = str->getChar()) == EOF) {
+       nBits = 0;
+       return gFalse;
+      }
+      if (n >= 8) {
+       x = (x << 8) | bitBuf;
+       n -= 8;
+      } else {
+       x = (x << n) | (bitBuf >> (8 - n));
+       nBits = 8 - n;
+       n = 0;
       }
-      c1[i] = obj2.getNum();
-      obj2.free();
     }
-    obj1.free();
   }
+  *val = x;
+  return gTrue;
+}
 
-  //----- N (exponent)
-  if (!dict->lookup("N", &obj1)->isNum()) {
-    error(-1, "Function has missing or invalid N");
+void GfxShadingBitBuf::flushBits() {
+  bitBuf = 0;
+  nBits = 0;
+}
+
+//------------------------------------------------------------------------
+// GfxGouraudTriangleShading
+//------------------------------------------------------------------------
+
+GfxGouraudTriangleShading::GfxGouraudTriangleShading(
+                              int typeA,
+                              GfxGouraudVertex *verticesA, int nVerticesA,
+                              int (*trianglesA)[3], int nTrianglesA,
+                              Function **funcsA, int nFuncsA):
+  GfxShading(typeA)
+{
+  int i;
+
+  vertices = verticesA;
+  nVertices = nVerticesA;
+  triangles = trianglesA;
+  nTriangles = nTrianglesA;
+  nFuncs = nFuncsA;
+  for (i = 0; i < nFuncs; ++i) {
+    funcs[i] = funcsA[i];
+  }
+}
+
+GfxGouraudTriangleShading::GfxGouraudTriangleShading(
+                              GfxGouraudTriangleShading *shading):
+  GfxShading(shading)
+{
+  int i;
+
+  nVertices = shading->nVertices;
+  vertices = (GfxGouraudVertex *)gmallocn(nVertices, sizeof(GfxGouraudVertex));
+  memcpy(vertices, shading->vertices, nVertices * sizeof(GfxGouraudVertex));
+  nTriangles = shading->nTriangles;
+  triangles = (int (*)[3])gmallocn(nTriangles * 3, sizeof(int));
+  memcpy(triangles, shading->triangles, nTriangles * 3 * sizeof(int));
+  nFuncs = shading->nFuncs;
+  for (i = 0; i < nFuncs; ++i) {
+    funcs[i] = shading->funcs[i]->copy();
+  }
+}
+
+GfxGouraudTriangleShading::~GfxGouraudTriangleShading() {
+  int i;
+
+  gfree(vertices);
+  gfree(triangles);
+  for (i = 0; i < nFuncs; ++i) {
+    delete funcs[i];
+  }
+}
+
+GfxGouraudTriangleShading *GfxGouraudTriangleShading::parse(int typeA,
+                                                           Dict *dict,
+                                                           Stream *str) {
+  GfxGouraudTriangleShading *shading;
+  Function *funcsA[gfxColorMaxComps];
+  int nFuncsA;
+  int coordBits, compBits, flagBits, vertsPerRow, nRows;
+  double xMin, xMax, yMin, yMax;
+  double cMin[gfxColorMaxComps], cMax[gfxColorMaxComps];
+  double xMul, yMul;
+  double cMul[gfxColorMaxComps];
+  GfxGouraudVertex *verticesA;
+  int (*trianglesA)[3];
+  int nComps, nVerticesA, nTrianglesA, vertSize, triSize;
+  Guint x, y, flag;
+  Guint c[gfxColorMaxComps];
+  GfxShadingBitBuf *bitBuf;
+  Object obj1, obj2;
+  int i, j, k, state;
+
+  if (dict->lookup("BitsPerCoordinate", &obj1)->isInt()) {
+    coordBits = obj1.getInt();
+  } else {
+    error(-1, "Missing or invalid BitsPerCoordinate in shading dictionary");
+    goto err2;
+  }
+  obj1.free();
+  if (dict->lookup("BitsPerComponent", &obj1)->isInt()) {
+    compBits = obj1.getInt();
+  } else {
+    error(-1, "Missing or invalid BitsPerComponent in shading dictionary");
+    goto err2;
+  }
+  obj1.free();
+  flagBits = vertsPerRow = 0; // make gcc happy
+  if (typeA == 4) {
+    if (dict->lookup("BitsPerFlag", &obj1)->isInt()) {
+      flagBits = obj1.getInt();
+    } else {
+      error(-1, "Missing or invalid BitsPerFlag in shading dictionary");
+      goto err2;
+    }
+    obj1.free();
+  } else {
+    if (dict->lookup("VerticesPerRow", &obj1)->isInt()) {
+      vertsPerRow = obj1.getInt();
+    } else {
+      error(-1, "Missing or invalid VerticesPerRow in shading dictionary");
+      goto err2;
+    }
+    obj1.free();
+  }
+  if (dict->lookup("Decode", &obj1)->isArray() &&
+      obj1.arrayGetLength() >= 6) {
+    xMin = obj1.arrayGet(0, &obj2)->getNum();
+    obj2.free();
+    xMax = obj1.arrayGet(1, &obj2)->getNum();
+    obj2.free();
+    xMul = (xMax - xMin) / (pow(2.0, coordBits) - 1);
+    yMin = obj1.arrayGet(2, &obj2)->getNum();
+    obj2.free();
+    yMax = obj1.arrayGet(3, &obj2)->getNum();
+    obj2.free();
+    yMul = (yMax - yMin) / (pow(2.0, coordBits) - 1);
+    for (i = 0; 5 + 2*i < obj1.arrayGetLength() && i < gfxColorMaxComps; ++i) {
+      cMin[i] = obj1.arrayGet(4 + 2*i, &obj2)->getNum();
+      obj2.free();
+      cMax[i] = obj1.arrayGet(5 + 2*i, &obj2)->getNum();
+      obj2.free();
+      cMul[i] = (cMax[i] - cMin[i]) / (double)((1 << compBits) - 1);
+    }
+    nComps = i;
+  } else {
+    error(-1, "Missing or invalid Decode array in shading dictionary");
     goto err2;
   }
-  e = obj1.getNum();
   obj1.free();
 
-  ok = gTrue;
-  return;
+  if (!dict->lookup("Function", &obj1)->isNull()) {
+    if (obj1.isArray()) {
+      nFuncsA = obj1.arrayGetLength();
+      if (nFuncsA > gfxColorMaxComps) {
+       error(-1, "Invalid Function array in shading dictionary");
+       goto err1;
+      }
+      for (i = 0; i < nFuncsA; ++i) {
+       obj1.arrayGet(i, &obj2);
+       if (!(funcsA[i] = Function::parse(&obj2))) {
+         obj1.free();
+         obj2.free();
+         goto err1;
+       }
+       obj2.free();
+      }
+    } else {
+      nFuncsA = 1;
+      if (!(funcsA[0] = Function::parse(&obj1))) {
+       obj1.free();
+       goto err1;
+      }
+    }
+  } else {
+    nFuncsA = 0;
+  }
+  obj1.free();
+
+  nVerticesA = nTrianglesA = 0;
+  verticesA = NULL;
+  trianglesA = NULL;
+  vertSize = triSize = 0;
+  state = 0;
+  flag = 0; // make gcc happy
+  bitBuf = new GfxShadingBitBuf(str);
+  while (1) {
+    if (typeA == 4) {
+      if (!bitBuf->getBits(flagBits, &flag)) {
+       break;
+      }
+    }
+    if (!bitBuf->getBits(coordBits, &x) ||
+       !bitBuf->getBits(coordBits, &y)) {
+      break;
+    }
+    for (i = 0; i < nComps; ++i) {
+      if (!bitBuf->getBits(compBits, &c[i])) {
+       break;
+      }
+    }
+    if (i < nComps) {
+      break;
+    }
+    if (nVerticesA == vertSize) {
+      vertSize = (vertSize == 0) ? 16 : 2 * vertSize;
+      verticesA = (GfxGouraudVertex *)
+                     greallocn(verticesA, vertSize, sizeof(GfxGouraudVertex));
+    }
+    verticesA[nVerticesA].x = xMin + xMul * (double)x;
+    verticesA[nVerticesA].y = yMin + yMul * (double)y;
+    for (i = 0; i < nComps; ++i) {
+      verticesA[nVerticesA].color.c[i] =
+         dblToCol(cMin[i] + cMul[i] * (double)c[i]);
+    }
+    ++nVerticesA;
+    bitBuf->flushBits();
+    if (typeA == 4) {
+      if (state == 0 || state == 1) {
+       ++state;
+      } else if (state == 2 || flag > 0) {
+       if (nTrianglesA == triSize) {
+         triSize = (triSize == 0) ? 16 : 2 * triSize;
+         trianglesA = (int (*)[3])
+                          greallocn(trianglesA, triSize * 3, sizeof(int));
+       }
+       if (state == 2) {
+         trianglesA[nTrianglesA][0] = nVerticesA - 3;
+         trianglesA[nTrianglesA][1] = nVerticesA - 2;
+         trianglesA[nTrianglesA][2] = nVerticesA - 1;
+         ++state;
+       } else if (flag == 1) {
+         trianglesA[nTrianglesA][0] = trianglesA[nTrianglesA - 1][1];
+         trianglesA[nTrianglesA][1] = trianglesA[nTrianglesA - 1][2];
+         trianglesA[nTrianglesA][2] = nVerticesA - 1;
+       } else { // flag == 2
+         trianglesA[nTrianglesA][0] = trianglesA[nTrianglesA - 1][0];
+         trianglesA[nTrianglesA][1] = trianglesA[nTrianglesA - 1][2];
+         trianglesA[nTrianglesA][2] = nVerticesA - 1;
+       }
+       ++nTrianglesA;
+      } else { // state == 3 && flag == 0
+       state = 1;
+      }
+    }
+  }
+  delete bitBuf;
+  if (typeA == 5) {
+    nRows = nVerticesA / vertsPerRow;
+    nTrianglesA = (nRows - 1) * 2 * (vertsPerRow - 1);
+    trianglesA = (int (*)[3])gmallocn(nTrianglesA * 3, sizeof(int));
+    k = 0;
+    for (i = 0; i < nRows - 1; ++i) {
+      for (j = 0; j < vertsPerRow - 1; ++j) {
+       trianglesA[k][0] = i * vertsPerRow + j;
+       trianglesA[k][1] = i * vertsPerRow + j+1;
+       trianglesA[k][2] = (i+1) * vertsPerRow + j;
+       ++k;
+       trianglesA[k][0] = i * vertsPerRow + j+1;
+       trianglesA[k][1] = (i+1) * vertsPerRow + j;
+       trianglesA[k][2] = (i+1) * vertsPerRow + j+1;
+       ++k;
+      }
+    }
+  }
+
+  shading = new GfxGouraudTriangleShading(typeA, verticesA, nVerticesA,
+                                         trianglesA, nTrianglesA,
+                                         funcsA, nFuncsA);
+  if (!shading->init(dict)) {
+    delete shading;
+    return NULL;
+  }
+  return shading;
 
- err3:
-  obj2.free();
  err2:
   obj1.free();
  err1:
-  return;
+  return NULL;
 }
 
-ExponentialFunction::~ExponentialFunction() {
+GfxShading *GfxGouraudTriangleShading::copy() {
+  return new GfxGouraudTriangleShading(this);
 }
 
-ExponentialFunction::ExponentialFunction(ExponentialFunction *func) {
-  memcpy(this, func, sizeof(ExponentialFunction));
+void GfxGouraudTriangleShading::getTriangle(
+                                   int i,
+                                   double *x0, double *y0, GfxColor *color0,
+                                   double *x1, double *y1, GfxColor *color1,
+                                   double *x2, double *y2, GfxColor *color2) {
+  double in;
+  double out[gfxColorMaxComps];
+  int v, j;
+
+  v = triangles[i][0];
+  *x0 = vertices[v].x;
+  *y0 = vertices[v].y;
+  if (nFuncs > 0) {
+    in = colToDbl(vertices[v].color.c[0]);
+    for (j = 0; j < nFuncs; ++j) {
+      funcs[j]->transform(&in, &out[j]);
+    }
+    for (j = 0; j < gfxColorMaxComps; ++j) {
+      color0->c[j] = dblToCol(out[j]);
+    }
+  } else {
+    *color0 = vertices[v].color;
+  }
+  v = triangles[i][1];
+  *x1 = vertices[v].x;
+  *y1 = vertices[v].y;
+  if (nFuncs > 0) {
+    in = colToDbl(vertices[v].color.c[0]);
+    for (j = 0; j < nFuncs; ++j) {
+      funcs[j]->transform(&in, &out[j]);
+    }
+    for (j = 0; j < gfxColorMaxComps; ++j) {
+      color1->c[j] = dblToCol(out[j]);
+    }
+  } else {
+    *color1 = vertices[v].color;
+  }
+  v = triangles[i][2];
+  *x2 = vertices[v].x;
+  *y2 = vertices[v].y;
+  if (nFuncs > 0) {
+    in = colToDbl(vertices[v].color.c[0]);
+    for (j = 0; j < nFuncs; ++j) {
+      funcs[j]->transform(&in, &out[j]);
+    }
+    for (j = 0; j < gfxColorMaxComps; ++j) {
+      color2->c[j] = dblToCol(out[j]);
+    }
+  } else {
+    *color2 = vertices[v].color;
+  }
 }
 
-void ExponentialFunction::transform(double *in, double *out) {
-  double x;
+//------------------------------------------------------------------------
+// GfxPatchMeshShading
+//------------------------------------------------------------------------
+
+GfxPatchMeshShading::GfxPatchMeshShading(int typeA,
+                                        GfxPatch *patchesA, int nPatchesA,
+                                        Function **funcsA, int nFuncsA):
+  GfxShading(typeA)
+{
+  int i;
+
+  patches = patchesA;
+  nPatches = nPatchesA;
+  nFuncs = nFuncsA;
+  for (i = 0; i < nFuncs; ++i) {
+    funcs[i] = funcsA[i];
+  }
+}
+
+GfxPatchMeshShading::GfxPatchMeshShading(GfxPatchMeshShading *shading):
+  GfxShading(shading)
+{
   int i;
 
-  if (in[0] < domain[0][0]) {
-    x = domain[0][0];
-  } else if (in[0] > domain[0][1]) {
-    x = domain[0][1];
+  nPatches = shading->nPatches;
+  patches = (GfxPatch *)gmallocn(nPatches, sizeof(GfxPatch));
+  memcpy(patches, shading->patches, nPatches * sizeof(GfxPatch));
+  nFuncs = shading->nFuncs;
+  for (i = 0; i < nFuncs; ++i) {
+    funcs[i] = shading->funcs[i]->copy();
+  }
+}
+
+GfxPatchMeshShading::~GfxPatchMeshShading() {
+  int i;
+
+  gfree(patches);
+  for (i = 0; i < nFuncs; ++i) {
+    delete funcs[i];
+  }
+}
+
+GfxPatchMeshShading *GfxPatchMeshShading::parse(int typeA, Dict *dict,
+                                               Stream *str) {
+  GfxPatchMeshShading *shading;
+  Function *funcsA[gfxColorMaxComps];
+  int nFuncsA;
+  int coordBits, compBits, flagBits;
+  double xMin, xMax, yMin, yMax;
+  double cMin[gfxColorMaxComps], cMax[gfxColorMaxComps];
+  double xMul, yMul;
+  double cMul[gfxColorMaxComps];
+  GfxPatch *patchesA, *p;
+  int nComps, nPatchesA, patchesSize, nPts, nColors;
+  Guint flag;
+  double x[16], y[16];
+  Guint xi, yi;
+  GfxColorComp c[4][gfxColorMaxComps];
+  Guint ci[4];
+  GfxShadingBitBuf *bitBuf;
+  Object obj1, obj2;
+  int i, j;
+
+  if (dict->lookup("BitsPerCoordinate", &obj1)->isInt()) {
+    coordBits = obj1.getInt();
   } else {
-    x = in[0];
+    error(-1, "Missing or invalid BitsPerCoordinate in shading dictionary");
+    goto err2;
   }
-  for (i = 0; i < n; ++i) {
-    out[i] = c0[i] + pow(x, e) * (c1[i] - c0[i]);
-    if (hasRange) {
-      if (out[i] < range[i][0]) {
-       out[i] = range[i][0];
-      } else if (out[i] > range[i][1]) {
-       out[i] = range[i][1];
+  obj1.free();
+  if (dict->lookup("BitsPerComponent", &obj1)->isInt()) {
+    compBits = obj1.getInt();
+  } else {
+    error(-1, "Missing or invalid BitsPerComponent in shading dictionary");
+    goto err2;
+  }
+  obj1.free();
+  if (dict->lookup("BitsPerFlag", &obj1)->isInt()) {
+    flagBits = obj1.getInt();
+  } else {
+    error(-1, "Missing or invalid BitsPerFlag in shading dictionary");
+    goto err2;
+  }
+  obj1.free();
+  if (dict->lookup("Decode", &obj1)->isArray() &&
+      obj1.arrayGetLength() >= 6) {
+    xMin = obj1.arrayGet(0, &obj2)->getNum();
+    obj2.free();
+    xMax = obj1.arrayGet(1, &obj2)->getNum();
+    obj2.free();
+    xMul = (xMax - xMin) / (pow(2.0, coordBits) - 1);
+    yMin = obj1.arrayGet(2, &obj2)->getNum();
+    obj2.free();
+    yMax = obj1.arrayGet(3, &obj2)->getNum();
+    obj2.free();
+    yMul = (yMax - yMin) / (pow(2.0, coordBits) - 1);
+    for (i = 0; 5 + 2*i < obj1.arrayGetLength() && i < gfxColorMaxComps; ++i) {
+      cMin[i] = obj1.arrayGet(4 + 2*i, &obj2)->getNum();
+      obj2.free();
+      cMax[i] = obj1.arrayGet(5 + 2*i, &obj2)->getNum();
+      obj2.free();
+      cMul[i] = (cMax[i] - cMin[i]) / (double)((1 << compBits) - 1);
+    }
+    nComps = i;
+  } else {
+    error(-1, "Missing or invalid Decode array in shading dictionary");
+    goto err2;
+  }
+  obj1.free();
+
+  if (!dict->lookup("Function", &obj1)->isNull()) {
+    if (obj1.isArray()) {
+      nFuncsA = obj1.arrayGetLength();
+      if (nFuncsA > gfxColorMaxComps) {
+       error(-1, "Invalid Function array in shading dictionary");
+       goto err1;
+      }
+      for (i = 0; i < nFuncsA; ++i) {
+       obj1.arrayGet(i, &obj2);
+       if (!(funcsA[i] = Function::parse(&obj2))) {
+         obj1.free();
+         obj2.free();
+         goto err1;
+       }
+       obj2.free();
+      }
+    } else {
+      nFuncsA = 1;
+      if (!(funcsA[0] = Function::parse(&obj1))) {
+       obj1.free();
+       goto err1;
       }
     }
+  } else {
+    nFuncsA = 0;
   }
-  return;
+  obj1.free();
+
+  nPatchesA = 0;
+  patchesA = NULL;
+  patchesSize = 0;
+  bitBuf = new GfxShadingBitBuf(str);
+  while (1) {
+    if (!bitBuf->getBits(flagBits, &flag)) {
+      break;
+    }
+    if (typeA == 6) {
+      switch (flag) {
+      case 0: nPts = 12; nColors = 4; break;
+      case 1:
+      case 2:
+      case 3:
+      default: nPts =  8; nColors = 2; break;
+      }
+    } else {
+      switch (flag) {
+      case 0: nPts = 16; nColors = 4; break;
+      case 1:
+      case 2:
+      case 3:
+      default: nPts = 12; nColors = 2; break;
+      }
+    }
+    for (i = 0; i < nPts; ++i) {
+      if (!bitBuf->getBits(coordBits, &xi) ||
+         !bitBuf->getBits(coordBits, &yi)) {
+       break;
+      }
+      x[i] = xMin + xMul * (double)xi;
+      y[i] = yMin + yMul * (double)yi;
+    }
+    if (i < nPts) {
+      break;
+    }
+    for (i = 0; i < nColors; ++i) {
+      for (j = 0; j < nComps; ++j) {
+       if (!bitBuf->getBits(compBits, &ci[j])) {
+         break;
+       }
+       c[i][j] = dblToCol(cMin[j] + cMul[j] * (double)ci[j]);
+      }
+      if (j < nComps) {
+       break;
+      }
+    }
+    if (i < nColors) {
+      break;
+    }
+    if (nPatchesA == patchesSize) {
+      patchesSize = (patchesSize == 0) ? 16 : 2 * patchesSize;
+      patchesA = (GfxPatch *)greallocn(patchesA,
+                                      patchesSize, sizeof(GfxPatch));
+    }
+    p = &patchesA[nPatchesA];
+    if (typeA == 6) {
+      switch (flag) {
+      case 0:
+       p->x[0][0] = x[0];
+       p->y[0][0] = y[0];
+       p->x[0][1] = x[1];
+       p->y[0][1] = y[1];
+       p->x[0][2] = x[2];
+       p->y[0][2] = y[2];
+       p->x[0][3] = x[3];
+       p->y[0][3] = y[3];
+       p->x[1][3] = x[4];
+       p->y[1][3] = y[4];
+       p->x[2][3] = x[5];
+       p->y[2][3] = y[5];
+       p->x[3][3] = x[6];
+       p->y[3][3] = y[6];
+       p->x[3][2] = x[7];
+       p->y[3][2] = y[7];
+       p->x[3][1] = x[8];
+       p->y[3][1] = y[8];
+       p->x[3][0] = x[9];
+       p->y[3][0] = y[9];
+       p->x[2][0] = x[10];
+       p->y[2][0] = y[10];
+       p->x[1][0] = x[11];
+       p->y[1][0] = y[11];
+       for (j = 0; j < nComps; ++j) {
+         p->color[0][0].c[j] = c[0][j];
+         p->color[0][1].c[j] = c[1][j];
+         p->color[1][1].c[j] = c[2][j];
+         p->color[1][0].c[j] = c[3][j];
+       }
+       break;
+      case 1:
+       p->x[0][0] = patchesA[nPatchesA-1].x[0][3];
+       p->y[0][0] = patchesA[nPatchesA-1].y[0][3];
+       p->x[0][1] = patchesA[nPatchesA-1].x[1][3];
+       p->y[0][1] = patchesA[nPatchesA-1].y[1][3];
+       p->x[0][2] = patchesA[nPatchesA-1].x[2][3];
+       p->y[0][2] = patchesA[nPatchesA-1].y[2][3];
+       p->x[0][3] = patchesA[nPatchesA-1].x[3][3];
+       p->y[0][3] = patchesA[nPatchesA-1].y[3][3];
+       p->x[1][3] = x[0];
+       p->y[1][3] = y[0];
+       p->x[2][3] = x[1];
+       p->y[2][3] = y[1];
+       p->x[3][3] = x[2];
+       p->y[3][3] = y[2];
+       p->x[3][2] = x[3];
+       p->y[3][2] = y[3];
+       p->x[3][1] = x[4];
+       p->y[3][1] = y[4];
+       p->x[3][0] = x[5];
+       p->y[3][0] = y[5];
+       p->x[2][0] = x[6];
+       p->y[2][0] = y[6];
+       p->x[1][0] = x[7];
+       p->y[1][0] = y[7];
+       for (j = 0; j < nComps; ++j) {
+         p->color[0][0].c[j] = patchesA[nPatchesA-1].color[0][1].c[j];
+         p->color[0][1].c[j] = patchesA[nPatchesA-1].color[1][1].c[j];
+         p->color[1][1].c[j] = c[0][j];
+         p->color[1][0].c[j] = c[1][j];
+       }
+       break;
+      case 2:
+       p->x[0][0] = patchesA[nPatchesA-1].x[3][3];
+       p->y[0][0] = patchesA[nPatchesA-1].y[3][3];
+       p->x[0][1] = patchesA[nPatchesA-1].x[3][2];
+       p->y[0][1] = patchesA[nPatchesA-1].y[3][2];
+       p->x[0][2] = patchesA[nPatchesA-1].x[3][1];
+       p->y[0][2] = patchesA[nPatchesA-1].y[3][1];
+       p->x[0][3] = patchesA[nPatchesA-1].x[3][0];
+       p->y[0][3] = patchesA[nPatchesA-1].y[3][0];
+       p->x[1][3] = x[0];
+       p->y[1][3] = y[0];
+       p->x[2][3] = x[1];
+       p->y[2][3] = y[1];
+       p->x[3][3] = x[2];
+       p->y[3][3] = y[2];
+       p->x[3][2] = x[3];
+       p->y[3][2] = y[3];
+       p->x[3][1] = x[4];
+       p->y[3][1] = y[4];
+       p->x[3][0] = x[5];
+       p->y[3][0] = y[5];
+       p->x[2][0] = x[6];
+       p->y[2][0] = y[6];
+       p->x[1][0] = x[7];
+       p->y[1][0] = y[7];
+       for (j = 0; j < nComps; ++j) {
+         p->color[0][0].c[j] = patchesA[nPatchesA-1].color[1][1].c[j];
+         p->color[0][1].c[j] = patchesA[nPatchesA-1].color[1][0].c[j];
+         p->color[1][1].c[j] = c[0][j];
+         p->color[1][0].c[j] = c[1][j];
+       }
+       break;
+      case 3:
+       p->x[0][0] = patchesA[nPatchesA-1].x[3][0];
+       p->y[0][0] = patchesA[nPatchesA-1].y[3][0];
+       p->x[0][1] = patchesA[nPatchesA-1].x[2][0];
+       p->y[0][1] = patchesA[nPatchesA-1].y[2][0];
+       p->x[0][2] = patchesA[nPatchesA-1].x[1][0];
+       p->y[0][2] = patchesA[nPatchesA-1].y[1][0];
+       p->x[0][3] = patchesA[nPatchesA-1].x[0][0];
+       p->y[0][3] = patchesA[nPatchesA-1].y[0][0];
+       p->x[1][3] = x[0];
+       p->y[1][3] = y[0];
+       p->x[2][3] = x[1];
+       p->y[2][3] = y[1];
+       p->x[3][3] = x[2];
+       p->y[3][3] = y[2];
+       p->x[3][2] = x[3];
+       p->y[3][2] = y[3];
+       p->x[3][1] = x[4];
+       p->y[3][1] = y[4];
+       p->x[3][0] = x[5];
+       p->y[3][0] = y[5];
+       p->x[2][0] = x[6];
+       p->y[2][0] = y[6];
+       p->x[1][0] = x[7];
+       p->y[1][0] = y[7];
+       for (j = 0; j < nComps; ++j) {
+         p->color[0][1].c[j] = patchesA[nPatchesA-1].color[1][0].c[j];
+         p->color[0][1].c[j] = patchesA[nPatchesA-1].color[0][0].c[j];
+         p->color[1][1].c[j] = c[0][j];
+         p->color[1][0].c[j] = c[1][j];
+       }
+       break;
+      }
+    } else {
+      switch (flag) {
+      case 0:
+       p->x[0][0] = x[0];
+       p->y[0][0] = y[0];
+       p->x[0][1] = x[1];
+       p->y[0][1] = y[1];
+       p->x[0][2] = x[2];
+       p->y[0][2] = y[2];
+       p->x[0][3] = x[3];
+       p->y[0][3] = y[3];
+       p->x[1][3] = x[4];
+       p->y[1][3] = y[4];
+       p->x[2][3] = x[5];
+       p->y[2][3] = y[5];
+       p->x[3][3] = x[6];
+       p->y[3][3] = y[6];
+       p->x[3][2] = x[7];
+       p->y[3][2] = y[7];
+       p->x[3][1] = x[8];
+       p->y[3][1] = y[8];
+       p->x[3][0] = x[9];
+       p->y[3][0] = y[9];
+       p->x[2][0] = x[10];
+       p->y[2][0] = y[10];
+       p->x[1][0] = x[11];
+       p->y[1][0] = y[11];
+       p->x[1][1] = x[12];
+       p->y[1][1] = y[12];
+       p->x[1][2] = x[13];
+       p->y[1][2] = y[13];
+       p->x[2][2] = x[14];
+       p->y[2][2] = y[14];
+       p->x[2][1] = x[15];
+       p->y[2][1] = y[15];
+       for (j = 0; j < nComps; ++j) {
+         p->color[0][0].c[j] = c[0][j];
+         p->color[0][1].c[j] = c[1][j];
+         p->color[1][1].c[j] = c[2][j];
+         p->color[1][0].c[j] = c[3][j];
+       }
+       break;
+      case 1:
+       p->x[0][0] = patchesA[nPatchesA-1].x[0][3];
+       p->y[0][0] = patchesA[nPatchesA-1].y[0][3];
+       p->x[0][1] = patchesA[nPatchesA-1].x[1][3];
+       p->y[0][1] = patchesA[nPatchesA-1].y[1][3];
+       p->x[0][2] = patchesA[nPatchesA-1].x[2][3];
+       p->y[0][2] = patchesA[nPatchesA-1].y[2][3];
+       p->x[0][3] = patchesA[nPatchesA-1].x[3][3];
+       p->y[0][3] = patchesA[nPatchesA-1].y[3][3];
+       p->x[1][3] = x[0];
+       p->y[1][3] = y[0];
+       p->x[2][3] = x[1];
+       p->y[2][3] = y[1];
+       p->x[3][3] = x[2];
+       p->y[3][3] = y[2];
+       p->x[3][2] = x[3];
+       p->y[3][2] = y[3];
+       p->x[3][1] = x[4];
+       p->y[3][1] = y[4];
+       p->x[3][0] = x[5];
+       p->y[3][0] = y[5];
+       p->x[2][0] = x[6];
+       p->y[2][0] = y[6];
+       p->x[1][0] = x[7];
+       p->y[1][0] = y[7];
+       p->x[1][1] = x[8];
+       p->y[1][1] = y[8];
+       p->x[1][2] = x[9];
+       p->y[1][2] = y[9];
+       p->x[2][2] = x[10];
+       p->y[2][2] = y[10];
+       p->x[2][1] = x[11];
+       p->y[2][1] = y[11];
+       for (j = 0; j < nComps; ++j) {
+         p->color[0][0].c[j] = patchesA[nPatchesA-1].color[0][1].c[j];
+         p->color[0][1].c[j] = patchesA[nPatchesA-1].color[1][1].c[j];
+         p->color[1][1].c[j] = c[0][j];
+         p->color[1][0].c[j] = c[1][j];
+       }
+       break;
+      case 2:
+       p->x[0][0] = patchesA[nPatchesA-1].x[3][3];
+       p->y[0][0] = patchesA[nPatchesA-1].y[3][3];
+       p->x[0][1] = patchesA[nPatchesA-1].x[3][2];
+       p->y[0][1] = patchesA[nPatchesA-1].y[3][2];
+       p->x[0][2] = patchesA[nPatchesA-1].x[3][1];
+       p->y[0][2] = patchesA[nPatchesA-1].y[3][1];
+       p->x[0][3] = patchesA[nPatchesA-1].x[3][0];
+       p->y[0][3] = patchesA[nPatchesA-1].y[3][0];
+       p->x[1][3] = x[0];
+       p->y[1][3] = y[0];
+       p->x[2][3] = x[1];
+       p->y[2][3] = y[1];
+       p->x[3][3] = x[2];
+       p->y[3][3] = y[2];
+       p->x[3][2] = x[3];
+       p->y[3][2] = y[3];
+       p->x[3][1] = x[4];
+       p->y[3][1] = y[4];
+       p->x[3][0] = x[5];
+       p->y[3][0] = y[5];
+       p->x[2][0] = x[6];
+       p->y[2][0] = y[6];
+       p->x[1][0] = x[7];
+       p->y[1][0] = y[7];
+       p->x[1][1] = x[8];
+       p->y[1][1] = y[8];
+       p->x[1][2] = x[9];
+       p->y[1][2] = y[9];
+       p->x[2][2] = x[10];
+       p->y[2][2] = y[10];
+       p->x[2][1] = x[11];
+       p->y[2][1] = y[11];
+       for (j = 0; j < nComps; ++j) {
+         p->color[0][0].c[j] = patchesA[nPatchesA-1].color[1][1].c[j];
+         p->color[0][1].c[j] = patchesA[nPatchesA-1].color[1][0].c[j];
+         p->color[1][1].c[j] = c[0][j];
+         p->color[1][0].c[j] = c[1][j];
+       }
+       break;
+      case 3:
+       p->x[0][0] = patchesA[nPatchesA-1].x[3][0];
+       p->y[0][0] = patchesA[nPatchesA-1].y[3][0];
+       p->x[0][1] = patchesA[nPatchesA-1].x[2][0];
+       p->y[0][1] = patchesA[nPatchesA-1].y[2][0];
+       p->x[0][2] = patchesA[nPatchesA-1].x[1][0];
+       p->y[0][2] = patchesA[nPatchesA-1].y[1][0];
+       p->x[0][3] = patchesA[nPatchesA-1].x[0][0];
+       p->y[0][3] = patchesA[nPatchesA-1].y[0][0];
+       p->x[1][3] = x[0];
+       p->y[1][3] = y[0];
+       p->x[2][3] = x[1];
+       p->y[2][3] = y[1];
+       p->x[3][3] = x[2];
+       p->y[3][3] = y[2];
+       p->x[3][2] = x[3];
+       p->y[3][2] = y[3];
+       p->x[3][1] = x[4];
+       p->y[3][1] = y[4];
+       p->x[3][0] = x[5];
+       p->y[3][0] = y[5];
+       p->x[2][0] = x[6];
+       p->y[2][0] = y[6];
+       p->x[1][0] = x[7];
+       p->y[1][0] = y[7];
+       p->x[1][1] = x[8];
+       p->y[1][1] = y[8];
+       p->x[1][2] = x[9];
+       p->y[1][2] = y[9];
+       p->x[2][2] = x[10];
+       p->y[2][2] = y[10];
+       p->x[2][1] = x[11];
+       p->y[2][1] = y[11];
+       for (j = 0; j < nComps; ++j) {
+         p->color[0][0].c[j] = patchesA[nPatchesA-1].color[1][0].c[j];
+         p->color[0][1].c[j] = patchesA[nPatchesA-1].color[0][0].c[j];
+         p->color[1][1].c[j] = c[0][j];
+         p->color[1][0].c[j] = c[1][j];
+       }
+       break;
+      }
+    }
+    ++nPatchesA;
+    bitBuf->flushBits();
+  }
+  delete bitBuf;
+
+  if (typeA == 6) {
+    for (i = 0; i < nPatchesA; ++i) {
+      p = &patchesA[i];
+      p->x[1][1] = (-4 * p->x[0][0]
+                   +6 * (p->x[0][1] + p->x[1][0])
+                   -2 * (p->x[0][3] + p->x[3][0])
+                   +3 * (p->x[3][1] + p->x[1][3])
+                   - p->x[3][3]) / 9;
+      p->y[1][1] = (-4 * p->y[0][0]
+                   +6 * (p->y[0][1] + p->y[1][0])
+                   -2 * (p->y[0][3] + p->y[3][0])
+                   +3 * (p->y[3][1] + p->y[1][3])
+                   - p->y[3][3]) / 9;
+      p->x[1][2] = (-4 * p->x[0][3]
+                   +6 * (p->x[0][2] + p->x[1][3])
+                   -2 * (p->x[0][0] + p->x[3][3])
+                   +3 * (p->x[3][2] + p->x[1][0])
+                   - p->x[3][0]) / 9;
+      p->y[1][2] = (-4 * p->y[0][3]
+                   +6 * (p->y[0][2] + p->y[1][3])
+                   -2 * (p->y[0][0] + p->y[3][3])
+                   +3 * (p->y[3][2] + p->y[1][0])
+                   - p->y[3][0]) / 9;
+      p->x[2][1] = (-4 * p->x[3][0]
+                   +6 * (p->x[3][1] + p->x[2][0])
+                   -2 * (p->x[3][3] + p->x[0][0])
+                   +3 * (p->x[0][1] + p->x[2][3])
+                   - p->x[0][3]) / 9;
+      p->y[2][1] = (-4 * p->y[3][0]
+                   +6 * (p->y[3][1] + p->y[2][0])
+                   -2 * (p->y[3][3] + p->y[0][0])
+                   +3 * (p->y[0][1] + p->y[2][3])
+                   - p->y[0][3]) / 9;
+      p->x[2][2] = (-4 * p->x[3][3]
+                   +6 * (p->x[3][2] + p->x[2][3])
+                   -2 * (p->x[3][0] + p->x[0][3])
+                   +3 * (p->x[0][2] + p->x[2][0])
+                   - p->x[0][0]) / 9;
+      p->y[2][2] = (-4 * p->y[3][3]
+                   +6 * (p->y[3][2] + p->y[2][3])
+                   -2 * (p->y[3][0] + p->y[0][3])
+                   +3 * (p->y[0][2] + p->y[2][0])
+                   - p->y[0][0]) / 9;
+    }
+  }
+
+  shading = new GfxPatchMeshShading(typeA, patchesA, nPatchesA,
+                                   funcsA, nFuncsA);
+  if (!shading->init(dict)) {
+    delete shading;
+    return NULL;
+  }
+  return shading;
+
+ err2:
+  obj1.free();
+ err1:
+  return NULL;
+}
+
+GfxShading *GfxPatchMeshShading::copy() {
+  return new GfxPatchMeshShading(this);
 }
 
 //------------------------------------------------------------------------
 // GfxImageColorMap
 //------------------------------------------------------------------------
 
-GfxImageColorMap::GfxImageColorMap(int bits, Object *decode,
-                                  GfxColorSpace *colorSpace) {
+GfxImageColorMap::GfxImageColorMap(int bitsA, Object *decode,
+                                  GfxColorSpace *colorSpaceA) {
   GfxIndexedColorSpace *indexedCS;
   GfxSeparationColorSpace *sepCS;
   int maxPixel, indexHigh;
   Guchar *lookup2;
   Function *sepFunc;
   Object obj;
-  double x;
+  double x[gfxColorMaxComps];
   double y[gfxColorMaxComps];
   int i, j, k;
 
   ok = gTrue;
 
   // bits per component and color space
-  this->bits = bits;
+  bits = bitsA;
   maxPixel = (1 << bits) - 1;
-  this->colorSpace = colorSpace;
+  colorSpace = colorSpaceA;
 
   // get decode map
   if (decode->isNull()) {
@@ -1740,18 +3140,6 @@ GfxImageColorMap::GfxImageColorMap(int bits, Object *decode,
     goto err1;
   }
 
-#if 0 //~
-  // handle the case where fewer than 2^n palette entries of an n-bit
-  // indexed color space are populated (this happens, e.g., in files
-  // optimized by Distiller)
-  if (colorSpace->getMode() == csIndexed) {
-    i = ((GfxIndexedColorSpace *)colorSpace)->getIndexHigh();
-    if (i < maxPixel) {
-      maxPixel = i;
-    }
-  }
-#endif
-
   // Construct a lookup table -- this stores pre-computed decoded
   // values for each component, i.e., the result of applying the
   // decode mapping to each possible image pixel component value.
@@ -1759,6 +3147,9 @@ GfxImageColorMap::GfxImageColorMap(int bits, Object *decode,
   // Optimization: for Indexed and Separation color spaces (which have
   // only one component), we store color values in the lookup table
   // rather than component values.
+  for (k = 0; k < gfxColorMaxComps; ++k) {
+    lookup[k] = NULL;
+  }
   colorSpace2 = NULL;
   nComps2 = 0;
   if (colorSpace->getMode() == csIndexed) {
@@ -1769,33 +3160,43 @@ GfxImageColorMap::GfxImageColorMap(int bits, Object *decode,
     colorSpace2 = indexedCS->getBase();
     indexHigh = indexedCS->getIndexHigh();
     nComps2 = colorSpace2->getNComps();
-    lookup = (double *)gmalloc((indexHigh + 1) * nComps2 * sizeof(double));
     lookup2 = indexedCS->getLookup();
-    for (i = 0; i <= indexHigh; ++i) {
-      j = (int)(decodeLow[0] +(i * decodeRange[0]) / maxPixel + 0.5);
-      for (k = 0; k < nComps2; ++k) {
-       lookup[i*nComps2 + k] = lookup2[i*nComps2 + k] / 255.0;
+    colorSpace2->getDefaultRanges(x, y, indexHigh);
+    for (k = 0; k < nComps2; ++k) {
+      lookup[k] = (GfxColorComp *)gmallocn(maxPixel + 1,
+                                          sizeof(GfxColorComp));
+      for (i = 0; i <= maxPixel; ++i) {
+       j = (int)(decodeLow[0] + (i * decodeRange[0]) / maxPixel + 0.5);
+       if (j < 0) {
+         j = 0;
+       } else if (j > indexHigh) {
+         j = indexHigh;
+       }
+       lookup[k][i] =
+           dblToCol(x[k] + (lookup2[j*nComps2 + k] / 255.0) * y[k]);
       }
     }
   } else if (colorSpace->getMode() == csSeparation) {
     sepCS = (GfxSeparationColorSpace *)colorSpace;
     colorSpace2 = sepCS->getAlt();
     nComps2 = colorSpace2->getNComps();
-    lookup = (double *)gmalloc((maxPixel + 1) * nComps2 * sizeof(double));
     sepFunc = sepCS->getFunc();
-    for (i = 0; i <= maxPixel; ++i) {
-      x = decodeLow[0] + (i * decodeRange[0]) / maxPixel;
-      sepFunc->transform(&x, y);
-      for (k = 0; k < nComps2; ++k) {
-       lookup[i*nComps2 + k] = y[k];
+    for (k = 0; k < nComps2; ++k) {
+      lookup[k] = (GfxColorComp *)gmallocn(maxPixel + 1,
+                                          sizeof(GfxColorComp));
+      for (i = 0; i <= maxPixel; ++i) {
+       x[0] = decodeLow[0] + (i * decodeRange[0]) / maxPixel;
+       sepFunc->transform(x, y);
+       lookup[k][i] = dblToCol(y[k]);
       }
     }
   } else {
-    lookup = (double *)gmalloc((maxPixel + 1) * nComps * sizeof(double));
-    for (i = 0; i <= maxPixel; ++i) {
-      for (k = 0; k < nComps; ++k) {
-       lookup[i*nComps + k] = decodeLow[k] +
-                                (i * decodeRange[k]) / maxPixel;
+    for (k = 0; k < nComps; ++k) {
+      lookup[k] = (GfxColorComp *)gmallocn(maxPixel + 1,
+                                          sizeof(GfxColorComp));
+      for (i = 0; i <= maxPixel; ++i) {
+       lookup[k][i] = dblToCol(decodeLow[k] +
+                               (i * decodeRange[k]) / maxPixel);
       }
     }
   }
@@ -1808,25 +3209,64 @@ GfxImageColorMap::GfxImageColorMap(int bits, Object *decode,
   ok = gFalse;
 }
 
+GfxImageColorMap::GfxImageColorMap(GfxImageColorMap *colorMap) {
+  int n, i, k;
+
+  colorSpace = colorMap->colorSpace->copy();
+  bits = colorMap->bits;
+  nComps = colorMap->nComps;
+  nComps2 = colorMap->nComps2;
+  colorSpace2 = NULL;
+  for (k = 0; k < gfxColorMaxComps; ++k) {
+    lookup[k] = NULL;
+  }
+  n = 1 << bits;
+  if (colorSpace->getMode() == csIndexed) {
+    colorSpace2 = ((GfxIndexedColorSpace *)colorSpace)->getBase();
+    for (k = 0; k < nComps2; ++k) {
+      lookup[k] = (GfxColorComp *)gmallocn(n, sizeof(GfxColorComp));
+      memcpy(lookup[k], colorMap->lookup[k], n * sizeof(GfxColorComp));
+    }
+  } else if (colorSpace->getMode() == csSeparation) {
+    colorSpace2 = ((GfxSeparationColorSpace *)colorSpace)->getAlt();
+    for (k = 0; k < nComps2; ++k) {
+      lookup[k] = (GfxColorComp *)gmallocn(n, sizeof(GfxColorComp));
+      memcpy(lookup[k], colorMap->lookup[k], n * sizeof(GfxColorComp));
+    }
+  } else {
+    for (k = 0; k < nComps; ++k) {
+      lookup[k] = (GfxColorComp *)gmallocn(n, sizeof(GfxColorComp));
+      memcpy(lookup[k], colorMap->lookup[k], n * sizeof(GfxColorComp));
+    }
+  }
+  for (i = 0; i < nComps; ++i) {
+    decodeLow[i] = colorMap->decodeLow[i];
+    decodeRange[i] = colorMap->decodeRange[i];
+  }
+  ok = gTrue;
+}
+
 GfxImageColorMap::~GfxImageColorMap() {
+  int i;
+
   delete colorSpace;
-  gfree(lookup);
+  for (i = 0; i < gfxColorMaxComps; ++i) {
+    gfree(lookup[i]);
+  }
 }
 
-void GfxImageColorMap::getGray(Guchar *x, double *gray) {
+void GfxImageColorMap::getGray(Guchar *x, GfxGray *gray) {
   GfxColor color;
-  double *p;
   int i;
 
   if (colorSpace2) {
-    p = &lookup[x[0] * nComps2];
     for (i = 0; i < nComps2; ++i) {
-      color.c[i] = *p++;
+      color.c[i] = lookup[i][x[0]];
     }
     colorSpace2->getGray(&color, gray);
   } else {
     for (i = 0; i < nComps; ++i) {
-      color.c[i] = lookup[x[i] * nComps + i];
+      color.c[i] = lookup[i][x[i]];
     }
     colorSpace->getGray(&color, gray);
   }
@@ -1834,18 +3274,16 @@ void GfxImageColorMap::getGray(Guchar *x, double *gray) {
 
 void GfxImageColorMap::getRGB(Guchar *x, GfxRGB *rgb) {
   GfxColor color;
-  double *p;
   int i;
 
   if (colorSpace2) {
-    p = &lookup[x[0] * nComps2];
     for (i = 0; i < nComps2; ++i) {
-      color.c[i] = *p++;
+      color.c[i] = lookup[i][x[0]];
     }
     colorSpace2->getRGB(&color, rgb);
   } else {
     for (i = 0; i < nComps; ++i) {
-      color.c[i] = lookup[x[i] * nComps + i];
+      color.c[i] = lookup[i][x[i]];
     }
     colorSpace->getRGB(&color, rgb);
   }
@@ -1853,32 +3291,39 @@ void GfxImageColorMap::getRGB(Guchar *x, GfxRGB *rgb) {
 
 void GfxImageColorMap::getCMYK(Guchar *x, GfxCMYK *cmyk) {
   GfxColor color;
-  double *p;
   int i;
 
   if (colorSpace2) {
-    p = &lookup[x[0] * nComps2];
     for (i = 0; i < nComps2; ++i) {
-      color.c[i] = *p++;
+      color.c[i] = lookup[i][x[0]];
     }
     colorSpace2->getCMYK(&color, cmyk);
   } else {
     for (i = 0; i < nComps; ++i) {
-      color.c[i] = lookup[x[i] * nComps + i];
+      color.c[i] = lookup[i][x[i]];
     }
     colorSpace->getCMYK(&color, cmyk);
   }
 }
 
+void GfxImageColorMap::getColor(Guchar *x, GfxColor *color) {
+  int maxPixel, i;
+
+  maxPixel = (1 << bits) - 1;
+  for (i = 0; i < nComps; ++i) {
+    color->c[i] = dblToCol(decodeLow[i] + (x[i] * decodeRange[i]) / maxPixel);
+  }
+}
+
 //------------------------------------------------------------------------
 // GfxSubpath and GfxPath
 //------------------------------------------------------------------------
 
 GfxSubpath::GfxSubpath(double x1, double y1) {
   size = 16;
-  x = (double *)gmalloc(size * sizeof(double));
-  y = (double *)gmalloc(size * sizeof(double));
-  curve = (GBool *)gmalloc(size * sizeof(GBool));
+  x = (double *)gmallocn(size, sizeof(double));
+  y = (double *)gmallocn(size, sizeof(double));
+  curve = (GBool *)gmallocn(size, sizeof(GBool));
   n = 1;
   x[0] = x1;
   y[0] = y1;
@@ -1896,9 +3341,9 @@ GfxSubpath::~GfxSubpath() {
 GfxSubpath::GfxSubpath(GfxSubpath *subpath) {
   size = subpath->size;
   n = subpath->n;
-  x = (double *)gmalloc(size * sizeof(double));
-  y = (double *)gmalloc(size * sizeof(double));
-  curve = (GBool *)gmalloc(size * sizeof(GBool));
+  x = (double *)gmallocn(size, sizeof(double));
+  y = (double *)gmallocn(size, sizeof(double));
+  curve = (GBool *)gmallocn(size, sizeof(GBool));
   memcpy(x, subpath->x, n * sizeof(double));
   memcpy(y, subpath->y, n * sizeof(double));
   memcpy(curve, subpath->curve, n * sizeof(GBool));
@@ -1908,9 +3353,9 @@ GfxSubpath::GfxSubpath(GfxSubpath *subpath) {
 void GfxSubpath::lineTo(double x1, double y1) {
   if (n >= size) {
     size += 16;
-    x = (double *)grealloc(x, size * sizeof(double));
-    y = (double *)grealloc(y, size * sizeof(double));
-    curve = (GBool *)grealloc(curve, size * sizeof(GBool));
+    x = (double *)greallocn(x, size, sizeof(double));
+    y = (double *)greallocn(y, size, sizeof(double));
+    curve = (GBool *)greallocn(curve, size, sizeof(GBool));
   }
   x[n] = x1;
   y[n] = y1;
@@ -1922,9 +3367,9 @@ void GfxSubpath::curveTo(double x1, double y1, double x2, double y2,
                         double x3, double y3) {
   if (n+3 > size) {
     size += 16;
-    x = (double *)grealloc(x, size * sizeof(double));
-    y = (double *)grealloc(y, size * sizeof(double));
-    curve = (GBool *)grealloc(curve, size * sizeof(GBool));
+    x = (double *)greallocn(x, size, sizeof(double));
+    y = (double *)greallocn(y, size, sizeof(double));
+    curve = (GBool *)greallocn(curve, size, sizeof(GBool));
   }
   x[n] = x1;
   y[n] = y1;
@@ -1944,12 +3389,21 @@ void GfxSubpath::close() {
   closed = gTrue;
 }
 
+void GfxSubpath::offset(double dx, double dy) {
+  int i;
+
+  for (i = 0; i < n; ++i) {
+    x[i] += dx;
+    y[i] += dy;
+  }
+}
+
 GfxPath::GfxPath() {
   justMoved = gFalse;
   size = 16;
   n = 0;
   firstX = firstY = 0;
-  subpaths = (GfxSubpath **)gmalloc(size * sizeof(GfxSubpath *));
+  subpaths = (GfxSubpath **)gmallocn(size, sizeof(GfxSubpath *));
 }
 
 GfxPath::~GfxPath() {
@@ -1970,7 +3424,7 @@ GfxPath::GfxPath(GBool justMoved1, double firstX1, double firstY1,
   firstY = firstY1;
   size = size1;
   n = n1;
-  subpaths = (GfxSubpath **)gmalloc(size * sizeof(GfxSubpath *));
+  subpaths = (GfxSubpath **)gmallocn(size, sizeof(GfxSubpath *));
   for (i = 0; i < n; ++i)
     subpaths[i] = subpaths1[i]->copy();
 }
@@ -1986,7 +3440,7 @@ void GfxPath::lineTo(double x, double y) {
     if (n >= size) {
       size += 16;
       subpaths = (GfxSubpath **)
-                  grealloc(subpaths, size * sizeof(GfxSubpath *));
+                  greallocn(subpaths, size, sizeof(GfxSubpath *));
     }
     subpaths[n] = new GfxSubpath(firstX, firstY);
     ++n;
@@ -2001,7 +3455,7 @@ void GfxPath::curveTo(double x1, double y1, double x2, double y2,
     if (n >= size) {
       size += 16;
       subpaths = (GfxSubpath **)
-                  grealloc(subpaths, size * sizeof(GfxSubpath *));
+                  greallocn(subpaths, size, sizeof(GfxSubpath *));
     }
     subpaths[n] = new GfxSubpath(firstX, firstY);
     ++n;
@@ -2010,56 +3464,95 @@ void GfxPath::curveTo(double x1, double y1, double x2, double y2,
   subpaths[n-1]->curveTo(x1, y1, x2, y2, x3, y3);
 }
 
+void GfxPath::close() {
+  // this is necessary to handle the pathological case of
+  // moveto/closepath/clip, which defines an empty clipping region
+  if (justMoved) {
+    if (n >= size) {
+      size += 16;
+      subpaths = (GfxSubpath **)
+                  greallocn(subpaths, size, sizeof(GfxSubpath *));
+    }
+    subpaths[n] = new GfxSubpath(firstX, firstY);
+    ++n;
+    justMoved = gFalse;
+  }
+  subpaths[n-1]->close();
+}
+
+void GfxPath::append(GfxPath *path) {
+  int i;
+
+  if (n + path->n > size) {
+    size = n + path->n;
+    subpaths = (GfxSubpath **)
+                 greallocn(subpaths, size, sizeof(GfxSubpath *));
+  }
+  for (i = 0; i < path->n; ++i) {
+    subpaths[n++] = path->subpaths[i]->copy();
+  }
+  justMoved = gFalse;
+}
+
+void GfxPath::offset(double dx, double dy) {
+  int i;
+
+  for (i = 0; i < n; ++i) {
+    subpaths[i]->offset(dx, dy);
+  }
+}
 
 //------------------------------------------------------------------------
 // GfxState
 //------------------------------------------------------------------------
 
-GfxState::GfxState(double dpi, double px1a, double py1a,
-                  double px2a, double py2a, int rotate, GBool upsideDown) {
-  double k;
-
-  px1 = px1a;
-  py1 = py1a;
-  px2 = px2a;
-  py2 = py2a;
-  k = dpi / 72.0;
+GfxState::GfxState(double hDPI, double vDPI, PDFRectangle *pageBox,
+                  int rotateA, GBool upsideDown) {
+  double kx, ky;
+
+  rotate = rotateA;
+  px1 = pageBox->x1;
+  py1 = pageBox->y1;
+  px2 = pageBox->x2;
+  py2 = pageBox->y2;
+  kx = hDPI / 72.0;
+  ky = vDPI / 72.0;
   if (rotate == 90) {
     ctm[0] = 0;
-    ctm[1] = upsideDown ? k : -k;
-    ctm[2] = k;
+    ctm[1] = upsideDown ? ky : -ky;
+    ctm[2] = kx;
     ctm[3] = 0;
-    ctm[4] = -k * py1;
-    ctm[5] = k * (upsideDown ? -px1 : px2);
-    pageWidth = k * (py2 - py1);
-    pageHeight = k * (px2 - px1);
+    ctm[4] = -kx * py1;
+    ctm[5] = ky * (upsideDown ? -px1 : px2);
+    pageWidth = kx * (py2 - py1);
+    pageHeight = ky * (px2 - px1);
   } else if (rotate == 180) {
-    ctm[0] = -k;
+    ctm[0] = -kx;
     ctm[1] = 0;
     ctm[2] = 0;
-    ctm[3] = upsideDown ? k : -k;
-    ctm[4] = k * px2;
-    ctm[5] = k * (upsideDown ? -py1 : py2);
-    pageWidth = k * (px2 - px1);
-    pageHeight = k * (py2 - py1);
+    ctm[3] = upsideDown ? ky : -ky;
+    ctm[4] = kx * px2;
+    ctm[5] = ky * (upsideDown ? -py1 : py2);
+    pageWidth = kx * (px2 - px1);
+    pageHeight = ky * (py2 - py1);
   } else if (rotate == 270) {
     ctm[0] = 0;
-    ctm[1] = upsideDown ? -k : k;
-    ctm[2] = -k;
+    ctm[1] = upsideDown ? -ky : ky;
+    ctm[2] = -kx;
     ctm[3] = 0;
-    ctm[4] = k * py2;
-    ctm[5] = k * (upsideDown ? px2 : -px1);
-    pageWidth = k * (py2 - py1);
-    pageHeight = k * (px2 - px1);
+    ctm[4] = kx * py2;
+    ctm[5] = ky * (upsideDown ? px2 : -px1);
+    pageWidth = kx * (py2 - py1);
+    pageHeight = ky * (px2 - px1);
   } else {
-    ctm[0] = k;
+    ctm[0] = kx;
     ctm[1] = 0;
     ctm[2] = 0;
-    ctm[3] = upsideDown ? -k : k;
-    ctm[4] = -k * px1;
-    ctm[5] = k * (upsideDown ? py2 : -py1);
-    pageWidth = k * (px2 - px1);
-    pageHeight = k * (py2 - py1);
+    ctm[3] = upsideDown ? -ky : ky;
+    ctm[4] = -kx * px1;
+    ctm[5] = ky * (upsideDown ? py2 : -py1);
+    pageWidth = kx * (px2 - px1);
+    pageHeight = ky * (py2 - py1);
   }
 
   fillColorSpace = new GfxDeviceGrayColorSpace();
@@ -2068,14 +3561,17 @@ GfxState::GfxState(double dpi, double px1a, double py1a,
   strokeColor.c[0] = 0;
   fillPattern = NULL;
   strokePattern = NULL;
+  blendMode = gfxBlendNormal;
   fillOpacity = 1;
   strokeOpacity = 1;
+  fillOverprint = gFalse;
+  strokeOverprint = gFalse;
 
   lineWidth = 1;
   lineDash = NULL;
   lineDashLength = 0;
   lineDashStart = 0;
-  flatness = 0;
+  flatness = 1;
   lineJoin = 0;
   lineCap = 0;
   miterLimit = 10;
@@ -2096,6 +3592,11 @@ GfxState::GfxState(double dpi, double px1a, double py1a,
   curX = curY = 0;
   lineX = lineY = 0;
 
+  clipXMin = 0;
+  clipYMin = 0;
+  clipXMax = pageWidth;
+  clipYMax = pageHeight;
+
   saved = NULL;
 }
 
@@ -2113,7 +3614,10 @@ GfxState::~GfxState() {
     delete strokePattern;
   }
   gfree(lineDash);
-  delete path;
+  if (path) {
+    // this gets set to NULL by restore()
+    delete path;
+  }
   if (saved) {
     delete saved;
   }
@@ -2135,13 +3639,78 @@ GfxState::GfxState(GfxState *state) {
     strokePattern = state->strokePattern->copy();
   }
   if (lineDashLength > 0) {
-    lineDash = (double *)gmalloc(lineDashLength * sizeof(double));
+    lineDash = (double *)gmallocn(lineDashLength, sizeof(double));
     memcpy(lineDash, state->lineDash, lineDashLength * sizeof(double));
   }
-  path = state->path->copy();
   saved = NULL;
 }
 
+void GfxState::setPath(GfxPath *pathA) {
+  delete path;
+  path = pathA;
+}
+
+void GfxState::getUserClipBBox(double *xMin, double *yMin,
+                              double *xMax, double *yMax) {
+  double ictm[6];
+  double xMin1, yMin1, xMax1, yMax1, det, tx, ty;
+
+  // invert the CTM
+  det = 1 / (ctm[0] * ctm[3] - ctm[1] * ctm[2]);
+  ictm[0] = ctm[3] * det;
+  ictm[1] = -ctm[1] * det;
+  ictm[2] = -ctm[2] * det;
+  ictm[3] = ctm[0] * det;
+  ictm[4] = (ctm[2] * ctm[5] - ctm[3] * ctm[4]) * det;
+  ictm[5] = (ctm[1] * ctm[4] - ctm[0] * ctm[5]) * det;
+
+  // transform all four corners of the clip bbox; find the min and max
+  // x and y values
+  xMin1 = xMax1 = clipXMin * ictm[0] + clipYMin * ictm[2] + ictm[4];
+  yMin1 = yMax1 = clipXMin * ictm[1] + clipYMin * ictm[3] + ictm[5];
+  tx = clipXMin * ictm[0] + clipYMax * ictm[2] + ictm[4];
+  ty = clipXMin * ictm[1] + clipYMax * ictm[3] + ictm[5];
+  if (tx < xMin1) {
+    xMin1 = tx;
+  } else if (tx > xMax1) {
+    xMax1 = tx;
+  }
+  if (ty < yMin1) {
+    yMin1 = ty;
+  } else if (ty > yMax1) {
+    yMax1 = ty;
+  }
+  tx = clipXMax * ictm[0] + clipYMin * ictm[2] + ictm[4];
+  ty = clipXMax * ictm[1] + clipYMin * ictm[3] + ictm[5];
+  if (tx < xMin1) {
+    xMin1 = tx;
+  } else if (tx > xMax1) {
+    xMax1 = tx;
+  }
+  if (ty < yMin1) {
+    yMin1 = ty;
+  } else if (ty > yMax1) {
+    yMax1 = ty;
+  }
+  tx = clipXMax * ictm[0] + clipYMax * ictm[2] + ictm[4];
+  ty = clipXMax * ictm[1] + clipYMax * ictm[3] + ictm[5];
+  if (tx < xMin1) {
+    xMin1 = tx;
+  } else if (tx > xMax1) {
+    xMax1 = tx;
+  }
+  if (ty < yMin1) {
+    yMin1 = ty;
+  } else if (ty > yMax1) {
+    yMax1 = ty;
+  }
+
+  *xMin = xMin1;
+  *yMin = yMin1;
+  *xMax = xMax1;
+  *yMax = yMax1;
+}
+
 double GfxState::transformWidth(double w) {
   double x, y;
 
@@ -2170,12 +3739,23 @@ void GfxState::getFontTransMat(double *m11, double *m12,
 
 void GfxState::setCTM(double a, double b, double c,
                      double d, double e, double f) {
+  int i;
+
   ctm[0] = a;
   ctm[1] = b;
   ctm[2] = c;
   ctm[3] = d;
   ctm[4] = e;
   ctm[5] = f;
+
+  // avoid FP exceptions on badly messed up PDF files
+  for (i = 0; i < 6; ++i) {
+    if (ctm[i] > 1e10) {
+      ctm[i] = 1e10;
+    } else if (ctm[i] < -1e10) {
+      ctm[i] = -1e10;
+    }
+  }
 }
 
 void GfxState::concatCTM(double a, double b, double c,
@@ -2184,6 +3764,7 @@ void GfxState::concatCTM(double a, double b, double c,
   double b1 = ctm[1];
   double c1 = ctm[2];
   double d1 = ctm[3];
+  int i;
 
   ctm[0] = a * a1 + b * c1;
   ctm[1] = a * b1 + b * d1;
@@ -2191,6 +3772,15 @@ void GfxState::concatCTM(double a, double b, double c,
   ctm[3] = c * b1 + d * d1;
   ctm[4] = e * a1 + f * c1 + ctm[4];
   ctm[5] = e * b1 + f * d1 + ctm[5];
+
+  // avoid FP exceptions on badly messed up PDF files
+  for (i = 0; i < 6; ++i) {
+    if (ctm[i] > 1e10) {
+      ctm[i] = 1e10;
+    } else if (ctm[i] < -1e10) {
+      ctm[i] = -1e10;
+    }
+  }
 }
 
 void GfxState::setFillColorSpace(GfxColorSpace *colorSpace) {
@@ -2234,12 +3824,45 @@ void GfxState::clearPath() {
   path = new GfxPath();
 }
 
-void GfxState::textShift(double tx) {
-  double dx, dy;
-
-  textTransformDelta(tx, 0, &dx, &dy);
-  curX += dx;
-  curY += dy;
+void GfxState::clip() {
+  double xMin, yMin, xMax, yMax, x, y;
+  GfxSubpath *subpath;
+  int i, j;
+
+  xMin = xMax = yMin = yMax = 0; // make gcc happy
+  for (i = 0; i < path->getNumSubpaths(); ++i) {
+    subpath = path->getSubpath(i);
+    for (j = 0; j < subpath->getNumPoints(); ++j) {
+      transform(subpath->getX(j), subpath->getY(j), &x, &y);
+      if (i == 0 && j == 0) {
+       xMin = xMax = x;
+       yMin = yMax = y;
+      } else {
+       if (x < xMin) {
+         xMin = x;
+       } else if (x > xMax) {
+         xMax = x;
+       }
+       if (y < yMin) {
+         yMin = y;
+       } else if (y > yMax) {
+         yMax = y;
+       }
+      }
+    }
+  }
+  if (xMin > clipXMin) {
+    clipXMin = xMin;
+  }
+  if (yMin > clipYMin) {
+    clipYMin = yMin;
+  }
+  if (xMax < clipXMax) {
+    clipXMax = xMax;
+  }
+  if (yMax < clipYMax) {
+    clipYMax = yMax;
+  }
 }
 
 void GfxState::textShift(double tx, double ty) {
@@ -2250,6 +3873,11 @@ void GfxState::textShift(double tx, double ty) {
   curY += dy;
 }
 
+void GfxState::shift(double dx, double dy) {
+  curX += dx;
+  curY += dy;
+}
+
 GfxState *GfxState::save() {
   GfxState *newState;
 
@@ -2263,10 +3891,56 @@ GfxState *GfxState::restore() {
 
   if (saved) {
     oldState = saved;
+
+    // these attributes aren't saved/restored by the q/Q operators
+    oldState->path = path;
+    oldState->curX = curX;
+    oldState->curY = curY;
+    oldState->lineX = lineX;
+    oldState->lineY = lineY;
+
+    path = NULL;
     saved = NULL;
     delete this;
+
   } else {
     oldState = this;
   }
+
   return oldState;
 }
+
+GBool GfxState::parseBlendMode(Object *obj, GfxBlendMode *mode) {
+  Object obj2;
+  int i, j;
+
+  if (obj->isName()) {
+    for (i = 0; i < nGfxBlendModeNames; ++i) {
+      if (!strcmp(obj->getName(), gfxBlendModeNames[i].name)) {
+       *mode = gfxBlendModeNames[i].mode;
+       return gTrue;
+      }
+    }
+    return gFalse;
+  } else if (obj->isArray()) {
+    for (i = 0; i < obj->arrayGetLength(); ++i) {
+      obj->arrayGet(i, &obj2);
+      if (!obj2.isName()) {
+       obj2.free();
+       return gFalse;
+      }
+      for (j = 0; j < nGfxBlendModeNames; ++j) {
+       if (!strcmp(obj2.getName(), gfxBlendModeNames[j].name)) {
+         obj2.free();
+         *mode = gfxBlendModeNames[j].mode;
+         return gTrue;
+       }
+      }
+      obj2.free();
+    }
+    *mode = gfxBlendNormal;
+    return gTrue;
+  } else {
+    return gFalse;
+  }
+}