upgraded to xpdf-3.01pl1
authorkramm <kramm>
Sat, 3 Dec 2005 14:47:09 +0000 (14:47 +0000)
committerkramm <kramm>
Sat, 3 Dec 2005 14:47:09 +0000 (14:47 +0000)
pdf2swf/xpdf/FoFiType1C.cc
pdf2swf/xpdf/Gfx.cc
pdf2swf/xpdf/GfxFont.h

index a1cec31..499414b 100644 (file)
@@ -120,7 +120,7 @@ Gushort *FoFiType1C::getCIDToGIDMap(int *nCIDs) {
     }
   }
   ++n;
-  map = (Gushort *)gmalloc(n * sizeof(Gushort));
+  map = (Gushort *)gmallocn(n, sizeof(Gushort));
   memset(map, 0, n * sizeof(Gushort));
   for (i = 0; i < nGlyphs; ++i) {
     map[charset[i]] = i;
@@ -231,11 +231,8 @@ void FoFiType1C::convertToType1(char **newEncoding, GBool ascii,
     (*outputFunc)(outputStream,
                  "0 1 255 {1 index exch /.notdef put} for\n", 40);
     enc = newEncoding ? newEncoding : encoding;
-    if(!enc) {
-       fprintf(stderr, "convertToType1: Warning: No Encoding\n");
-    }
     for (i = 0; i < 256; ++i) {
-      if (enc && enc[i]) {
+      if (enc[i]) {
        sprintf(buf, "dup %d /%s put\n", i, enc[i]);
        (*outputFunc)(outputStream, buf, strlen(buf));
       }
@@ -406,7 +403,7 @@ void FoFiType1C::convertToCIDType0(char *psName,
       nCIDs = charset[i] + 1;
     }
   }
-  cidMap = (int *)gmalloc(nCIDs * sizeof(int));
+  cidMap = (int *)gmallocn(nCIDs, sizeof(int));
   for (i = 0; i < nCIDs; ++i) {
     cidMap[i] = -1;
   }
@@ -416,7 +413,7 @@ void FoFiType1C::convertToCIDType0(char *psName,
 
   // build the charstrings
   charStrings = new GString();
-  charStringOffsets = (int *)gmalloc((nCIDs + 1) * sizeof(int));
+  charStringOffsets = (int *)gmallocn(nCIDs + 1, sizeof(int));
   for (i = 0; i < nCIDs; ++i) {
     charStringOffsets[i] = charStrings->getLength();
     if ((gid = cidMap[i]) >= 0) {
@@ -479,10 +476,18 @@ void FoFiType1C::convertToCIDType0(char *psName,
   sprintf(buf, "  /Supplement %d def\n", topDict.supplement);
   (*outputFunc)(outputStream, buf, strlen(buf));
   (*outputFunc)(outputStream, "end def\n", 8);
-  sprintf(buf, "/FontMatrix [%g %g %g %g %g %g] def\n",
-         topDict.fontMatrix[0], topDict.fontMatrix[1], topDict.fontMatrix[2],
-         topDict.fontMatrix[3], topDict.fontMatrix[4], topDict.fontMatrix[5]);
-  (*outputFunc)(outputStream, buf, strlen(buf));
+  if (topDict.hasFontMatrix) {
+    sprintf(buf, "/FontMatrix [%g %g %g %g %g %g] def\n",
+           topDict.fontMatrix[0], topDict.fontMatrix[1],
+           topDict.fontMatrix[2], topDict.fontMatrix[3],
+           topDict.fontMatrix[4], topDict.fontMatrix[5]);
+    (*outputFunc)(outputStream, buf, strlen(buf));
+  } else if (privateDicts[0].hasFontMatrix) {
+    (*outputFunc)(outputStream, "/FontMatrix [1 0 0 1 0 0] def\n", 30);
+  } else {
+    (*outputFunc)(outputStream,
+                 "/FontMatrix [0.001 0 0 0.001 0 0] def\n", 38);
+  }
   sprintf(buf, "/FontBBox [%g %g %g %g] def\n",
          topDict.fontBBox[0], topDict.fontBBox[1],
          topDict.fontBBox[2], topDict.fontBBox[3]);
@@ -512,7 +517,18 @@ void FoFiType1C::convertToCIDType0(char *psName,
     sprintf(buf, "dup %d 10 dict begin\n", i);
     (*outputFunc)(outputStream, buf, strlen(buf));
     (*outputFunc)(outputStream, "/FontType 1 def\n", 16);
-    (*outputFunc)(outputStream, "/FontMatrix [1 0 0 1 0 0] def\n", 30);
+    if (privateDicts[i].hasFontMatrix) {
+      sprintf(buf, "/FontMatrix [%g %g %g %g %g %g] def\n",
+             privateDicts[i].fontMatrix[0],
+             privateDicts[i].fontMatrix[1],
+             privateDicts[i].fontMatrix[2],
+             privateDicts[i].fontMatrix[3],
+             privateDicts[i].fontMatrix[4],
+             privateDicts[i].fontMatrix[5]);
+      (*outputFunc)(outputStream, buf, strlen(buf));
+    } else {
+      (*outputFunc)(outputStream, "/FontMatrix [1 0 0 1 0 0] def\n", 30);
+    }
     sprintf(buf, "/PaintType %d def\n", topDict.paintType);
     (*outputFunc)(outputStream, buf, strlen(buf));
     (*outputFunc)(outputStream, "/Private 32 dict begin\n", 23);
@@ -673,7 +689,7 @@ void FoFiType1C::convertToType0(char *psName,
       nCIDs = charset[i] + 1;
     }
   }
-  cidMap = (int *)gmalloc(nCIDs * sizeof(int));
+  cidMap = (int *)gmallocn(nCIDs, sizeof(int));
   for (i = 0; i < nCIDs; ++i) {
     cidMap[i] = -1;
   }
@@ -702,11 +718,21 @@ void FoFiType1C::convertToType0(char *psName,
     sprintf(buf, "_%02x def\n", i >> 8);
     (*outputFunc)(outputStream, buf, strlen(buf));
     (*outputFunc)(outputStream, "/FontType 1 def\n", 16);
-    sprintf(buf, "/FontMatrix [%g %g %g %g %g %g] def\n",
-           topDict.fontMatrix[0], topDict.fontMatrix[1],
-           topDict.fontMatrix[2], topDict.fontMatrix[3],
-           topDict.fontMatrix[4], topDict.fontMatrix[5]);
-    (*outputFunc)(outputStream, buf, strlen(buf));
+    if (privateDicts[fd].hasFontMatrix) {
+      sprintf(buf, "/FontMatrix [%g %g %g %g %g %g] def\n",
+             privateDicts[fd].fontMatrix[0],
+             privateDicts[fd].fontMatrix[1],
+             privateDicts[fd].fontMatrix[2],
+             privateDicts[fd].fontMatrix[3],
+             privateDicts[fd].fontMatrix[4],
+             privateDicts[fd].fontMatrix[5]);
+      (*outputFunc)(outputStream, buf, strlen(buf));
+    } else if (topDict.hasFontMatrix) {
+      (*outputFunc)(outputStream, "/FontMatrix [1 0 0 1 0 0] def\n", 30);
+    } else {
+      (*outputFunc)(outputStream,
+                   "/FontMatrix [0.001 0 0 0.001 0 0] def\n", 38);
+    }
     sprintf(buf, "/FontBBox [%g %g %g %g] def\n",
            topDict.fontBBox[0], topDict.fontBBox[1],
            topDict.fontBBox[2], topDict.fontBBox[3]);
@@ -890,7 +916,15 @@ void FoFiType1C::convertToType0(char *psName,
   (*outputFunc)(outputStream, psName, strlen(psName));
   (*outputFunc)(outputStream, " def\n", 5);
   (*outputFunc)(outputStream, "/FontType 0 def\n", 16);
-  (*outputFunc)(outputStream, "/FontMatrix [1 0 0 1 0 0] def\n", 30);
+  if (topDict.hasFontMatrix) {
+    sprintf(buf, "/FontMatrix [%g %g %g %g %g %g] def\n",
+           topDict.fontMatrix[0], topDict.fontMatrix[1],
+           topDict.fontMatrix[2], topDict.fontMatrix[3],
+           topDict.fontMatrix[4], topDict.fontMatrix[5]);
+    (*outputFunc)(outputStream, buf, strlen(buf));
+  } else {
+    (*outputFunc)(outputStream, "/FontMatrix [1 0 0 1 0 0] def\n", 30);
+  }
   (*outputFunc)(outputStream, "/FMapType 2 def\n", 16);
   (*outputFunc)(outputStream, "/Encoding [\n", 12);
   for (i = 0; i < nCIDs; i += 256) {
@@ -950,6 +984,7 @@ void FoFiType1C::cvtGlyph(int offset, int nBytes, GString *charBuf,
     nOps = 0;
     nHints = 0;
     firstOp = gTrue;
+    openPath = gFalse;
   }
 
   pos = offset;
@@ -973,6 +1008,7 @@ void FoFiType1C::cvtGlyph(int offset, int nBytes, GString *charBuf,
        d = 0;
        dFP = gFalse;
        for (k = 0; k < nOps; k += 2) {
+         // convert Type 2 edge hints (-20 or -21) to Type 1 ghost hints
          if (ops[k+1].num < 0) {
            d += ops[k].num + ops[k+1].num;
            dFP |= ops[k].isFP | ops[k+1].isFP;
@@ -1002,6 +1038,7 @@ void FoFiType1C::cvtGlyph(int offset, int nBytes, GString *charBuf,
        d = 0;
        dFP = gFalse;
        for (k = 0; k < nOps; k += 2) {
+         // convert Type 2 edge hints (-20 or -21) to Type 1 ghost hints
          if (ops[k+1].num < 0) {
            d += ops[k].num + ops[k+1].num;
            dFP |= ops[k].isFP | ops[k+1].isFP;
@@ -1025,6 +1062,10 @@ void FoFiType1C::cvtGlyph(int offset, int nBytes, GString *charBuf,
          cvtGlyphWidth(nOps == 2, charBuf, pDict);
          firstOp = gFalse;
        }
+       if (openPath) {
+         charBuf->append((char)9);
+         openPath = gFalse;
+       }
        if (nOps != 1) {
          //~ error(-1, "Wrong number of args (%d) to Type 2 vmoveto", nOps);
        }
@@ -1042,6 +1083,7 @@ void FoFiType1C::cvtGlyph(int offset, int nBytes, GString *charBuf,
          charBuf->append((char)5);
        }
        nOps = 0;
+       openPath = gTrue;
        break;
       case 0x0006:             // hlineto
        if (nOps < 1) {
@@ -1052,6 +1094,7 @@ void FoFiType1C::cvtGlyph(int offset, int nBytes, GString *charBuf,
          charBuf->append((char)((k & 1) ? 7 : 6));
        }
        nOps = 0;
+       openPath = gTrue;
        break;
       case 0x0007:             // vlineto
        if (nOps < 1) {
@@ -1062,6 +1105,7 @@ void FoFiType1C::cvtGlyph(int offset, int nBytes, GString *charBuf,
          charBuf->append((char)((k & 1) ? 6 : 7));
        }
        nOps = 0;
+       openPath = gTrue;
        break;
       case 0x0008:             // rrcurveto
        if (nOps < 6 || nOps % 6 != 0) {
@@ -1077,6 +1121,7 @@ void FoFiType1C::cvtGlyph(int offset, int nBytes, GString *charBuf,
          charBuf->append((char)8);
        }
        nOps = 0;
+       openPath = gTrue;
        break;
       case 0x000a:             // callsubr
        if (nOps >= 1) {
@@ -1102,6 +1147,10 @@ void FoFiType1C::cvtGlyph(int offset, int nBytes, GString *charBuf,
          cvtGlyphWidth(nOps == 1 || nOps == 5, charBuf, pDict);
          firstOp = gFalse;
        }
+       if (openPath) {
+         charBuf->append((char)9);
+         openPath = gFalse;
+       }
        if (nOps == 4) {
          cvtNum(0, gFalse, charBuf);
          cvtNum(ops[0].num, ops[0].isFP, charBuf);
@@ -1177,6 +1226,10 @@ void FoFiType1C::cvtGlyph(int offset, int nBytes, GString *charBuf,
          cvtGlyphWidth(nOps == 3, charBuf, pDict);
          firstOp = gFalse;
        }
+       if (openPath) {
+         charBuf->append((char)9);
+         openPath = gFalse;
+       }
        if (nOps != 2) {
          //~ error(-1, "Wrong number of args (%d) to Type 2 rmoveto", nOps);
        }
@@ -1190,6 +1243,10 @@ void FoFiType1C::cvtGlyph(int offset, int nBytes, GString *charBuf,
          cvtGlyphWidth(nOps == 2, charBuf, pDict);
          firstOp = gFalse;
        }
+       if (openPath) {
+         charBuf->append((char)9);
+         openPath = gFalse;
+       }
        if (nOps != 1) {
          //~ error(-1, "Wrong number of args (%d) to Type 2 hmoveto", nOps);
        }
@@ -1226,6 +1283,7 @@ void FoFiType1C::cvtGlyph(int offset, int nBytes, GString *charBuf,
        cvtNum(ops[k+1].num, ops[k].isFP, charBuf);
        charBuf->append((char)5);
        nOps = 0;
+       openPath = gTrue;
        break;
       case 0x0019:             // rlinecurve
        if (nOps < 8 || (nOps - 6) % 2 != 0) {
@@ -1244,6 +1302,7 @@ void FoFiType1C::cvtGlyph(int offset, int nBytes, GString *charBuf,
        cvtNum(ops[k+5].num, ops[k+5].isFP, charBuf);
        charBuf->append((char)8);
        nOps = 0;
+       openPath = gTrue;
        break;
       case 0x001a:             // vvcurveto
        if (nOps < 4 || !(nOps % 4 == 0 || (nOps-1) % 4 == 0)) {
@@ -1271,6 +1330,7 @@ void FoFiType1C::cvtGlyph(int offset, int nBytes, GString *charBuf,
          charBuf->append((char)8);
        }
        nOps = 0;
+       openPath = gTrue;
        break;
       case 0x001b:             // hhcurveto
        if (nOps < 4 || !(nOps % 4 == 0 || (nOps-1) % 4 == 0)) {
@@ -1298,6 +1358,7 @@ void FoFiType1C::cvtGlyph(int offset, int nBytes, GString *charBuf,
          charBuf->append((char)8);
        }
        nOps = 0;
+       openPath = gTrue;
        break;
       case 0x001d:             // callgsubr
        if (nOps >= 1) {
@@ -1351,6 +1412,7 @@ void FoFiType1C::cvtGlyph(int offset, int nBytes, GString *charBuf,
          charBuf->append((char)8);
        }
        nOps = 0;
+       openPath = gTrue;
        break;
       case 0x001f:             // hvcurveto
        if (nOps < 4 || !(nOps % 4 == 0 || (nOps-1) % 4 == 0)) {
@@ -1390,6 +1452,7 @@ void FoFiType1C::cvtGlyph(int offset, int nBytes, GString *charBuf,
          charBuf->append((char)8);
        }
        nOps = 0;
+       openPath = gTrue;
        break;
       case 0x0c00:             // dotsection (should be Type 1 only?)
        // ignored
@@ -1439,6 +1502,7 @@ void FoFiType1C::cvtGlyph(int offset, int nBytes, GString *charBuf,
        cvtNum(0, gFalse, charBuf);
        charBuf->append((char)8);
        nOps = 0;
+       openPath = gTrue;
        break;
       case 0x0c23:             // flex
        if (nOps != 13) {
@@ -1459,6 +1523,7 @@ void FoFiType1C::cvtGlyph(int offset, int nBytes, GString *charBuf,
        cvtNum(ops[11].num, ops[11].isFP, charBuf);
        charBuf->append((char)8);
        nOps = 0;
+       openPath = gTrue;
        break;
       case 0x0c24:             // hflex1
        if (nOps != 9) {
@@ -1480,6 +1545,7 @@ void FoFiType1C::cvtGlyph(int offset, int nBytes, GString *charBuf,
               ops[1].isFP | ops[3].isFP | ops[7].isFP, charBuf);
        charBuf->append((char)8);
        nOps = 0;
+       openPath = gTrue;
        break;
       case 0x0c25:             // flex1
        if (nOps != 11) {
@@ -1509,6 +1575,7 @@ void FoFiType1C::cvtGlyph(int offset, int nBytes, GString *charBuf,
        }
        charBuf->append((char)8);
        nOps = 0;
+       openPath = gTrue;
        break;
       default:
        //~ error(-1, "Illegal Type 2 charstring op: %04x",
@@ -1695,7 +1762,7 @@ GBool FoFiType1C::parse() {
       }
       nFDs = fdIdx.len;
       privateDicts = (Type1CPrivateDict *)
-                        gmalloc(nFDs * sizeof(Type1CPrivateDict));
+                        gmallocn(nFDs, sizeof(Type1CPrivateDict));
       for (i = 0; i < nFDs; ++i) {
        getIndexVal(&fdIdx, i, &val, &parsedOk);
        if (!parsedOk) {
@@ -1776,6 +1843,7 @@ void FoFiType1C::readTopDict() {
   topDict.fontMatrix[3] = 0.001;
   topDict.fontMatrix[4] = 0;
   topDict.fontMatrix[5] = 0;
+  topDict.hasFontMatrix = gFalse;
   topDict.uniqueID = 0;
   topDict.fontBBox[0] = 0;
   topDict.fontBBox[1] = 0;
@@ -1824,7 +1892,8 @@ void FoFiType1C::readTopDict() {
                   topDict.fontMatrix[2] = ops[2].num;
                   topDict.fontMatrix[3] = ops[3].num;
                   topDict.fontMatrix[4] = ops[4].num;
-                  topDict.fontMatrix[5] = ops[5].num; break;
+                  topDict.fontMatrix[5] = ops[5].num;
+                  topDict.hasFontMatrix = gTrue; break;
       case 0x000d: topDict.uniqueID = (int)ops[0].num; break;
       case 0x0005: topDict.fontBBox[0] = ops[0].num;
                   topDict.fontBBox[1] = ops[1].num;
@@ -1848,10 +1917,14 @@ void FoFiType1C::readTopDict() {
 }
 
 // Read a CID font dict (FD) - this pulls out the private dict
-// pointer, and reads the private dict.
+// pointer, and reads the private dict.  It also pulls the FontMatrix
+// (if any) out of the FD.
 void FoFiType1C::readFD(int offset, int length, Type1CPrivateDict *pDict) {
   int pos, pSize, pOffset;
+  double fontMatrix[6];
+  GBool hasFontMatrix;
 
+  hasFontMatrix = gFalse;
   pSize = pOffset = 0;
   pos = offset;
   nOps = 0;
@@ -1869,17 +1942,35 @@ void FoFiType1C::readFD(int offset, int length, Type1CPrivateDict *pDict) {
        pSize = (int)ops[0].num;
        pOffset = (int)ops[1].num;
        break;
+      } else if (ops[nOps - 1].op == 0x0c07) {
+       fontMatrix[0] = ops[0].num;
+       fontMatrix[1] = ops[1].num;
+       fontMatrix[2] = ops[2].num;
+       fontMatrix[3] = ops[3].num;
+       fontMatrix[4] = ops[4].num;
+       fontMatrix[5] = ops[5].num;
+       hasFontMatrix = gTrue;
       }
       nOps = 0;
     }
   }
   readPrivateDict(pOffset, pSize, pDict);
+  if (hasFontMatrix) {
+    pDict->fontMatrix[0] = fontMatrix[0];
+    pDict->fontMatrix[1] = fontMatrix[1];
+    pDict->fontMatrix[2] = fontMatrix[2];
+    pDict->fontMatrix[3] = fontMatrix[3];
+    pDict->fontMatrix[4] = fontMatrix[4];
+    pDict->fontMatrix[5] = fontMatrix[5];
+    pDict->hasFontMatrix = gTrue;
+  }
 }
 
 void FoFiType1C::readPrivateDict(int offset, int length,
                                 Type1CPrivateDict *pDict) {
   int pos;
 
+  pDict->hasFontMatrix = gFalse;
   pDict->nBlueValues = 0;
   pDict->nOtherBlues = 0;
   pDict->nFamilyBlues = 0;
@@ -1898,9 +1989,9 @@ void FoFiType1C::readPrivateDict(int offset, int length,
   pDict->initialRandomSeed = 0;
   pDict->subrsOffset = 0;
   pDict->defaultWidthX = 0;
-  pDict->defaultWidthXFP = 0;
+  pDict->defaultWidthXFP = gFalse;
   pDict->nominalWidthX = 0;
-  pDict->nominalWidthXFP = 0;
+  pDict->nominalWidthXFP = gFalse;
 
   // no dictionary
   if (offset == 0 || length == 0) {
@@ -1979,9 +2070,11 @@ void FoFiType1C::readPrivateDict(int offset, int length,
        break;
       case 0x0014:
        pDict->defaultWidthX = ops[0].num;
+       pDict->defaultWidthXFP = ops[0].isFP;
        break;
       case 0x0015:
        pDict->nominalWidthX = ops[0].num;
+       pDict->nominalWidthXFP = ops[0].isFP;
        break;
       }
       nOps = 0;
@@ -2052,7 +2145,7 @@ void FoFiType1C::buildEncoding() {
     encoding = fofiType1ExpertEncoding;
 
   } else {
-    encoding = (char **)gmalloc(256 * sizeof(char *));
+    encoding = (char **)gmallocn(256, sizeof(char *));
     for (i = 0; i < 256; ++i) {
       encoding[i] = NULL;
     }
@@ -2139,7 +2232,7 @@ GBool FoFiType1C::readCharset() {
   } else if (topDict.charsetOffset == 2) {
     charset = fofiType1CExpertSubsetCharset;
   } else {
-    charset = (Gushort *)gmalloc(nGlyphs * sizeof(Gushort));
+    charset = (Gushort *)gmallocn(nGlyphs, sizeof(Gushort));
     for (i = 0; i < nGlyphs; ++i) {
       charset[i] = 0;
     }
@@ -2324,9 +2417,9 @@ void FoFiType1C::getIndex(int pos, Type1CIndex *idx, GBool *ok) {
   idx->pos = pos;
   idx->len = getU16BE(pos, ok);
   if (idx->len == 0) {
-    // empty indexes are legal
+    // empty indexes are legal and contain just the length field
     idx->offSize = 0;
-    idx->startPos = idx->endPos = 0;
+    idx->startPos = idx->endPos = pos + 2;
   } else {
     idx->offSize = getU8(pos + 2, ok);
     if (idx->offSize < 1 || idx->offSize > 4) {
@@ -2356,7 +2449,7 @@ void FoFiType1C::getIndexVal(Type1CIndex *idx, int i,
                                   idx->offSize, ok);
   pos1 = idx->startPos + getUVarBE(idx->pos + 3 + (i + 1) * idx->offSize,
                                   idx->offSize, ok);
-  if (pos0 < idx->startPos || pos0 >= idx->endPos ||
+  if (pos0 < idx->startPos || pos0 > idx->endPos ||
       pos1 <= idx->startPos || pos1 > idx->endPos ||
       pos1 < pos0) {
     *ok = gFalse;
@@ -2374,7 +2467,7 @@ char *FoFiType1C::getString(int sid, char *buf, GBool *ok) {
   } else {
     sid -= 391;
     getIndexVal(&stringIdx, sid, &val, ok);
-    if (ok) {
+    if (*ok) {
       if ((n = val.len) > 255) {
        n = 255;
       }
index e86d6b1..a13c615 100644 (file)
@@ -12,6 +12,7 @@
 #pragma implementation
 #endif
 
+#include <stdlib.h>
 #include <stdio.h>
 #include <stddef.h>
 #include <string.h>
 #define functionMaxDepth 6
 
 // Max delta allowed in any color component for a function shading fill.
-#define functionColorDelta (1 / 256.0)
+#define functionColorDelta (dblToCol(1 / 256.0))
 
 // Max number of splits along the t axis for an axial shading fill.
 #define axialMaxSplits 256
 
 // Max delta allowed in any color component for an axial shading fill.
-#define axialColorDelta (1 / 256.0)
+#define axialColorDelta (dblToCol(1 / 256.0))
 
 // Max number of splits along the t axis for a radial shading fill.
 #define radialMaxSplits 256
 
 // Max delta allowed in any color component for a radial shading fill.
-#define radialColorDelta (1 / 256.0)
+#define radialColorDelta (dblToCol(1 / 256.0))
+
+// Max recursive depth for a Gouraud triangle shading fill.
+#define gouraudMaxDepth 4
+
+// Max delta allowed in any color component for a Gouraud triangle
+// shading fill.
+#define gouraudColorDelta (dblToCol(1 / 256.0))
+
+// Max recursive depth for a patch mesh shading fill.
+#define patchMaxDepth 6
+
+// Max delta allowed in any color component for a patch mesh shading
+// fill.
+#define patchColorDelta (dblToCol(1 / 256.0))
 
 //------------------------------------------------------------------------
 // Operator table
@@ -405,7 +420,7 @@ GBool GfxResources::lookupGState(char *name, Object *obj) {
 //------------------------------------------------------------------------
 
 Gfx::Gfx(XRef *xrefA, OutputDev *outA, int pageNum, Dict *resDict,
-        double hDPI, double vDPI, PDFRectangle *box, GBool crop,
+        double hDPI, double vDPI, PDFRectangle *box,
         PDFRectangle *cropBox, int rotate,
         GBool (*abortCheckCbkA)(void *data),
         void *abortCheckCbkDataA) {
@@ -424,7 +439,7 @@ Gfx::Gfx(XRef *xrefA, OutputDev *outA, int pageNum, Dict *resDict,
   fontChanged = gFalse;
   clip = clipNone;
   ignoreUndef = 0;
-  out->startPage(pageNum, state, cropBox->x1,cropBox->y1,cropBox->x2,cropBox->y2);
+  out->startPage(pageNum, state);
   out->setDefaultCTM(state->getCTM());
   out->updateAll(state);
   for (i = 0; i < 6; ++i) {
@@ -435,7 +450,7 @@ Gfx::Gfx(XRef *xrefA, OutputDev *outA, int pageNum, Dict *resDict,
   abortCheckCbkData = abortCheckCbkDataA;
 
   // set crop box
-  /*if (crop) {
+  if (cropBox) {
     state->moveTo(cropBox->x1, cropBox->y1);
     state->lineTo(cropBox->x2, cropBox->y1);
     state->lineTo(cropBox->x2, cropBox->y2);
@@ -444,11 +459,11 @@ Gfx::Gfx(XRef *xrefA, OutputDev *outA, int pageNum, Dict *resDict,
     state->clip();
     out->clip(state);
     state->clearPath();
-  }*/
+  }
 }
 
 Gfx::Gfx(XRef *xrefA, OutputDev *outA, Dict *resDict,
-        PDFRectangle *box, GBool crop, PDFRectangle *cropBox,
+        PDFRectangle *box, PDFRectangle *cropBox,
         GBool (*abortCheckCbkA)(void *data),
         void *abortCheckCbkDataA) {
   int i;
@@ -474,7 +489,7 @@ Gfx::Gfx(XRef *xrefA, OutputDev *outA, Dict *resDict,
   abortCheckCbkData = abortCheckCbkDataA;
 
   // set crop box
-  if (crop) {
+  if (cropBox) {
     state->moveTo(cropBox->x1, cropBox->y1);
     state->lineTo(cropBox->x2, cropBox->y1);
     state->lineTo(cropBox->x2, cropBox->y2);
@@ -734,7 +749,7 @@ void Gfx::opSetDash(Object args[], int numArgs) {
   if (length == 0) {
     dash = NULL;
   } else {
-    dash = (double *)gmalloc(length * sizeof(double));
+    dash = (double *)gmallocn(length, sizeof(double));
     for (i = 0; i < length; ++i) {
       dash[i] = a->get(i, &obj)->getNum();
       obj.free();
@@ -771,6 +786,8 @@ void Gfx::opSetLineWidth(Object args[], int numArgs) {
 
 void Gfx::opSetExtGState(Object args[], int numArgs) {
   Object obj1, obj2;
+  GfxBlendMode mode;
+  GBool haveFillOP;
 
   if (!res->lookupGState(args[0].getName(), &obj1)) {
     return;
@@ -780,6 +797,17 @@ void Gfx::opSetExtGState(Object args[], int numArgs) {
     obj1.free();
     return;
   }
+
+  // transparency support: blend mode, fill/stroke opacity
+  if (!obj1.dictLookup("BM", &obj2)->isNull()) {
+    if (state->parseBlendMode(&obj2, &mode)) {
+      state->setBlendMode(mode);
+      out->updateBlendMode(state);
+    } else {
+      error(getPos(), "Invalid blend mode in ExtGState");
+    }
+  }
+  obj2.free();
   if (obj1.dictLookup("ca", &obj2)->isNum()) {
     state->setFillOpacity(obj2.getNum());
     out->updateFillOpacity(state);
@@ -790,6 +818,23 @@ void Gfx::opSetExtGState(Object args[], int numArgs) {
     out->updateStrokeOpacity(state);
   }
   obj2.free();
+
+  // fill/stroke overprint
+  if ((haveFillOP = (obj1.dictLookup("op", &obj2)->isBool()))) {
+    state->setFillOverprint(obj2.getBool());
+    out->updateFillOverprint(state);
+  }
+  obj2.free();
+  if (obj1.dictLookup("OP", &obj2)->isBool()) {
+    state->setStrokeOverprint(obj2.getBool());
+    out->updateStrokeOverprint(state);
+    if (!haveFillOP) {
+      state->setFillOverprint(obj2.getBool());
+      out->updateFillOverprint(state);
+    }
+  }
+  obj2.free();
+
   obj1.free();
 }
 
@@ -805,7 +850,8 @@ void Gfx::opSetFillGray(Object args[], int numArgs) {
 
   state->setFillPattern(NULL);
   state->setFillColorSpace(new GfxDeviceGrayColorSpace());
-  color.c[0] = args[0].getNum();
+  out->updateFillColorSpace(state);
+  color.c[0] = dblToCol(args[0].getNum());
   state->setFillColor(&color);
   out->updateFillColor(state);
 }
@@ -815,7 +861,8 @@ void Gfx::opSetStrokeGray(Object args[], int numArgs) {
 
   state->setStrokePattern(NULL);
   state->setStrokeColorSpace(new GfxDeviceGrayColorSpace());
-  color.c[0] = args[0].getNum();
+  out->updateStrokeColorSpace(state);
+  color.c[0] = dblToCol(args[0].getNum());
   state->setStrokeColor(&color);
   out->updateStrokeColor(state);
 }
@@ -826,8 +873,9 @@ void Gfx::opSetFillCMYKColor(Object args[], int numArgs) {
 
   state->setFillPattern(NULL);
   state->setFillColorSpace(new GfxDeviceCMYKColorSpace());
+  out->updateFillColorSpace(state);
   for (i = 0; i < 4; ++i) {
-    color.c[i] = args[i].getNum();
+    color.c[i] = dblToCol(args[i].getNum());
   }
   state->setFillColor(&color);
   out->updateFillColor(state);
@@ -839,8 +887,9 @@ void Gfx::opSetStrokeCMYKColor(Object args[], int numArgs) {
 
   state->setStrokePattern(NULL);
   state->setStrokeColorSpace(new GfxDeviceCMYKColorSpace());
+  out->updateStrokeColorSpace(state);
   for (i = 0; i < 4; ++i) {
-    color.c[i] = args[i].getNum();
+    color.c[i] = dblToCol(args[i].getNum());
   }
   state->setStrokeColor(&color);
   out->updateStrokeColor(state);
@@ -852,8 +901,9 @@ void Gfx::opSetFillRGBColor(Object args[], int numArgs) {
 
   state->setFillPattern(NULL);
   state->setFillColorSpace(new GfxDeviceRGBColorSpace());
+  out->updateFillColorSpace(state);
   for (i = 0; i < 3; ++i) {
-    color.c[i] = args[i].getNum();
+    color.c[i] = dblToCol(args[i].getNum());
   }
   state->setFillColor(&color);
   out->updateFillColor(state);
@@ -865,8 +915,9 @@ void Gfx::opSetStrokeRGBColor(Object args[], int numArgs) {
 
   state->setStrokePattern(NULL);
   state->setStrokeColorSpace(new GfxDeviceRGBColorSpace());
+  out->updateStrokeColorSpace(state);
   for (i = 0; i < 3; ++i) {
-    color.c[i] = args[i].getNum();
+    color.c[i] = dblToCol(args[i].getNum());
   }
   state->setStrokeColor(&color);
   out->updateStrokeColor(state);
@@ -888,6 +939,7 @@ void Gfx::opSetFillColorSpace(Object args[], int numArgs) {
   obj.free();
   if (colorSpace) {
     state->setFillColorSpace(colorSpace);
+    out->updateFillColorSpace(state);
   } else {
     error(getPos(), "Bad color space (fill)");
   }
@@ -914,6 +966,7 @@ void Gfx::opSetStrokeColorSpace(Object args[], int numArgs) {
   obj.free();
   if (colorSpace) {
     state->setStrokeColorSpace(colorSpace);
+    out->updateStrokeColorSpace(state);
   } else {
     error(getPos(), "Bad color space (stroke)");
   }
@@ -930,7 +983,7 @@ void Gfx::opSetFillColor(Object args[], int numArgs) {
 
   state->setFillPattern(NULL);
   for (i = 0; i < numArgs; ++i) {
-    color.c[i] = args[i].getNum();
+    color.c[i] = dblToCol(args[i].getNum());
   }
   state->setFillColor(&color);
   out->updateFillColor(state);
@@ -942,7 +995,7 @@ void Gfx::opSetStrokeColor(Object args[], int numArgs) {
 
   state->setStrokePattern(NULL);
   for (i = 0; i < numArgs; ++i) {
-    color.c[i] = args[i].getNum();
+    color.c[i] = dblToCol(args[i].getNum());
   }
   state->setStrokeColor(&color);
   out->updateStrokeColor(state);
@@ -957,7 +1010,7 @@ void Gfx::opSetFillColorN(Object args[], int numArgs) {
     if (numArgs > 1) {
       for (i = 0; i < numArgs && i < 4; ++i) {
        if (args[i].isNum()) {
-         color.c[i] = args[i].getNum();
+         color.c[i] = dblToCol(args[i].getNum());
        }
       }
       state->setFillColor(&color);
@@ -972,7 +1025,7 @@ void Gfx::opSetFillColorN(Object args[], int numArgs) {
     state->setFillPattern(NULL);
     for (i = 0; i < numArgs && i < 4; ++i) {
       if (args[i].isNum()) {
-       color.c[i] = args[i].getNum();
+       color.c[i] = dblToCol(args[i].getNum());
       }
     }
     state->setFillColor(&color);
@@ -989,7 +1042,7 @@ void Gfx::opSetStrokeColorN(Object args[], int numArgs) {
     if (numArgs > 1) {
       for (i = 0; i < numArgs && i < 4; ++i) {
        if (args[i].isNum()) {
-         color.c[i] = args[i].getNum();
+         color.c[i] = dblToCol(args[i].getNum());
        }
       }
       state->setStrokeColor(&color);
@@ -1004,7 +1057,7 @@ void Gfx::opSetStrokeColorN(Object args[], int numArgs) {
     state->setStrokePattern(NULL);
     for (i = 0; i < numArgs && i < 4; ++i) {
       if (args[i].isNum()) {
-       color.c[i] = args[i].getNum();
+       color.c[i] = dblToCol(args[i].getNum());
       }
     }
     state->setStrokeColor(&color);
@@ -1294,7 +1347,7 @@ void Gfx::doTilingPatternFill(GfxTilingPattern *tPat, GBool eoFill) {
   m[4] = m1[4] * ictm[0] + m1[5] * ictm[2] + ictm[4];
   m[5] = m1[4] * ictm[1] + m1[5] * ictm[3] + ictm[5];
 
-  // construct a (base space) -> (pattern space) transform matrix
+  // construct a (device space) -> (pattern space) transform matrix
   det = 1 / (m1[0] * m1[3] - m1[1] * m1[2]);
   imb[0] = m1[3] * det;
   imb[1] = -m1[1] * det;
@@ -1312,11 +1365,15 @@ void Gfx::doTilingPatternFill(GfxTilingPattern *tPat, GBool eoFill) {
   // Adobe's behavior
   if (tPat->getPaintType() == 2 && (cs = patCS->getUnder())) {
     state->setFillColorSpace(cs->copy());
+    out->updateFillColorSpace(state);
     state->setStrokeColorSpace(cs->copy());
+    out->updateStrokeColorSpace(state);
     state->setStrokeColor(state->getFillColor());
   } else {
     state->setFillColorSpace(new GfxDeviceGrayColorSpace());
+    out->updateFillColorSpace(state);
     state->setStrokeColorSpace(new GfxDeviceGrayColorSpace());
+    out->updateStrokeColorSpace(state);
   }
   state->setFillPattern(NULL);
   out->updateFillColor(state);
@@ -1334,8 +1391,13 @@ void Gfx::doTilingPatternFill(GfxTilingPattern *tPat, GBool eoFill) {
   }
   state->clearPath();
 
-  // transform clip region bbox to pattern space
+  // get the clip region, check for empty
   state->getClipBBox(&cxMin, &cyMin, &cxMax, &cyMax);
+  if (cxMin > cxMax || cyMin > cyMax) {
+    goto err;
+  }
+
+  // transform clip region bbox to pattern space
   xMin = xMax = cxMin * imb[0] + cyMin * imb[2] + imb[4];
   yMin = yMax = cxMin * imb[1] + cyMin * imb[3] + imb[5];
   x1 = cxMin * imb[0] + cyMax * imb[2] + imb[4];
@@ -1387,18 +1449,28 @@ void Gfx::doTilingPatternFill(GfxTilingPattern *tPat, GBool eoFill) {
   for (i = 0; i < 4; ++i) {
     m1[i] = m[i];
   }
-  for (yi = yi0; yi < yi1; ++yi) {
-    for (xi = xi0; xi < xi1; ++xi) {
-      x = xi * xstep;
-      y = yi * ystep;
-      m1[4] = x * m[0] + y * m[2] + m[4];
-      m1[5] = x * m[1] + y * m[3] + m[5];
-      doForm1(tPat->getContentStream(), tPat->getResDict(),
-             m1, tPat->getBBox());
+  if (out->useTilingPatternFill()) {
+    m1[4] = m[4];
+    m1[5] = m[5];
+    out->tilingPatternFill(state, tPat->getContentStream(),
+                          tPat->getPaintType(), tPat->getResDict(),
+                          m1, tPat->getBBox(),
+                          xi0, yi0, xi1, yi1, xstep, ystep);
+  } else {
+    for (yi = yi0; yi < yi1; ++yi) {
+      for (xi = xi0; xi < xi1; ++xi) {
+       x = xi * xstep;
+       y = yi * ystep;
+       m1[4] = x * m[0] + y * m[2] + m[4];
+       m1[5] = x * m[1] + y * m[3] + m[5];
+       doForm1(tPat->getContentStream(), tPat->getResDict(),
+               m1, tPat->getBBox());
+      }
     }
   }
 
   // restore graphics state
+ err:
   restoreState();
   state->setPath(savedPath);
 }
@@ -1427,7 +1499,7 @@ void Gfx::doShadingPatternFill(GfxShadingPattern *sPat, GBool eoFill) {
     state->closePath();
     state->clip();
     out->clip(state);
-    state->clearPath();
+    state->setPath(savedPath->copy());
   }
 
   // clip to current path
@@ -1437,6 +1509,17 @@ void Gfx::doShadingPatternFill(GfxShadingPattern *sPat, GBool eoFill) {
   } else {
     out->clip(state);
   }
+
+  // set the color space
+  state->setFillColorSpace(shading->getColorSpace()->copy());
+  out->updateFillColorSpace(state);
+
+  // background color fill
+  if (shading->getHasBackground()) {
+    state->setFillColor(shading->getBackground());
+    out->updateFillColor(state);
+    out->fill(state);
+  }
   state->clearPath();
 
   // construct a (pattern space) -> (current space) transform matrix
@@ -1470,9 +1553,6 @@ void Gfx::doShadingPatternFill(GfxShadingPattern *sPat, GBool eoFill) {
   state->concatCTM(m[0], m[1], m[2], m[3], m[4], m[5]);
   out->updateCTM(state, m[0], m[1], m[2], m[3], m[4], m[5]);
 
-  // set the color space
-  state->setFillColorSpace(shading->getColorSpace()->copy());
-
   // do shading type-specific operations
   switch (shading->getType()) {
   case 1:
@@ -1484,6 +1564,14 @@ void Gfx::doShadingPatternFill(GfxShadingPattern *sPat, GBool eoFill) {
   case 3:
     doRadialShFill((GfxRadialShading *)shading);
     break;
+  case 4:
+  case 5:
+    doGouraudTriangleShFill((GfxGouraudTriangleShading *)shading);
+    break;
+  case 6:
+  case 7:
+    doPatchMeshShFill((GfxPatchMeshShading *)shading);
+    break;
   }
 
   // restore graphics state
@@ -1519,6 +1607,7 @@ void Gfx::opShFill(Object args[], int numArgs) {
 
   // set the color space
   state->setFillColorSpace(shading->getColorSpace()->copy());
+  out->updateFillColorSpace(state);
 
   // do shading type-specific operations
   switch (shading->getType()) {
@@ -1531,6 +1620,14 @@ void Gfx::opShFill(Object args[], int numArgs) {
   case 3:
     doRadialShFill((GfxRadialShading *)shading);
     break;
+  case 4:
+  case 5:
+    doGouraudTriangleShFill((GfxGouraudTriangleShading *)shading);
+    break;
+  case 6:
+  case 7:
+    doPatchMeshShFill((GfxPatchMeshShading *)shading);
+    break;
   }
 
   // restore graphics state
@@ -1544,12 +1641,16 @@ void Gfx::doFunctionShFill(GfxFunctionShading *shading) {
   double x0, y0, x1, y1;
   GfxColor colors[4];
 
-  shading->getDomain(&x0, &y0, &x1, &y1);
-  shading->getColor(x0, y0, &colors[0]);
-  shading->getColor(x0, y1, &colors[1]);
-  shading->getColor(x1, y0, &colors[2]);
-  shading->getColor(x1, y1, &colors[3]);
-  doFunctionShFill1(shading, x0, y0, x1, y1, colors, 0);
+  if (out->useShadedFills()) {
+    out->functionShadedFill(state, shading);
+  } else {
+    shading->getDomain(&x0, &y0, &x1, &y1);
+    shading->getColor(x0, y0, &colors[0]);
+    shading->getColor(x0, y1, &colors[1]);
+    shading->getColor(x1, y0, &colors[2]);
+    shading->getColor(x1, y1, &colors[3]);
+    doFunctionShFill1(shading, x0, y0, x1, y1, colors, 0);
+  }
 }
 
 void Gfx::doFunctionShFill1(GfxFunctionShading *shading,
@@ -1569,7 +1670,7 @@ void Gfx::doFunctionShFill1(GfxFunctionShading *shading,
   // compare the four corner colors
   for (i = 0; i < 4; ++i) {
     for (j = 0; j < nComps; ++j) {
-      if (fabs(colors[i].c[j] - colors[(i+1)&3].c[j]) > functionColorDelta) {
+      if (abs(colors[i].c[j] - colors[(i+1)&3].c[j]) > functionColorDelta) {
        break;
       }
     }
@@ -1581,8 +1682,6 @@ void Gfx::doFunctionShFill1(GfxFunctionShading *shading,
   // center of the rectangle
   xM = 0.5 * (x0 + x1);
   yM = 0.5 * (y0 + y1);
-  
-  out->useGradients();
 
   // the four corner colors are close (or we hit the recursive limit)
   // -- fill the rectangle; but require at least one subdivision
@@ -1666,6 +1765,7 @@ void Gfx::doAxialShFill(GfxAxialShading *shading) {
   double xMin, yMin, xMax, yMax;
   double x0, y0, x1, y1;
   double dx, dy, mul;
+  GBool dxZero, dyZero;
   double tMin, tMax, t, tx, ty;
   double s[4], sMin, sMax, tmp;
   double ux0, uy0, ux1, uy1, vx0, vy0, vx1, vy1;
@@ -1676,180 +1776,113 @@ void Gfx::doAxialShFill(GfxAxialShading *shading) {
   int nComps;
   int i, j, k, kk;
 
-  // get the clip region bbox
-  state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax);
-
-  // compute min and max t values, based on the four corners of the
-  // clip region bbox
-  shading->getCoords(&x0, &y0, &x1, &y1);
-  dx = x1 - x0;
-  dy = y1 - y0;
-  mul = 1 / (dx * dx + dy * dy);
-  tMin = tMax = ((xMin - x0) * dx + (yMin - y0) * dy) * mul;
-  t = ((xMin - x0) * dx + (yMax - y0) * dy) * mul;
-  if (t < tMin) {
-    tMin = t;
-  } else if (t > tMax) {
-    tMax = t;
-  }
-  t = ((xMax - x0) * dx + (yMin - y0) * dy) * mul;
-  if (t < tMin) {
-    tMin = t;
-  } else if (t > tMax) {
-    tMax = t;
-  }
-  t = ((xMax - x0) * dx + (yMax - y0) * dy) * mul;
-  if (t < tMin) {
-    tMin = t;
-  } else if (t > tMax) {
-    tMax = t;
-  }
-  if (tMin < 0 && !shading->getExtend0()) {
-    tMin = 0;
-  }
-  if (tMax > 1 && !shading->getExtend1()) {
-    tMax = 1;
-  }
-
-  // get the function domain
-  t0 = shading->getDomain0();
-  t1 = shading->getDomain1();
-
-  // Traverse the t axis and do the shading.
-  //
-  // For each point (tx, ty) on the t axis, consider a line through
-  // that point perpendicular to the t axis:
-  //
-  //     x(s) = tx + s * -dy   -->   s = (x - tx) / -dy
-  //     y(s) = ty + s * dx    -->   s = (y - ty) / dx
-  //
-  // Then look at the intersection of this line with the bounding box
-  // (xMin, yMin, xMax, yMax).  In the general case, there are four
-  // intersection points:
-  //
-  //     s0 = (xMin - tx) / -dy
-  //     s1 = (xMax - tx) / -dy
-  //     s2 = (yMin - ty) / dx
-  //     s3 = (yMax - ty) / dx
-  //
-  // and we want the middle two s values.
-  //
-  // In the case where dx = 0, take s0 and s1; in the case where dy =
-  // 0, take s2 and s3.
-  //
-  // Each filled polygon is bounded by two of these line segments
-  // perpdendicular to the t axis.
-  //
-  // The t axis is bisected into smaller regions until the color
-  // difference across a region is small enough, and then the region
-  // is painted with a single color.
-
-  // set up: require at least one split to avoid problems when the two
-  // ends of the t axis have the same color
-  nComps = shading->getColorSpace()->getNComps();
-  ta[0] = tMin;
-  next[0] = axialMaxSplits / 2;
-  ta[axialMaxSplits / 2] = 0.5 * (tMin + tMax);
-  next[axialMaxSplits / 2] = axialMaxSplits;
-  ta[axialMaxSplits] = tMax;
-
-  // compute the color at t = tMin
-  if (tMin < 0) {
-    tt = t0;
-  } else if (tMin > 1) {
-    tt = t1;
-  } else {
-    tt = t0 + (t1 - t0) * tMin;
-  }
-  shading->getColor(tt, &color0);
-
-  // compute the coordinates of the point on the t axis at t = tMin;
-  // then compute the intersection of the perpendicular line with the
-  // bounding box
-  tx = x0 + tMin * dx;
-  ty = y0 + tMin * dy;
-  if (dx == 0 && dy == 0) {
-    sMin = sMax = 0;
-  } if (dx == 0) {
-    sMin = (xMin - tx) / -dy;
-    sMax = (xMax - tx) / -dy;
-    if (sMin > sMax) { tmp = sMin; sMin = sMax; sMax = tmp; }
-  } else if (dy == 0) {
-    sMin = (yMin - ty) / dx;
-    sMax = (yMax - ty) / dx;
-    if (sMin > sMax) { tmp = sMin; sMin = sMax; sMax = tmp; }
+  if (out->useShadedFills()) {
+
+    out->axialShadedFill(state, shading);
+
   } else {
-    s[0] = (yMin - ty) / dx;
-    s[1] = (yMax - ty) / dx;
-    s[2] = (xMin - tx) / -dy;
-    s[3] = (xMax - tx) / -dy;
-    for (j = 0; j < 3; ++j) {
-      kk = j;
-      for (k = j + 1; k < 4; ++k) {
-       if (s[k] < s[kk]) {
-         kk = k;
-       }
-      }
-      tmp = s[j]; s[j] = s[kk]; s[kk] = tmp;
-    }
-    sMin = s[1];
-    sMax = s[2];
-  }
-  ux0 = tx - sMin * dy;
-  uy0 = ty + sMin * dx;
-  vx0 = tx - sMax * dy;
-  vy0 = ty + sMax * dx;
-
-  i = 0;
-  if(i < axialMaxSplits)
-    out->useGradients();
-
-  while (i < axialMaxSplits) {
-
-    // bisect until color difference is small enough or we hit the
-    // bisection limit
-    j = next[i];
-    while (j > i + 1) {
-      if (ta[j] < 0) {
-       tt = t0;
-      } else if (ta[j] > 1) {
-       tt = t1;
-      } else {
-       tt = t0 + (t1 - t0) * ta[j];
-      }
-      shading->getColor(tt, &color1);
-      for (k = 0; k < nComps; ++k) {
-       if (fabs(color1.c[k] - color0.c[k]) > axialColorDelta) {
-         break;
-       }
-      }
-      if (k == nComps) {
-       break;
-      }
-      k = (i + j) / 2;
-      ta[k] = 0.5 * (ta[i] + ta[j]);
-      next[i] = k;
-      next[k] = j;
-      j = k;
+
+    // get the clip region bbox
+    state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax);
+
+    // compute min and max t values, based on the four corners of the
+    // clip region bbox
+    shading->getCoords(&x0, &y0, &x1, &y1);
+    dx = x1 - x0;
+    dy = y1 - y0;
+    dxZero = fabs(dx) < 0.001;
+    dyZero = fabs(dy) < 0.001;
+    mul = 1 / (dx * dx + dy * dy);
+    tMin = tMax = ((xMin - x0) * dx + (yMin - y0) * dy) * mul;
+    t = ((xMin - x0) * dx + (yMax - y0) * dy) * mul;
+    if (t < tMin) {
+      tMin = t;
+    } else if (t > tMax) {
+      tMax = t;
+    }
+    t = ((xMax - x0) * dx + (yMin - y0) * dy) * mul;
+    if (t < tMin) {
+      tMin = t;
+    } else if (t > tMax) {
+      tMax = t;
+    }
+    t = ((xMax - x0) * dx + (yMax - y0) * dy) * mul;
+    if (t < tMin) {
+      tMin = t;
+    } else if (t > tMax) {
+      tMax = t;
+    }
+    if (tMin < 0 && !shading->getExtend0()) {
+      tMin = 0;
+    }
+    if (tMax > 1 && !shading->getExtend1()) {
+      tMax = 1;
     }
 
-    // use the average of the colors of the two sides of the region
-    for (k = 0; k < nComps; ++k) {
-      color0.c[k] = 0.5 * (color0.c[k] + color1.c[k]);
+    // get the function domain
+    t0 = shading->getDomain0();
+    t1 = shading->getDomain1();
+
+    // Traverse the t axis and do the shading.
+    //
+    // For each point (tx, ty) on the t axis, consider a line through
+    // that point perpendicular to the t axis:
+    //
+    //     x(s) = tx + s * -dy   -->   s = (x - tx) / -dy
+    //     y(s) = ty + s * dx    -->   s = (y - ty) / dx
+    //
+    // Then look at the intersection of this line with the bounding box
+    // (xMin, yMin, xMax, yMax).  In the general case, there are four
+    // intersection points:
+    //
+    //     s0 = (xMin - tx) / -dy
+    //     s1 = (xMax - tx) / -dy
+    //     s2 = (yMin - ty) / dx
+    //     s3 = (yMax - ty) / dx
+    //
+    // and we want the middle two s values.
+    //
+    // In the case where dx = 0, take s0 and s1; in the case where dy =
+    // 0, take s2 and s3.
+    //
+    // Each filled polygon is bounded by two of these line segments
+    // perpdendicular to the t axis.
+    //
+    // The t axis is bisected into smaller regions until the color
+    // difference across a region is small enough, and then the region
+    // is painted with a single color.
+
+    // set up: require at least one split to avoid problems when the two
+    // ends of the t axis have the same color
+    nComps = shading->getColorSpace()->getNComps();
+    ta[0] = tMin;
+    next[0] = axialMaxSplits / 2;
+    ta[axialMaxSplits / 2] = 0.5 * (tMin + tMax);
+    next[axialMaxSplits / 2] = axialMaxSplits;
+    ta[axialMaxSplits] = tMax;
+
+    // compute the color at t = tMin
+    if (tMin < 0) {
+      tt = t0;
+    } else if (tMin > 1) {
+      tt = t1;
+    } else {
+      tt = t0 + (t1 - t0) * tMin;
     }
+    shading->getColor(tt, &color0);
 
-    // compute the coordinates of the point on the t axis; then
-    // compute the intersection of the perpendicular line with the
+    // compute the coordinates of the point on the t axis at t = tMin;
+    // then compute the intersection of the perpendicular line with the
     // bounding box
-    tx = x0 + ta[j] * dx;
-    ty = y0 + ta[j] * dy;
-    if (dx == 0 && dy == 0) {
+    tx = x0 + tMin * dx;
+    ty = y0 + tMin * dy;
+    if (dxZero && dyZero) {
       sMin = sMax = 0;
-    } if (dx == 0) {
+    } if (dxZero) {
       sMin = (xMin - tx) / -dy;
       sMax = (xMax - tx) / -dy;
       if (sMin > sMax) { tmp = sMin; sMin = sMax; sMax = tmp; }
-    } else if (dy == 0) {
+    } else if (dyZero) {
       sMin = (yMin - ty) / dx;
       sMax = (yMax - ty) / dx;
       if (sMin > sMax) { tmp = sMin; sMin = sMax; sMax = tmp; }
@@ -1870,31 +1903,104 @@ void Gfx::doAxialShFill(GfxAxialShading *shading) {
       sMin = s[1];
       sMax = s[2];
     }
-    ux1 = tx - sMin * dy;
-    uy1 = ty + sMin * dx;
-    vx1 = tx - sMax * dy;
-    vy1 = ty + sMax * dx;
+    ux0 = tx - sMin * dy;
+    uy0 = ty + sMin * dx;
+    vx0 = tx - sMax * dy;
+    vy0 = ty + sMax * dx;
+
+    i = 0;
+    while (i < axialMaxSplits) {
+
+      // bisect until color difference is small enough or we hit the
+      // bisection limit
+      j = next[i];
+      while (j > i + 1) {
+       if (ta[j] < 0) {
+         tt = t0;
+       } else if (ta[j] > 1) {
+         tt = t1;
+       } else {
+         tt = t0 + (t1 - t0) * ta[j];
+       }
+       shading->getColor(tt, &color1);
+       for (k = 0; k < nComps; ++k) {
+         if (abs(color1.c[k] - color0.c[k]) > axialColorDelta) {
+           break;
+         }
+       }
+       if (k == nComps) {
+         break;
+       }
+       k = (i + j) / 2;
+       ta[k] = 0.5 * (ta[i] + ta[j]);
+       next[i] = k;
+       next[k] = j;
+       j = k;
+      }
 
-    // set the color
-    state->setFillColor(&color0);
-    out->updateFillColor(state);
+      // use the average of the colors of the two sides of the region
+      for (k = 0; k < nComps; ++k) {
+       color0.c[k] = (color0.c[k] + color1.c[k]) / 2;
+      }
 
-    // fill the region
-    state->moveTo(ux0, uy0);
-    state->lineTo(vx0, vy0);
-    state->lineTo(vx1, vy1);
-    state->lineTo(ux1, uy1);
-    state->closePath();
-    out->fill(state);
-    state->clearPath();
+      // compute the coordinates of the point on the t axis; then
+      // compute the intersection of the perpendicular line with the
+      // bounding box
+      tx = x0 + ta[j] * dx;
+      ty = y0 + ta[j] * dy;
+      if (dxZero && dyZero) {
+       sMin = sMax = 0;
+      } if (dxZero) {
+       sMin = (xMin - tx) / -dy;
+       sMax = (xMax - tx) / -dy;
+       if (sMin > sMax) { tmp = sMin; sMin = sMax; sMax = tmp; }
+      } else if (dyZero) {
+       sMin = (yMin - ty) / dx;
+       sMax = (yMax - ty) / dx;
+       if (sMin > sMax) { tmp = sMin; sMin = sMax; sMax = tmp; }
+      } else {
+       s[0] = (yMin - ty) / dx;
+       s[1] = (yMax - ty) / dx;
+       s[2] = (xMin - tx) / -dy;
+       s[3] = (xMax - tx) / -dy;
+       for (j = 0; j < 3; ++j) {
+         kk = j;
+         for (k = j + 1; k < 4; ++k) {
+           if (s[k] < s[kk]) {
+             kk = k;
+           }
+         }
+         tmp = s[j]; s[j] = s[kk]; s[kk] = tmp;
+       }
+       sMin = s[1];
+       sMax = s[2];
+      }
+      ux1 = tx - sMin * dy;
+      uy1 = ty + sMin * dx;
+      vx1 = tx - sMax * dy;
+      vy1 = ty + sMax * dx;
 
-    // set up for next region
-    ux0 = ux1;
-    uy0 = uy1;
-    vx0 = vx1;
-    vy0 = vy1;
-    color0 = color1;
-    i = next[i];
+      // set the color
+      state->setFillColor(&color0);
+      out->updateFillColor(state);
+
+      // fill the region
+      state->moveTo(ux0, uy0);
+      state->lineTo(vx0, vy0);
+      state->lineTo(vx1, vy1);
+      state->lineTo(ux1, uy1);
+      state->closePath();
+      out->fill(state);
+      state->clearPath();
+
+      // set up for next region
+      ux0 = ux1;
+      uy0 = uy1;
+      vx0 = vx1;
+      vy0 = vy1;
+      color0 = color1;
+      i = next[i];
+    }
   }
 }
 
@@ -1907,144 +2013,134 @@ void Gfx::doRadialShFill(GfxRadialShading *shading) {
   double ta, tb, sa, sb;
   int ia, ib, k, n;
   double *ctm;
-  double angle, t;
+  double angle, t, d0, d1;
 
-  // get the shading info
-  shading->getCoords(&x0, &y0, &r0, &x1, &y1, &r1);
-  t0 = shading->getDomain0();
-  t1 = shading->getDomain1();
-  nComps = shading->getColorSpace()->getNComps();
+  if (out->useShadedFills()) {
 
-  // compute the (possibly extended) s range
-  sMin = 0;
-  sMax = 1;
-  if (shading->getExtend0()) {
-    if (r0 < r1) {
-      // extend the smaller end
-      sMin = -r0 / (r1 - r0);
-    } else {
-      // extend the larger end
-      //~ this computes the diagonal of the bounding box -- we should
-      //~ really compute the intersection of the moving/expanding
-      //~ circles with each of the four corners and look for the max
-      //~ radius
-      state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax);
-      sMin = (sqrt((xMax - xMin) * (xMax - xMin) +
-                  (yMax - yMin) * (yMax - yMin)) - r0) / (r1 - r0);
-      if (sMin > 0) {
-       sMin = 0;
-      } else if (sMin < -20) {
-       // sanity check
-       sMin = -20;
+    out->radialShadedFill(state, shading);
+
+  } else {
+
+    // get the shading info
+    shading->getCoords(&x0, &y0, &r0, &x1, &y1, &r1);
+    t0 = shading->getDomain0();
+    t1 = shading->getDomain1();
+    nComps = shading->getColorSpace()->getNComps();
+
+    // compute the (possibly extended) s range
+    sMin = 0;
+    sMax = 1;
+    if (shading->getExtend0()) {
+      if (r0 < r1) {
+       // extend the smaller end
+       sMin = -r0 / (r1 - r0);
+      } else {
+       // extend the larger end
+       state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax);
+       d0 = (x0 - xMin) * (x0 - xMin);
+       d1 = (x0 - xMax) * (x0 - xMax);
+       sMin = d0 > d1 ? d0 : d1;
+       d0 = (y0 - yMin) * (y0 - yMin);
+       d1 = (y0 - yMax) * (y0 - yMax);
+       sMin += d0 > d1 ? d0 : d1;
+       sMin = (sqrt(sMin) - r0) / (r1 - r0);
+       if (sMin > 0) {
+         sMin = 0;
+       } else if (sMin < -20) {
+         // sanity check
+         sMin = -20;
+       }
       }
     }
-  }
-  if (shading->getExtend1()) {
-    if (r1 < r0) {
-      // extend the smaller end
-      sMax = -r0 / (r1 - r0);
-    } else if (r1 > r0) {
-      // extend the larger end
-      state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax);
-      sMax = (sqrt((xMax - xMin) * (xMax - xMin) +
-                  (yMax - yMin) * (yMax - yMin)) - r0) / (r1 - r0);
-      if (sMax < 1) {
-       sMin = 1;
-      } else if (sMax > 20) {
-       // sanity check
-       sMax = 20;
+    if (shading->getExtend1()) {
+      if (r1 < r0) {
+       // extend the smaller end
+       sMax = -r0 / (r1 - r0);
+      } else if (r1 > r0) {
+       // extend the larger end
+       state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax);
+       d0 = (x1 - xMin) * (x1 - xMin);
+       d1 = (x1 - xMax) * (x1 - xMax);
+       sMax = d0 > d1 ? d0 : d1;
+       d0 = (y1 - yMin) * (y1 - yMin);
+       d1 = (y1 - yMax) * (y1 - yMax);
+       sMax += d0 > d1 ? d0 : d1;
+       sMax = (sqrt(sMax) - r0) / (r1 - r0);
+       if (sMax < 1) {
+         sMax = 1;
+       } else if (sMax > 20) {
+         // sanity check
+         sMax = 20;
+       }
       }
     }
-  }
 
-  // compute the number of steps into which circles must be divided to
-  // achieve a curve flatness of 0.1 pixel in device space for the
-  // largest circle (note that "device space" is 72 dpi when generating
-  // PostScript, hence the relatively small 0.1 pixel accuracy)
-  ctm = state->getCTM();
-  t = fabs(ctm[0]);
-  if (fabs(ctm[1]) > t) {
-    t = fabs(ctm[1]);
-  }
-  if (fabs(ctm[2]) > t) {
-    t = fabs(ctm[2]);
-  }
-  if (fabs(ctm[3]) > t) {
-    t = fabs(ctm[3]);
-  }
-  if (r0 > r1) {
-    t *= r0;
-  } else {
-    t *= r1;
-  }
-  if (t < 1) {
-    n = 3;
-  } else {
-    n = (int)(M_PI / acos(1 - 0.1 / t));
-    if (n < 3) {
-      n = 3;
-    } else if (n > 200) {
-      n = 200;
-    }
-  }
-
-  // Traverse the t axis and do the shading.
-  //
-  // This generates and fills a series of rings.  Each ring is defined
-  // by two circles:
-  //   sa, ta, xa, ya, ra, colorA
-  //   sb, tb, xb, yb, rb, colorB
-  //
-  // The s/t axis is divided into radialMaxSplits parts; these parts
-  // are combined as much as possible while respecting the
-  // radialColorDelta parameter.
-
-  // setup for the start circle
-  ia = 0;
-  sa = sMin;
-  ta = t0 + sa * (t1 - t0);
-  xa = x0 + sa * (x1 - x0);
-  ya = y0 + sa * (y1 - y0);
-  ra = r0 + sa * (r1 - r0);
-  if (ta < t0) {
-    shading->getColor(t0, &colorA);
-  } else if (ta > t1) {
-    shading->getColor(t1, &colorA);
-  } else {
-    shading->getColor(ta, &colorA);
-  }
-
-  if(ia < radialMaxSplits)
-    out->useGradients();
-
-  while (ia < radialMaxSplits) {
-
-    // go as far along the t axis (toward t1) as we can, such that the
-    // color difference is within the tolerance (radialColorDelta) --
-    // this uses bisection (between the current value, t, and t1),
-    // limited to radialMaxSplits points along the t axis; require at
-    // least one split to avoid problems when the innermost and
-    // outermost colors are the same
-    ib = radialMaxSplits;
-    sb = sMin + ((double)ib / (double)radialMaxSplits) * (sMax - sMin);
-    tb = t0 + sb * (t1 - t0);
-    if (tb < t0) {
-      shading->getColor(t0, &colorB);
-    } else if (tb > t1) {
-      shading->getColor(t1, &colorB);
+    // compute the number of steps into which circles must be divided to
+    // achieve a curve flatness of 0.1 pixel in device space for the
+    // largest circle (note that "device space" is 72 dpi when generating
+    // PostScript, hence the relatively small 0.1 pixel accuracy)
+    ctm = state->getCTM();
+    t = fabs(ctm[0]);
+    if (fabs(ctm[1]) > t) {
+      t = fabs(ctm[1]);
+    }
+    if (fabs(ctm[2]) > t) {
+      t = fabs(ctm[2]);
+    }
+    if (fabs(ctm[3]) > t) {
+      t = fabs(ctm[3]);
+    }
+    if (r0 > r1) {
+      t *= r0;
     } else {
-      shading->getColor(tb, &colorB);
+      t *= r1;
     }
-    while (ib - ia > 1) {
-      for (k = 0; k < nComps; ++k) {
-       if (fabs(colorB.c[k] - colorA.c[k]) > radialColorDelta) {
-         break;
-       }
-      }
-      if (k == nComps && ib < radialMaxSplits) {
-       break;
+    if (t < 1) {
+      n = 3;
+    } else {
+      n = (int)(M_PI / acos(1 - 0.1 / t));
+      if (n < 3) {
+       n = 3;
+      } else if (n > 200) {
+       n = 200;
       }
-      ib = (ia + ib) / 2;
+    }
+
+    // Traverse the t axis and do the shading.
+    //
+    // This generates and fills a series of rings.  Each ring is defined
+    // by two circles:
+    //   sa, ta, xa, ya, ra, colorA
+    //   sb, tb, xb, yb, rb, colorB
+    //
+    // The s/t axis is divided into radialMaxSplits parts; these parts
+    // are combined as much as possible while respecting the
+    // radialColorDelta parameter.
+
+    // setup for the start circle
+    ia = 0;
+    sa = sMin;
+    ta = t0 + sa * (t1 - t0);
+    xa = x0 + sa * (x1 - x0);
+    ya = y0 + sa * (y1 - y0);
+    ra = r0 + sa * (r1 - r0);
+    if (ta < t0) {
+      shading->getColor(t0, &colorA);
+    } else if (ta > t1) {
+      shading->getColor(t1, &colorA);
+    } else {
+      shading->getColor(ta, &colorA);
+    }
+
+    while (ia < radialMaxSplits) {
+
+      // go as far along the t axis (toward t1) as we can, such that the
+      // color difference is within the tolerance (radialColorDelta) --
+      // this uses bisection (between the current value, t, and t1),
+      // limited to radialMaxSplits points along the t axis; require at
+      // least one split to avoid problems when the innermost and
+      // outermost colors are the same
+      ib = radialMaxSplits;
       sb = sMin + ((double)ib / (double)radialMaxSplits) * (sMax - sMin);
       tb = t0 + sb * (t1 - t0);
       if (tb < t0) {
@@ -2054,48 +2150,276 @@ void Gfx::doRadialShFill(GfxRadialShading *shading) {
       } else {
        shading->getColor(tb, &colorB);
       }
-    }
+      while (ib - ia > 1) {
+       for (k = 0; k < nComps; ++k) {
+         if (abs(colorB.c[k] - colorA.c[k]) > radialColorDelta) {
+           break;
+         }
+       }
+       if (k == nComps && ib < radialMaxSplits) {
+         break;
+       }
+       ib = (ia + ib) / 2;
+       sb = sMin + ((double)ib / (double)radialMaxSplits) * (sMax - sMin);
+       tb = t0 + sb * (t1 - t0);
+       if (tb < t0) {
+         shading->getColor(t0, &colorB);
+       } else if (tb > t1) {
+         shading->getColor(t1, &colorB);
+       } else {
+         shading->getColor(tb, &colorB);
+       }
+      }
+
+      // compute center and radius of the circle
+      xb = x0 + sb * (x1 - x0);
+      yb = y0 + sb * (y1 - y0);
+      rb = r0 + sb * (r1 - r0);
+
+      // use the average of the colors at the two circles
+      for (k = 0; k < nComps; ++k) {
+       colorA.c[k] = (colorA.c[k] + colorB.c[k]) / 2;
+      }
+      state->setFillColor(&colorA);
+      out->updateFillColor(state);
+
+      // construct path for first circle
+      state->moveTo(xa + ra, ya);
+      for (k = 1; k < n; ++k) {
+       angle = ((double)k / (double)n) * 2 * M_PI;
+       state->lineTo(xa + ra * cos(angle), ya + ra * sin(angle));
+      }
+      state->closePath();
 
-    // compute center and radius of the circle
-    xb = x0 + sb * (x1 - x0);
-    yb = y0 + sb * (y1 - y0);
-    rb = r0 + sb * (r1 - r0);
+      // construct and append path for second circle
+      state->moveTo(xb + rb, yb);
+      for (k = 1; k < n; ++k) {
+       angle = ((double)k / (double)n) * 2 * M_PI;
+       state->lineTo(xb + rb * cos(angle), yb + rb * sin(angle));
+      }
+      state->closePath();
 
-    // use the average of the colors at the two circles
-    for (k = 0; k < nComps; ++k) {
-      colorA.c[k] = 0.5 * (colorA.c[k] + colorB.c[k]);
+      // fill the ring
+      out->eoFill(state);
+      state->clearPath();
+
+      // step to the next value of t
+      ia = ib;
+      sa = sb;
+      ta = tb;
+      xa = xb;
+      ya = yb;
+      ra = rb;
+      colorA = colorB;
     }
-    state->setFillColor(&colorA);
-    out->updateFillColor(state);
+  }
+}
+
+void Gfx::doGouraudTriangleShFill(GfxGouraudTriangleShading *shading) {
+  double x0, y0, x1, y1, x2, y2;
+  GfxColor color0, color1, color2;
+  int i;
+
+  for (i = 0; i < shading->getNTriangles(); ++i) {
+    shading->getTriangle(i, &x0, &y0, &color0,
+                        &x1, &y1, &color1,
+                        &x2, &y2, &color2);
+    gouraudFillTriangle(x0, y0, &color0, x1, y1, &color1, x2, y2, &color2,
+                       shading->getColorSpace()->getNComps(), 0);
+  }
+}
+
+void Gfx::gouraudFillTriangle(double x0, double y0, GfxColor *color0,
+                             double x1, double y1, GfxColor *color1,
+                             double x2, double y2, GfxColor *color2,
+                             int nComps, int depth) {
+  double x01, y01, x12, y12, x20, y20;
+  GfxColor color01, color12, color20;
+  int i;
 
-    // construct path for first circle
-    state->moveTo(xa + ra, ya);
-    for (k = 1; k < n; ++k) {
-      angle = ((double)k / (double)n) * 2 * M_PI;
-      state->lineTo(xa + ra * cos(angle), ya + ra * sin(angle));
+  for (i = 0; i < nComps; ++i) {
+    if (abs(color0->c[i] - color1->c[i]) > gouraudColorDelta ||
+       abs(color1->c[i] - color2->c[i]) > gouraudColorDelta) {
+      break;
     }
+  }
+  if (i == nComps || depth == gouraudMaxDepth) {
+    state->setFillColor(color0);
+    out->updateFillColor(state);
+    state->moveTo(x0, y0);
+    state->lineTo(x1, y1);
+    state->lineTo(x2, y2);
     state->closePath();
+    out->fill(state);
+    state->clearPath();
+  } else {
+    x01 = 0.5 * (x0 + x1);
+    y01 = 0.5 * (y0 + y1);
+    x12 = 0.5 * (x1 + x2);
+    y12 = 0.5 * (y1 + y2);
+    x20 = 0.5 * (x2 + x0);
+    y20 = 0.5 * (y2 + y0);
+    //~ if the shading has a Function, this should interpolate on the
+    //~ function parameter, not on the color components
+    for (i = 0; i < nComps; ++i) {
+      color01.c[i] = (color0->c[i] + color1->c[i]) / 2;
+      color12.c[i] = (color1->c[i] + color2->c[i]) / 2;
+      color20.c[i] = (color2->c[i] + color0->c[i]) / 2;
+    }
+    gouraudFillTriangle(x0, y0, color0, x01, y01, &color01,
+                       x20, y20, &color20, nComps, depth + 1);
+    gouraudFillTriangle(x01, y01, &color01, x1, y1, color1,
+                       x12, y12, &color12, nComps, depth + 1);
+    gouraudFillTriangle(x01, y01, &color01, x12, y12, &color12,
+                       x20, y20, &color20, nComps, depth + 1);
+    gouraudFillTriangle(x20, y20, &color20, x12, y12, &color12,
+                       x2, y2, color2, nComps, depth + 1);
+  }
+}
+
+void Gfx::doPatchMeshShFill(GfxPatchMeshShading *shading) {
+  int start, i;
+
+  if (shading->getNPatches() > 128) {
+    start = 3;
+  } else if (shading->getNPatches() > 64) {
+    start = 2;
+  } else if (shading->getNPatches() > 16) {
+    start = 1;
+  } else {
+    start = 0;
+  }
+  for (i = 0; i < shading->getNPatches(); ++i) {
+    fillPatch(shading->getPatch(i), shading->getColorSpace()->getNComps(),
+             start);
+  }
+}
 
-    // construct and append path for second circle
-    state->moveTo(xb + rb, yb);
-    for (k = 1; k < n; ++k) {
-      angle = ((double)k / (double)n) * 2 * M_PI;
-      state->lineTo(xb + rb * cos(angle), yb + rb * sin(angle));
+void Gfx::fillPatch(GfxPatch *patch, int nComps, int depth) {
+  GfxPatch patch00, patch01, patch10, patch11;
+  double xx[4][8], yy[4][8];
+  double xxm, yym;
+  int i;
+
+  for (i = 0; i < nComps; ++i) {
+    if (abs(patch->color[0][0].c[i] - patch->color[0][1].c[i])
+         > patchColorDelta ||
+       abs(patch->color[0][1].c[i] - patch->color[1][1].c[i])
+         > patchColorDelta ||
+       abs(patch->color[1][1].c[i] - patch->color[1][0].c[i])
+         > patchColorDelta ||
+       abs(patch->color[1][0].c[i] - patch->color[0][0].c[i])
+         > patchColorDelta) {
+      break;
     }
+  }
+  if (i == nComps || depth == patchMaxDepth) {
+    state->setFillColor(&patch->color[0][0]);
+    out->updateFillColor(state);
+    state->moveTo(patch->x[0][0], patch->y[0][0]);
+    state->curveTo(patch->x[0][1], patch->y[0][1],
+                  patch->x[0][2], patch->y[0][2],
+                  patch->x[0][3], patch->y[0][3]);
+    state->curveTo(patch->x[1][3], patch->y[1][3],
+                  patch->x[2][3], patch->y[2][3],
+                  patch->x[3][3], patch->y[3][3]);
+    state->curveTo(patch->x[3][2], patch->y[3][2],
+                  patch->x[3][1], patch->y[3][1],
+                  patch->x[3][0], patch->y[3][0]);
+    state->curveTo(patch->x[2][0], patch->y[2][0],
+                  patch->x[1][0], patch->y[1][0],
+                  patch->x[0][0], patch->y[0][0]);
     state->closePath();
-
-    // fill the ring
-    out->eoFill(state);
+    out->fill(state);
     state->clearPath();
-
-    // step to the next value of t
-    ia = ib;
-    sa = sb;
-    ta = tb;
-    xa = xb;
-    ya = yb;
-    ra = rb;
-    colorA = colorB;
+  } else {
+    for (i = 0; i < 4; ++i) {
+      xx[i][0] = patch->x[i][0];
+      yy[i][0] = patch->y[i][0];
+      xx[i][1] = 0.5 * (patch->x[i][0] + patch->x[i][1]);
+      yy[i][1] = 0.5 * (patch->y[i][0] + patch->y[i][1]);
+      xxm = 0.5 * (patch->x[i][1] + patch->x[i][2]);
+      yym = 0.5 * (patch->y[i][1] + patch->y[i][2]);
+      xx[i][6] = 0.5 * (patch->x[i][2] + patch->x[i][3]);
+      yy[i][6] = 0.5 * (patch->y[i][2] + patch->y[i][3]);
+      xx[i][2] = 0.5 * (xx[i][1] + xxm);
+      yy[i][2] = 0.5 * (yy[i][1] + yym);
+      xx[i][5] = 0.5 * (xxm + xx[i][6]);
+      yy[i][5] = 0.5 * (yym + yy[i][6]);
+      xx[i][3] = xx[i][4] = 0.5 * (xx[i][2] + xx[i][5]);
+      yy[i][3] = yy[i][4] = 0.5 * (yy[i][2] + yy[i][5]);
+      xx[i][7] = patch->x[i][3];
+      yy[i][7] = patch->y[i][3];
+    }
+    for (i = 0; i < 4; ++i) {
+      patch00.x[0][i] = xx[0][i];
+      patch00.y[0][i] = yy[0][i];
+      patch00.x[1][i] = 0.5 * (xx[0][i] + xx[1][i]);
+      patch00.y[1][i] = 0.5 * (yy[0][i] + yy[1][i]);
+      xxm = 0.5 * (xx[1][i] + xx[2][i]);
+      yym = 0.5 * (yy[1][i] + yy[2][i]);
+      patch10.x[2][i] = 0.5 * (xx[2][i] + xx[3][i]);
+      patch10.y[2][i] = 0.5 * (yy[2][i] + yy[3][i]);
+      patch00.x[2][i] = 0.5 * (patch00.x[1][i] + xxm);
+      patch00.y[2][i] = 0.5 * (patch00.y[1][i] + yym);
+      patch10.x[1][i] = 0.5 * (xxm + patch10.x[2][i]);
+      patch10.y[1][i] = 0.5 * (yym + patch10.y[2][i]);
+      patch00.x[3][i] = 0.5 * (patch00.x[2][i] + patch10.x[1][i]);
+      patch00.y[3][i] = 0.5 * (patch00.y[2][i] + patch10.y[1][i]);
+      patch10.x[0][i] = patch00.x[3][i];
+      patch10.y[0][i] = patch00.y[3][i];
+      patch10.x[3][i] = xx[3][i];
+      patch10.y[3][i] = yy[3][i];
+    }
+    for (i = 4; i < 8; ++i) {
+      patch01.x[0][i-4] = xx[0][i];
+      patch01.y[0][i-4] = yy[0][i];
+      patch01.x[1][i-4] = 0.5 * (xx[0][i] + xx[1][i]);
+      patch01.y[1][i-4] = 0.5 * (yy[0][i] + yy[1][i]);
+      xxm = 0.5 * (xx[1][i] + xx[2][i]);
+      yym = 0.5 * (yy[1][i] + yy[2][i]);
+      patch11.x[2][i-4] = 0.5 * (xx[2][i] + xx[3][i]);
+      patch11.y[2][i-4] = 0.5 * (yy[2][i] + yy[3][i]);
+      patch01.x[2][i-4] = 0.5 * (patch01.x[1][i-4] + xxm);
+      patch01.y[2][i-4] = 0.5 * (patch01.y[1][i-4] + yym);
+      patch11.x[1][i-4] = 0.5 * (xxm + patch11.x[2][i-4]);
+      patch11.y[1][i-4] = 0.5 * (yym + patch11.y[2][i-4]);
+      patch01.x[3][i-4] = 0.5 * (patch01.x[2][i-4] + patch11.x[1][i-4]);
+      patch01.y[3][i-4] = 0.5 * (patch01.y[2][i-4] + patch11.y[1][i-4]);
+      patch11.x[0][i-4] = patch01.x[3][i-4];
+      patch11.y[0][i-4] = patch01.y[3][i-4];
+      patch11.x[3][i-4] = xx[3][i];
+      patch11.y[3][i-4] = yy[3][i];
+    }
+    //~ if the shading has a Function, this should interpolate on the
+    //~ function parameter, not on the color components
+    for (i = 0; i < nComps; ++i) {
+      patch00.color[0][0].c[i] = patch->color[0][0].c[i];
+      patch00.color[0][1].c[i] = (patch->color[0][0].c[i] +
+                                 patch->color[0][1].c[i]) / 2;
+      patch01.color[0][0].c[i] = patch00.color[0][1].c[i];
+      patch01.color[0][1].c[i] = patch->color[0][1].c[i];
+      patch01.color[1][1].c[i] = (patch->color[0][1].c[i] +
+                                 patch->color[1][1].c[i]) / 2;
+      patch11.color[0][1].c[i] = patch01.color[1][1].c[i];
+      patch11.color[1][1].c[i] = patch->color[1][1].c[i];
+      patch11.color[1][0].c[i] = (patch->color[1][1].c[i] +
+                                 patch->color[1][0].c[i]) / 2;
+      patch10.color[1][1].c[i] = patch11.color[1][0].c[i];
+      patch10.color[1][0].c[i] = patch->color[1][0].c[i];
+      patch10.color[0][0].c[i] = (patch->color[1][0].c[i] +
+                                 patch->color[0][0].c[i]) / 2;
+      patch00.color[1][0].c[i] = patch10.color[0][0].c[i];
+      patch00.color[1][1].c[i] = (patch00.color[1][0].c[i] +
+                                 patch01.color[1][1].c[i]) / 2;
+      patch01.color[1][0].c[i] = patch00.color[1][1].c[i];
+      patch11.color[0][0].c[i] = patch00.color[1][1].c[i];
+      patch10.color[0][1].c[i] = patch00.color[1][1].c[i];
+    }
+    fillPatch(&patch00, nComps, depth + 1);
+    fillPatch(&patch10, nComps, depth + 1);
+    fillPatch(&patch01, nComps, depth + 1);
+    fillPatch(&patch11, nComps, depth + 1);
   }
 }
 
@@ -2243,7 +2567,13 @@ void Gfx::opShowText(Object args[], int numArgs) {
     error(getPos(), "No font in show");
     return;
   }
+  if (fontChanged) {
+    out->updateFont(state);
+    fontChanged = gFalse;
+  }
+  out->beginStringOp(state);
   doShowText(args[0].getString());
+  out->endStringOp(state);
 }
 
 void Gfx::opMoveShowText(Object args[], int numArgs) {
@@ -2253,11 +2583,17 @@ void Gfx::opMoveShowText(Object args[], int numArgs) {
     error(getPos(), "No font in move/show");
     return;
   }
+  if (fontChanged) {
+    out->updateFont(state);
+    fontChanged = gFalse;
+  }
   tx = state->getLineX();
   ty = state->getLineY() - state->getLeading();
   state->textMoveTo(tx, ty);
   out->updateTextPos(state);
+  out->beginStringOp(state);
   doShowText(args[0].getString());
+  out->endStringOp(state);
 }
 
 void Gfx::opMoveSetShowText(Object args[], int numArgs) {
@@ -2267,6 +2603,10 @@ void Gfx::opMoveSetShowText(Object args[], int numArgs) {
     error(getPos(), "No font in move/set/show");
     return;
   }
+  if (fontChanged) {
+    out->updateFont(state);
+    fontChanged = gFalse;
+  }
   state->setWordSpace(args[0].getNum());
   state->setCharSpace(args[1].getNum());
   tx = state->getLineX();
@@ -2275,7 +2615,9 @@ void Gfx::opMoveSetShowText(Object args[], int numArgs) {
   out->updateWordSpace(state);
   out->updateCharSpace(state);
   out->updateTextPos(state);
+  out->beginStringOp(state);
   doShowText(args[2].getString());
+  out->endStringOp(state);
 }
 
 void Gfx::opShowSpaceText(Object args[], int numArgs) {
@@ -2288,15 +2630,24 @@ void Gfx::opShowSpaceText(Object args[], int numArgs) {
     error(getPos(), "No font in show/space");
     return;
   }
+  if (fontChanged) {
+    out->updateFont(state);
+    fontChanged = gFalse;
+  }
+  out->beginStringOp(state);
   wMode = state->getFont()->getWMode();
   a = args[0].getArray();
   for (i = 0; i < a->getLength(); ++i) {
     a->get(i, &obj);
     if (obj.isNum()) {
+      // this uses the absolute value of the font size to match
+      // Acrobat's behavior
       if (wMode) {
-       state->textShift(0, -obj.getNum() * 0.001 * state->getFontSize());
+       state->textShift(0, -obj.getNum() * 0.001 *
+                           fabs(state->getFontSize()));
       } else {
-       state->textShift(-obj.getNum() * 0.001 * state->getFontSize(), 0);
+       state->textShift(-obj.getNum() * 0.001 *
+                        fabs(state->getFontSize()), 0);
       }
       out->updateTextShift(state, obj.getNum());
     } else if (obj.isString()) {
@@ -2306,6 +2657,7 @@ void Gfx::opShowSpaceText(Object args[], int numArgs) {
     }
     obj.free();
   }
+  out->endStringOp(state);
 }
 
 void Gfx::doShowText(GString *s) {
@@ -2324,10 +2676,6 @@ void Gfx::doShowText(GString *s) {
   char *p;
   int len, n, uLen, nChars, nSpaces, i;
 
-  if (fontChanged) {
-    out->updateFont(state);
-    fontChanged = gFalse;
-  }
   font = state->getFont();
   wMode = font->getWMode();
 
@@ -2436,7 +2784,7 @@ void Gfx::doShowText(GString *s) {
       originY *= state->getFontSize();
       state->textTransformDelta(originX, originY, &tOriginX, &tOriginY);
       out->drawChar(state, state->getCurX() + riseX, state->getCurY() + riseY,
-                   tdx, tdy, tOriginX, tOriginY, code, u, uLen);
+                   tdx, tdy, tOriginX, tOriginY, code, n, u, uLen);
       state->shift(tdx, tdy);
       p += n;
       len -= n;
@@ -2510,9 +2858,11 @@ void Gfx::opXObject(Object args[], int numArgs) {
 #endif
   obj1.streamGetDict()->lookup("Subtype", &obj2);
   if (obj2.isName("Image")) {
-    res->lookupXObjectNF(args[0].getName(), &refObj);
-    doImage(&refObj, obj1.getStream(), gFalse);
-    refObj.free();
+    if (out->needNonText()) {
+      res->lookupXObjectNF(args[0].getName(), &refObj);
+      doImage(&refObj, obj1.getStream(), gFalse);
+      refObj.free();
+    }
   } else if (obj2.isName("Form")) {
     doForm(&obj1);
   } else if (obj2.isName("PS")) {
@@ -2535,19 +2885,28 @@ void Gfx::opXObject(Object args[], int numArgs) {
 }
 
 void Gfx::doImage(Object *ref, Stream *str, GBool inlineImg) {
-  Dict *dict;
+  Dict *dict, *maskDict;
   int width, height;
-  int bits;
+  int bits, maskBits;
+  StreamColorSpaceMode csMode;
   GBool mask;
   GBool invert;
-  GfxColorSpace *colorSpace;
-  GfxImageColorMap *colorMap;
-  Object maskObj;
-  GBool haveMask;
+  GfxColorSpace *colorSpace, *maskColorSpace;
+  GfxImageColorMap *colorMap, *maskColorMap;
+  Object maskObj, smaskObj;
+  GBool haveColorKeyMask, haveExplicitMask, haveSoftMask;
   int maskColors[2*gfxColorMaxComps];
+  int maskWidth, maskHeight;
+  GBool maskInvert;
+  Stream *maskStr;
   Object obj1, obj2;
   int i;
 
+  // get info from the stream
+  bits = 0;
+  csMode = streamCSNone;
+  str->getImageParams(&bits, &csMode);
+
   // get stream dict
   dict = str->getDict();
 
@@ -2585,19 +2944,21 @@ void Gfx::doImage(Object *ref, Stream *str, GBool inlineImg) {
   obj1.free();
 
   // bit depth
-  dict->lookup("BitsPerComponent", &obj1);
-  if (obj1.isNull()) {
+  if (bits == 0) {
+    dict->lookup("BitsPerComponent", &obj1);
+    if (obj1.isNull()) {
+      obj1.free();
+      dict->lookup("BPC", &obj1);
+    }
+    if (obj1.isInt()) {
+      bits = obj1.getInt();
+    } else if (mask) {
+      bits = 1;
+    } else {
+      goto err2;
+    }
     obj1.free();
-    dict->lookup("BPC", &obj1);
   }
-  if (obj1.isInt()) {
-    bits = obj1.getInt();
-  } else if (mask) {
-    bits = 1;
-  } else {
-    goto err2;
-  }
-  obj1.free();
 
   // display a mask
   if (mask) {
@@ -2641,7 +3002,17 @@ void Gfx::doImage(Object *ref, Stream *str, GBool inlineImg) {
        obj2.free();
       }
     }
-    colorSpace = GfxColorSpace::parse(&obj1);
+    if (!obj1.isNull()) {
+      colorSpace = GfxColorSpace::parse(&obj1);
+    } else if (csMode == streamCSDeviceGray) {
+      colorSpace = new GfxDeviceGrayColorSpace();
+    } else if (csMode == streamCSDeviceRGB) {
+      colorSpace = new GfxDeviceRGBColorSpace();
+    } else if (csMode == streamCSDeviceCMYK) {
+      colorSpace = new GfxDeviceCMYKColorSpace();
+    } else {
+      colorSpace = NULL;
+    }
     obj1.free();
     if (!colorSpace) {
       goto err1;
@@ -2659,23 +3030,163 @@ void Gfx::doImage(Object *ref, Stream *str, GBool inlineImg) {
     }
 
     // get the mask
-    haveMask = gFalse;
+    haveColorKeyMask = haveExplicitMask = haveSoftMask = gFalse;
+    maskStr = NULL; // make gcc happy
+    maskWidth = maskHeight = 0; // make gcc happy
+    maskInvert = gFalse; // make gcc happy
+    maskColorMap = NULL; // make gcc happy
     dict->lookup("Mask", &maskObj);
-    if (maskObj.isArray()) {
-      for (i = 0; i < maskObj.arrayGetLength(); ++i) {
+    dict->lookup("SMask", &smaskObj);
+    if (smaskObj.isStream()) {
+      // soft mask
+      if (inlineImg) {
+       goto err1;
+      }
+      maskStr = smaskObj.getStream();
+      maskDict = smaskObj.streamGetDict();
+      maskDict->lookup("Width", &obj1);
+      if (obj1.isNull()) {
+       obj1.free();
+       maskDict->lookup("W", &obj1);
+      }
+      if (!obj1.isInt()) {
+       goto err2;
+      }
+      maskWidth = obj1.getInt();
+      obj1.free();
+      maskDict->lookup("Height", &obj1);
+      if (obj1.isNull()) {
+       obj1.free();
+       maskDict->lookup("H", &obj1);
+      }
+      if (!obj1.isInt()) {
+       goto err2;
+      }
+      maskHeight = obj1.getInt();
+      obj1.free();
+      maskDict->lookup("BitsPerComponent", &obj1);
+      if (obj1.isNull()) {
+       obj1.free();
+       maskDict->lookup("BPC", &obj1);
+      }
+      if (!obj1.isInt()) {
+       goto err2;
+      }
+      maskBits = obj1.getInt();
+      obj1.free();
+      maskDict->lookup("ColorSpace", &obj1);
+      if (obj1.isNull()) {
+       obj1.free();
+       maskDict->lookup("CS", &obj1);
+      }
+      if (obj1.isName()) {
+       res->lookupColorSpace(obj1.getName(), &obj2);
+       if (!obj2.isNull()) {
+         obj1.free();
+         obj1 = obj2;
+       } else {
+         obj2.free();
+       }
+      }
+      maskColorSpace = GfxColorSpace::parse(&obj1);
+      obj1.free();
+      if (!maskColorSpace || maskColorSpace->getMode() != csDeviceGray) {
+       goto err1;
+      }
+      maskDict->lookup("Decode", &obj1);
+      if (obj1.isNull()) {
+       obj1.free();
+       maskDict->lookup("D", &obj1);
+      }
+      maskColorMap = new GfxImageColorMap(maskBits, &obj1, maskColorSpace);
+      obj1.free();
+      if (!maskColorMap->isOk()) {
+       delete maskColorMap;
+       goto err1;
+      }
+      //~ handle the Matte entry
+      haveSoftMask = gTrue;
+    } else if (maskObj.isArray()) {
+      // color key mask
+      for (i = 0;
+          i < maskObj.arrayGetLength() && i < 2*gfxColorMaxComps;
+          ++i) {
        maskObj.arrayGet(i, &obj1);
        maskColors[i] = obj1.getInt();
        obj1.free();
       }
-      haveMask = gTrue;
+      haveColorKeyMask = gTrue;
+    } else if (maskObj.isStream()) {
+      // explicit mask
+      if (inlineImg) {
+       goto err1;
+      }
+      maskStr = maskObj.getStream();
+      maskDict = maskObj.streamGetDict();
+      maskDict->lookup("Width", &obj1);
+      if (obj1.isNull()) {
+       obj1.free();
+       maskDict->lookup("W", &obj1);
+      }
+      if (!obj1.isInt()) {
+       goto err2;
+      }
+      maskWidth = obj1.getInt();
+      obj1.free();
+      maskDict->lookup("Height", &obj1);
+      if (obj1.isNull()) {
+       obj1.free();
+       maskDict->lookup("H", &obj1);
+      }
+      if (!obj1.isInt()) {
+       goto err2;
+      }
+      maskHeight = obj1.getInt();
+      obj1.free();
+      maskDict->lookup("ImageMask", &obj1);
+      if (obj1.isNull()) {
+       obj1.free();
+       maskDict->lookup("IM", &obj1);
+      }
+      if (!obj1.isBool() || !obj1.getBool()) {
+       goto err2;
+      }
+      obj1.free();
+      maskInvert = gFalse;
+      maskDict->lookup("Decode", &obj1);
+      if (obj1.isNull()) {
+       obj1.free();
+       maskDict->lookup("D", &obj1);
+      }
+      if (obj1.isArray()) {
+       obj1.arrayGet(0, &obj2);
+       if (obj2.isInt() && obj2.getInt() == 1) {
+         maskInvert = gTrue;
+       }
+       obj2.free();
+      } else if (!obj1.isNull()) {
+       goto err2;
+      }
+      obj1.free();
+      haveExplicitMask = gTrue;
     }
 
     // draw it
-    out->drawImage(state, ref, str, width, height, colorMap,
-                  haveMask ? maskColors : (int *)NULL,  inlineImg);
+    if (haveSoftMask) {
+      out->drawSoftMaskedImage(state, ref, str, width, height, colorMap,
+                              maskStr, maskWidth, maskHeight, maskColorMap);
+      delete maskColorMap;
+    } else if (haveExplicitMask) {
+      out->drawMaskedImage(state, ref, str, width, height, colorMap,
+                          maskStr, maskWidth, maskHeight, maskInvert);
+    } else {
+      out->drawImage(state, ref, str, width, height, colorMap,
+                    haveColorKeyMask ? maskColors : (int *)NULL, inlineImg);
+    }
     delete colorMap;
 
     maskObj.free();
+    smaskObj.free();
   }
 
   if ((i = width * height) > 1000) {
@@ -2710,7 +3221,7 @@ void Gfx::doForm(Object *str) {
 
   // check form type
   dict->lookup("FormType", &obj1);
-  if (!(obj1.isInt() && obj1.getInt() == 1)) {
+  if (!(obj1.isNull() || (obj1.isInt() && obj1.getInt() == 1))) {
     error(getPos(), "Unknown form type");
   }
   obj1.free();
index df1e00c..62dfd08 100644 (file)
@@ -207,7 +207,7 @@ public:
   CharCodeToUnicode *getToUnicode();
 
   // Return the character name associated with <code>.
-  char *getCharName(int code) { return code>=256?0:enc[code]; }
+  char *getCharName(int code) { return enc[code]; }
 
   // Returns true if the PDF font specified an encoding.
   GBool getHasEncoding() { return hasEncoding; }