[65164] trunk/dports/kde/kdegraphics3

takanori at macports.org takanori at macports.org
Mon Mar 22 22:43:38 PDT 2010


Revision: 65164
          http://trac.macports.org/changeset/65164
Author:   takanori at macports.org
Date:     2010-03-22 22:43:34 -0700 (Mon, 22 Mar 2010)
Log Message:
-----------
kdegraphics3: updated xpdf to 3.02pl4

Modified Paths:
--------------
    trunk/dports/kde/kdegraphics3/Portfile

Added Paths:
-----------
    trunk/dports/kde/kdegraphics3/files/xpdf-3.02pl3.patch
    trunk/dports/kde/kdegraphics3/files/xpdf-3.02pl4.patch

Modified: trunk/dports/kde/kdegraphics3/Portfile
===================================================================
--- trunk/dports/kde/kdegraphics3/Portfile	2010-03-23 04:54:22 UTC (rev 65163)
+++ trunk/dports/kde/kdegraphics3/Portfile	2010-03-23 05:43:34 UTC (rev 65164)
@@ -6,7 +6,7 @@
 
 name            kdegraphics3
 version         3.5.10
-revision        3
+revision        4
 set kdeadmin    kde-admindir-1502
 categories      kde kde3
 maintainers     nomaintainer
@@ -67,6 +67,10 @@
                     reinplace "s|%i|${destroot}${prefix}|g" ${worksrcpath}/${f}
                     file attributes ${worksrcpath}/${f} -permissions 0755
                 }
+
+                foreach f {xpdf-3.02pl3.patch xpdf-3.02pl4.patch} {
+                    system "cd ${worksrcpath}/kpdf/xpdf && patch -p1 < ${filespath}/${f}"
+                }
 }
 
 configure.args  --without-kamera --with-imlib-config=${prefix}/bin --with-distribution='MacPorts/Mac OS X' --without-arts

Added: trunk/dports/kde/kdegraphics3/files/xpdf-3.02pl3.patch
===================================================================
--- trunk/dports/kde/kdegraphics3/files/xpdf-3.02pl3.patch	                        (rev 0)
+++ trunk/dports/kde/kdegraphics3/files/xpdf-3.02pl3.patch	2010-03-23 05:43:34 UTC (rev 65164)
@@ -0,0 +1,872 @@
+diff -Naur xpdf.orig/goo/gmem.cc xpdf/goo/gmem.cc
+--- xpdf.orig/goo/gmem.cc	2008-02-13 18:37:05.000000000 +0900
++++ xpdf/goo/gmem.cc	2010-03-22 23:56:09.000000000 +0900
+@@ -55,7 +55,15 @@
+   void *data;
+   unsigned long *trl, *p;
+ 
+-  if (size <= 0) {
++  if (size < 0) {
++#if USE_EXCEPTIONS
++    throw GMemException();
++#else
++    fprintf(stderr, "Invalid memory allocation size\n");
++    exit(1);
++#endif
++  }
++  if (size == 0) {
+     return NULL;
+   }
+   size1 = gMemDataSize(size);
+@@ -91,7 +99,15 @@
+ #else
+   void *p;
+ 
+-  if (size <= 0) {
++  if (size < 0) {
++#if USE_EXCEPTIONS
++    throw GMemException();
++#else
++    fprintf(stderr, "Invalid memory allocation size\n");
++    exit(1);
++#endif
++  }
++  if (size == 0) {
+     return NULL;
+   }
+   if (!(p = malloc(size))) {
+@@ -112,7 +128,15 @@
+   void *q;
+   int oldSize;
+ 
+-  if (size <= 0) {
++  if (size < 0) {
++#if USE_EXCEPTIONS
++    throw GMemException();
++#else
++    fprintf(stderr, "Invalid memory allocation size\n");
++    exit(1);
++#endif
++  }
++  if (size == 0) {
+     if (p) {
+       gfree(p);
+     }
+@@ -131,7 +155,15 @@
+ #else
+   void *q;
+ 
+-  if (size <= 0) {
++  if (size < 0) {
++#if USE_EXCEPTIONS
++    throw GMemException();
++#else
++    fprintf(stderr, "Invalid memory allocation size\n");
++    exit(1);
++#endif
++  }
++  if (size == 0) {
+     if (p) {
+       free(p);
+     }
+diff -Naur xpdf.orig/xpdf/JBIG2Stream.cc xpdf/xpdf/JBIG2Stream.cc
+--- xpdf.orig/xpdf/JBIG2Stream.cc	2008-08-20 03:12:37.000000000 +0900
++++ xpdf/xpdf/JBIG2Stream.cc	2010-03-23 00:11:26.000000000 +0900
+@@ -422,12 +422,14 @@
+   table[i] = table[len];
+ 
+   // assign prefixes
+-  i = 0;
+-  prefix = 0;
+-  table[i++].prefix = prefix++;
+-  for (; table[i].rangeLen != jbig2HuffmanEOT; ++i) {
+-    prefix <<= table[i].prefixLen - table[i-1].prefixLen;
+-    table[i].prefix = prefix++;
++  if (table[0].rangeLen != jbig2HuffmanEOT) {
++    i = 0;
++    prefix = 0;
++    table[i++].prefix = prefix++;
++    for (; table[i].rangeLen != jbig2HuffmanEOT; ++i) {
++      prefix <<= table[i].prefixLen - table[i-1].prefixLen;
++      table[i].prefix = prefix++;
++    }
+   }
+ }
+ 
+@@ -491,7 +493,7 @@
+   }
+   if (p->bits < 0) {
+     error(str->getPos(), "Bad two dim code in JBIG2 MMR stream");
+-    return 0;
++    return EOF;
+   }
+   bufLen -= p->bits;
+   return p->n;
+@@ -684,8 +686,9 @@
+   h = hA;
+   line = (wA + 7) >> 3;
+   if (w <= 0 || h <= 0 || line <= 0 || h >= (INT_MAX - 1) / line) {
+-    data = NULL;
+-    return;
++    // force a call to gmalloc(-1), which will throw an exception
++    h = -1;
++    line = 2;
+   }
+   // need to allocate one extra guard byte for use in combine()
+   data = (Guchar *)gmalloc(h * line + 1);
+@@ -699,8 +702,9 @@
+   h = bitmap->h;
+   line = bitmap->line;
+   if (w <= 0 || h <= 0 || line <= 0 || h >= (INT_MAX - 1) / line) {
+-    data = NULL;
+-    return;
++    // force a call to gmalloc(-1), which will throw an exception
++    h = -1;
++    line = 2;
+   }
+   // need to allocate one extra guard byte for use in combine()
+   data = (Guchar *)gmalloc(h * line + 1);
+@@ -755,6 +759,8 @@
+ inline void JBIG2Bitmap::getPixelPtr(int x, int y, JBIG2BitmapPtr *ptr) {
+   if (y < 0 || y >= h || x >= w) {
+     ptr->p = NULL;
++    ptr->shift = 0; // make gcc happy
++    ptr->x = 0; // make gcc happy
+   } else if (x < 0) {
+     ptr->p = &data[y * line];
+     ptr->shift = 7;
+@@ -799,6 +805,10 @@
+   Guint src0, src1, src, dest, s1, s2, m1, m2, m3;
+   GBool oneByte;
+ 
++  // check for the pathological case where y = -2^31
++  if (y < -0x7fffffff) {
++    return;
++  }
+   if (y < 0) {
+     y0 = -y;
+   } else {
+@@ -1012,8 +1022,13 @@
+ JBIG2SymbolDict::JBIG2SymbolDict(Guint segNumA, Guint sizeA):
+   JBIG2Segment(segNumA)
+ {
++  Guint i;
++
+   size = sizeA;
+   bitmaps = (JBIG2Bitmap **)gmallocn(size, sizeof(JBIG2Bitmap *));
++  for (i = 0; i < size; ++i) {
++    bitmaps[i] = NULL;
++  }
+   genericRegionStats = NULL;
+   refinementRegionStats = NULL;
+ }
+@@ -1022,7 +1037,9 @@
+   Guint i;
+ 
+   for (i = 0; i < size; ++i) {
+-    delete bitmaps[i];
++    if (bitmaps[i]) {
++      delete bitmaps[i];
++    }
+   }
+   gfree(bitmaps);
+   if (genericRegionStats) {
+@@ -1301,6 +1318,13 @@
+     // keep track of the start of the segment data 
+     segDataPos = getPos();
+ 
++    // check for missing page information segment
++    if (!pageBitmap && ((segType >= 4 && segType <= 7) ||
++			(segType >= 20 && segType <= 43))) {
++      error(getPos(), "First JBIG2 segment associated with a page must be a page information segment");
++      goto syntaxError;
++    }
++
+     // read the segment data
+     switch (segType) {
+     case 0:
+@@ -1455,6 +1479,8 @@
+   Guint i, j, k;
+   Guchar *p;
+ 
++  symWidths = NULL;
++
+   // symbol dictionary flags
+   if (!readUWord(&flags)) {
+     goto eofError;
+@@ -1515,7 +1541,13 @@
+     // part of it
+     if ((seg = findSegment(refSegs[i]))) {
+       if (seg->getType() == jbig2SegSymbolDict) {
+-        numInputSyms += ((JBIG2SymbolDict *)seg)->getSize();
++        j = ((JBIG2SymbolDict *)seg)->getSize();
++        if (numInputSyms > UINT_MAX - j) {
++          error(getPos(), "Too many input symbols in JBIG2 symbol dictionary");
++          delete codeTables;
++          goto eofError;
++        }
++        numInputSyms += j;
+       } else if (seg->getType() == jbig2SegCodeTable) {
+         codeTables->append(seg);
+       }
+@@ -1523,13 +1555,18 @@
+       return gFalse;
+     }
+   }
++  if (numInputSyms > UINT_MAX - numNewSyms) {
++    error(getPos(), "Too many input symbols in JBIG2 symbol dictionary");
++    delete codeTables;
++    goto eofError;
++  }
+ 
+   // compute symbol code length
+-  symCodeLen = 0;
+-  i = 1;
+-  while (i < numInputSyms + numNewSyms) {
++  symCodeLen = 1;
++  i = (numInputSyms + numNewSyms) >> 1;
++  while (i) {
+     ++symCodeLen;
+-    i <<= 1;
++    i >>= 1;
+   }
+ 
+   // get the input symbol bitmaps
+@@ -1541,11 +1578,12 @@
+   k = 0;
+   inputSymbolDict = NULL;
+   for (i = 0; i < nRefSegs; ++i) {
+-    seg = findSegment(refSegs[i]);
+-    if (seg->getType() == jbig2SegSymbolDict) {
+-      inputSymbolDict = (JBIG2SymbolDict *)seg;
+-      for (j = 0; j < inputSymbolDict->getSize(); ++j) {
+-	bitmaps[k++] = inputSymbolDict->getBitmap(j);
++    if ((seg = findSegment(refSegs[i]))) {
++      if (seg->getType() == jbig2SegSymbolDict) {
++	inputSymbolDict = (JBIG2SymbolDict *)seg;
++	for (j = 0; j < inputSymbolDict->getSize(); ++j) {
++	  bitmaps[k++] = inputSymbolDict->getBitmap(j);
++	}
+       }
+     }
+   }
+@@ -1560,6 +1598,9 @@
+     } else if (huffDH == 1) {
+       huffDHTable = huffTableE;
+     } else {
++      if (i >= (Guint)codeTables->getLength()) {
++	goto codeTableError;
++      }
+       huffDHTable = ((JBIG2CodeTable *)codeTables->get(i++))->getHuffTable();
+     }
+     if (huffDW == 0) {
+@@ -1567,17 +1608,26 @@
+     } else if (huffDW == 1) {
+       huffDWTable = huffTableC;
+     } else {
++      if (i >= (Guint)codeTables->getLength()) {
++	goto codeTableError;
++      }
+       huffDWTable = ((JBIG2CodeTable *)codeTables->get(i++))->getHuffTable();
+     }
+     if (huffBMSize == 0) {
+       huffBMSizeTable = huffTableA;
+     } else {
++      if (i >= (Guint)codeTables->getLength()) {
++	goto codeTableError;
++      }
+       huffBMSizeTable =
+ 	  ((JBIG2CodeTable *)codeTables->get(i++))->getHuffTable();
+     }
+     if (huffAggInst == 0) {
+       huffAggInstTable = huffTableA;
+     } else {
++      if (i >= (Guint)codeTables->getLength()) {
++	goto codeTableError;
++      }
+       huffAggInstTable =
+ 	  ((JBIG2CodeTable *)codeTables->get(i++))->getHuffTable();
+     }
+@@ -1610,7 +1660,6 @@
+   }
+ 
+   // allocate symbol widths storage
+-  symWidths = NULL;
+   if (huff && !refAgg) {
+     symWidths = (Guint *)gmallocn(numNewSyms, sizeof(Guint));
+   }
+@@ -1652,6 +1701,10 @@
+ 	goto syntaxError;
+       }
+       symWidth += dw;
++      if (i >= numNewSyms) {
++	error(getPos(), "Too many symbols in JBIG2 symbol dictionary");
++	goto syntaxError;
++      }
+ 
+       // using a collective bitmap, so don't read a bitmap here
+       if (huff && !refAgg) {
+@@ -1688,6 +1741,10 @@
+ 	    arithDecoder->decodeInt(&refDX, iardxStats);
+ 	    arithDecoder->decodeInt(&refDY, iardyStats);
+ 	  }
++	  if (symID >= numInputSyms + i) {
++	    error(getPos(), "Invalid symbol ID in JBIG2 symbol dictionary");
++	    goto syntaxError;
++	  }
+ 	  refBitmap = bitmaps[symID];
+ 	  bitmaps[numInputSyms + i] =
+ 	      readGenericRefinementRegion(symWidth, symHeight,
+@@ -1754,6 +1811,12 @@
+     } else {
+       arithDecoder->decodeInt(&run, iaexStats);
+     }
++    if (i + run > numInputSyms + numNewSyms ||
++	(ex && j + run > numExSyms)) {
++      error(getPos(), "Too many exported symbols in JBIG2 symbol dictionary");
++      delete symbolDict;
++      goto syntaxError;
++    }
+     if (ex) {
+       for (cnt = 0; cnt < run; ++cnt) {
+ 	symbolDict->setBitmap(j++, bitmaps[i++]->copy());
+@@ -1763,6 +1826,11 @@
+     }
+     ex = !ex;
+   }
++  if (j != numExSyms) {
++    error(getPos(), "Too few symbols in JBIG2 symbol dictionary");
++    delete symbolDict;
++    goto syntaxError;
++  }
+ 
+   for (i = 0; i < numNewSyms; ++i) {
+     delete bitmaps[numInputSyms + i];
+@@ -1785,6 +1853,10 @@
+ 
+   return gTrue;
+ 
++ codeTableError:
++  error(getPos(), "Missing code table in JBIG2 symbol dictionary");
++  delete codeTables;
++
+  syntaxError:
+   for (i = 0; i < numNewSyms; ++i) {
+     if (bitmaps[numInputSyms + i]) {
+@@ -1887,6 +1959,8 @@
+       }
+     } else {
+       error(getPos(), "Invalid segment reference in JBIG2 text region");
++      delete codeTables;
++      return;
+     }
+   }
+   symCodeLen = 0;
+@@ -1921,6 +1995,9 @@
+     } else if (huffFS == 1) {
+       huffFSTable = huffTableG;
+     } else {
++      if (i >= (Guint)codeTables->getLength()) {
++	goto codeTableError;
++      }
+       huffFSTable = ((JBIG2CodeTable *)codeTables->get(i++))->getHuffTable();
+     }
+     if (huffDS == 0) {
+@@ -1930,6 +2007,9 @@
+     } else if (huffDS == 2) {
+       huffDSTable = huffTableJ;
+     } else {
++      if (i >= (Guint)codeTables->getLength()) {
++	goto codeTableError;
++      }
+       huffDSTable = ((JBIG2CodeTable *)codeTables->get(i++))->getHuffTable();
+     }
+     if (huffDT == 0) {
+@@ -1939,6 +2019,9 @@
+     } else if (huffDT == 2) {
+       huffDTTable = huffTableM;
+     } else {
++      if (i >= (Guint)codeTables->getLength()) {
++	goto codeTableError;
++      }
+       huffDTTable = ((JBIG2CodeTable *)codeTables->get(i++))->getHuffTable();
+     }
+     if (huffRDW == 0) {
+@@ -1946,6 +2029,9 @@
+     } else if (huffRDW == 1) {
+       huffRDWTable = huffTableO;
+     } else {
++      if (i >= (Guint)codeTables->getLength()) {
++	goto codeTableError;
++      }
+       huffRDWTable = ((JBIG2CodeTable *)codeTables->get(i++))->getHuffTable();
+     }
+     if (huffRDH == 0) {
+@@ -1953,6 +2039,9 @@
+     } else if (huffRDH == 1) {
+       huffRDHTable = huffTableO;
+     } else {
++      if (i >= (Guint)codeTables->getLength()) {
++	goto codeTableError;
++      }
+       huffRDHTable = ((JBIG2CodeTable *)codeTables->get(i++))->getHuffTable();
+     }
+     if (huffRDX == 0) {
+@@ -1960,6 +2049,9 @@
+     } else if (huffRDX == 1) {
+       huffRDXTable = huffTableO;
+     } else {
++      if (i >= (Guint)codeTables->getLength()) {
++	goto codeTableError;
++      }
+       huffRDXTable = ((JBIG2CodeTable *)codeTables->get(i++))->getHuffTable();
+     }
+     if (huffRDY == 0) {
+@@ -1967,11 +2059,17 @@
+     } else if (huffRDY == 1) {
+       huffRDYTable = huffTableO;
+     } else {
++      if (i >= (Guint)codeTables->getLength()) {
++	goto codeTableError;
++      }
+       huffRDYTable = ((JBIG2CodeTable *)codeTables->get(i++))->getHuffTable();
+     }
+     if (huffRSize == 0) {
+       huffRSizeTable = huffTableA;
+     } else {
++      if (i >= (Guint)codeTables->getLength()) {
++	goto codeTableError;
++      }
+       huffRSizeTable =
+ 	  ((JBIG2CodeTable *)codeTables->get(i++))->getHuffTable();
+     }
+@@ -2066,8 +2164,15 @@
+ 
+   return;
+ 
++ codeTableError:
++  error(getPos(), "Missing code table in JBIG2 text region");
++  gfree(codeTables);
++  delete syms;
++  return;
++
+  eofError:
+   error(getPos(), "Unexpected EOF in JBIG2 stream");
++  return;
+ }
+ 
+ JBIG2Bitmap *JBIG2Stream::readTextRegion(GBool huff, GBool refine,
+@@ -2374,8 +2479,8 @@
+     error(getPos(), "Bad symbol dictionary reference in JBIG2 halftone segment");
+     return;
+   }
+-  seg = findSegment(refSegs[0]);
+-  if (seg->getType() != jbig2SegPatternDict) {
++  if (!(seg = findSegment(refSegs[0])) ||
++      seg->getType() != jbig2SegPatternDict) {
+     error(getPos(), "Bad symbol dictionary reference in JBIG2 halftone segment");
+     return;
+   }
+@@ -2533,7 +2638,7 @@
+ 
+   // read the bitmap
+   bitmap = readGenericBitmap(mmr, w, h, templ, tpgdOn, gFalse,
+-			     NULL, atx, aty, mmr ? 0 : length - 18);
++			     NULL, atx, aty, mmr ? length - 18 : 0);
+ 
+   // combine the region bitmap into the page bitmap
+   if (imm) {
+@@ -2555,6 +2660,43 @@
+   error(getPos(), "Unexpected EOF in JBIG2 stream");
+ }
+ 
++inline void JBIG2Stream::mmrAddPixels(int a1, int blackPixels,
++				      int *codingLine, int *a0i, int w) {
++  if (a1 > codingLine[*a0i]) {
++    if (a1 > w) {
++      error(getPos(), "JBIG2 MMR row is wrong length ({0:d})", a1);
++      a1 = w;
++    }
++    if ((*a0i & 1) ^ blackPixels) {
++      ++*a0i;
++    }
++    codingLine[*a0i] = a1;
++  }
++}
++
++inline void JBIG2Stream::mmrAddPixelsNeg(int a1, int blackPixels,
++					 int *codingLine, int *a0i, int w) {
++  if (a1 > codingLine[*a0i]) {
++    if (a1 > w) {
++      error(getPos(), "JBIG2 MMR row is wrong length ({0:d})", a1);
++      a1 = w;
++    }
++    if ((*a0i & 1) ^ blackPixels) {
++      ++*a0i;
++    }
++    codingLine[*a0i] = a1;
++  } else if (a1 < codingLine[*a0i]) {
++    if (a1 < 0) {
++      error(getPos(), "Invalid JBIG2 MMR code");
++      a1 = 0;
++    }
++    while (*a0i > 0 && a1 <= codingLine[*a0i - 1]) {
++      --*a0i;
++    }
++    codingLine[*a0i] = a1;
++  }
++}
++
+ JBIG2Bitmap *JBIG2Stream::readGenericBitmap(GBool mmr, int w, int h,
+ 					    int templ, GBool tpgdOn,
+ 					    GBool useSkip, JBIG2Bitmap *skip,
+@@ -2567,7 +2709,7 @@
+   JBIG2BitmapPtr atPtr0, atPtr1, atPtr2, atPtr3;
+   int *refLine, *codingLine;
+   int code1, code2, code3;
+-  int x, y, a0, pix, i, refI, codingI;
++  int x, y, a0i, b1i, blackPixels, pix, i;
+ 
+   bitmap = new JBIG2Bitmap(0, w, h);
+   bitmap->clearToZero();
+@@ -2577,9 +2719,18 @@
+   if (mmr) {
+ 
+     mmrDecoder->reset();
++    if (w > INT_MAX - 2) {
++      error(getPos(), "Bad width in JBIG2 generic bitmap");
++      // force a call to gmalloc(-1), which will throw an exception
++      w = -3;
++    }
++    // 0 <= codingLine[0] < codingLine[1] < ... < codingLine[n] = w
++    // ---> max codingLine size = w + 1
++    // refLine has one extra guard entry at the end
++    // ---> max refLine size = w + 2
++    codingLine = (int *)gmallocn(w + 1, sizeof(int));
+     refLine = (int *)gmallocn(w + 2, sizeof(int));
+-    codingLine = (int *)gmallocn(w + 2, sizeof(int));
+-    codingLine[0] = codingLine[1] = w;
++    codingLine[0] = w;
+ 
+     for (y = 0; y < h; ++y) {
+ 
+@@ -2587,128 +2738,157 @@
+       for (i = 0; codingLine[i] < w; ++i) {
+ 	refLine[i] = codingLine[i];
+       }
+-      refLine[i] = refLine[i + 1] = w;
++      refLine[i++] = w;
++      refLine[i] = w;
+ 
+       // decode a line
+-      refI = 0;     // b1 = refLine[refI]
+-      codingI = 0;  // a1 = codingLine[codingI]
+-      a0 = 0;
+-      do {
++      codingLine[0] = 0;
++      a0i = 0;
++      b1i = 0;
++      blackPixels = 0;
++      // invariant:
++      // refLine[b1i-1] <= codingLine[a0i] < refLine[b1i] < refLine[b1i+1] <= w
++      // exception at left edge:
++      //   codingLine[a0i = 0] = refLine[b1i = 0] = 0 is possible
++      // exception at right edge:
++      //   refLine[b1i] = refLine[b1i+1] = w is possible
++      while (codingLine[a0i] < w) {
+ 	code1 = mmrDecoder->get2DCode();
+ 	switch (code1) {
+ 	case twoDimPass:
+-	  if (refLine[refI] < w) {
+-	    a0 = refLine[refI + 1];
+-	    refI += 2;
+-	  }
+-	  break;
++          mmrAddPixels(refLine[b1i + 1], blackPixels, codingLine, &a0i, w);
++          if (refLine[b1i + 1] < w) {
++            b1i += 2;
++          }
++          break;
+ 	case twoDimHoriz:
+-	  if (codingI & 1) {
+-	    code1 = 0;
+-	    do {
+-	      code1 += code3 = mmrDecoder->getBlackCode();
+-	    } while (code3 >= 64);
+-	    code2 = 0;
+-	    do {
+-	      code2 += code3 = mmrDecoder->getWhiteCode();
+-	    } while (code3 >= 64);
+-	  } else {
+-	    code1 = 0;
+-	    do {
+-	      code1 += code3 = mmrDecoder->getWhiteCode();
+-	    } while (code3 >= 64);
+-	    code2 = 0;
+-	    do {
+-	      code2 += code3 = mmrDecoder->getBlackCode();
+-	    } while (code3 >= 64);
+-	  }
+-	  if (code1 > 0 || code2 > 0) {
+-	    a0 = codingLine[codingI++] = a0 + code1;
+-	    a0 = codingLine[codingI++] = a0 + code2;
+-	    while (refLine[refI] <= a0 && refLine[refI] < w) {
+-	      refI += 2;
+-	    }
+-	  }
+-	  break;
+-	case twoDimVert0:
+-	  a0 = codingLine[codingI++] = refLine[refI];
+-	  if (refLine[refI] < w) {
+-	    ++refI;
+-	  }
+-	  break;
+-	case twoDimVertR1:
+-	  a0 = codingLine[codingI++] = refLine[refI] + 1;
+-	  if (refLine[refI] < w) {
+-	    ++refI;
+-	    while (refLine[refI] <= a0 && refLine[refI] < w) {
+-	      refI += 2;
+-	    }
+-	  }
+-	  break;
+-	case twoDimVertR2:
+-	  a0 = codingLine[codingI++] = refLine[refI] + 2;
+-	  if (refLine[refI] < w) {
+-	    ++refI;
+-	    while (refLine[refI] <= a0 && refLine[refI] < w) {
+-	      refI += 2;
+-	    }
+-	  }
+-	  break;
++          code1 = code2 = 0;
++          if (blackPixels) {
++            do {
++              code1 += code3 = mmrDecoder->getBlackCode();
++            } while (code3 >= 64);
++            do {
++              code2 += code3 = mmrDecoder->getWhiteCode();
++            } while (code3 >= 64);
++          } else {
++            do {
++              code1 += code3 = mmrDecoder->getWhiteCode();
++            } while (code3 >= 64);
++            do {
++              code2 += code3 = mmrDecoder->getBlackCode();
++            } while (code3 >= 64);
++          }
++          mmrAddPixels(codingLine[a0i] + code1, blackPixels,
++		       codingLine, &a0i, w);
++          if (codingLine[a0i] < w) {
++            mmrAddPixels(codingLine[a0i] + code2, blackPixels ^ 1,
++			 codingLine, &a0i, w);
++          }
++          while (refLine[b1i] <= codingLine[a0i] && refLine[b1i] < w) {
++            b1i += 2;
++          }
++          break;
+ 	case twoDimVertR3:
+-	  a0 = codingLine[codingI++] = refLine[refI] + 3;
+-	  if (refLine[refI] < w) {
+-	    ++refI;
+-	    while (refLine[refI] <= a0 && refLine[refI] < w) {
+-	      refI += 2;
+-	    }
+-	  }
+-	  break;
+-	case twoDimVertL1:
+-	  a0 = codingLine[codingI++] = refLine[refI] - 1;
+-	  if (refI > 0) {
+-	    --refI;
+-	  } else {
+-	    ++refI;
+-	  }
+-	  while (refLine[refI] <= a0 && refLine[refI] < w) {
+-	    refI += 2;
+-	  }
+-	  break;
+-	case twoDimVertL2:
+-	  a0 = codingLine[codingI++] = refLine[refI] - 2;
+-	  if (refI > 0) {
+-	    --refI;
+-	  } else {
+-	    ++refI;
+-	  }
+-	  while (refLine[refI] <= a0 && refLine[refI] < w) {
+-	    refI += 2;
+-	  }
+-	  break;
++          mmrAddPixels(refLine[b1i] + 3, blackPixels, codingLine, &a0i, w);
++          blackPixels ^= 1;
++          if (codingLine[a0i] < w) {
++            ++b1i;
++            while (refLine[b1i] <= codingLine[a0i] && refLine[b1i] < w) {
++              b1i += 2;
++            }
++          }
++          break;
++	case twoDimVertR2:
++          mmrAddPixels(refLine[b1i] + 2, blackPixels, codingLine, &a0i, w);
++          blackPixels ^= 1;
++          if (codingLine[a0i] < w) {
++            ++b1i;
++            while (refLine[b1i] <= codingLine[a0i] && refLine[b1i] < w) {
++              b1i += 2;
++            }
++          }
++          break;
++	case twoDimVertR1:
++          mmrAddPixels(refLine[b1i] + 1, blackPixels, codingLine, &a0i, w);
++          blackPixels ^= 1;
++          if (codingLine[a0i] < w) {
++            ++b1i;
++            while (refLine[b1i] <= codingLine[a0i] && refLine[b1i] < w) {
++              b1i += 2;
++            }
++          }
++          break;
++	case twoDimVert0:
++          mmrAddPixels(refLine[b1i], blackPixels, codingLine, &a0i, w);
++          blackPixels ^= 1;
++          if (codingLine[a0i] < w) {
++            ++b1i;
++            while (refLine[b1i] <= codingLine[a0i] && refLine[b1i] < w) {
++              b1i += 2;
++            }
++          }
++          break;
+ 	case twoDimVertL3:
+-	  a0 = codingLine[codingI++] = refLine[refI] - 3;
+-	  if (refI > 0) {
+-	    --refI;
+-	  } else {
+-	    ++refI;
+-	  }
+-	  while (refLine[refI] <= a0 && refLine[refI] < w) {
+-	    refI += 2;
+-	  }
+-	  break;
++          mmrAddPixelsNeg(refLine[b1i] - 3, blackPixels, codingLine, &a0i, w);
++          blackPixels ^= 1;
++          if (codingLine[a0i] < w) {
++            if (b1i > 0) {
++              --b1i;
++            } else {
++              ++b1i;
++            }
++            while (refLine[b1i] <= codingLine[a0i] && refLine[b1i] < w) {
++              b1i += 2;
++            }
++          }
++          break;
++	case twoDimVertL2:
++          mmrAddPixelsNeg(refLine[b1i] - 2, blackPixels, codingLine, &a0i, w);
++          blackPixels ^= 1;
++          if (codingLine[a0i] < w) {
++            if (b1i > 0) {
++              --b1i;
++            } else {
++              ++b1i;
++            }
++            while (refLine[b1i] <= codingLine[a0i] && refLine[b1i] < w) {
++              b1i += 2;
++            }
++          }
++          break;
++	case twoDimVertL1:
++          mmrAddPixelsNeg(refLine[b1i] - 1, blackPixels, codingLine, &a0i, w);
++          blackPixels ^= 1;
++          if (codingLine[a0i] < w) {
++            if (b1i > 0) {
++              --b1i;
++            } else {
++              ++b1i;
++            }
++            while (refLine[b1i] <= codingLine[a0i] && refLine[b1i] < w) {
++              b1i += 2;
++            }
++          }
++          break;
++	case EOF:
++          mmrAddPixels(w, 0, codingLine, &a0i, w);
++          break;
+ 	default:
+ 	  error(getPos(), "Illegal code in JBIG2 MMR bitmap data");
++          mmrAddPixels(w, 0, codingLine, &a0i, w);
+ 	  break;
+ 	}
+-      } while (a0 < w);
+-      codingLine[codingI++] = w;
++      }
+ 
+       // convert the run lengths to a bitmap line
+       i = 0;
+-      while (codingLine[i] < w) {
++      while (1) {
+ 	for (x = codingLine[i]; x < codingLine[i+1]; ++x) {
+ 	  bitmap->setPixel(x, y);
+ 	}
++	if (codingLine[i+1] >= w || codingLine[i+2] >= w) {
++	  break;
++	}
+ 	i += 2;
+       }
+     }
+@@ -2756,7 +2936,9 @@
+ 	  ltp = !ltp;
+ 	}
+ 	if (ltp) {
+-	  bitmap->duplicateRow(y, y-1);
++	  if (y > 0) {
++	    bitmap->duplicateRow(y, y-1);
++	  }
+ 	  continue;
+ 	}
+       }
+@@ -2959,8 +3141,8 @@
+     return;
+   }
+   if (nRefSegs == 1) {
+-    seg = findSegment(refSegs[0]);
+-    if (seg->getType() != jbig2SegBitmap) {
++    if (!(seg = findSegment(refSegs[0])) ||
++	seg->getType() != jbig2SegBitmap) {
+       error(getPos(), "Bad bitmap reference in JBIG2 generic refinement segment");
+       return;
+     }
+@@ -3054,6 +3236,10 @@
+ 	tpgrCX2 = refBitmap->nextPixel(&tpgrCXPtr2);
+ 	tpgrCX2 = (tpgrCX2 << 1) | refBitmap->nextPixel(&tpgrCXPtr2);
+ 	tpgrCX2 = (tpgrCX2 << 1) | refBitmap->nextPixel(&tpgrCXPtr2);
++      } else {
++	tpgrCXPtr0.p = tpgrCXPtr1.p = tpgrCXPtr2.p = NULL; // make gcc happy
++	tpgrCXPtr0.shift = tpgrCXPtr1.shift = tpgrCXPtr2.shift = 0;
++	tpgrCXPtr0.x = tpgrCXPtr1.x = tpgrCXPtr2.x = 0;
+       }
+ 
+       for (x = 0; x < w; ++x) {
+@@ -3125,6 +3311,10 @@
+ 	tpgrCX2 = refBitmap->nextPixel(&tpgrCXPtr2);
+ 	tpgrCX2 = (tpgrCX2 << 1) | refBitmap->nextPixel(&tpgrCXPtr2);
+ 	tpgrCX2 = (tpgrCX2 << 1) | refBitmap->nextPixel(&tpgrCXPtr2);
++      } else {
++	tpgrCXPtr0.p = tpgrCXPtr1.p = tpgrCXPtr2.p = NULL; // make gcc happy
++	tpgrCXPtr0.shift = tpgrCXPtr1.shift = tpgrCXPtr2.shift = 0;
++	tpgrCXPtr0.x = tpgrCXPtr1.x = tpgrCXPtr2.x = 0;
+       }
+ 
+       for (x = 0; x < w; ++x) {
+diff -Naur xpdf.orig/xpdf/JBIG2Stream.h xpdf/xpdf/JBIG2Stream.h
+--- xpdf.orig/xpdf/JBIG2Stream.h	2007-05-14 16:39:30.000000000 +0900
++++ xpdf/xpdf/JBIG2Stream.h	2010-03-22 23:56:09.000000000 +0900
+@@ -78,6 +78,10 @@
+ 			     Guint *refSegs, Guint nRefSegs);
+   void readGenericRegionSeg(Guint segNum, GBool imm,
+ 			    GBool lossless, Guint length);
++  void mmrAddPixels(int a1, int blackPixels,
++		    int *codingLine, int *a0i, int w);
++  void mmrAddPixelsNeg(int a1, int blackPixels,
++		       int *codingLine, int *a0i, int w);
+   JBIG2Bitmap *readGenericBitmap(GBool mmr, int w, int h,
+ 				 int templ, GBool tpgdOn,
+ 				 GBool useSkip, JBIG2Bitmap *skip,

Added: trunk/dports/kde/kdegraphics3/files/xpdf-3.02pl4.patch
===================================================================
--- trunk/dports/kde/kdegraphics3/files/xpdf-3.02pl4.patch	                        (rev 0)
+++ trunk/dports/kde/kdegraphics3/files/xpdf-3.02pl4.patch	2010-03-23 05:43:34 UTC (rev 65164)
@@ -0,0 +1,9896 @@
+diff -Naur xpdf.orig/splash/Splash.cc xpdf/splash/Splash.cc
+--- xpdf.orig/splash/Splash.cc	2010-03-22 23:55:22.000000000 +0900
++++ xpdf/splash/Splash.cc	2010-03-23 00:14:18.000000000 +0900
+@@ -12,6 +12,7 @@
+ 
+ #include <stdlib.h>
+ #include <string.h>
++#include <limits.h>
+ #include "gmem.h"
+ #include "SplashErrorCodes.h"
+ #include "SplashMath.h"
+@@ -1937,7 +1938,10 @@
+   xq = w % scaledWidth;
+ 
+   // allocate pixel buffer
+-  pixBuf = (SplashColorPtr)gmalloc((yp + 1) * w);
++  if (yp < 0 || yp > INT_MAX - 1) {
++    return splashErrBadArg;
++  }
++  pixBuf = (SplashColorPtr)gmallocn(yp + 1, w);
+ 
+   // initialize the pixel pipe
+   pipeInit(&pipe, 0, 0, state->fillPattern, NULL, state->fillAlpha,
+@@ -2233,9 +2237,12 @@
+   xq = w % scaledWidth;
+ 
+   // allocate pixel buffers
+-  colorBuf = (SplashColorPtr)gmalloc((yp + 1) * w * nComps);
++  if (yp < 0 || yp > INT_MAX - 1 || w > INT_MAX / nComps) {
++    return splashErrBadArg;
++  }
++  colorBuf = (SplashColorPtr)gmallocn(yp + 1, w * nComps);
+   if (srcAlpha) {
+-    alphaBuf = (Guchar *)gmalloc((yp + 1) * w);
++    alphaBuf = (Guchar *)gmallocn(yp + 1, w);
+   } else {
+     alphaBuf = NULL;
+   }
+diff -Naur xpdf.orig/splash/Splash.cc.orig xpdf/splash/Splash.cc.orig
+--- xpdf.orig/splash/Splash.cc.orig	1970-01-01 09:00:00.000000000 +0900
++++ xpdf/splash/Splash.cc.orig	2010-03-23 00:14:09.000000000 +0900
+@@ -0,0 +1,3335 @@
++//========================================================================
++//
++// Splash.cc
++//
++//========================================================================
++
++#include <aconf.h>
++
++#ifdef USE_GCC_PRAGMAS
++#pragma implementation
++#endif
++
++#include <stdlib.h>
++#include <string.h>
++#include "gmem.h"
++#include "SplashErrorCodes.h"
++#include "SplashMath.h"
++#include "SplashBitmap.h"
++#include "SplashState.h"
++#include "SplashPath.h"
++#include "SplashXPath.h"
++#include "SplashXPathScanner.h"
++#include "SplashPattern.h"
++#include "SplashScreen.h"
++#include "SplashFont.h"
++#include "SplashGlyphBitmap.h"
++#include "Splash.h"
++
++//------------------------------------------------------------------------
++
++// distance of Bezier control point from center for circle approximation
++// = (4 * (sqrt(2) - 1) / 3) * r
++#define bezierCircle ((SplashCoord)0.55228475)
++#define bezierCircle2 ((SplashCoord)(0.5 * 0.55228475))
++
++// Divide a 16-bit value (in [0, 255*255]) by 255, returning an 8-bit result.
++static inline Guchar div255(int x) {
++  return (Guchar)((x + (x >> 8) + 0x80) >> 8);
++}
++
++//------------------------------------------------------------------------
++// SplashPipe
++//------------------------------------------------------------------------
++
++#define splashPipeMaxStages 9
++
++struct SplashPipe {
++  // pixel coordinates
++  int x, y;
++
++  // source pattern
++  SplashPattern *pattern;
++
++  // source alpha and color
++  SplashCoord aInput;
++  GBool usesShape;
++  Guchar aSrc;
++  SplashColorPtr cSrc;
++  SplashColor cSrcVal;
++
++  // non-isolated group alpha0
++  Guchar *alpha0Ptr;
++
++  // soft mask
++  SplashColorPtr softMaskPtr;
++
++  // destination alpha and color
++  SplashColorPtr destColorPtr;
++  int destColorMask;
++  Guchar *destAlphaPtr;
++
++  // shape
++  SplashCoord shape;
++
++  // result alpha and color
++  GBool noTransparency;
++  SplashPipeResultColorCtrl resultColorCtrl;
++
++  // non-isolated group correction
++  int nonIsolatedGroup;
++};
++
++SplashPipeResultColorCtrl Splash::pipeResultColorNoAlphaBlend[] = {
++  splashPipeResultColorNoAlphaBlendMono,
++  splashPipeResultColorNoAlphaBlendMono,
++  splashPipeResultColorNoAlphaBlendRGB,
++  splashPipeResultColorNoAlphaBlendRGB
++#if SPLASH_CMYK
++  ,
++  splashPipeResultColorNoAlphaBlendCMYK
++#endif
++};
++
++SplashPipeResultColorCtrl Splash::pipeResultColorAlphaNoBlend[] = {
++  splashPipeResultColorAlphaNoBlendMono,
++  splashPipeResultColorAlphaNoBlendMono,
++  splashPipeResultColorAlphaNoBlendRGB,
++  splashPipeResultColorAlphaNoBlendRGB
++#if SPLASH_CMYK
++  ,
++  splashPipeResultColorAlphaNoBlendCMYK
++#endif
++};
++
++SplashPipeResultColorCtrl Splash::pipeResultColorAlphaBlend[] = {
++  splashPipeResultColorAlphaBlendMono,
++  splashPipeResultColorAlphaBlendMono,
++  splashPipeResultColorAlphaBlendRGB,
++  splashPipeResultColorAlphaBlendRGB
++#if SPLASH_CMYK
++  ,
++  splashPipeResultColorAlphaBlendCMYK
++#endif
++};
++
++//------------------------------------------------------------------------
++
++static void blendXor(SplashColorPtr src, SplashColorPtr dest,
++		     SplashColorPtr blend, SplashColorMode cm) {
++  int i;
++
++  for (i = 0; i < splashColorModeNComps[cm]; ++i) {
++    blend[i] = src[i] ^ dest[i];
++  }
++}
++
++//------------------------------------------------------------------------
++// modified region
++//------------------------------------------------------------------------
++
++void Splash::clearModRegion() {
++  modXMin = bitmap->getWidth();
++  modYMin = bitmap->getHeight();
++  modXMax = -1;
++  modYMax = -1;
++}
++
++inline void Splash::updateModX(int x) {
++  if (x < modXMin) {
++    modXMin = x;
++  }
++  if (x > modXMax) {
++    modXMax = x;
++  }
++}
++
++inline void Splash::updateModY(int y) {
++  if (y < modYMin) {
++    modYMin = y;
++  }
++  if (y > modYMax) {
++    modYMax = y;
++  }
++}
++
++//------------------------------------------------------------------------
++// pipeline
++//------------------------------------------------------------------------
++
++inline void Splash::pipeInit(SplashPipe *pipe, int x, int y,
++			     SplashPattern *pattern, SplashColorPtr cSrc,
++			     SplashCoord aInput, GBool usesShape,
++			     GBool nonIsolatedGroup) {
++  pipeSetXY(pipe, x, y);
++  pipe->pattern = NULL;
++
++  // source color
++  if (pattern) {
++    if (pattern->isStatic()) {
++      pattern->getColor(x, y, pipe->cSrcVal);
++    } else {
++      pipe->pattern = pattern;
++    }
++    pipe->cSrc = pipe->cSrcVal;
++  } else {
++    pipe->cSrc = cSrc;
++  }
++
++  // source alpha
++  pipe->aInput = aInput;
++  if (!state->softMask) {
++    if (usesShape) {
++      pipe->aInput *= 255;
++    } else {
++      pipe->aSrc = (Guchar)splashRound(pipe->aInput * 255);
++    }
++  }
++  pipe->usesShape = usesShape;
++
++  // result alpha
++  if (aInput == 1 && !state->softMask && !usesShape &&
++      !state->inNonIsolatedGroup) {
++    pipe->noTransparency = gTrue;
++  } else {
++    pipe->noTransparency = gFalse;
++  }
++
++  // result color
++  if (pipe->noTransparency) {
++    // the !state->blendFunc case is handled separately in pipeRun
++    pipe->resultColorCtrl = pipeResultColorNoAlphaBlend[bitmap->mode];
++  } else if (!state->blendFunc) {
++    pipe->resultColorCtrl = pipeResultColorAlphaNoBlend[bitmap->mode];
++  } else {
++    pipe->resultColorCtrl = pipeResultColorAlphaBlend[bitmap->mode];
++  }
++
++  // non-isolated group correction
++  if (nonIsolatedGroup) {
++    pipe->nonIsolatedGroup = splashColorModeNComps[bitmap->mode];
++  } else {
++    pipe->nonIsolatedGroup = 0;
++  }
++}
++
++inline void Splash::pipeRun(SplashPipe *pipe) {
++  Guchar aSrc, aDest, alpha2, alpha0, aResult;
++  SplashColor cDest, cBlend;
++  Guchar cResult0, cResult1, cResult2, cResult3;
++
++  //----- source color
++
++  // static pattern: handled in pipeInit
++  // fixed color: handled in pipeInit
++
++  // dynamic pattern
++  if (pipe->pattern) {
++    pipe->pattern->getColor(pipe->x, pipe->y, pipe->cSrcVal);
++  }
++
++  if (pipe->noTransparency && !state->blendFunc) {
++
++    //----- write destination pixel
++
++    switch (bitmap->mode) {
++    case splashModeMono1:
++      cResult0 = pipe->cSrc[0];
++      if (state->screen->test(pipe->x, pipe->y, cResult0)) {
++	*pipe->destColorPtr |= pipe->destColorMask;
++      } else {
++	*pipe->destColorPtr &= ~pipe->destColorMask;
++      }
++      if (!(pipe->destColorMask >>= 1)) {
++	pipe->destColorMask = 0x80;
++	++pipe->destColorPtr;
++      }
++      break;
++    case splashModeMono8:
++      *pipe->destColorPtr++ = pipe->cSrc[0];
++      break;
++    case splashModeRGB8:
++      *pipe->destColorPtr++ = pipe->cSrc[0];
++      *pipe->destColorPtr++ = pipe->cSrc[1];
++      *pipe->destColorPtr++ = pipe->cSrc[2];
++      break;
++    case splashModeBGR8:
++      *pipe->destColorPtr++ = pipe->cSrc[2];
++      *pipe->destColorPtr++ = pipe->cSrc[1];
++      *pipe->destColorPtr++ = pipe->cSrc[0];
++      break;
++#if SPLASH_CMYK
++    case splashModeCMYK8:
++      *pipe->destColorPtr++ = pipe->cSrc[0];
++      *pipe->destColorPtr++ = pipe->cSrc[1];
++      *pipe->destColorPtr++ = pipe->cSrc[2];
++      *pipe->destColorPtr++ = pipe->cSrc[3];
++      break;
++#endif
++    }
++    if (pipe->destAlphaPtr) {
++      *pipe->destAlphaPtr++ = 255;
++    }
++
++  } else {
++
++    //----- read destination pixel
++
++    switch (bitmap->mode) {
++    case splashModeMono1:
++      cDest[0] = (*pipe->destColorPtr & pipe->destColorMask) ? 0xff : 0x00;
++      break;
++    case splashModeMono8:
++      cDest[0] = *pipe->destColorPtr;
++      break;
++    case splashModeRGB8:
++      cDest[0] = pipe->destColorPtr[0];
++      cDest[1] = pipe->destColorPtr[1];
++      cDest[2] = pipe->destColorPtr[2];
++      break;
++    case splashModeBGR8:
++      cDest[0] = pipe->destColorPtr[2];
++      cDest[1] = pipe->destColorPtr[1];
++      cDest[2] = pipe->destColorPtr[0];
++      break;
++#if SPLASH_CMYK
++    case splashModeCMYK8:
++      cDest[0] = pipe->destColorPtr[0];
++      cDest[1] = pipe->destColorPtr[1];
++      cDest[2] = pipe->destColorPtr[2];
++      cDest[3] = pipe->destColorPtr[3];
++      break;
++#endif
++    }
++    if (pipe->destAlphaPtr) {
++      aDest = *pipe->destAlphaPtr;
++    } else {
++      aDest = 0xff;
++    }
++
++    //----- blend function
++
++    if (state->blendFunc) {
++      (*state->blendFunc)(pipe->cSrc, cDest, cBlend, bitmap->mode);
++    }
++
++    //----- source alpha
++
++    if (state->softMask) {
++      if (pipe->usesShape) {
++	aSrc = (Guchar)splashRound(pipe->aInput * *pipe->softMaskPtr++
++				   * pipe->shape);
++      } else {
++	aSrc = (Guchar)splashRound(pipe->aInput * *pipe->softMaskPtr++);
++      }
++    } else if (pipe->usesShape) {
++      // pipe->aInput is premultiplied by 255 in pipeInit
++      aSrc = (Guchar)splashRound(pipe->aInput * pipe->shape);
++    } else {
++      // precomputed in pipeInit
++      aSrc = pipe->aSrc;
++    }
++
++    //----- result alpha and non-isolated group element correction
++
++    if (pipe->noTransparency) {
++      alpha2 = aResult = 255;
++    } else {
++      aResult = aSrc + aDest - div255(aSrc * aDest);
++
++      if (pipe->alpha0Ptr) {
++	alpha0 = *pipe->alpha0Ptr++;
++	alpha2 = aResult + alpha0 - div255(aResult * alpha0);
++      } else {
++	alpha2 = aResult;
++      }
++    }
++
++    //----- result color
++
++    cResult0 = cResult1 = cResult2 = cResult3 = 0; // make gcc happy
++
++    switch (pipe->resultColorCtrl) {
++
++#if SPLASH_CMYK
++    case splashPipeResultColorNoAlphaBlendCMYK:
++      cResult3 = div255((255 - aDest) * pipe->cSrc[3] + aDest * cBlend[3]);
++#endif
++    case splashPipeResultColorNoAlphaBlendRGB:
++      cResult2 = div255((255 - aDest) * pipe->cSrc[2] + aDest * cBlend[2]);
++      cResult1 = div255((255 - aDest) * pipe->cSrc[1] + aDest * cBlend[1]);
++    case splashPipeResultColorNoAlphaBlendMono:
++      cResult0 = div255((255 - aDest) * pipe->cSrc[0] + aDest * cBlend[0]);
++      break;
++
++    case splashPipeResultColorAlphaNoBlendMono:
++      if (alpha2 == 0) {
++	cResult0 = 0;
++      } else {
++	cResult0 = (Guchar)(((alpha2 - aSrc) * cDest[0] +
++			     aSrc * pipe->cSrc[0]) / alpha2);
++      }
++      break;
++    case splashPipeResultColorAlphaNoBlendRGB:
++      if (alpha2 == 0) {
++	cResult0 = 0;
++	cResult1 = 0;
++	cResult2 = 0;
++      } else {
++	cResult0 = (Guchar)(((alpha2 - aSrc) * cDest[0] +
++			     aSrc * pipe->cSrc[0]) / alpha2);
++	cResult1 = (Guchar)(((alpha2 - aSrc) * cDest[1] +
++			     aSrc * pipe->cSrc[1]) / alpha2);
++	cResult2 = (Guchar)(((alpha2 - aSrc) * cDest[2] +
++			     aSrc * pipe->cSrc[2]) / alpha2);
++      }
++      break;
++#if SPLASH_CMYK
++    case splashPipeResultColorAlphaNoBlendCMYK:
++      if (alpha2 == 0) {
++	cResult0 = 0;
++	cResult1 = 0;
++	cResult2 = 0;
++	cResult3 = 0;
++      } else {
++	cResult0 = (Guchar)(((alpha2 - aSrc) * cDest[0] +
++			     aSrc * pipe->cSrc[0]) / alpha2);
++	cResult1 = (Guchar)(((alpha2 - aSrc) * cDest[1] +
++			     aSrc * pipe->cSrc[1]) / alpha2);
++	cResult2 = (Guchar)(((alpha2 - aSrc) * cDest[2] +
++			     aSrc * pipe->cSrc[2]) / alpha2);
++	cResult3 = (Guchar)(((alpha2 - aSrc) * cDest[3] +
++			     aSrc * pipe->cSrc[3]) / alpha2);
++      }
++      break;
++#endif
++
++    case splashPipeResultColorAlphaBlendMono:
++      if (alpha2 == 0) {
++	cResult0 = 0;
++      } else {
++	cResult0 = (Guchar)(((alpha2 - aSrc) * cDest[0] +
++			     aSrc * ((255 - aDest) * pipe->cSrc[0] +
++				     aDest * cBlend[0]) / 255) /
++			    alpha2);
++      }
++      break;
++    case splashPipeResultColorAlphaBlendRGB:
++      if (alpha2 == 0) {
++	cResult0 = 0;
++	cResult1 = 0;
++	cResult2 = 0;
++      } else {
++	cResult0 = (Guchar)(((alpha2 - aSrc) * cDest[0] +
++			     aSrc * ((255 - aDest) * pipe->cSrc[0] +
++				     aDest * cBlend[0]) / 255) /
++			    alpha2);
++	cResult1 = (Guchar)(((alpha2 - aSrc) * cDest[1] +
++			     aSrc * ((255 - aDest) * pipe->cSrc[1] +
++				     aDest * cBlend[1]) / 255) /
++			    alpha2);
++	cResult2 = (Guchar)(((alpha2 - aSrc) * cDest[2] +
++			     aSrc * ((255 - aDest) * pipe->cSrc[2] +
++				     aDest * cBlend[2]) / 255) /
++			    alpha2);
++      }
++      break;
++#if SPLASH_CMYK
++    case splashPipeResultColorAlphaBlendCMYK:
++      if (alpha2 == 0) {
++	cResult0 = 0;
++	cResult1 = 0;
++	cResult2 = 0;
++	cResult3 = 0;
++      } else {
++	cResult0 = (Guchar)(((alpha2 - aSrc) * cDest[0] +
++			     aSrc * ((255 - aDest) * pipe->cSrc[0] +
++				     aDest * cBlend[0]) / 255) /
++			    alpha2);
++	cResult1 = (Guchar)(((alpha2 - aSrc) * cDest[1] +
++			     aSrc * ((255 - aDest) * pipe->cSrc[1] +
++				     aDest * cBlend[1]) / 255) /
++			    alpha2);
++	cResult2 = (Guchar)(((alpha2 - aSrc) * cDest[2] +
++			     aSrc * ((255 - aDest) * pipe->cSrc[2] +
++				     aDest * cBlend[2]) / 255) /
++			    alpha2);
++	cResult3 = (Guchar)(((alpha2 - aSrc) * cDest[3] +
++			     aSrc * ((255 - aDest) * pipe->cSrc[3] +
++				     aDest * cBlend[3]) / 255) /
++			    alpha2);
++      }
++      break;
++#endif
++    }
++
++    //----- non-isolated group correction
++
++    if (aResult != 0) {
++      switch (pipe->nonIsolatedGroup) {
++#if SPLASH_CMYK
++      case 4:
++	cResult3 += (cResult3 - cDest[3]) * aDest *
++	            (255 - aResult) / (255 * aResult);
++#endif
++      case 3:
++	cResult2 += (cResult2 - cDest[2]) * aDest *
++	            (255 - aResult) / (255 * aResult);
++	cResult1 += (cResult1 - cDest[1]) * aDest *
++	            (255 - aResult) / (255 * aResult);
++      case 1:
++	cResult0 += (cResult0 - cDest[0]) * aDest *
++	            (255 - aResult) / (255 * aResult);
++      case 0:
++	break;
++      }
++    }
++
++    //----- write destination pixel
++
++    switch (bitmap->mode) {
++    case splashModeMono1:
++      if (state->screen->test(pipe->x, pipe->y, cResult0)) {
++	*pipe->destColorPtr |= pipe->destColorMask;
++      } else {
++	*pipe->destColorPtr &= ~pipe->destColorMask;
++      }
++      if (!(pipe->destColorMask >>= 1)) {
++	pipe->destColorMask = 0x80;
++	++pipe->destColorPtr;
++      }
++      break;
++    case splashModeMono8:
++      *pipe->destColorPtr++ = cResult0;
++      break;
++    case splashModeRGB8:
++      *pipe->destColorPtr++ = cResult0;
++      *pipe->destColorPtr++ = cResult1;
++      *pipe->destColorPtr++ = cResult2;
++      break;
++    case splashModeBGR8:
++      *pipe->destColorPtr++ = cResult2;
++      *pipe->destColorPtr++ = cResult1;
++      *pipe->destColorPtr++ = cResult0;
++      break;
++#if SPLASH_CMYK
++    case splashModeCMYK8:
++      *pipe->destColorPtr++ = cResult0;
++      *pipe->destColorPtr++ = cResult1;
++      *pipe->destColorPtr++ = cResult2;
++      *pipe->destColorPtr++ = cResult3;
++      break;
++#endif
++    }
++    if (pipe->destAlphaPtr) {
++      *pipe->destAlphaPtr++ = aResult;
++    }
++
++  }
++
++  ++pipe->x;
++}
++
++inline void Splash::pipeSetXY(SplashPipe *pipe, int x, int y) {
++  pipe->x = x;
++  pipe->y = y;
++  if (state->softMask) {
++    pipe->softMaskPtr =
++        &state->softMask->data[y * state->softMask->rowSize + x];
++  }
++  switch (bitmap->mode) {
++  case splashModeMono1:
++    pipe->destColorPtr = &bitmap->data[y * bitmap->rowSize + (x >> 3)];
++    pipe->destColorMask = 0x80 >> (x & 7);
++    break;
++  case splashModeMono8:
++    pipe->destColorPtr = &bitmap->data[y * bitmap->rowSize + x];
++    break;
++  case splashModeRGB8:
++  case splashModeBGR8:
++    pipe->destColorPtr = &bitmap->data[y * bitmap->rowSize + 3 * x];
++    break;
++#if SPLASH_CMYK
++  case splashModeCMYK8:
++    pipe->destColorPtr = &bitmap->data[y * bitmap->rowSize + 4 * x];
++    break;
++#endif
++  }
++  if (bitmap->alpha) {
++    pipe->destAlphaPtr = &bitmap->alpha[y * bitmap->width + x];
++  } else {
++    pipe->destAlphaPtr = NULL;
++  }
++  if (state->inNonIsolatedGroup && alpha0Bitmap->alpha) {
++    pipe->alpha0Ptr =
++        &alpha0Bitmap->alpha[(alpha0Y + y) * alpha0Bitmap->width +
++			     (alpha0X + x)];
++  } else {
++    pipe->alpha0Ptr = NULL;
++  }
++}
++
++inline void Splash::pipeIncX(SplashPipe *pipe) {
++  ++pipe->x;
++  if (state->softMask) {
++    ++pipe->softMaskPtr;
++  }
++  switch (bitmap->mode) {
++  case splashModeMono1:
++    if (!(pipe->destColorMask >>= 1)) {
++      pipe->destColorMask = 0x80;
++      ++pipe->destColorPtr;
++    }
++    break;
++  case splashModeMono8:
++    ++pipe->destColorPtr;
++    break;
++  case splashModeRGB8:
++  case splashModeBGR8:
++    pipe->destColorPtr += 3;
++    break;
++#if SPLASH_CMYK
++  case splashModeCMYK8:
++    pipe->destColorPtr += 4;
++    break;
++#endif
++  }
++  if (pipe->destAlphaPtr) {
++    ++pipe->destAlphaPtr;
++  }
++  if (pipe->alpha0Ptr) {
++    ++pipe->alpha0Ptr;
++  }
++}
++
++inline void Splash::drawPixel(SplashPipe *pipe, int x, int y, GBool noClip) {
++  if (noClip || state->clip->test(x, y)) {
++    pipeSetXY(pipe, x, y);
++    pipeRun(pipe);
++    updateModX(x);
++    updateModY(y);
++  }
++}
++
++inline void Splash::drawAAPixelInit() {
++  aaBufY = -1;
++}
++
++inline void Splash::drawAAPixel(SplashPipe *pipe, int x, int y) {
++#if splashAASize == 4
++  static int bitCount4[16] = { 0, 1, 1, 2, 1, 2, 2, 3,
++			       1, 2, 2, 3, 2, 3, 3, 4 };
++  int w;
++#else
++  int xx, yy;
++#endif
++  SplashColorPtr p;
++  int x0, x1, t;
++
++  if (x < 0 || x >= bitmap->width ||
++      y < state->clip->getYMinI() || y > state->clip->getYMaxI()) {
++    return;
++  }
++
++  // update aaBuf
++  if (y != aaBufY) {
++    memset(aaBuf->getDataPtr(), 0xff,
++	   aaBuf->getRowSize() * aaBuf->getHeight());
++    x0 = 0;
++    x1 = bitmap->width - 1;
++    state->clip->clipAALine(aaBuf, &x0, &x1, y);
++    aaBufY = y;
++  }
++
++  // compute the shape value
++#if splashAASize == 4
++  p = aaBuf->getDataPtr() + (x >> 1);
++  w = aaBuf->getRowSize();
++  if (x & 1) {
++    t = bitCount4[*p & 0x0f] + bitCount4[p[w] & 0x0f] +
++        bitCount4[p[2*w] & 0x0f] + bitCount4[p[3*w] & 0x0f];
++  } else {
++    t = bitCount4[*p >> 4] + bitCount4[p[w] >> 4] +
++        bitCount4[p[2*w] >> 4] + bitCount4[p[3*w] >> 4];
++  }
++#else
++  t = 0;
++  for (yy = 0; yy < splashAASize; ++yy) {
++    for (xx = 0; xx < splashAASize; ++xx) {
++      p = aaBuf->getDataPtr() + yy * aaBuf->getRowSize() +
++	  ((x * splashAASize + xx) >> 3);
++      t += (*p >> (7 - ((x * splashAASize + xx) & 7))) & 1;
++    }
++  }
++#endif
++
++  // draw the pixel
++  if (t != 0) {
++    pipeSetXY(pipe, x, y);
++    pipe->shape *= aaGamma[t];
++    pipeRun(pipe);
++    updateModX(x);
++    updateModY(y);
++  }
++}
++
++inline void Splash::drawSpan(SplashPipe *pipe, int x0, int x1, int y,
++			     GBool noClip) {
++  int x;
++
++  pipeSetXY(pipe, x0, y);
++  if (noClip) {
++    for (x = x0; x <= x1; ++x) {
++      pipeRun(pipe);
++    }
++    updateModX(x0);
++    updateModX(x1);
++    updateModY(y);
++  } else {
++    for (x = x0; x <= x1; ++x) {
++      if (state->clip->test(x, y)) {
++	pipeRun(pipe);
++	updateModX(x);
++	updateModY(y);
++      } else {
++	pipeIncX(pipe);
++      }
++    }
++  }
++}
++
++inline void Splash::drawAALine(SplashPipe *pipe, int x0, int x1, int y) {
++#if splashAASize == 4
++  static int bitCount4[16] = { 0, 1, 1, 2, 1, 2, 2, 3,
++			       1, 2, 2, 3, 2, 3, 3, 4 };
++  SplashColorPtr p0, p1, p2, p3;
++  int t;
++#else
++  SplashColorPtr p;
++  int xx, yy, t;
++#endif
++  int x;
++
++#if splashAASize == 4
++  p0 = aaBuf->getDataPtr() + (x0 >> 1);
++  p1 = p0 + aaBuf->getRowSize();
++  p2 = p1 + aaBuf->getRowSize();
++  p3 = p2 + aaBuf->getRowSize();
++#endif
++  pipeSetXY(pipe, x0, y);
++  for (x = x0; x <= x1; ++x) {
++
++    // compute the shape value
++#if splashAASize == 4
++    if (x & 1) {
++      t = bitCount4[*p0 & 0x0f] + bitCount4[*p1 & 0x0f] +
++	  bitCount4[*p2 & 0x0f] + bitCount4[*p3 & 0x0f];
++      ++p0; ++p1; ++p2; ++p3;
++    } else {
++      t = bitCount4[*p0 >> 4] + bitCount4[*p1 >> 4] +
++	  bitCount4[*p2 >> 4] + bitCount4[*p3 >> 4];
++    }
++#else
++    t = 0;
++    for (yy = 0; yy < splashAASize; ++yy) {
++      for (xx = 0; xx < splashAASize; ++xx) {
++	p = aaBuf->getDataPtr() + yy * aaBuf->getRowSize() +
++	    ((x * splashAASize + xx) >> 3);
++	t += (*p >> (7 - ((x * splashAASize + xx) & 7))) & 1;
++      }
++    }
++#endif
++
++    if (t != 0) {
++      pipe->shape = aaGamma[t];
++      pipeRun(pipe);
++      updateModX(x);
++      updateModY(y);
++    } else {
++      pipeIncX(pipe);
++    }
++  }
++}
++
++//------------------------------------------------------------------------
++
++// Transform a point from user space to device space.
++inline void Splash::transform(SplashCoord *matrix,
++			      SplashCoord xi, SplashCoord yi,
++			      SplashCoord *xo, SplashCoord *yo) {
++  //                          [ m[0] m[1] 0 ]
++  // [xo yo 1] = [xi yi 1] *  [ m[2] m[3] 0 ]
++  //                          [ m[4] m[5] 1 ]
++  *xo = xi * matrix[0] + yi * matrix[2] + matrix[4];
++  *yo = xi * matrix[1] + yi * matrix[3] + matrix[5];
++}
++
++//------------------------------------------------------------------------
++// Splash
++//------------------------------------------------------------------------
++
++Splash::Splash(SplashBitmap *bitmapA, GBool vectorAntialiasA,
++	       SplashScreenParams *screenParams) {
++  int i;
++
++  bitmap = bitmapA;
++  vectorAntialias = vectorAntialiasA;
++  state = new SplashState(bitmap->width, bitmap->height, vectorAntialias,
++			  screenParams);
++  if (vectorAntialias) {
++    aaBuf = new SplashBitmap(splashAASize * bitmap->width, splashAASize,
++			     1, splashModeMono1, gFalse);
++    for (i = 0; i <= splashAASize * splashAASize; ++i) {
++      aaGamma[i] = splashPow((SplashCoord)i /
++			       (SplashCoord)(splashAASize * splashAASize),
++			     1.5);
++    }
++  } else {
++    aaBuf = NULL;
++  }
++  clearModRegion();
++  debugMode = gFalse;
++}
++
++Splash::Splash(SplashBitmap *bitmapA, GBool vectorAntialiasA,
++	       SplashScreen *screenA) {
++  int i;
++
++  bitmap = bitmapA;
++  vectorAntialias = vectorAntialiasA;
++  state = new SplashState(bitmap->width, bitmap->height, vectorAntialias,
++			  screenA);
++  if (vectorAntialias) {
++    aaBuf = new SplashBitmap(splashAASize * bitmap->width, splashAASize,
++			     1, splashModeMono1, gFalse);
++    for (i = 0; i <= splashAASize * splashAASize; ++i) {
++      aaGamma[i] = splashPow((SplashCoord)i /
++			       (SplashCoord)(splashAASize * splashAASize),
++			     1.5);
++    }
++  } else {
++    aaBuf = NULL;
++  }
++  clearModRegion();
++  debugMode = gFalse;
++}
++
++Splash::~Splash() {
++  while (state->next) {
++    restoreState();
++  }
++  delete state;
++  if (vectorAntialias) {
++    delete aaBuf;
++  }
++}
++
++//------------------------------------------------------------------------
++// state read
++//------------------------------------------------------------------------
++
++SplashCoord *Splash::getMatrix() {
++  return state->matrix;
++}
++
++SplashPattern *Splash::getStrokePattern() {
++  return state->strokePattern;
++}
++
++SplashPattern *Splash::getFillPattern() {
++  return state->fillPattern;
++}
++
++SplashScreen *Splash::getScreen() {
++  return state->screen;
++}
++
++SplashBlendFunc Splash::getBlendFunc() {
++  return state->blendFunc;
++}
++
++SplashCoord Splash::getStrokeAlpha() {
++  return state->strokeAlpha;
++}
++
++SplashCoord Splash::getFillAlpha() {
++  return state->fillAlpha;
++}
++
++SplashCoord Splash::getLineWidth() {
++  return state->lineWidth;
++}
++
++int Splash::getLineCap() {
++  return state->lineCap;
++}
++
++int Splash::getLineJoin() {
++  return state->lineJoin;
++}
++
++SplashCoord Splash::getMiterLimit() {
++  return state->miterLimit;
++}
++
++SplashCoord Splash::getFlatness() {
++  return state->flatness;
++}
++
++SplashCoord *Splash::getLineDash() {
++  return state->lineDash;
++}
++
++int Splash::getLineDashLength() {
++  return state->lineDashLength;
++}
++
++SplashCoord Splash::getLineDashPhase() {
++  return state->lineDashPhase;
++}
++
++SplashClip *Splash::getClip() {
++  return state->clip;
++}
++
++SplashBitmap *Splash::getSoftMask() {
++  return state->softMask;
++}
++
++GBool Splash::getInNonIsolatedGroup() {
++  return state->inNonIsolatedGroup;
++}
++
++//------------------------------------------------------------------------
++// state write
++//------------------------------------------------------------------------
++
++void Splash::setMatrix(SplashCoord *matrix) {
++  memcpy(state->matrix, matrix, 6 * sizeof(SplashCoord));
++}
++
++void Splash::setStrokePattern(SplashPattern *strokePattern) {
++  state->setStrokePattern(strokePattern);
++}
++
++void Splash::setFillPattern(SplashPattern *fillPattern) {
++  state->setFillPattern(fillPattern);
++}
++
++void Splash::setScreen(SplashScreen *screen) {
++  state->setScreen(screen);
++}
++
++void Splash::setBlendFunc(SplashBlendFunc func) {
++  state->blendFunc = func;
++}
++
++void Splash::setStrokeAlpha(SplashCoord alpha) {
++  state->strokeAlpha = alpha;
++}
++
++void Splash::setFillAlpha(SplashCoord alpha) {
++  state->fillAlpha = alpha;
++}
++
++void Splash::setLineWidth(SplashCoord lineWidth) {
++  state->lineWidth = lineWidth;
++}
++
++void Splash::setLineCap(int lineCap) {
++  state->lineCap = lineCap;
++}
++
++void Splash::setLineJoin(int lineJoin) {
++  state->lineJoin = lineJoin;
++}
++
++void Splash::setMiterLimit(SplashCoord miterLimit) {
++  state->miterLimit = miterLimit;
++}
++
++void Splash::setFlatness(SplashCoord flatness) {
++  if (flatness < 1) {
++    state->flatness = 1;
++  } else {
++    state->flatness = flatness;
++  }
++}
++
++void Splash::setLineDash(SplashCoord *lineDash, int lineDashLength,
++			 SplashCoord lineDashPhase) {
++  state->setLineDash(lineDash, lineDashLength, lineDashPhase);
++}
++
++void Splash::setStrokeAdjust(GBool strokeAdjust) {
++  state->strokeAdjust = strokeAdjust;
++}
++
++void Splash::clipResetToRect(SplashCoord x0, SplashCoord y0,
++			     SplashCoord x1, SplashCoord y1) {
++  state->clip->resetToRect(x0, y0, x1, y1);
++}
++
++SplashError Splash::clipToRect(SplashCoord x0, SplashCoord y0,
++			       SplashCoord x1, SplashCoord y1) {
++  return state->clip->clipToRect(x0, y0, x1, y1);
++}
++
++SplashError Splash::clipToPath(SplashPath *path, GBool eo) {
++  return state->clip->clipToPath(path, state->matrix, state->flatness, eo);
++}
++
++void Splash::setSoftMask(SplashBitmap *softMask) {
++  state->setSoftMask(softMask);
++}
++
++void Splash::setInNonIsolatedGroup(SplashBitmap *alpha0BitmapA,
++				   int alpha0XA, int alpha0YA) {
++  alpha0Bitmap = alpha0BitmapA;
++  alpha0X = alpha0XA;
++  alpha0Y = alpha0YA;
++  state->inNonIsolatedGroup = gTrue;
++}
++
++//------------------------------------------------------------------------
++// state save/restore
++//------------------------------------------------------------------------
++
++void Splash::saveState() {
++  SplashState *newState;
++
++  newState = state->copy();
++  newState->next = state;
++  state = newState;
++}
++
++SplashError Splash::restoreState() {
++  SplashState *oldState;
++
++  if (!state->next) {
++    return splashErrNoSave;
++  }
++  oldState = state;
++  state = state->next;
++  delete oldState;
++  return splashOk;
++}
++
++//------------------------------------------------------------------------
++// drawing operations
++//------------------------------------------------------------------------
++
++void Splash::clear(SplashColorPtr color, Guchar alpha) {
++  SplashColorPtr row, p;
++  Guchar mono;
++  int x, y;
++
++  switch (bitmap->mode) {
++  case splashModeMono1:
++    mono = (color[0] & 0x80) ? 0xff : 0x00;
++    if (bitmap->rowSize < 0) {
++      memset(bitmap->data + bitmap->rowSize * (bitmap->height - 1),
++	     mono, -bitmap->rowSize * bitmap->height);
++    } else {
++      memset(bitmap->data, mono, bitmap->rowSize * bitmap->height);
++    }
++    break;
++  case splashModeMono8:
++    if (bitmap->rowSize < 0) {
++      memset(bitmap->data + bitmap->rowSize * (bitmap->height - 1),
++	     color[0], -bitmap->rowSize * bitmap->height);
++    } else {
++      memset(bitmap->data, color[0], bitmap->rowSize * bitmap->height);
++    }
++    break;
++  case splashModeRGB8:
++    if (color[0] == color[1] && color[1] == color[2]) {
++      if (bitmap->rowSize < 0) {
++	memset(bitmap->data + bitmap->rowSize * (bitmap->height - 1),
++	       color[0], -bitmap->rowSize * bitmap->height);
++      } else {
++	memset(bitmap->data, color[0], bitmap->rowSize * bitmap->height);
++      }
++    } else {
++      row = bitmap->data;
++      for (y = 0; y < bitmap->height; ++y) {
++	p = row;
++	for (x = 0; x < bitmap->width; ++x) {
++	  *p++ = color[2];
++	  *p++ = color[1];
++	  *p++ = color[0];
++	}
++	row += bitmap->rowSize;
++      }
++    }
++    break;
++  case splashModeBGR8:
++    if (color[0] == color[1] && color[1] == color[2]) {
++      if (bitmap->rowSize < 0) {
++	memset(bitmap->data + bitmap->rowSize * (bitmap->height - 1),
++	       color[0], -bitmap->rowSize * bitmap->height);
++      } else {
++	memset(bitmap->data, color[0], bitmap->rowSize * bitmap->height);
++      }
++    } else {
++      row = bitmap->data;
++      for (y = 0; y < bitmap->height; ++y) {
++	p = row;
++	for (x = 0; x < bitmap->width; ++x) {
++	  *p++ = color[0];
++	  *p++ = color[1];
++	  *p++ = color[2];
++	}
++	row += bitmap->rowSize;
++      }
++    }
++    break;
++#if SPLASH_CMYK
++  case splashModeCMYK8:
++    if (color[0] == color[1] && color[1] == color[2] && color[2] == color[3]) {
++      if (bitmap->rowSize < 0) {
++	memset(bitmap->data + bitmap->rowSize * (bitmap->height - 1),
++	       color[0], -bitmap->rowSize * bitmap->height);
++      } else {
++	memset(bitmap->data, color[0], bitmap->rowSize * bitmap->height);
++      }
++    } else {
++      row = bitmap->data;
++      for (y = 0; y < bitmap->height; ++y) {
++	p = row;
++	for (x = 0; x < bitmap->width; ++x) {
++	  *p++ = color[0];
++	  *p++ = color[1];
++	  *p++ = color[2];
++	  *p++ = color[3];
++	}
++	row += bitmap->rowSize;
++      }
++    }
++    break;
++#endif
++  }
++
++  if (bitmap->alpha) {
++    memset(bitmap->alpha, alpha, bitmap->width * bitmap->height);
++  }
++
++  updateModX(0);
++  updateModY(0);
++  updateModX(bitmap->width - 1);
++  updateModY(bitmap->height - 1);
++}
++
++SplashError Splash::stroke(SplashPath *path) {
++  SplashPath *path2, *dPath;
++
++  if (debugMode) {
++    printf("stroke [dash:%d] [width:%.2f]:\n",
++	   state->lineDashLength, (double)state->lineWidth);
++    dumpPath(path);
++  }
++  opClipRes = splashClipAllOutside;
++  if (path->length == 0) {
++    return splashErrEmptyPath;
++  }
++  path2 = flattenPath(path, state->matrix, state->flatness);
++  if (state->lineDashLength > 0) {
++    dPath = makeDashedPath(path2);
++    delete path2;
++    path2 = dPath;
++  }
++  if (state->lineWidth == 0) {
++    strokeNarrow(path2);
++  } else {
++    strokeWide(path2);
++  }
++  delete path2;
++  return splashOk;
++}
++
++void Splash::strokeNarrow(SplashPath *path) {
++  SplashPipe pipe;
++  SplashXPath *xPath;
++  SplashXPathSeg *seg;
++  int x0, x1, x2, x3, y0, y1, x, y, t;
++  SplashCoord dx, dy, dxdy;
++  SplashClipResult clipRes;
++  int nClipRes[3];
++  int i;
++
++  nClipRes[0] = nClipRes[1] = nClipRes[2] = 0;
++
++  xPath = new SplashXPath(path, state->matrix, state->flatness, gFalse);
++
++  pipeInit(&pipe, 0, 0, state->strokePattern, NULL, state->strokeAlpha,
++	   gFalse, gFalse);
++
++  for (i = 0, seg = xPath->segs; i < xPath->length; ++i, ++seg) {
++
++    x0 = splashFloor(seg->x0);
++    x1 = splashFloor(seg->x1);
++    y0 = splashFloor(seg->y0);
++    y1 = splashFloor(seg->y1);
++
++    // horizontal segment
++    if (y0 == y1) {
++      if (x0 > x1) {
++	t = x0; x0 = x1; x1 = t;
++      }
++      if ((clipRes = state->clip->testSpan(x0, x1, y0))
++	  != splashClipAllOutside) {
++	drawSpan(&pipe, x0, x1, y0, clipRes == splashClipAllInside);
++      }
++
++    // segment with |dx| > |dy|
++    } else if (splashAbs(seg->dxdy) > 1) {
++      dx = seg->x1 - seg->x0;
++      dy = seg->y1 - seg->y0;
++      dxdy = seg->dxdy;
++      if (y0 > y1) {
++	t = y0; y0 = y1; y1 = t;
++	t = x0; x0 = x1; x1 = t;
++	dx = -dx;
++	dy = -dy;
++      }
++      if ((clipRes = state->clip->testRect(x0 <= x1 ? x0 : x1, y0,
++					   x0 <= x1 ? x1 : x0, y1))
++	  != splashClipAllOutside) {
++	if (dx > 0) {
++	  x2 = x0;
++	  x3 = splashFloor(seg->x0 + ((SplashCoord)y0 + 1 - seg->y0) * dxdy);
++	  drawSpan(&pipe, x2, (x2 <= x3 - 1) ? x3 - 1 : x2, y0,
++		   clipRes == splashClipAllInside);
++	  x2 = x3;
++	  for (y = y0 + 1; y <= y1 - 1; ++y) {
++	    x3 = splashFloor(seg->x0 + ((SplashCoord)y + 1 - seg->y0) * dxdy);
++	    drawSpan(&pipe, x2, x3 - 1, y, clipRes == splashClipAllInside);
++	    x2 = x3;
++	  }
++	  drawSpan(&pipe, x2, x2 <= x1 ? x1 : x2, y1,
++		   clipRes == splashClipAllInside);
++	} else {
++	  x2 = x0;
++	  x3 = splashFloor(seg->x0 + ((SplashCoord)y0 + 1 - seg->y0) * dxdy);
++	  drawSpan(&pipe, (x3 + 1 <= x2) ? x3 + 1 : x2, x2, y0,
++		   clipRes == splashClipAllInside);
++	  x2 = x3;
++	  for (y = y0 + 1; y <= y1 - 1; ++y) {
++	    x3 = splashFloor(seg->x0 + ((SplashCoord)y + 1 - seg->y0) * dxdy);
++	    drawSpan(&pipe, x3 + 1, x2, y, clipRes == splashClipAllInside);
++	    x2 = x3;
++	  }
++	  drawSpan(&pipe, x1, (x1 <= x2) ? x2 : x1, y1,
++		   clipRes == splashClipAllInside);
++	}
++      }
++
++    // segment with |dy| > |dx|
++    } else {
++      dxdy = seg->dxdy;
++      if (y0 > y1) {
++	t = x0; x0 = x1; x1 = t;
++	t = y0; y0 = y1; y1 = t;
++      }
++      if ((clipRes = state->clip->testRect(x0 <= x1 ? x0 : x1, y0,
++					   x0 <= x1 ? x1 : x0, y1))
++	  != splashClipAllOutside) {
++	drawPixel(&pipe, x0, y0, clipRes == splashClipAllInside);
++	for (y = y0 + 1; y <= y1 - 1; ++y) {
++	  x = splashFloor(seg->x0 + ((SplashCoord)y - seg->y0) * dxdy);
++	  drawPixel(&pipe, x, y, clipRes == splashClipAllInside);
++	}
++	drawPixel(&pipe, x1, y1, clipRes == splashClipAllInside);
++      }
++    }
++    ++nClipRes[clipRes];
++  }
++  if (nClipRes[splashClipPartial] ||
++      (nClipRes[splashClipAllInside] && nClipRes[splashClipAllOutside])) {
++    opClipRes = splashClipPartial;
++  } else if (nClipRes[splashClipAllInside]) {
++    opClipRes = splashClipAllInside;
++  } else {
++    opClipRes = splashClipAllOutside;
++  }
++
++  delete xPath;
++}
++
++void Splash::strokeWide(SplashPath *path) {
++  SplashPath *path2;
++
++  path2 = makeStrokePath(path, gFalse);
++  fillWithPattern(path2, gFalse, state->strokePattern, state->strokeAlpha);
++  delete path2;
++}
++
++SplashPath *Splash::flattenPath(SplashPath *path, SplashCoord *matrix,
++				SplashCoord flatness) {
++  SplashPath *fPath;
++  SplashCoord flatness2;
++  Guchar flag;
++  int i;
++
++  fPath = new SplashPath();
++  flatness2 = flatness * flatness;
++  i = 0;
++  while (i < path->length) {
++    flag = path->flags[i];
++    if (flag & splashPathFirst) {
++      fPath->moveTo(path->pts[i].x, path->pts[i].y);
++      ++i;
++    } else {
++      if (flag & splashPathCurve) {
++	flattenCurve(path->pts[i-1].x, path->pts[i-1].y,
++		     path->pts[i  ].x, path->pts[i  ].y,
++		     path->pts[i+1].x, path->pts[i+1].y,
++		     path->pts[i+2].x, path->pts[i+2].y,
++		     matrix, flatness2, fPath);
++	i += 3;
++      } else {
++	fPath->lineTo(path->pts[i].x, path->pts[i].y);
++	++i;
++      }
++      if (path->flags[i-1] & splashPathClosed) {
++	fPath->close();
++      }
++    }
++  }
++  return fPath;
++}
++
++void Splash::flattenCurve(SplashCoord x0, SplashCoord y0,
++			  SplashCoord x1, SplashCoord y1,
++			  SplashCoord x2, SplashCoord y2,
++			  SplashCoord x3, SplashCoord y3,
++			  SplashCoord *matrix, SplashCoord flatness2,
++			  SplashPath *fPath) {
++  SplashCoord cx[splashMaxCurveSplits + 1][3];
++  SplashCoord cy[splashMaxCurveSplits + 1][3];
++  int cNext[splashMaxCurveSplits + 1];
++  SplashCoord xl0, xl1, xl2, xr0, xr1, xr2, xr3, xx1, xx2, xh;
++  SplashCoord yl0, yl1, yl2, yr0, yr1, yr2, yr3, yy1, yy2, yh;
++  SplashCoord dx, dy, mx, my, tx, ty, d1, d2;
++  int p1, p2, p3;
++
++  // initial segment
++  p1 = 0;
++  p2 = splashMaxCurveSplits;
++  cx[p1][0] = x0;  cy[p1][0] = y0;
++  cx[p1][1] = x1;  cy[p1][1] = y1;
++  cx[p1][2] = x2;  cy[p1][2] = y2;
++  cx[p2][0] = x3;  cy[p2][0] = y3;
++  cNext[p1] = p2;
++
++  while (p1 < splashMaxCurveSplits) {
++
++    // get the next segment
++    xl0 = cx[p1][0];  yl0 = cy[p1][0];
++    xx1 = cx[p1][1];  yy1 = cy[p1][1];
++    xx2 = cx[p1][2];  yy2 = cy[p1][2];
++    p2 = cNext[p1];
++    xr3 = cx[p2][0];  yr3 = cy[p2][0];
++
++    // compute the distances (in device space) from the control points
++    // to the midpoint of the straight line (this is a bit of a hack,
++    // but it's much faster than computing the actual distances to the
++    // line)
++    transform(matrix, (xl0 + xr3) * 0.5, (yl0 + yr3) * 0.5, &mx, &my);
++    transform(matrix, xx1, yy1, &tx, &ty);
++    dx = tx - mx;
++    dy = ty - my;
++    d1 = dx*dx + dy*dy;
++    transform(matrix, xx2, yy2, &tx, &ty);
++    dx = tx - mx;
++    dy = ty - my;
++    d2 = dx*dx + dy*dy;
++
++    // if the curve is flat enough, or no more subdivisions are
++    // allowed, add the straight line segment
++    if (p2 - p1 == 1 || (d1 <= flatness2 && d2 <= flatness2)) {
++      fPath->lineTo(xr3, yr3);
++      p1 = p2;
++
++    // otherwise, subdivide the curve
++    } else {
++      xl1 = (xl0 + xx1) * 0.5;
++      yl1 = (yl0 + yy1) * 0.5;
++      xh = (xx1 + xx2) * 0.5;
++      yh = (yy1 + yy2) * 0.5;
++      xl2 = (xl1 + xh) * 0.5;
++      yl2 = (yl1 + yh) * 0.5;
++      xr2 = (xx2 + xr3) * 0.5;
++      yr2 = (yy2 + yr3) * 0.5;
++      xr1 = (xh + xr2) * 0.5;
++      yr1 = (yh + yr2) * 0.5;
++      xr0 = (xl2 + xr1) * 0.5;
++      yr0 = (yl2 + yr1) * 0.5;
++      // add the new subdivision points
++      p3 = (p1 + p2) / 2;
++      cx[p1][1] = xl1;  cy[p1][1] = yl1;
++      cx[p1][2] = xl2;  cy[p1][2] = yl2;
++      cNext[p1] = p3;
++      cx[p3][0] = xr0;  cy[p3][0] = yr0;
++      cx[p3][1] = xr1;  cy[p3][1] = yr1;
++      cx[p3][2] = xr2;  cy[p3][2] = yr2;
++      cNext[p3] = p2;
++    }
++  }
++}
++
++SplashPath *Splash::makeDashedPath(SplashPath *path) {
++  SplashPath *dPath;
++  SplashCoord lineDashTotal;
++  SplashCoord lineDashStartPhase, lineDashDist, segLen;
++  SplashCoord x0, y0, x1, y1, xa, ya;
++  GBool lineDashStartOn, lineDashOn, newPath;
++  int lineDashStartIdx, lineDashIdx;
++  int i, j, k;
++
++  lineDashTotal = 0;
++  for (i = 0; i < state->lineDashLength; ++i) {
++    lineDashTotal += state->lineDash[i];
++  }
++  lineDashStartPhase = state->lineDashPhase;
++  i = splashFloor(lineDashStartPhase / lineDashTotal);
++  lineDashStartPhase -= (SplashCoord)i * lineDashTotal;
++  lineDashStartOn = gTrue;
++  lineDashStartIdx = 0;
++  while (lineDashStartPhase >= state->lineDash[lineDashStartIdx]) {
++    lineDashStartOn = !lineDashStartOn;
++    lineDashStartPhase -= state->lineDash[lineDashStartIdx];
++    ++lineDashStartIdx;
++  }
++
++  dPath = new SplashPath();
++
++  // process each subpath
++  i = 0;
++  while (i < path->length) {
++
++    // find the end of the subpath
++    for (j = i;
++	 j < path->length - 1 && !(path->flags[j] & splashPathLast);
++	 ++j) ;
++
++    // initialize the dash parameters
++    lineDashOn = lineDashStartOn;
++    lineDashIdx = lineDashStartIdx;
++    lineDashDist = state->lineDash[lineDashIdx] - lineDashStartPhase;
++
++    // process each segment of the subpath
++    newPath = gTrue;
++    for (k = i; k < j; ++k) {
++
++      // grab the segment
++      x0 = path->pts[k].x;
++      y0 = path->pts[k].y;
++      x1 = path->pts[k+1].x;
++      y1 = path->pts[k+1].y;
++      segLen = splashDist(x0, y0, x1, y1);
++
++      // process the segment
++      while (segLen > 0) {
++
++	if (lineDashDist >= segLen) {
++	  if (lineDashOn) {
++	    if (newPath) {
++	      dPath->moveTo(x0, y0);
++	      newPath = gFalse;
++	    }
++	    dPath->lineTo(x1, y1);
++	  }
++	  lineDashDist -= segLen;
++	  segLen = 0;
++
++	} else {
++	  xa = x0 + (lineDashDist / segLen) * (x1 - x0);
++	  ya = y0 + (lineDashDist / segLen) * (y1 - y0);
++	  if (lineDashOn) {
++	    if (newPath) {
++	      dPath->moveTo(x0, y0);
++	      newPath = gFalse;
++	    }
++	    dPath->lineTo(xa, ya);
++	  }
++	  x0 = xa;
++	  y0 = ya;
++	  segLen -= lineDashDist;
++	  lineDashDist = 0;
++	}
++
++	// get the next entry in the dash array
++	if (lineDashDist <= 0) {
++	  lineDashOn = !lineDashOn;
++	  if (++lineDashIdx == state->lineDashLength) {
++	    lineDashIdx = 0;
++	  }
++	  lineDashDist = state->lineDash[lineDashIdx];
++	  newPath = gTrue;
++	}
++      }
++    }
++    i = j + 1;
++  }
++
++  return dPath;
++}
++
++SplashError Splash::fill(SplashPath *path, GBool eo) {
++  if (debugMode) {
++    printf("fill [eo:%d]:\n", eo);
++    dumpPath(path);
++  }
++  return fillWithPattern(path, eo, state->fillPattern, state->fillAlpha);
++}
++
++SplashError Splash::fillWithPattern(SplashPath *path, GBool eo,
++				    SplashPattern *pattern,
++				    SplashCoord alpha) {
++  SplashPipe pipe;
++  SplashXPath *xPath;
++  SplashXPathScanner *scanner;
++  int xMinI, yMinI, xMaxI, yMaxI, x0, x1, y;
++  SplashClipResult clipRes, clipRes2;
++
++  if (path->length == 0) {
++    return splashErrEmptyPath;
++  }
++  xPath = new SplashXPath(path, state->matrix, state->flatness, gTrue);
++  if (vectorAntialias) {
++    xPath->aaScale();
++  }
++  xPath->sort();
++  scanner = new SplashXPathScanner(xPath, eo);
++
++  // get the min and max x and y values
++  if (vectorAntialias) {
++    scanner->getBBoxAA(&xMinI, &yMinI, &xMaxI, &yMaxI);
++  } else {
++    scanner->getBBox(&xMinI, &yMinI, &xMaxI, &yMaxI);
++  }
++
++  // check clipping
++  if ((clipRes = state->clip->testRect(xMinI, yMinI, xMaxI, yMaxI))
++      != splashClipAllOutside) {
++
++    // limit the y range
++    if (yMinI < state->clip->getYMinI()) {
++      yMinI = state->clip->getYMinI();
++    }
++    if (yMaxI > state->clip->getYMaxI()) {
++      yMaxI = state->clip->getYMaxI();
++    }
++
++    pipeInit(&pipe, 0, yMinI, pattern, NULL, alpha, vectorAntialias, gFalse);
++
++    // draw the spans
++    if (vectorAntialias) {
++      for (y = yMinI; y <= yMaxI; ++y) {
++	scanner->renderAALine(aaBuf, &x0, &x1, y);
++	if (clipRes != splashClipAllInside) {
++	  state->clip->clipAALine(aaBuf, &x0, &x1, y);
++	}
++	drawAALine(&pipe, x0, x1, y);
++      }
++    } else {
++      for (y = yMinI; y <= yMaxI; ++y) {
++	while (scanner->getNextSpan(y, &x0, &x1)) {
++	  if (clipRes == splashClipAllInside) {
++	    drawSpan(&pipe, x0, x1, y, gTrue);
++	  } else {
++	    // limit the x range
++	    if (x0 < state->clip->getXMinI()) {
++	      x0 = state->clip->getXMinI();
++	    }
++	    if (x1 > state->clip->getXMaxI()) {
++	      x1 = state->clip->getXMaxI();
++	    }
++	    clipRes2 = state->clip->testSpan(x0, x1, y);
++	    drawSpan(&pipe, x0, x1, y, clipRes2 == splashClipAllInside);
++	  }
++	}
++      }
++    }
++  }
++  opClipRes = clipRes;
++
++  delete scanner;
++  delete xPath;
++  return splashOk;
++}
++
++SplashError Splash::xorFill(SplashPath *path, GBool eo) {
++  SplashPipe pipe;
++  SplashXPath *xPath;
++  SplashXPathScanner *scanner;
++  int xMinI, yMinI, xMaxI, yMaxI, x0, x1, y;
++  SplashClipResult clipRes, clipRes2;
++  SplashBlendFunc origBlendFunc;
++
++  if (path->length == 0) {
++    return splashErrEmptyPath;
++  }
++  xPath = new SplashXPath(path, state->matrix, state->flatness, gTrue);
++  xPath->sort();
++  scanner = new SplashXPathScanner(xPath, eo);
++
++  // get the min and max x and y values
++  scanner->getBBox(&xMinI, &yMinI, &xMaxI, &yMaxI);
++
++  // check clipping
++  if ((clipRes = state->clip->testRect(xMinI, yMinI, xMaxI, yMaxI))
++      != splashClipAllOutside) {
++
++    // limit the y range
++    if (yMinI < state->clip->getYMinI()) {
++      yMinI = state->clip->getYMinI();
++    }
++    if (yMaxI > state->clip->getYMaxI()) {
++      yMaxI = state->clip->getYMaxI();
++    }
++
++    origBlendFunc = state->blendFunc;
++    state->blendFunc = &blendXor;
++    pipeInit(&pipe, 0, yMinI, state->fillPattern, NULL, 1, gFalse, gFalse);
++
++    // draw the spans
++    for (y = yMinI; y <= yMaxI; ++y) {
++      while (scanner->getNextSpan(y, &x0, &x1)) {
++	if (clipRes == splashClipAllInside) {
++	  drawSpan(&pipe, x0, x1, y, gTrue);
++	} else {
++	  // limit the x range
++	  if (x0 < state->clip->getXMinI()) {
++	    x0 = state->clip->getXMinI();
++	  }
++	  if (x1 > state->clip->getXMaxI()) {
++	    x1 = state->clip->getXMaxI();
++	  }
++	  clipRes2 = state->clip->testSpan(x0, x1, y);
++	  drawSpan(&pipe, x0, x1, y, clipRes2 == splashClipAllInside);
++	}
++      }
++    }
++    state->blendFunc = origBlendFunc;
++  }
++  opClipRes = clipRes;
++
++  delete scanner;
++  delete xPath;
++  return splashOk;
++}
++
++SplashError Splash::fillChar(SplashCoord x, SplashCoord y,
++			     int c, SplashFont *font) {
++  SplashGlyphBitmap glyph;
++  SplashCoord xt, yt;
++  int x0, y0, xFrac, yFrac;
++  SplashClipResult clipRes;
++
++  if (debugMode) {
++    printf("fillChar: x=%.2f y=%.2f c=%3d=0x%02x='%c'\n",
++	   (double)x, (double)y, c, c, c);
++  }
++  transform(state->matrix, x, y, &xt, &yt);
++  x0 = splashFloor(xt);
++  xFrac = splashFloor((xt - x0) * splashFontFraction);
++  y0 = splashFloor(yt);
++  yFrac = splashFloor((yt - y0) * splashFontFraction);
++  if (!font->getGlyph(c, xFrac, yFrac, &glyph, x0, y0, state->clip, &clipRes)) {
++    return splashErrNoGlyph;
++  }
++  if (clipRes != splashClipAllOutside) {
++    fillGlyph2(x0, y0, &glyph, clipRes == splashClipAllInside);
++  }
++  opClipRes = clipRes;
++  if (glyph.freeData) {
++    gfree(glyph.data);
++  }
++  return splashOk;
++}
++
++void Splash::fillGlyph(SplashCoord x, SplashCoord y,
++			      SplashGlyphBitmap *glyph) {
++  SplashCoord xt, yt;
++  int x0, y0;
++
++  transform(state->matrix, x, y, &xt, &yt);
++  x0 = splashFloor(xt);
++  y0 = splashFloor(yt);
++  SplashClipResult clipRes = state->clip->testRect(x0 - glyph->x,
++                             y0 - glyph->y,
++                             x0 - glyph->x + glyph->w - 1,
++                             y0 - glyph->y + glyph->h - 1);
++  if (clipRes != splashClipAllOutside) {
++    fillGlyph2(x0, y0, glyph, clipRes == splashClipAllInside);
++  }
++  opClipRes = clipRes;
++}
++
++void Splash::fillGlyph2(int x0, int y0, SplashGlyphBitmap *glyph, GBool noClip) {
++  SplashPipe pipe;
++  int alpha0, alpha;
++  Guchar *p;
++  int x1, y1, xx, xx1, yy;
++
++  p = glyph->data;
++  int xStart = x0 - glyph->x;
++  int yStart = y0 - glyph->y;
++  int xxLimit = glyph->w;
++  int yyLimit = glyph->h;
++
++  if (yStart < 0)
++  {
++    p += glyph->w * -yStart; // move p to the beginning of the first painted row
++    yyLimit += yStart;
++    yStart = 0;
++  }
++
++  if (xStart < 0)
++  {
++    p += -xStart; // move p to the first painted pixel
++    xxLimit += xStart;
++    xStart = 0;
++  }
++
++  if (xxLimit + xStart >= bitmap->width) xxLimit = bitmap->width - xStart;
++  if (yyLimit + yStart >= bitmap->height) yyLimit = bitmap->height - yStart;
++
++    if (noClip) {
++      if (glyph->aa) {
++	pipeInit(&pipe, xStart, yStart,
++		 state->fillPattern, NULL, state->fillAlpha, gTrue, gFalse);
++	for (yy = 0, y1 = yStart; yy < yyLimit; ++yy, ++y1) {
++	  pipeSetXY(&pipe, xStart, y1);
++	  for (xx = 0, x1 = xStart; xx < xxLimit; ++xx, ++x1) {
++	    alpha = p[xx];
++	    if (alpha != 0) {
++	      pipe.shape = (SplashCoord)(alpha / 255.0);
++	      pipeRun(&pipe);
++	      updateModX(x1);
++	      updateModY(y1);
++	    } else {
++	      pipeIncX(&pipe);
++	    }
++	  }
++	  p += glyph->w;
++	}
++      } else {
++	const int widthEight = (int)ceil(glyph->w / 8.0);
++
++	pipeInit(&pipe, xStart, yStart,
++		 state->fillPattern, NULL, state->fillAlpha, gFalse, gFalse);
++	for (yy = 0, y1 = yStart; yy < yyLimit; ++yy, ++y1) {
++	  pipeSetXY(&pipe, xStart, y1);
++	  for (xx = 0, x1 = xStart; xx < xxLimit; xx += 8) {
++	    alpha0 = p[xx / 8];
++	    for (xx1 = 0; xx1 < 8 && xx + xx1 < xxLimit; ++xx1, ++x1) {
++	      if (alpha0 & 0x80) {
++		pipeRun(&pipe);
++		updateModX(x1);
++		updateModY(y1);
++	      } else {
++		pipeIncX(&pipe);
++	      }
++	      alpha0 <<= 1;
++	    }
++	  }
++	  p += widthEight;
++	}
++      }
++    } else {
++      if (glyph->aa) {
++	pipeInit(&pipe, xStart, yStart,
++		 state->fillPattern, NULL, state->fillAlpha, gTrue, gFalse);
++	for (yy = 0, y1 = yStart; yy < yyLimit; ++yy, ++y1) {
++	  pipeSetXY(&pipe, xStart, y1);
++	  for (xx = 0, x1 = xStart; xx < xxLimit; ++xx, ++x1) {
++	    if (state->clip->test(x1, y1)) {
++	      alpha = p[xx];
++	      if (alpha != 0) {
++		pipe.shape = (SplashCoord)(alpha / 255.0);
++		pipeRun(&pipe);
++		updateModX(x1);
++		updateModY(y1);
++	      } else {
++		pipeIncX(&pipe);
++	      }
++	    } else {
++	      pipeIncX(&pipe);
++	    }
++	  }
++	  p += glyph->w;
++	}
++      } else {
++	const int widthEight = (int)ceil(glyph->w / 8.0);
++
++	pipeInit(&pipe, xStart, yStart,
++		 state->fillPattern, NULL, state->fillAlpha, gFalse, gFalse);
++	for (yy = 0, y1 = yStart; yy < yyLimit; ++yy, ++y1) {
++	  pipeSetXY(&pipe, xStart, y1);
++	  for (xx = 0, x1 = xStart; xx < xxLimit; xx += 8) {
++	    alpha0 = p[xx / 8];
++	    for (xx1 = 0; xx1 < 8 && xx + xx1 < xxLimit; ++xx1, ++x1) {
++	      if (state->clip->test(x1, y1)) {
++		if (alpha0 & 0x80) {
++		  pipeRun(&pipe);
++		  updateModX(x1);
++		  updateModY(y1);
++		} else {
++		  pipeIncX(&pipe);
++		}
++	      } else {
++		pipeIncX(&pipe);
++	      }
++	      alpha0 <<= 1;
++	    }
++	  }
++	  p += widthEight;
++	}
++      }
++    }
++}
++
++SplashError Splash::fillImageMask(SplashImageMaskSource src, void *srcData,
++				  int w, int h, SplashCoord *mat,
++				  GBool glyphMode) {
++  SplashPipe pipe;
++  GBool rot;
++  SplashCoord xScale, yScale, xShear, yShear, yShear1;
++  int tx, tx2, ty, ty2, scaledWidth, scaledHeight, xSign, ySign;
++  int ulx, uly, llx, lly, urx, ury, lrx, lry;
++  int ulx1, uly1, llx1, lly1, urx1, ury1, lrx1, lry1;
++  int xMin, xMax, yMin, yMax;
++  SplashClipResult clipRes, clipRes2;
++  int yp, yq, yt, yStep, lastYStep;
++  int xp, xq, xt, xStep, xSrc;
++  int k1, spanXMin, spanXMax, spanY;
++  SplashColorPtr pixBuf, p;
++  int pixAcc;
++  int x, y, x1, x2, y2;
++  SplashCoord y1;
++  int n, m, i, j;
++
++  if (debugMode) {
++    printf("fillImageMask: w=%d h=%d mat=[%.2f %.2f %.2f %.2f %.2f %.2f]\n",
++	   w, h, (double)mat[0], (double)mat[1], (double)mat[2],
++	   (double)mat[3], (double)mat[4], (double)mat[5]);
++  }
++
++  if (w == 0 && h == 0) return splashErrZeroImage;
++
++  // check for singular matrix
++  if (splashAbs(mat[0] * mat[3] - mat[1] * mat[2]) < 0.000001) {
++    return splashErrSingularMatrix;
++  }
++
++  // compute scale, shear, rotation, translation parameters
++  rot = splashAbs(mat[1]) > splashAbs(mat[0]);
++  if (rot) {
++    xScale = -mat[1];
++    yScale = mat[2] - (mat[0] * mat[3]) / mat[1];
++    xShear = -mat[3] / yScale;
++    yShear = -mat[0] / mat[1];
++  } else {
++    xScale = mat[0];
++    yScale = mat[3] - (mat[1] * mat[2]) / mat[0];
++    xShear = mat[2] / yScale;
++    yShear = mat[1] / mat[0];
++  }
++  // Note 1: The PDF spec says that all pixels whose *centers* lie
++  // within the region get painted -- but that doesn't seem to match
++  // up with what Acrobat actually does: it ends up leaving gaps
++  // between image stripes.  So we use the same rule here as for
++  // fills: any pixel that overlaps the region gets painted.
++  // Note 2: The "glyphMode" flag is a kludge: it switches back to
++  // "correct" behavior (matching the spec), for use in rendering Type
++  // 3 fonts.
++  // Note 3: The +/-0.01 in these computations is to avoid floating
++  // point precision problems which can lead to gaps between image
++  // stripes (it can cause image stripes to overlap, but that's a much
++  // less visible problem).
++  if (glyphMode) {
++    if (xScale >= 0) {
++      tx = splashRound(mat[4]);
++      tx2 = splashRound(mat[4] + xScale) - 1;
++    } else {
++      tx = splashRound(mat[4]) - 1;
++      tx2 = splashRound(mat[4] + xScale);
++    }
++  } else {
++    if (xScale >= 0) {
++      tx = splashFloor(mat[4] - 0.01);
++      tx2 = splashFloor(mat[4] + xScale + 0.01);
++    } else {
++      tx = splashFloor(mat[4] + 0.01);
++      tx2 = splashFloor(mat[4] + xScale - 0.01);
++    }
++  }
++  scaledWidth = abs(tx2 - tx) + 1;
++  if (glyphMode) {
++    if (yScale >= 0) {
++      ty = splashRound(mat[5]);
++      ty2 = splashRound(mat[5] + yScale) - 1;
++    } else {
++      ty = splashRound(mat[5]) - 1;
++      ty2 = splashRound(mat[5] + yScale);
++    }
++  } else {
++    if (yScale >= 0) {
++      ty = splashFloor(mat[5] - 0.01);
++      ty2 = splashFloor(mat[5] + yScale + 0.01);
++    } else {
++      ty = splashFloor(mat[5] + 0.01);
++      ty2 = splashFloor(mat[5] + yScale - 0.01);
++    }
++  }
++  scaledHeight = abs(ty2 - ty) + 1;
++  xSign = (xScale < 0) ? -1 : 1;
++  ySign = (yScale < 0) ? -1 : 1;
++  yShear1 = (SplashCoord)xSign * yShear;
++
++  // clipping
++  ulx1 = 0;
++  uly1 = 0;
++  urx1 = xSign * (scaledWidth - 1);
++  ury1 = (int)(yShear * urx1);
++  llx1 = splashRound(xShear * ySign * (scaledHeight - 1));
++  lly1 = ySign * (scaledHeight - 1) + (int)(yShear * llx1);
++  lrx1 = xSign * (scaledWidth - 1) +
++           splashRound(xShear * ySign * (scaledHeight - 1));
++  lry1 = ySign * (scaledHeight - 1) + (int)(yShear * lrx1);
++  if (rot) {
++    ulx = tx + uly1;    uly = ty - ulx1;
++    urx = tx + ury1;    ury = ty - urx1;
++    llx = tx + lly1;    lly = ty - llx1;
++    lrx = tx + lry1;    lry = ty - lrx1;
++  } else {
++    ulx = tx + ulx1;    uly = ty + uly1;
++    urx = tx + urx1;    ury = ty + ury1;
++    llx = tx + llx1;    lly = ty + lly1;
++    lrx = tx + lrx1;    lry = ty + lry1;
++  }
++  xMin = (ulx < urx) ? (ulx < llx) ? (ulx < lrx) ? ulx : lrx
++                                   : (llx < lrx) ? llx : lrx
++		     : (urx < llx) ? (urx < lrx) ? urx : lrx
++                                   : (llx < lrx) ? llx : lrx;
++  xMax = (ulx > urx) ? (ulx > llx) ? (ulx > lrx) ? ulx : lrx
++                                   : (llx > lrx) ? llx : lrx
++		     : (urx > llx) ? (urx > lrx) ? urx : lrx
++                                   : (llx > lrx) ? llx : lrx;
++  yMin = (uly < ury) ? (uly < lly) ? (uly < lry) ? uly : lry
++                                   : (lly < lry) ? lly : lry
++		     : (ury < lly) ? (ury < lry) ? ury : lry
++                                   : (lly < lry) ? lly : lry;
++  yMax = (uly > ury) ? (uly > lly) ? (uly > lry) ? uly : lry
++                                   : (lly > lry) ? lly : lry
++		     : (ury > lly) ? (ury > lry) ? ury : lry
++                                   : (lly > lry) ? lly : lry;
++  clipRes = state->clip->testRect(xMin, yMin, xMax, yMax);
++  opClipRes = clipRes;
++
++  // compute Bresenham parameters for x and y scaling
++  yp = h / scaledHeight;
++  yq = h % scaledHeight;
++  xp = w / scaledWidth;
++  xq = w % scaledWidth;
++
++  // allocate pixel buffer
++  pixBuf = (SplashColorPtr)gmalloc((yp + 1) * w);
++
++  // initialize the pixel pipe
++  pipeInit(&pipe, 0, 0, state->fillPattern, NULL, state->fillAlpha,
++	   gTrue, gFalse);
++  if (vectorAntialias) {
++    drawAAPixelInit();
++  }
++
++  // init y scale Bresenham
++  yt = 0;
++  lastYStep = 1;
++
++  for (y = 0; y < scaledHeight; ++y) {
++
++    // y scale Bresenham
++    yStep = yp;
++    yt += yq;
++    if (yt >= scaledHeight) {
++      yt -= scaledHeight;
++      ++yStep;
++    }
++
++    // read row(s) from image
++    n = (yp > 0) ? yStep : lastYStep;
++    if (n > 0) {
++      p = pixBuf;
++      for (i = 0; i < n; ++i) {
++	(*src)(srcData, p);
++	p += w;
++      }
++    }
++    lastYStep = yStep;
++
++    // loop-invariant constants
++    k1 = splashRound(xShear * ySign * y);
++
++    // clipping test
++    if (clipRes != splashClipAllInside &&
++	!rot &&
++	(int)(yShear * k1) ==
++	  (int)(yShear * (xSign * (scaledWidth - 1) + k1))) {
++      if (xSign > 0) {
++	spanXMin = tx + k1;
++	spanXMax = spanXMin + (scaledWidth - 1);
++      } else {
++	spanXMax = tx + k1;
++	spanXMin = spanXMax - (scaledWidth - 1);
++      }
++      spanY = ty + ySign * y + (int)(yShear * k1);
++      clipRes2 = state->clip->testSpan(spanXMin, spanXMax, spanY);
++      if (clipRes2 == splashClipAllOutside) {
++	continue;
++      }
++    } else {
++      clipRes2 = clipRes;
++    }
++
++    // init x scale Bresenham
++    xt = 0;
++    xSrc = 0;
++
++    // x shear
++    x1 = k1;
++
++    // y shear
++    y1 = (SplashCoord)ySign * y + yShear * x1;
++    // this is a kludge: if yShear1 is negative, then (int)y1 would
++    // change immediately after the first pixel, which is not what we
++    // want
++    if (yShear1 < 0) {
++      y1 += 0.999;
++    }
++
++    // loop-invariant constants
++    n = yStep > 0 ? yStep : 1;
++
++    for (x = 0; x < scaledWidth; ++x) {
++
++      // x scale Bresenham
++      xStep = xp;
++      xt += xq;
++      if (xt >= scaledWidth) {
++	xt -= scaledWidth;
++	++xStep;
++      }
++
++      // rotation
++      if (rot) {
++	x2 = (int)y1;
++	y2 = -x1;
++      } else {
++	x2 = x1;
++	y2 = (int)y1;
++      }
++
++      // compute the alpha value for (x,y) after the x and y scaling
++      // operations
++      m = xStep > 0 ? xStep : 1;
++      p = pixBuf + xSrc;
++      pixAcc = 0;
++      for (i = 0; i < n; ++i) {
++	for (j = 0; j < m; ++j) {
++	  pixAcc += *p++;
++	}
++	p += w - m;
++      }
++
++      // blend fill color with background
++      if (pixAcc != 0) {
++	pipe.shape = (pixAcc == n * m)
++	                 ? (SplashCoord)1
++	                 : (SplashCoord)pixAcc / (SplashCoord)(n * m);
++	if (vectorAntialias && clipRes2 != splashClipAllInside) {
++	  drawAAPixel(&pipe, tx + x2, ty + y2);
++	} else {
++	  drawPixel(&pipe, tx + x2, ty + y2, clipRes2 == splashClipAllInside);
++	}
++      }
++
++      // x scale Bresenham
++      xSrc += xStep;
++
++      // x shear
++      x1 += xSign;
++
++      // y shear
++      y1 += yShear1;
++    }
++  }
++
++  // free memory
++  gfree(pixBuf);
++
++  return splashOk;
++}
++
++SplashError Splash::drawImage(SplashImageSource src, void *srcData,
++			      SplashColorMode srcMode, GBool srcAlpha,
++			      int w, int h, SplashCoord *mat) {
++  SplashPipe pipe;
++  GBool ok, rot;
++  SplashCoord xScale, yScale, xShear, yShear, yShear1;
++  int tx, tx2, ty, ty2, scaledWidth, scaledHeight, xSign, ySign;
++  int ulx, uly, llx, lly, urx, ury, lrx, lry;
++  int ulx1, uly1, llx1, lly1, urx1, ury1, lrx1, lry1;
++  int xMin, xMax, yMin, yMax;
++  SplashClipResult clipRes, clipRes2;
++  int yp, yq, yt, yStep, lastYStep;
++  int xp, xq, xt, xStep, xSrc;
++  int k1, spanXMin, spanXMax, spanY;
++  SplashColorPtr colorBuf, p;
++  SplashColor pix;
++  Guchar *alphaBuf, *q;
++#if SPLASH_CMYK
++  int pixAcc0, pixAcc1, pixAcc2, pixAcc3;
++#else
++  int pixAcc0, pixAcc1, pixAcc2;
++#endif
++  int alphaAcc;
++  SplashCoord pixMul, alphaMul, alpha;
++  int x, y, x1, x2, y2;
++  SplashCoord y1;
++  int nComps, n, m, i, j;
++
++  if (debugMode) {
++    printf("drawImage: srcMode=%d srcAlpha=%d w=%d h=%d mat=[%.2f %.2f %.2f %.2f %.2f %.2f]\n",
++	   srcMode, srcAlpha, w, h, (double)mat[0], (double)mat[1], (double)mat[2],
++	   (double)mat[3], (double)mat[4], (double)mat[5]);
++  }
++
++  // check color modes
++  ok = gFalse; // make gcc happy
++  nComps = 0; // make gcc happy
++  switch (bitmap->mode) {
++  case splashModeMono1:
++  case splashModeMono8:
++    ok = srcMode == splashModeMono8;
++    nComps = 1;
++    break;
++  case splashModeRGB8:
++    ok = srcMode == splashModeRGB8;
++    nComps = 3;
++    break;
++  case splashModeBGR8:
++    ok = srcMode == splashModeBGR8;
++    nComps = 3;
++    break;
++#if SPLASH_CMYK
++  case splashModeCMYK8:
++    ok = srcMode == splashModeCMYK8;
++    nComps = 4;
++    break;
++#endif
++  }
++  if (!ok) {
++    return splashErrModeMismatch;
++  }
++
++  // check for singular matrix
++  if (splashAbs(mat[0] * mat[3] - mat[1] * mat[2]) < 0.000001) {
++    return splashErrSingularMatrix;
++  }
++
++  // compute scale, shear, rotation, translation parameters
++  rot = splashAbs(mat[1]) > splashAbs(mat[0]);
++  if (rot) {
++    xScale = -mat[1];
++    yScale = mat[2] - (mat[0] * mat[3]) / mat[1];
++    xShear = -mat[3] / yScale;
++    yShear = -mat[0] / mat[1];
++  } else {
++    xScale = mat[0];
++    yScale = mat[3] - (mat[1] * mat[2]) / mat[0];
++    xShear = mat[2] / yScale;
++    yShear = mat[1] / mat[0];
++  }
++  // Note 1: The PDF spec says that all pixels whose *centers* lie
++  // within the region get painted -- but that doesn't seem to match
++  // up with what Acrobat actually does: it ends up leaving gaps
++  // between image stripes.  So we use the same rule here as for
++  // fills: any pixel that overlaps the region gets painted.
++  // Note 2: The +/-0.01 in these computations is to avoid floating
++  // point precision problems which can lead to gaps between image
++  // stripes (it can cause image stripes to overlap, but that's a much
++  // less visible problem).
++  if (xScale >= 0) {
++    tx = splashFloor(mat[4] - 0.01);
++    tx2 = splashFloor(mat[4] + xScale + 0.01);
++  } else {
++    tx = splashFloor(mat[4] + 0.01);
++    tx2 = splashFloor(mat[4] + xScale - 0.01);
++  }
++  scaledWidth = abs(tx2 - tx) + 1;
++  if (yScale >= 0) {
++    ty = splashFloor(mat[5] - 0.01);
++    ty2 = splashFloor(mat[5] + yScale + 0.01);
++  } else {
++    ty = splashFloor(mat[5] + 0.01);
++    ty2 = splashFloor(mat[5] + yScale - 0.01);
++  }
++  scaledHeight = abs(ty2 - ty) + 1;
++  xSign = (xScale < 0) ? -1 : 1;
++  ySign = (yScale < 0) ? -1 : 1;
++  yShear1 = (SplashCoord)xSign * yShear;
++
++  // clipping
++  ulx1 = 0;
++  uly1 = 0;
++  urx1 = xSign * (scaledWidth - 1);
++  ury1 = (int)(yShear * urx1);
++  llx1 = splashRound(xShear * ySign * (scaledHeight - 1));
++  lly1 = ySign * (scaledHeight - 1) + (int)(yShear * llx1);
++  lrx1 = xSign * (scaledWidth - 1) +
++           splashRound(xShear * ySign * (scaledHeight - 1));
++  lry1 = ySign * (scaledHeight - 1) + (int)(yShear * lrx1);
++  if (rot) {
++    ulx = tx + uly1;    uly = ty - ulx1;
++    urx = tx + ury1;    ury = ty - urx1;
++    llx = tx + lly1;    lly = ty - llx1;
++    lrx = tx + lry1;    lry = ty - lrx1;
++  } else {
++    ulx = tx + ulx1;    uly = ty + uly1;
++    urx = tx + urx1;    ury = ty + ury1;
++    llx = tx + llx1;    lly = ty + lly1;
++    lrx = tx + lrx1;    lry = ty + lry1;
++  }
++  xMin = (ulx < urx) ? (ulx < llx) ? (ulx < lrx) ? ulx : lrx
++                                   : (llx < lrx) ? llx : lrx
++		     : (urx < llx) ? (urx < lrx) ? urx : lrx
++                                   : (llx < lrx) ? llx : lrx;
++  xMax = (ulx > urx) ? (ulx > llx) ? (ulx > lrx) ? ulx : lrx
++                                   : (llx > lrx) ? llx : lrx
++		     : (urx > llx) ? (urx > lrx) ? urx : lrx
++                                   : (llx > lrx) ? llx : lrx;
++  yMin = (uly < ury) ? (uly < lly) ? (uly < lry) ? uly : lry
++                                   : (lly < lry) ? lly : lry
++		     : (ury < lly) ? (ury < lry) ? ury : lry
++                                   : (lly < lry) ? lly : lry;
++  yMax = (uly > ury) ? (uly > lly) ? (uly > lry) ? uly : lry
++                                   : (lly > lry) ? lly : lry
++		     : (ury > lly) ? (ury > lry) ? ury : lry
++                                   : (lly > lry) ? lly : lry;
++  clipRes = state->clip->testRect(xMin, yMin, xMax, yMax);
++  opClipRes = clipRes;
++  if (clipRes == splashClipAllOutside) {
++    return splashOk;
++  }
++
++  // compute Bresenham parameters for x and y scaling
++  yp = h / scaledHeight;
++  yq = h % scaledHeight;
++  xp = w / scaledWidth;
++  xq = w % scaledWidth;
++
++  // allocate pixel buffers
++  colorBuf = (SplashColorPtr)gmalloc((yp + 1) * w * nComps);
++  if (srcAlpha) {
++    alphaBuf = (Guchar *)gmalloc((yp + 1) * w);
++  } else {
++    alphaBuf = NULL;
++  }
++
++  pixAcc0 = pixAcc1 = pixAcc2 = 0; // make gcc happy
++#if SPLASH_CMYK
++  pixAcc3 = 0; // make gcc happy
++#endif
++
++  // initialize the pixel pipe
++  pipeInit(&pipe, 0, 0, NULL, pix, state->fillAlpha,
++	   srcAlpha || (vectorAntialias && clipRes != splashClipAllInside),
++	   gFalse);
++  if (vectorAntialias) {
++    drawAAPixelInit();
++  }
++
++  if (srcAlpha) {
++
++    // init y scale Bresenham
++    yt = 0;
++    lastYStep = 1;
++
++    for (y = 0; y < scaledHeight; ++y) {
++
++      // y scale Bresenham
++      yStep = yp;
++      yt += yq;
++      if (yt >= scaledHeight) {
++	yt -= scaledHeight;
++	++yStep;
++      }
++
++      // read row(s) from image
++      n = (yp > 0) ? yStep : lastYStep;
++      if (n > 0) {
++	p = colorBuf;
++	q = alphaBuf;
++	for (i = 0; i < n; ++i) {
++	  (*src)(srcData, p, q);
++	  p += w * nComps;
++	  q += w;
++	}
++      }
++      lastYStep = yStep;
++
++      // loop-invariant constants
++      k1 = splashRound(xShear * ySign * y);
++
++      // clipping test
++      if (clipRes != splashClipAllInside &&
++	  !rot &&
++	  (int)(yShear * k1) ==
++	    (int)(yShear * (xSign * (scaledWidth - 1) + k1))) {
++	if (xSign > 0) {
++	  spanXMin = tx + k1;
++	  spanXMax = spanXMin + (scaledWidth - 1);
++	} else {
++	  spanXMax = tx + k1;
++	  spanXMin = spanXMax - (scaledWidth - 1);
++	}
++	spanY = ty + ySign * y + (int)(yShear * k1);
++	clipRes2 = state->clip->testSpan(spanXMin, spanXMax, spanY);
++	if (clipRes2 == splashClipAllOutside) {
++	  continue;
++	}
++      } else {
++	clipRes2 = clipRes;
++      }
++
++      // init x scale Bresenham
++      xt = 0;
++      xSrc = 0;
++
++      // x shear
++      x1 = k1;
++
++      // y shear
++      y1 = (SplashCoord)ySign * y + yShear * x1;
++      // this is a kludge: if yShear1 is negative, then (int)y1 would
++      // change immediately after the first pixel, which is not what
++      // we want
++      if (yShear1 < 0) {
++	y1 += 0.999;
++      }
++
++      // loop-invariant constants
++      n = yStep > 0 ? yStep : 1;
++
++      switch (srcMode) {
++
++      case splashModeMono1:
++      case splashModeMono8:
++	for (x = 0; x < scaledWidth; ++x) {
++
++	  // x scale Bresenham
++	  xStep = xp;
++	  xt += xq;
++	  if (xt >= scaledWidth) {
++	    xt -= scaledWidth;
++	    ++xStep;
++	  }
++
++	  // rotation
++	  if (rot) {
++	    x2 = (int)y1;
++	    y2 = -x1;
++	  } else {
++	    x2 = x1;
++	    y2 = (int)y1;
++	  }
++
++	  // compute the filtered pixel at (x,y) after the x and y scaling
++	  // operations
++	  m = xStep > 0 ? xStep : 1;
++	  alphaAcc = 0;
++	  p = colorBuf + xSrc;
++	  q = alphaBuf + xSrc;
++	  pixAcc0 = 0;
++	  for (i = 0; i < n; ++i) {
++	    for (j = 0; j < m; ++j) {
++	      pixAcc0 += *p++;
++	      alphaAcc += *q++;
++	    }
++	    p += w - m;
++	    q += w - m;
++	  }
++	  pixMul = (SplashCoord)1 / (SplashCoord)(n * m);
++	  alphaMul = pixMul * (1.0 / 255.0);
++	  alpha = (SplashCoord)alphaAcc * alphaMul;
++
++	  if (alpha > 0) {
++	    pix[0] = (int)((SplashCoord)pixAcc0 * pixMul);
++
++	    // set pixel
++	    pipe.shape = alpha;
++	    if (vectorAntialias && clipRes != splashClipAllInside) {
++	      drawAAPixel(&pipe, tx + x2, ty + y2);
++	    } else {
++	      drawPixel(&pipe, tx + x2, ty + y2,
++			clipRes2 == splashClipAllInside);
++	    }
++	  }
++
++	  // x scale Bresenham
++	  xSrc += xStep;
++
++	  // x shear
++	  x1 += xSign;
++
++	  // y shear
++	  y1 += yShear1;
++	}
++	break;
++
++      case splashModeRGB8:
++      case splashModeBGR8:
++	for (x = 0; x < scaledWidth; ++x) {
++
++	  // x scale Bresenham
++	  xStep = xp;
++	  xt += xq;
++	  if (xt >= scaledWidth) {
++	    xt -= scaledWidth;
++	    ++xStep;
++	  }
++
++	  // rotation
++	  if (rot) {
++	    x2 = (int)y1;
++	    y2 = -x1;
++	  } else {
++	    x2 = x1;
++	    y2 = (int)y1;
++	  }
++
++	  // compute the filtered pixel at (x,y) after the x and y scaling
++	  // operations
++	  m = xStep > 0 ? xStep : 1;
++	  alphaAcc = 0;
++	  p = colorBuf + xSrc * 3;
++	  q = alphaBuf + xSrc;
++	  pixAcc0 = pixAcc1 = pixAcc2 = 0;
++	  for (i = 0; i < n; ++i) {
++	    for (j = 0; j < m; ++j) {
++	      pixAcc0 += *p++;
++	      pixAcc1 += *p++;
++	      pixAcc2 += *p++;
++	      alphaAcc += *q++;
++	    }
++	    p += 3 * (w - m);
++	    q += w - m;
++	  }
++	  pixMul = (SplashCoord)1 / (SplashCoord)(n * m);
++	  alphaMul = pixMul * (1.0 / 255.0);
++	  alpha = (SplashCoord)alphaAcc * alphaMul;
++
++	  if (alpha > 0) {
++	    pix[0] = (int)((SplashCoord)pixAcc0 * pixMul);
++	    pix[1] = (int)((SplashCoord)pixAcc1 * pixMul);
++	    pix[2] = (int)((SplashCoord)pixAcc2 * pixMul);
++
++	    // set pixel
++	    pipe.shape = alpha;
++	    if (vectorAntialias && clipRes != splashClipAllInside) {
++	      drawAAPixel(&pipe, tx + x2, ty + y2);
++	    } else {
++	      drawPixel(&pipe, tx + x2, ty + y2,
++			clipRes2 == splashClipAllInside);
++	    }
++	  }
++
++	  // x scale Bresenham
++	  xSrc += xStep;
++
++	  // x shear
++	  x1 += xSign;
++
++	  // y shear
++	  y1 += yShear1;
++	}
++	break;
++
++#if SPLASH_CMYK
++      case splashModeCMYK8:
++	for (x = 0; x < scaledWidth; ++x) {
++
++	  // x scale Bresenham
++	  xStep = xp;
++	  xt += xq;
++	  if (xt >= scaledWidth) {
++	    xt -= scaledWidth;
++	    ++xStep;
++	  }
++
++	  // rotation
++	  if (rot) {
++	    x2 = (int)y1;
++	    y2 = -x1;
++	  } else {
++	    x2 = x1;
++	    y2 = (int)y1;
++	  }
++
++	  // compute the filtered pixel at (x,y) after the x and y scaling
++	  // operations
++	  m = xStep > 0 ? xStep : 1;
++	  alphaAcc = 0;
++	  p = colorBuf + xSrc * 4;
++	  q = alphaBuf + xSrc;
++	  pixAcc0 = pixAcc1 = pixAcc2 = pixAcc3 = 0;
++	  for (i = 0; i < n; ++i) {
++	    for (j = 0; j < m; ++j) {
++	      pixAcc0 += *p++;
++	      pixAcc1 += *p++;
++	      pixAcc2 += *p++;
++	      pixAcc3 += *p++;
++	      alphaAcc += *q++;
++	    }
++	    p += 4 * (w - m);
++	    q += w - m;
++	  }
++	  pixMul = (SplashCoord)1 / (SplashCoord)(n * m);
++	  alphaMul = pixMul * (1.0 / 255.0);
++	  alpha = (SplashCoord)alphaAcc * alphaMul;
++
++	  if (alpha > 0) {
++	    pix[0] = (int)((SplashCoord)pixAcc0 * pixMul);
++	    pix[1] = (int)((SplashCoord)pixAcc1 * pixMul);
++	    pix[2] = (int)((SplashCoord)pixAcc2 * pixMul);
++	    pix[3] = (int)((SplashCoord)pixAcc3 * pixMul);
++
++	    // set pixel
++	    pipe.shape = alpha;
++	    if (vectorAntialias && clipRes != splashClipAllInside) {
++	      drawAAPixel(&pipe, tx + x2, ty + y2);
++	    } else {
++	      drawPixel(&pipe, tx + x2, ty + y2,
++			clipRes2 == splashClipAllInside);
++	    }
++	  }
++
++	  // x scale Bresenham
++	  xSrc += xStep;
++
++	  // x shear
++	  x1 += xSign;
++
++	  // y shear
++	  y1 += yShear1;
++	}
++	break;
++#endif // SPLASH_CMYK
++      }
++    }
++
++  } else {
++
++    // init y scale Bresenham
++    yt = 0;
++    lastYStep = 1;
++
++    for (y = 0; y < scaledHeight; ++y) {
++
++      // y scale Bresenham
++      yStep = yp;
++      yt += yq;
++      if (yt >= scaledHeight) {
++	yt -= scaledHeight;
++	++yStep;
++      }
++
++      // read row(s) from image
++      n = (yp > 0) ? yStep : lastYStep;
++      if (n > 0) {
++	p = colorBuf;
++	for (i = 0; i < n; ++i) {
++	  (*src)(srcData, p, NULL);
++	  p += w * nComps;
++	}
++      }
++      lastYStep = yStep;
++
++      // loop-invariant constants
++      k1 = splashRound(xShear * ySign * y);
++
++      // clipping test
++      if (clipRes != splashClipAllInside &&
++	  !rot &&
++	  (int)(yShear * k1) ==
++	    (int)(yShear * (xSign * (scaledWidth - 1) + k1))) {
++	if (xSign > 0) {
++	  spanXMin = tx + k1;
++	  spanXMax = spanXMin + (scaledWidth - 1);
++	} else {
++	  spanXMax = tx + k1;
++	  spanXMin = spanXMax - (scaledWidth - 1);
++	}
++	spanY = ty + ySign * y + (int)(yShear * k1);
++	clipRes2 = state->clip->testSpan(spanXMin, spanXMax, spanY);
++	if (clipRes2 == splashClipAllOutside) {
++	  continue;
++	}
++      } else {
++	clipRes2 = clipRes;
++      }
++
++      // init x scale Bresenham
++      xt = 0;
++      xSrc = 0;
++
++      // x shear
++      x1 = k1;
++
++      // y shear
++      y1 = (SplashCoord)ySign * y + yShear * x1;
++      // this is a kludge: if yShear1 is negative, then (int)y1 would
++      // change immediately after the first pixel, which is not what
++      // we want
++      if (yShear1 < 0) {
++	y1 += 0.999;
++      }
++
++      // loop-invariant constants
++      n = yStep > 0 ? yStep : 1;
++
++      switch (srcMode) {
++
++      case splashModeMono1:
++      case splashModeMono8:
++	for (x = 0; x < scaledWidth; ++x) {
++
++	  // x scale Bresenham
++	  xStep = xp;
++	  xt += xq;
++	  if (xt >= scaledWidth) {
++	    xt -= scaledWidth;
++	    ++xStep;
++	  }
++
++	  // rotation
++	  if (rot) {
++	    x2 = (int)y1;
++	    y2 = -x1;
++	  } else {
++	    x2 = x1;
++	    y2 = (int)y1;
++	  }
++
++	  // compute the filtered pixel at (x,y) after the x and y scaling
++	  // operations
++	  m = xStep > 0 ? xStep : 1;
++	  p = colorBuf + xSrc;
++	  pixAcc0 = 0;
++	  for (i = 0; i < n; ++i) {
++	    for (j = 0; j < m; ++j) {
++	      pixAcc0 += *p++;
++	    }
++	    p += w - m;
++	  }
++	  pixMul = (SplashCoord)1 / (SplashCoord)(n * m);
++
++	  pix[0] = (int)((SplashCoord)pixAcc0 * pixMul);
++
++	  // set pixel
++	  if (vectorAntialias && clipRes != splashClipAllInside) {
++	    pipe.shape = (SplashCoord)1;
++	    drawAAPixel(&pipe, tx + x2, ty + y2);
++	  } else {
++	    drawPixel(&pipe, tx + x2, ty + y2,
++		      clipRes2 == splashClipAllInside);
++	  }
++
++	  // x scale Bresenham
++	  xSrc += xStep;
++
++	  // x shear
++	  x1 += xSign;
++
++	  // y shear
++	  y1 += yShear1;
++	}
++	break;
++
++      case splashModeRGB8:
++      case splashModeBGR8:
++	for (x = 0; x < scaledWidth; ++x) {
++
++	  // x scale Bresenham
++	  xStep = xp;
++	  xt += xq;
++	  if (xt >= scaledWidth) {
++	    xt -= scaledWidth;
++	    ++xStep;
++	  }
++
++	  // rotation
++	  if (rot) {
++	    x2 = (int)y1;
++	    y2 = -x1;
++	  } else {
++	    x2 = x1;
++	    y2 = (int)y1;
++	  }
++
++	  // compute the filtered pixel at (x,y) after the x and y scaling
++	  // operations
++	  m = xStep > 0 ? xStep : 1;
++	  p = colorBuf + xSrc * 3;
++	  pixAcc0 = pixAcc1 = pixAcc2 = 0;
++	  for (i = 0; i < n; ++i) {
++	    for (j = 0; j < m; ++j) {
++	      pixAcc0 += *p++;
++	      pixAcc1 += *p++;
++	      pixAcc2 += *p++;
++	    }
++	    p += 3 * (w - m);
++	  }
++	  pixMul = (SplashCoord)1 / (SplashCoord)(n * m);
++
++	  pix[0] = (int)((SplashCoord)pixAcc0 * pixMul);
++	  pix[1] = (int)((SplashCoord)pixAcc1 * pixMul);
++	  pix[2] = (int)((SplashCoord)pixAcc2 * pixMul);
++
++	  // set pixel
++	  if (vectorAntialias && clipRes != splashClipAllInside) {
++	    pipe.shape = (SplashCoord)1;
++	    drawAAPixel(&pipe, tx + x2, ty + y2);
++	  } else {
++	    drawPixel(&pipe, tx + x2, ty + y2,
++		      clipRes2 == splashClipAllInside);
++	  }
++
++	  // x scale Bresenham
++	  xSrc += xStep;
++
++	  // x shear
++	  x1 += xSign;
++
++	  // y shear
++	  y1 += yShear1;
++	}
++	break;
++
++#if SPLASH_CMYK
++      case splashModeCMYK8:
++	for (x = 0; x < scaledWidth; ++x) {
++
++	  // x scale Bresenham
++	  xStep = xp;
++	  xt += xq;
++	  if (xt >= scaledWidth) {
++	    xt -= scaledWidth;
++	    ++xStep;
++	  }
++
++	  // rotation
++	  if (rot) {
++	    x2 = (int)y1;
++	    y2 = -x1;
++	  } else {
++	    x2 = x1;
++	    y2 = (int)y1;
++	  }
++
++	  // compute the filtered pixel at (x,y) after the x and y scaling
++	  // operations
++	  m = xStep > 0 ? xStep : 1;
++	  p = colorBuf + xSrc * 4;
++	  pixAcc0 = pixAcc1 = pixAcc2 = pixAcc3 = 0;
++	  for (i = 0; i < n; ++i) {
++	    for (j = 0; j < m; ++j) {
++	      pixAcc0 += *p++;
++	      pixAcc1 += *p++;
++	      pixAcc2 += *p++;
++	      pixAcc3 += *p++;
++	    }
++	    p += 4 * (w - m);
++	  }
++	  pixMul = (SplashCoord)1 / (SplashCoord)(n * m);
++
++	  pix[0] = (int)((SplashCoord)pixAcc0 * pixMul);
++	  pix[1] = (int)((SplashCoord)pixAcc1 * pixMul);
++	  pix[2] = (int)((SplashCoord)pixAcc2 * pixMul);
++	  pix[3] = (int)((SplashCoord)pixAcc3 * pixMul);
++
++	  // set pixel
++	  if (vectorAntialias && clipRes != splashClipAllInside) {
++	    pipe.shape = (SplashCoord)1;
++	    drawAAPixel(&pipe, tx + x2, ty + y2);
++	  } else {
++	    drawPixel(&pipe, tx + x2, ty + y2,
++		      clipRes2 == splashClipAllInside);
++	  }
++
++	  // x scale Bresenham
++	  xSrc += xStep;
++
++	  // x shear
++	  x1 += xSign;
++
++	  // y shear
++	  y1 += yShear1;
++	}
++	break;
++#endif // SPLASH_CMYK
++      }
++    }
++
++  }
++
++  gfree(colorBuf);
++  gfree(alphaBuf);
++
++  return splashOk;
++}
++
++SplashError Splash::composite(SplashBitmap *src, int xSrc, int ySrc,
++			      int xDest, int yDest, int w, int h,
++			      GBool noClip, GBool nonIsolated) {
++  SplashPipe pipe;
++  SplashColor pixel;
++  Guchar alpha;
++  Guchar *ap;
++  int x, y;
++
++  if (src->mode != bitmap->mode) {
++    return splashErrModeMismatch;
++  }
++
++  if (src->alpha) {
++    pipeInit(&pipe, xDest, yDest, NULL, pixel, state->fillAlpha,
++	     gTrue, nonIsolated);
++    for (y = 0; y < h; ++y) {
++      pipeSetXY(&pipe, xDest, yDest + y);
++      ap = src->getAlphaPtr() + (ySrc + y) * src->getWidth() + xSrc;
++      for (x = 0; x < w; ++x) {
++	src->getPixel(xSrc + x, ySrc + y, pixel);
++	alpha = *ap++;
++	if (noClip || state->clip->test(xDest + x, yDest + y)) {
++	  // this uses shape instead of alpha, which isn't technically
++	  // correct, but works out the same
++	  pipe.shape = (SplashCoord)(alpha / 255.0);
++	  pipeRun(&pipe);
++	  updateModX(xDest + x);
++	  updateModY(yDest + y);
++	} else {
++	  pipeIncX(&pipe);
++	}
++      }
++    }
++  } else {
++    pipeInit(&pipe, xDest, yDest, NULL, pixel, state->fillAlpha,
++	     gFalse, nonIsolated);
++    for (y = 0; y < h; ++y) {
++      pipeSetXY(&pipe, xDest, yDest + y);
++      for (x = 0; x < w; ++x) {
++	src->getPixel(xSrc + x, ySrc + y, pixel);
++	if (noClip || state->clip->test(xDest + x, yDest + y)) {
++	  pipeRun(&pipe);
++	  updateModX(xDest + x);
++	  updateModY(yDest + y);
++	} else {
++	  pipeIncX(&pipe);
++	}
++      }
++    }
++  }
++
++  return splashOk;
++}
++
++void Splash::compositeBackground(SplashColorPtr color) {
++  SplashColorPtr p;
++  Guchar *q;
++  Guchar alpha, alpha1, c, color0, color1, color2, color3;
++  int x, y, mask;
++
++  switch (bitmap->mode) {
++  case splashModeMono1:
++    color0 = color[0];
++    for (y = 0; y < bitmap->height; ++y) {
++      p = &bitmap->data[y * bitmap->rowSize];
++      q = &bitmap->alpha[y * bitmap->width];
++      mask = 0x80;
++      for (x = 0; x < bitmap->width; ++x) {
++	alpha = *q++;
++	alpha1 = 255 - alpha;
++	c = (*p & mask) ? 0xff : 0x00;
++	c = div255(alpha1 * color0 + alpha * c);
++	if (c & 0x80) {
++	  *p |= mask;
++	} else {
++	  *p &= ~mask;
++	}
++	if (!(mask >>= 1)) {
++	  mask = 0x80;
++	  ++p;
++	}
++      }
++    }
++    break;
++  case splashModeMono8:
++    color0 = color[0];
++    for (y = 0; y < bitmap->height; ++y) {
++      p = &bitmap->data[y * bitmap->rowSize];
++      q = &bitmap->alpha[y * bitmap->width];
++      for (x = 0; x < bitmap->width; ++x) {
++	alpha = *q++;
++	alpha1 = 255 - alpha;
++	p[0] = div255(alpha1 * color0 + alpha * p[0]);
++	++p;
++      }
++    }
++    break;
++  case splashModeRGB8:
++  case splashModeBGR8:
++    color0 = color[0];
++    color1 = color[1];
++    color2 = color[2];
++    for (y = 0; y < bitmap->height; ++y) {
++      p = &bitmap->data[y * bitmap->rowSize];
++      q = &bitmap->alpha[y * bitmap->width];
++      for (x = 0; x < bitmap->width; ++x) {
++	alpha = *q++;
++	alpha1 = 255 - alpha;
++	p[0] = div255(alpha1 * color0 + alpha * p[0]);
++	p[1] = div255(alpha1 * color1 + alpha * p[1]);
++	p[2] = div255(alpha1 * color2 + alpha * p[2]);
++	p += 3;
++      }
++    }
++    break;
++#if SPLASH_CMYK
++  case splashModeCMYK8:
++    color0 = color[0];
++    color1 = color[1];
++    color2 = color[2];
++    color3 = color[3];
++    for (y = 0; y < bitmap->height; ++y) {
++      p = &bitmap->data[y * bitmap->rowSize];
++      q = &bitmap->alpha[y * bitmap->width];
++      for (x = 0; x < bitmap->width; ++x) {
++	alpha = *q++;
++	alpha1 = 255 - alpha;
++	p[0] = div255(alpha1 * color0 + alpha * p[0]);
++	p[1] = div255(alpha1 * color1 + alpha * p[1]);
++	p[2] = div255(alpha1 * color2 + alpha * p[2]);
++	p[3] = div255(alpha1 * color3 + alpha * p[3]);
++	p += 4;
++      }
++    }
++    break;
++#endif
++  }
++  memset(bitmap->alpha, 255, bitmap->width * bitmap->height);
++}
++
++SplashError Splash::blitTransparent(SplashBitmap *src, int xSrc, int ySrc,
++				    int xDest, int yDest, int w, int h) {
++  SplashColor pixel;
++  SplashColorPtr p;
++  Guchar *q;
++  int x, y, mask;
++
++  if (src->mode != bitmap->mode) {
++    return splashErrModeMismatch;
++  }
++
++  switch (bitmap->mode) {
++  case splashModeMono1:
++    for (y = 0; y < h; ++y) {
++      p = &bitmap->data[(yDest + y) * bitmap->rowSize + (xDest >> 3)];
++      mask = 0x80 >> (xDest & 7);
++      for (x = 0; x < w; ++x) {
++	src->getPixel(xSrc + x, ySrc + y, pixel);
++	if (pixel[0]) {
++	  *p |= mask;
++	} else {
++	  *p &= ~mask;
++	}
++	if (!(mask >>= 1)) {
++	  mask = 0x80;
++	  ++p;
++	}
++      }
++    }
++    break;
++  case splashModeMono8:
++    for (y = 0; y < h; ++y) {
++      p = &bitmap->data[(yDest + y) * bitmap->rowSize + xDest];
++      for (x = 0; x < w; ++x) {
++	src->getPixel(xSrc + x, ySrc + y, pixel);
++	*p++ = pixel[0];
++      }
++    }
++    break;
++  case splashModeRGB8:
++  case splashModeBGR8:
++    for (y = 0; y < h; ++y) {
++      p = &bitmap->data[(yDest + y) * bitmap->rowSize + 3 * xDest];
++      for (x = 0; x < w; ++x) {
++	src->getPixel(xSrc + x, ySrc + y, pixel);
++	*p++ = pixel[0];
++	*p++ = pixel[1];
++	*p++ = pixel[2];
++      }
++    }
++    break;
++#if SPLASH_CMYK
++  case splashModeCMYK8:
++    for (y = 0; y < h; ++y) {
++      p = &bitmap->data[(yDest + y) * bitmap->rowSize + 4 * xDest];
++      for (x = 0; x < w; ++x) {
++	src->getPixel(xSrc + x, ySrc + y, pixel);
++	*p++ = pixel[0];
++	*p++ = pixel[1];
++	*p++ = pixel[2];
++	*p++ = pixel[3];
++      }
++    }
++    break;
++#endif
++  }
++
++  if (bitmap->alpha) {
++    for (y = 0; y < h; ++y) {
++      q = &bitmap->alpha[(yDest + y) * bitmap->width + xDest];
++      for (x = 0; x < w; ++x) {
++	*q++ = 0x00;
++      }
++    }
++  }
++
++  return splashOk;
++}
++
++SplashPath *Splash::makeStrokePath(SplashPath *path, GBool flatten) {
++  SplashPath *pathIn, *pathOut;
++  SplashCoord w, d, dx, dy, wdx, wdy, dxNext, dyNext, wdxNext, wdyNext;
++  SplashCoord crossprod, dotprod, miter, m;
++  GBool first, last, closed;
++  int subpathStart, next, i;
++  int left0, left1, left2, right0, right1, right2, join0, join1, join2;
++  int leftFirst, rightFirst, firstPt;
++
++  if (flatten) {
++    pathIn = flattenPath(path, state->matrix, state->flatness);
++    if (state->lineDashLength > 0) {
++      pathOut = makeDashedPath(pathIn);
++      delete pathIn;
++      pathIn = pathOut;
++    }
++  } else {
++    pathIn = path;
++  }
++
++  subpathStart = 0; // make gcc happy
++  closed = gFalse; // make gcc happy
++  left0 = left1 = right0 = right1 = join0 = join1 = 0; // make gcc happy
++  leftFirst = rightFirst = firstPt = 0; // make gcc happy
++
++  pathOut = new SplashPath();
++  w = state->lineWidth;
++
++  for (i = 0; i < pathIn->length - 1; ++i) {
++    if (pathIn->flags[i] & splashPathLast) {
++      continue;
++    }
++    if ((first = pathIn->flags[i] & splashPathFirst)) {
++      subpathStart = i;
++      closed = pathIn->flags[i] & splashPathClosed;
++    }
++    last = pathIn->flags[i+1] & splashPathLast;
++
++    // compute the deltas for segment (i, i+1)
++    d = splashDist(pathIn->pts[i].x, pathIn->pts[i].y,
++		   pathIn->pts[i+1].x, pathIn->pts[i+1].y);
++    if (d == 0) {
++      // we need to draw end caps on zero-length lines
++      //~ not clear what the behavior should be for splashLineCapButt
++      //~   with d==0
++      dx = 0;
++      dy = 1;
++    } else {
++      d = (SplashCoord)1 / d;
++      dx = d * (pathIn->pts[i+1].x - pathIn->pts[i].x);
++      dy = d * (pathIn->pts[i+1].y - pathIn->pts[i].y);
++    }
++    wdx = (SplashCoord)0.5 * w * dx;
++    wdy = (SplashCoord)0.5 * w * dy;
++
++    // compute the deltas for segment (i+1, next)
++    next = last ? subpathStart + 1 : i + 2;
++    d = splashDist(pathIn->pts[i+1].x, pathIn->pts[i+1].y,
++		   pathIn->pts[next].x, pathIn->pts[next].y);
++    if (d == 0) {
++      // we need to draw end caps on zero-length lines
++      //~ not clear what the behavior should be for splashLineCapButt
++      //~   with d==0
++      dxNext = 0;
++      dyNext = 1;
++    } else {
++      d = (SplashCoord)1 / d;
++      dxNext = d * (pathIn->pts[next].x - pathIn->pts[i+1].x);
++      dyNext = d * (pathIn->pts[next].y - pathIn->pts[i+1].y);
++    }
++    wdxNext = (SplashCoord)0.5 * w * dxNext;
++    wdyNext = (SplashCoord)0.5 * w * dyNext;
++
++    // draw the start cap
++    pathOut->moveTo(pathIn->pts[i].x - wdy, pathIn->pts[i].y + wdx);
++    if (i == subpathStart) {
++      firstPt = pathOut->length - 1;
++    }
++    if (first && !closed) {
++      switch (state->lineCap) {
++      case splashLineCapButt:
++	pathOut->lineTo(pathIn->pts[i].x + wdy, pathIn->pts[i].y - wdx);
++	break;
++      case splashLineCapRound:
++	pathOut->curveTo(pathIn->pts[i].x - wdy - bezierCircle * wdx,
++			 pathIn->pts[i].y + wdx - bezierCircle * wdy,
++			 pathIn->pts[i].x - wdx - bezierCircle * wdy,
++			 pathIn->pts[i].y - wdy + bezierCircle * wdx,
++			 pathIn->pts[i].x - wdx,
++			 pathIn->pts[i].y - wdy);
++	pathOut->curveTo(pathIn->pts[i].x - wdx + bezierCircle * wdy,
++			 pathIn->pts[i].y - wdy - bezierCircle * wdx,
++			 pathIn->pts[i].x + wdy - bezierCircle * wdx,
++			 pathIn->pts[i].y - wdx - bezierCircle * wdy,
++			 pathIn->pts[i].x + wdy,
++			 pathIn->pts[i].y - wdx);
++	break;
++      case splashLineCapProjecting:
++	pathOut->lineTo(pathIn->pts[i].x - wdx - wdy,
++			pathIn->pts[i].y + wdx - wdy);
++	pathOut->lineTo(pathIn->pts[i].x - wdx + wdy,
++			pathIn->pts[i].y - wdx - wdy);
++	pathOut->lineTo(pathIn->pts[i].x + wdy,
++			pathIn->pts[i].y - wdx);
++	break;
++      }
++    } else {
++      pathOut->lineTo(pathIn->pts[i].x + wdy, pathIn->pts[i].y - wdx);
++    }
++
++    // draw the left side of the segment rectangle
++    left2 = pathOut->length - 1;
++    pathOut->lineTo(pathIn->pts[i+1].x + wdy, pathIn->pts[i+1].y - wdx);
++
++    // draw the end cap
++    if (last && !closed) {
++      switch (state->lineCap) {
++      case splashLineCapButt:
++	pathOut->lineTo(pathIn->pts[i+1].x - wdy, pathIn->pts[i+1].y + wdx);
++	break;
++      case splashLineCapRound:
++	pathOut->curveTo(pathIn->pts[i+1].x + wdy + bezierCircle * wdx,
++			 pathIn->pts[i+1].y - wdx + bezierCircle * wdy,
++			 pathIn->pts[i+1].x + wdx + bezierCircle * wdy,
++			 pathIn->pts[i+1].y + wdy - bezierCircle * wdx,
++			 pathIn->pts[i+1].x + wdx,
++			 pathIn->pts[i+1].y + wdy);
++	pathOut->curveTo(pathIn->pts[i+1].x + wdx - bezierCircle * wdy,
++			 pathIn->pts[i+1].y + wdy + bezierCircle * wdx,
++			 pathIn->pts[i+1].x - wdy + bezierCircle * wdx,
++			 pathIn->pts[i+1].y + wdx + bezierCircle * wdy,
++			 pathIn->pts[i+1].x - wdy,
++			 pathIn->pts[i+1].y + wdx);
++	break;
++      case splashLineCapProjecting:
++	pathOut->lineTo(pathIn->pts[i+1].x + wdy + wdx,
++			pathIn->pts[i+1].y - wdx + wdy);
++	pathOut->lineTo(pathIn->pts[i+1].x - wdy + wdx,
++			pathIn->pts[i+1].y + wdx + wdy);
++	pathOut->lineTo(pathIn->pts[i+1].x - wdy,
++			pathIn->pts[i+1].y + wdx);
++	break;
++      }
++    } else {
++      pathOut->lineTo(pathIn->pts[i+1].x - wdy, pathIn->pts[i+1].y + wdx);
++    }
++
++    // draw the right side of the segment rectangle
++    right2 = pathOut->length - 1;
++    pathOut->close();
++
++    // draw the join
++    join2 = pathOut->length;
++    if (!last || closed) {
++      crossprod = dx * dyNext - dy * dxNext;
++      dotprod = -(dx * dxNext + dy * dyNext);
++      if (dotprod > 0.99999) {
++	// avoid a divide-by-zero -- set miter to something arbitrary
++	// such that sqrt(miter) will exceed miterLimit (and m is never
++	// used in that situation)
++	miter = (state->miterLimit + 1) * (state->miterLimit + 1);
++	m = 0;
++      } else {
++	miter = (SplashCoord)2 / ((SplashCoord)1 - dotprod);
++	if (miter < 1) {
++	  // this can happen because of floating point inaccuracies
++	  miter = 1;
++	}
++	m = splashSqrt(miter - 1);
++      }
++
++      // round join
++      if (state->lineJoin == splashLineJoinRound) {
++	pathOut->moveTo(pathIn->pts[i+1].x + (SplashCoord)0.5 * w,
++			pathIn->pts[i+1].y);
++	pathOut->curveTo(pathIn->pts[i+1].x + (SplashCoord)0.5 * w,
++			 pathIn->pts[i+1].y + bezierCircle2 * w,
++			 pathIn->pts[i+1].x + bezierCircle2 * w,
++			 pathIn->pts[i+1].y + (SplashCoord)0.5 * w,
++			 pathIn->pts[i+1].x,
++			 pathIn->pts[i+1].y + (SplashCoord)0.5 * w);
++	pathOut->curveTo(pathIn->pts[i+1].x - bezierCircle2 * w,
++			 pathIn->pts[i+1].y + (SplashCoord)0.5 * w,
++			 pathIn->pts[i+1].x - (SplashCoord)0.5 * w,
++			 pathIn->pts[i+1].y + bezierCircle2 * w,
++			 pathIn->pts[i+1].x - (SplashCoord)0.5 * w,
++			 pathIn->pts[i+1].y);
++	pathOut->curveTo(pathIn->pts[i+1].x - (SplashCoord)0.5 * w,
++			 pathIn->pts[i+1].y - bezierCircle2 * w,
++			 pathIn->pts[i+1].x - bezierCircle2 * w,
++			 pathIn->pts[i+1].y - (SplashCoord)0.5 * w,
++			 pathIn->pts[i+1].x,
++			 pathIn->pts[i+1].y - (SplashCoord)0.5 * w);
++	pathOut->curveTo(pathIn->pts[i+1].x + bezierCircle2 * w,
++			 pathIn->pts[i+1].y - (SplashCoord)0.5 * w,
++			 pathIn->pts[i+1].x + (SplashCoord)0.5 * w,
++			 pathIn->pts[i+1].y - bezierCircle2 * w,
++			 pathIn->pts[i+1].x + (SplashCoord)0.5 * w,
++			 pathIn->pts[i+1].y);
++
++      } else {
++	pathOut->moveTo(pathIn->pts[i+1].x, pathIn->pts[i+1].y);
++
++	// angle < 180
++	if (crossprod < 0) {
++	  pathOut->lineTo(pathIn->pts[i+1].x - wdyNext,
++			  pathIn->pts[i+1].y + wdxNext);
++	  // miter join inside limit
++	  if (state->lineJoin == splashLineJoinMiter &&
++	      splashSqrt(miter) <= state->miterLimit) {
++	    pathOut->lineTo(pathIn->pts[i+1].x - wdy + wdx * m,
++			    pathIn->pts[i+1].y + wdx + wdy * m);
++	    pathOut->lineTo(pathIn->pts[i+1].x - wdy,
++			    pathIn->pts[i+1].y + wdx);
++	  // bevel join or miter join outside limit
++	  } else {
++	    pathOut->lineTo(pathIn->pts[i+1].x - wdy, pathIn->pts[i+1].y + wdx);
++	  }
++
++	// angle >= 180
++	} else {
++	  pathOut->lineTo(pathIn->pts[i+1].x + wdy,
++			  pathIn->pts[i+1].y - wdx);
++	  // miter join inside limit
++	  if (state->lineJoin == splashLineJoinMiter &&
++	      splashSqrt(miter) <= state->miterLimit) {
++	    pathOut->lineTo(pathIn->pts[i+1].x + wdy + wdx * m,
++			    pathIn->pts[i+1].y - wdx + wdy * m);
++	    pathOut->lineTo(pathIn->pts[i+1].x + wdyNext,
++			    pathIn->pts[i+1].y - wdxNext);
++	  // bevel join or miter join outside limit
++	  } else {
++	    pathOut->lineTo(pathIn->pts[i+1].x + wdyNext,
++			    pathIn->pts[i+1].y - wdxNext);
++	  }
++	}
++      }
++
++      pathOut->close();
++    }
++
++    // add stroke adjustment hints
++    if (state->strokeAdjust) {
++      if (i >= subpathStart + 1) {
++	if (i >= subpathStart + 2) {
++	  pathOut->addStrokeAdjustHint(left1, right1, left0 + 1, right0);
++	  pathOut->addStrokeAdjustHint(left1, right1, join0, left2);
++	} else {
++	  pathOut->addStrokeAdjustHint(left1, right1, firstPt, left2);
++	}
++	pathOut->addStrokeAdjustHint(left1, right1, right2 + 1, right2 + 1);
++      }
++      left0 = left1;
++      left1 = left2;
++      right0 = right1;
++      right1 = right2;
++      join0 = join1;
++      join1 = join2;
++      if (i == subpathStart) {
++	leftFirst = left2;
++	rightFirst = right2;
++      }
++      if (last) {
++	if (i >= subpathStart + 2) {
++	  pathOut->addStrokeAdjustHint(left1, right1, left0 + 1, right0);
++	  pathOut->addStrokeAdjustHint(left1, right1,
++				       join0, pathOut->length - 1);
++	} else {
++	  pathOut->addStrokeAdjustHint(left1, right1,
++				       firstPt, pathOut->length - 1);
++	}
++	if (closed) {
++	  pathOut->addStrokeAdjustHint(left1, right1, firstPt, leftFirst);
++	  pathOut->addStrokeAdjustHint(left1, right1,
++				       rightFirst + 1, rightFirst + 1);
++	  pathOut->addStrokeAdjustHint(leftFirst, rightFirst,
++				       left1 + 1, right1);
++	  pathOut->addStrokeAdjustHint(leftFirst, rightFirst,
++				       join1, pathOut->length - 1);
++	}
++      }
++    }
++  }
++
++  if (pathIn != path) {
++    delete pathIn;
++  }
++
++  return pathOut;
++}
++
++void Splash::dumpPath(SplashPath *path) {
++  int i;
++
++  for (i = 0; i < path->length; ++i) {
++    printf("  %3d: x=%8.2f y=%8.2f%s%s%s%s\n",
++	   i, (double)path->pts[i].x, (double)path->pts[i].y,
++	   (path->flags[i] & splashPathFirst) ? " first" : "",
++	   (path->flags[i] & splashPathLast) ? " last" : "",
++	   (path->flags[i] & splashPathClosed) ? " closed" : "",
++	   (path->flags[i] & splashPathCurve) ? " curve" : "");
++  }
++}
++
++void Splash::dumpXPath(SplashXPath *path) {
++  int i;
++
++  for (i = 0; i < path->length; ++i) {
++    printf("  %4d: x0=%8.2f y0=%8.2f x1=%8.2f y1=%8.2f %s%s%s%s%s%s%s\n",
++	   i, (double)path->segs[i].x0, (double)path->segs[i].y0,
++	   (double)path->segs[i].x1, (double)path->segs[i].y1,
++	   (path->segs[i].flags	& splashXPathFirst) ? "F" : " ",
++	   (path->segs[i].flags	& splashXPathLast) ? "L" : " ",
++	   (path->segs[i].flags	& splashXPathEnd0) ? "0" : " ",
++	   (path->segs[i].flags	& splashXPathEnd1) ? "1" : " ",
++	   (path->segs[i].flags	& splashXPathHoriz) ? "H" : " ",
++	   (path->segs[i].flags	& splashXPathVert) ? "V" : " ",
++	   (path->segs[i].flags	& splashXPathFlip) ? "P" : " ");
++  }
++}
+diff -Naur xpdf.orig/splash/SplashBitmap.cc xpdf/splash/SplashBitmap.cc
+--- xpdf.orig/splash/SplashBitmap.cc	2010-03-22 23:55:22.000000000 +0900
++++ xpdf/splash/SplashBitmap.cc	2010-03-23 00:14:18.000000000 +0900
+@@ -11,6 +11,7 @@
+ #endif
+ 
+ #include <stdio.h>
++#include <limits.h>
+ #include "gmem.h"
+ #include "SplashErrorCodes.h"
+ #include "SplashBitmap.h"
+@@ -27,30 +28,48 @@
+   mode = modeA;
+   switch (mode) {
+   case splashModeMono1:
+-    rowSize = (width + 7) >> 3;
++    if (width > 0) {
++      rowSize = (width + 7) >> 3;
++    } else {
++      rowSize = -1;
++    }
+     break;
+   case splashModeMono8:
+-    rowSize = width;
++    if (width > 0) {
++      rowSize = width;
++    } else {
++      rowSize = -1;
++    }
+     break;
+   case splashModeRGB8:
+   case splashModeBGR8:
+-    rowSize = width * 3;
++    if (width > 0 && width <= INT_MAX / 3) {
++      rowSize = width * 3;
++    } else {
++      rowSize = -1;
++    }
+     break;
+ #if SPLASH_CMYK
+   case splashModeCMYK8:
+-    rowSize = width * 4;
++    if (width > 0 && width <= INT_MAX / 4) {
++      rowSize = width * 4;
++    } else {
++      rowSize = -1;
++    }
+     break;
+ #endif
+   }
+-  rowSize += rowPad - 1;
+-  rowSize -= rowSize % rowPad;
+-  data = (SplashColorPtr)gmalloc(rowSize * height);
++  if (rowSize > 0) {
++    rowSize += rowPad - 1;
++    rowSize -= rowSize % rowPad;
++  }
++  data = (SplashColorPtr)gmallocn(height, rowSize);
+   if (!topDown) {
+     data += (height - 1) * rowSize;
+     rowSize = -rowSize;
+   }
+   if (alphaA) {
+-    alpha = (Guchar *)gmalloc(width * height);
++    alpha = (Guchar *)gmallocn(width, height);
+   } else {
+     alpha = NULL;
+   }
+diff -Naur xpdf.orig/splash/SplashErrorCodes.h xpdf/splash/SplashErrorCodes.h
+--- xpdf.orig/splash/SplashErrorCodes.h	2010-03-22 23:55:22.000000000 +0900
++++ xpdf/splash/SplashErrorCodes.h	2010-03-23 00:14:18.000000000 +0900
+@@ -31,4 +31,6 @@
+ 
+ #define splashErrZeroImage       9      // image of 0x0
+ 
++#define splashErrBadArg          9	// bad argument
++
+ #endif
+diff -Naur xpdf.orig/splash/SplashErrorCodes.h.orig xpdf/splash/SplashErrorCodes.h.orig
+--- xpdf.orig/splash/SplashErrorCodes.h.orig	1970-01-01 09:00:00.000000000 +0900
++++ xpdf/splash/SplashErrorCodes.h.orig	2010-03-23 00:14:09.000000000 +0900
+@@ -0,0 +1,34 @@
++//========================================================================
++//
++// SplashErrorCodes.h
++//
++//========================================================================
++
++#ifndef SPLASHERRORCODES_H
++#define SPLASHERRORCODES_H
++
++#include <aconf.h>
++
++//------------------------------------------------------------------------
++
++#define splashOk                 0	// no error
++
++#define splashErrNoCurPt         1	// no current point
++
++#define splashErrEmptyPath       2	// zero points in path
++
++#define splashErrBogusPath       3	// only one point in subpath
++
++#define splashErrNoSave	         4	// state stack is empty
++
++#define splashErrOpenFile        5	// couldn't open file
++
++#define splashErrNoGlyph         6	// couldn't get the requested glyph
++
++#define splashErrModeMismatch    7	// invalid combination of color modes
++
++#define splashErrSingularMatrix  8	// matrix is singular
++
++#define splashErrZeroImage       9      // image of 0x0
++
++#endif
+diff -Naur xpdf.orig/xpdf/PSOutputDev.cc xpdf/xpdf/PSOutputDev.cc
+--- xpdf.orig/xpdf/PSOutputDev.cc	2010-03-22 23:55:22.000000000 +0900
++++ xpdf/xpdf/PSOutputDev.cc	2010-03-23 00:14:18.000000000 +0900
+@@ -4386,7 +4386,7 @@
+ 	     width, -height, height);
+ 
+   // allocate a line buffer
+-  lineBuf = (Guchar *)gmalloc(4 * width);
++  lineBuf = (Guchar *)gmallocn(width, 4);
+ 
+   // set up to process the data stream
+   imgStr = new ImageStream(str, width, colorMap->getNumPixelComps(),
+diff -Naur xpdf.orig/xpdf/PSOutputDev.cc.orig xpdf/xpdf/PSOutputDev.cc.orig
+--- xpdf.orig/xpdf/PSOutputDev.cc.orig	1970-01-01 09:00:00.000000000 +0900
++++ xpdf/xpdf/PSOutputDev.cc.orig	2010-03-23 00:14:09.000000000 +0900
+@@ -0,0 +1,6307 @@
++//========================================================================
++//
++// PSOutputDev.cc
++//
++// Copyright 1996-2003 Glyph & Cog, LLC
++//
++//========================================================================
++
++#include <aconf.h>
++#include <locale.h>
++
++#ifdef USE_GCC_PRAGMAS
++#pragma implementation
++#endif
++
++#include <stdio.h>
++#include <stddef.h>
++#include <stdarg.h>
++#include <signal.h>
++#include <math.h>
++#include "GString.h"
++#include "GList.h"
++#include "config.h"
++#include "GlobalParams.h"
++#include "Object.h"
++#include "Error.h"
++#include "Function.h"
++#include "Gfx.h"
++#include "GfxState.h"
++#include "GfxFont.h"
++#include "UnicodeMap.h"
++#include "FoFiType1C.h"
++#include "FoFiTrueType.h"
++#include "Catalog.h"
++#include "Page.h"
++#include "Stream.h"
++#include "Annot.h"
++#include "XRef.h"
++#include "PreScanOutputDev.h"
++#if HAVE_SPLASH
++#  include "Splash.h"
++#  include "SplashBitmap.h"
++#  include "SplashOutputDev.h"
++#endif
++#include "PSOutputDev.h"
++
++#ifdef MACOS
++// needed for setting type/creator of MacOS files
++#include "ICSupport.h"
++#endif
++
++// the MSVC math.h doesn't define this
++#ifndef M_PI
++#define M_PI 3.14159265358979323846
++#endif
++
++//------------------------------------------------------------------------
++
++// Resolution at which pages with transparency will be rasterized.
++#define splashDPI 300
++
++//------------------------------------------------------------------------
++// PostScript prolog and setup
++//------------------------------------------------------------------------
++
++// The '~' escapes mark prolog code that is emitted only in certain
++// levels:
++//
++//   ~[123][sn]
++//      ^   ^----- s=psLevel*Sep, n=psLevel*
++//      +----- 1=psLevel1*, 2=psLevel2*, 3=psLevel3*
++
++static char *prolog[] = {
++  "/xpdf 75 dict def xpdf begin",
++  "% PDF special state",
++  "/pdfDictSize 15 def",
++  "~1sn",
++  "/pdfStates 64 array def",
++  "  0 1 63 {",
++  "    pdfStates exch pdfDictSize dict",
++  "    dup /pdfStateIdx 3 index put",
++  "    put",
++  "  } for",
++  "~123sn",
++  "/pdfSetup {",
++  "  3 1 roll 2 array astore",
++  "  /setpagedevice where {",
++  "    pop 3 dict begin",
++  "      /PageSize exch def",
++  "      /ImagingBBox null def",
++  "      /Policies 1 dict dup begin /PageSize 3 def end def",
++  "      { /Duplex true def } if",
++  "    currentdict end setpagedevice",
++  "  } {",
++  "    pop pop",
++  "  } ifelse",
++  "} def",
++  "~1sn",
++  "/pdfOpNames [",
++  "  /pdfFill /pdfStroke /pdfLastFill /pdfLastStroke",
++  "  /pdfTextMat /pdfFontSize /pdfCharSpacing /pdfTextRender",
++  "  /pdfTextRise /pdfWordSpacing /pdfHorizScaling /pdfTextClipPath",
++  "] def",
++  "~123sn",
++  "/pdfStartPage {",
++  "~1sn",
++  "  pdfStates 0 get begin",
++  "~23sn",
++  "  pdfDictSize dict begin",
++  "~23n",
++  "  /pdfFillCS [] def",
++  "  /pdfFillXform {} def",
++  "  /pdfStrokeCS [] def",
++  "  /pdfStrokeXform {} def",
++  "~1n",
++  "  /pdfFill 0 def",
++  "  /pdfStroke 0 def",
++  "~1s",
++  "  /pdfFill [0 0 0 1] def",
++  "  /pdfStroke [0 0 0 1] def",
++  "~23sn",
++  "  /pdfFill [0] def",
++  "  /pdfStroke [0] def",
++  "  /pdfFillOP false def",
++  "  /pdfStrokeOP false def",
++  "~123sn",
++  "  /pdfLastFill false def",
++  "  /pdfLastStroke false def",
++  "  /pdfTextMat [1 0 0 1 0 0] def",
++  "  /pdfFontSize 0 def",
++  "  /pdfCharSpacing 0 def",
++  "  /pdfTextRender 0 def",
++  "  /pdfTextRise 0 def",
++  "  /pdfWordSpacing 0 def",
++  "  /pdfHorizScaling 1 def",
++  "  /pdfTextClipPath [] def",
++  "} def",
++  "/pdfEndPage { end } def",
++  "~23s",
++  "% separation convention operators",
++  "/findcmykcustomcolor where {",
++  "  pop",
++  "}{",
++  "  /findcmykcustomcolor { 5 array astore } def",
++  "} ifelse",
++  "/setcustomcolor where {",
++  "  pop",
++  "}{",
++  "  /setcustomcolor {",
++  "    exch",
++  "    [ exch /Separation exch dup 4 get exch /DeviceCMYK exch",
++  "      0 4 getinterval cvx",
++  "      [ exch /dup load exch { mul exch dup } /forall load",
++  "        /pop load dup ] cvx",
++  "    ] setcolorspace setcolor",
++  "  } def",
++  "} ifelse",
++  "/customcolorimage where {",
++  "  pop",
++  "}{",
++  "  /customcolorimage {",
++  "    gsave",
++  "    [ exch /Separation exch dup 4 get exch /DeviceCMYK exch",
++  "      0 4 getinterval",
++  "      [ exch /dup load exch { mul exch dup } /forall load",
++  "        /pop load dup ] cvx",
++  "    ] setcolorspace",
++  "    10 dict begin",
++  "      /ImageType 1 def",
++  "      /DataSource exch def",
++  "      /ImageMatrix exch def",
++  "      /BitsPerComponent exch def",
++  "      /Height exch def",
++  "      /Width exch def",
++  "      /Decode [1 0] def",
++  "    currentdict end",
++  "    image",
++  "    grestore",
++  "  } def",
++  "} ifelse",
++  "~123sn",
++  "% PDF color state",
++  "~1n",
++  "/g { dup /pdfFill exch def setgray",
++  "     /pdfLastFill true def /pdfLastStroke false def } def",
++  "/G { dup /pdfStroke exch def setgray",
++  "     /pdfLastStroke true def /pdfLastFill false def } def",
++  "/fCol {",
++  "  pdfLastFill not {",
++  "    pdfFill setgray",
++  "    /pdfLastFill true def /pdfLastStroke false def",
++  "  } if",
++  "} def",
++  "/sCol {",
++  "  pdfLastStroke not {",
++  "    pdfStroke setgray",
++  "    /pdfLastStroke true def /pdfLastFill false def",
++  "  } if",
++  "} def",
++  "~1s",
++  "/k { 4 copy 4 array astore /pdfFill exch def setcmykcolor",
++  "     /pdfLastFill true def /pdfLastStroke false def } def",
++  "/K { 4 copy 4 array astore /pdfStroke exch def setcmykcolor",
++  "     /pdfLastStroke true def /pdfLastFill false def } def",
++  "/fCol {",
++  "  pdfLastFill not {",
++  "    pdfFill aload pop setcmykcolor",
++  "    /pdfLastFill true def /pdfLastStroke false def",
++  "  } if",
++  "} def",
++  "/sCol {",
++  "  pdfLastStroke not {",
++  "    pdfStroke aload pop setcmykcolor",
++  "    /pdfLastStroke true def /pdfLastFill false def",
++  "  } if",
++  "} def",
++  "~23n",
++  "/cs { /pdfFillXform exch def dup /pdfFillCS exch def",
++  "      setcolorspace } def",
++  "/CS { /pdfStrokeXform exch def dup /pdfStrokeCS exch def",
++  "      setcolorspace } def",
++  "/sc { pdfLastFill not { pdfFillCS setcolorspace } if",
++  "      dup /pdfFill exch def aload pop pdfFillXform setcolor",
++  "     /pdfLastFill true def /pdfLastStroke false def } def",
++  "/SC { pdfLastStroke not { pdfStrokeCS setcolorspace } if",
++  "      dup /pdfStroke exch def aload pop pdfStrokeXform setcolor",
++  "     /pdfLastStroke true def /pdfLastFill false def } def",
++  "/op { /pdfFillOP exch def",
++  "      pdfLastFill { pdfFillOP setoverprint } if } def",
++  "/OP { /pdfStrokeOP exch def",
++  "      pdfLastStroke { pdfStrokeOP setoverprint } if } def",
++  "/fCol {",
++  "  pdfLastFill not {",
++  "    pdfFillCS setcolorspace",
++  "    pdfFill aload pop pdfFillXform setcolor",
++  "    pdfFillOP setoverprint",
++  "    /pdfLastFill true def /pdfLastStroke false def",
++  "  } if",
++  "} def",
++  "/sCol {",
++  "  pdfLastStroke not {",
++  "    pdfStrokeCS setcolorspace",
++  "    pdfStroke aload pop pdfStrokeXform setcolor",
++  "    pdfStrokeOP setoverprint",
++  "    /pdfLastStroke true def /pdfLastFill false def",
++  "  } if",
++  "} def",
++  "~23s",
++  "/k { 4 copy 4 array astore /pdfFill exch def setcmykcolor",
++  "     /pdfLastFill true def /pdfLastStroke false def } def",
++  "/K { 4 copy 4 array astore /pdfStroke exch def setcmykcolor",
++  "     /pdfLastStroke true def /pdfLastFill false def } def",
++  "/ck { 6 copy 6 array astore /pdfFill exch def",
++  "      findcmykcustomcolor exch setcustomcolor",
++  "      /pdfLastFill true def /pdfLastStroke false def } def",
++  "/CK { 6 copy 6 array astore /pdfStroke exch def",
++  "      findcmykcustomcolor exch setcustomcolor",
++  "      /pdfLastStroke true def /pdfLastFill false def } def",
++  "/op { /pdfFillOP exch def",
++  "      pdfLastFill { pdfFillOP setoverprint } if } def",
++  "/OP { /pdfStrokeOP exch def",
++  "      pdfLastStroke { pdfStrokeOP setoverprint } if } def",
++  "/fCol {",
++  "  pdfLastFill not {",
++  "    pdfFill aload length 4 eq {",
++  "      setcmykcolor",
++  "    }{",
++  "      findcmykcustomcolor exch setcustomcolor",
++  "    } ifelse",
++  "    pdfFillOP setoverprint",
++  "    /pdfLastFill true def /pdfLastStroke false def",
++  "  } if",
++  "} def",
++  "/sCol {",
++  "  pdfLastStroke not {",
++  "    pdfStroke aload length 4 eq {",
++  "      setcmykcolor",
++  "    }{",
++  "      findcmykcustomcolor exch setcustomcolor",
++  "    } ifelse",
++  "    pdfStrokeOP setoverprint",
++  "    /pdfLastStroke true def /pdfLastFill false def",
++  "  } if",
++  "} def",
++  "~123sn",
++  "% build a font",
++  "/pdfMakeFont {",
++  "  4 3 roll findfont",
++  "  4 2 roll matrix scale makefont",
++  "  dup length dict begin",
++  "    { 1 index /FID ne { def } { pop pop } ifelse } forall",
++  "    /Encoding exch def",
++  "    currentdict",
++  "  end",
++  "  definefont pop",
++  "} def",
++  "/pdfMakeFont16 {",
++  "  exch findfont",
++  "  dup length dict begin",
++  "    { 1 index /FID ne { def } { pop pop } ifelse } forall",
++  "    /WMode exch def",
++  "    currentdict",
++  "  end",
++  "  definefont pop",
++  "} def",
++  "~3sn",
++  "/pdfMakeFont16L3 {",
++  "  1 index /CIDFont resourcestatus {",
++  "    pop pop 1 index /CIDFont findresource /CIDFontType known",
++  "  } {",
++  "    false",
++  "  } ifelse",
++  "  {",
++  "    0 eq { /Identity-H } { /Identity-V } ifelse",
++  "    exch 1 array astore composefont pop",
++  "  } {",
++  "    pdfMakeFont16",
++  "  } ifelse",
++  "} def",
++  "~123sn",
++  "% graphics state operators",
++  "~1sn",
++  "/q {",
++  "  gsave",
++  "  pdfOpNames length 1 sub -1 0 { pdfOpNames exch get load } for",
++  "  pdfStates pdfStateIdx 1 add get begin",
++  "  pdfOpNames { exch def } forall",
++  "} def",
++  "/Q { end grestore } def",
++  "~23sn",
++  "/q { gsave pdfDictSize dict begin } def",
++  "/Q {",
++  "  end grestore",
++  "  /pdfLastFill where {",
++  "    pop",
++  "    pdfLastFill {",
++  "      pdfFillOP setoverprint",
++  "    } {",
++  "      pdfStrokeOP setoverprint",
++  "    } ifelse",
++  "  } if",
++  "} def",
++  "~123sn",
++  "/cm { concat } def",
++  "/d { setdash } def",
++  "/i { setflat } def",
++  "/j { setlinejoin } def",
++  "/J { setlinecap } def",
++  "/M { setmiterlimit } def",
++  "/w { setlinewidth } def",
++  "% path segment operators",
++  "/m { moveto } def",
++  "/l { lineto } def",
++  "/c { curveto } def",
++  "/re { 4 2 roll moveto 1 index 0 rlineto 0 exch rlineto",
++  "      neg 0 rlineto closepath } def",
++  "/h { closepath } def",
++  "% path painting operators",
++  "/S { sCol stroke } def",
++  "/Sf { fCol stroke } def",
++  "/f { fCol fill } def",
++  "/f* { fCol eofill } def",
++  "% clipping operators",
++  "/W { clip newpath } def",
++  "/W* { eoclip newpath } def",
++  "/Ws { strokepath clip newpath } def",
++  "% text state operators",
++  "/Tc { /pdfCharSpacing exch def } def",
++  "/Tf { dup /pdfFontSize exch def",
++  "      dup pdfHorizScaling mul exch matrix scale",
++  "      pdfTextMat matrix concatmatrix dup 4 0 put dup 5 0 put",
++  "      exch findfont exch makefont setfont } def",
++  "/Tr { /pdfTextRender exch def } def",
++  "/Ts { /pdfTextRise exch def } def",
++  "/Tw { /pdfWordSpacing exch def } def",
++  "/Tz { /pdfHorizScaling exch def } def",
++  "% text positioning operators",
++  "/Td { pdfTextMat transform moveto } def",
++  "/Tm { /pdfTextMat exch def } def",
++  "% text string operators",
++  "/cshow where {",
++  "  pop",
++  "  /cshow2 {",
++  "    dup {",
++  "      pop pop",
++  "      1 string dup 0 3 index put 3 index exec",
++  "    } exch cshow",
++  "    pop pop",
++  "  } def",
++  "}{",
++  "  /cshow2 {",
++  "    currentfont /FontType get 0 eq {",
++  "      0 2 2 index length 1 sub {",
++  "        2 copy get exch 1 add 2 index exch get",
++  "        2 copy exch 256 mul add",
++  "        2 string dup 0 6 5 roll put dup 1 5 4 roll put",
++  "        3 index exec",
++  "      } for",
++  "    } {",
++  "      dup {",
++  "        1 string dup 0 3 index put 3 index exec",
++  "      } forall",
++  "    } ifelse",
++  "    pop pop",
++  "  } def",
++  "} ifelse",
++  "/awcp {", // awidthcharpath
++  "  exch {",
++  "    false charpath",
++  "    5 index 5 index rmoveto",
++  "    6 index eq { 7 index 7 index rmoveto } if",
++  "  } exch cshow2",
++  "  6 {pop} repeat",
++  "} def",
++  "/Tj {",
++  "  fCol",  // because stringwidth has to draw Type 3 chars
++  "  1 index stringwidth pdfTextMat idtransform pop",
++  "  sub 1 index length dup 0 ne { div } { pop pop 0 } ifelse",
++  "  pdfWordSpacing pdfHorizScaling mul 0 pdfTextMat dtransform 32",
++  "  4 3 roll pdfCharSpacing pdfHorizScaling mul add 0",
++  "  pdfTextMat dtransform",
++  "  6 5 roll Tj1",
++  "} def",
++  "/Tj16 {",
++  "  fCol",  // because stringwidth has to draw Type 3 chars
++  "  2 index stringwidth pdfTextMat idtransform pop",
++  "  sub exch div",
++  "  pdfWordSpacing pdfHorizScaling mul 0 pdfTextMat dtransform 32",
++  "  4 3 roll pdfCharSpacing pdfHorizScaling mul add 0",
++  "  pdfTextMat dtransform",
++  "  6 5 roll Tj1",
++  "} def",
++  "/Tj16V {",
++  "  fCol",  // because stringwidth has to draw Type 3 chars
++  "  2 index stringwidth pdfTextMat idtransform exch pop",
++  "  sub exch div",
++  "  0 pdfWordSpacing pdfTextMat dtransform 32",
++  "  4 3 roll pdfCharSpacing add 0 exch",
++  "  pdfTextMat dtransform",
++  "  6 5 roll Tj1",
++  "} def",
++  "/Tj1 {",
++  "  0 pdfTextRise pdfTextMat dtransform rmoveto",
++  "  currentpoint 8 2 roll",
++  "  pdfTextRender 1 and 0 eq {",
++  "    6 copy awidthshow",
++  "  } if",
++  "  pdfTextRender 3 and dup 1 eq exch 2 eq or {",
++  "    7 index 7 index moveto",
++  "    6 copy",
++  "    currentfont /FontType get 3 eq { fCol } { sCol } ifelse",
++  "    false awcp currentpoint stroke moveto",
++  "  } if",
++  "  pdfTextRender 4 and 0 ne {",
++  "    8 6 roll moveto",
++  "    false awcp",
++  "    /pdfTextClipPath [ pdfTextClipPath aload pop",
++  "      {/moveto cvx}",
++  "      {/lineto cvx}",
++  "      {/curveto cvx}",
++  "      {/closepath cvx}",
++  "    pathforall ] def",
++  "    currentpoint newpath moveto",
++  "  } {",
++  "    8 {pop} repeat",
++  "  } ifelse",
++  "  0 pdfTextRise neg pdfTextMat dtransform rmoveto",
++  "} def",
++  "/TJm { pdfFontSize 0.001 mul mul neg 0",
++  "       pdfTextMat dtransform rmoveto } def",
++  "/TJmV { pdfFontSize 0.001 mul mul neg 0 exch",
++  "        pdfTextMat dtransform rmoveto } def",
++  "/Tclip { pdfTextClipPath cvx exec clip newpath",
++  "         /pdfTextClipPath [] def } def",
++  "~1ns",
++  "% Level 1 image operators",
++  "~1n",
++  "/pdfIm1 {",
++  "  /pdfImBuf1 4 index string def",
++  "  { currentfile pdfImBuf1 readhexstring pop } image",
++  "} def",
++  "~1s",
++  "/pdfIm1Sep {",
++  "  /pdfImBuf1 4 index string def",
++  "  /pdfImBuf2 4 index string def",
++  "  /pdfImBuf3 4 index string def",
++  "  /pdfImBuf4 4 index string def",
++  "  { currentfile pdfImBuf1 readhexstring pop }",
++  "  { currentfile pdfImBuf2 readhexstring pop }",
++  "  { currentfile pdfImBuf3 readhexstring pop }",
++  "  { currentfile pdfImBuf4 readhexstring pop }",
++  "  true 4 colorimage",
++  "} def",
++  "~1ns",
++  "/pdfImM1 {",
++  "  fCol /pdfImBuf1 4 index 7 add 8 idiv string def",
++  "  { currentfile pdfImBuf1 readhexstring pop } imagemask",
++  "} def",
++  "/pdfImM1a {",
++  "  { 2 copy get exch 1 add exch } imagemask",
++  "  pop pop",
++  "} def",
++  "~23sn",
++  "% Level 2 image operators",
++  "/pdfImBuf 100 string def",
++  "/pdfIm {",
++  "  image",
++  "  { currentfile pdfImBuf readline",
++  "    not { pop exit } if",
++  "    (%-EOD-) eq { exit } if } loop",
++  "} def",
++  "~23s",
++  "/pdfImSep {",
++  "  findcmykcustomcolor exch",
++  "  dup /Width get /pdfImBuf1 exch string def",
++  "  dup /Decode get aload pop 1 index sub /pdfImDecodeRange exch def",
++  "  /pdfImDecodeLow exch def",
++  "  begin Width Height BitsPerComponent ImageMatrix DataSource end",
++  "  /pdfImData exch def",
++  "  { pdfImData pdfImBuf1 readstring pop",
++  "    0 1 2 index length 1 sub {",
++  "      1 index exch 2 copy get",
++  "      pdfImDecodeRange mul 255 div pdfImDecodeLow add round cvi",
++  "      255 exch sub put",
++  "    } for }",
++  "  6 5 roll customcolorimage",
++  "  { currentfile pdfImBuf readline",
++  "    not { pop exit } if",
++  "    (%-EOD-) eq { exit } if } loop",
++  "} def",
++  "~23sn",
++  "/pdfImM {",
++  "  fCol imagemask",
++  "  { currentfile pdfImBuf readline",
++  "    not { pop exit } if",
++  "    (%-EOD-) eq { exit } if } loop",
++  "} def",
++  "/pr { 2 index 2 index 3 2 roll putinterval 4 add } def",
++  "/pdfImClip {",
++  "  gsave",
++  "  0 2 4 index length 1 sub {",
++  "    dup 4 index exch 2 copy",
++  "    get 5 index div put",
++  "    1 add 3 index exch 2 copy",
++  "    get 3 index div put",
++  "  } for",
++  "  pop pop rectclip",
++  "} def",
++  "/pdfImClipEnd { grestore } def",
++  "~23sn",
++  "% shading operators",
++  "/colordelta {",
++  "  false 0 1 3 index length 1 sub {",
++  "    dup 4 index exch get 3 index 3 2 roll get sub abs 0.004 gt {",
++  "      pop true",
++  "    } if",
++  "  } for",
++  "  exch pop exch pop",
++  "} def",
++  "/funcCol { func n array astore } def",
++  "/funcSH {",
++  "  dup 0 eq {",
++  "    true",
++  "  } {",
++  "    dup 6 eq {",
++  "      false",
++  "    } {",
++  "      4 index 4 index funcCol dup",
++  "      6 index 4 index funcCol dup",
++  "      3 1 roll colordelta 3 1 roll",
++  "      5 index 5 index funcCol dup",
++  "      3 1 roll colordelta 3 1 roll",
++  "      6 index 8 index funcCol dup",
++  "      3 1 roll colordelta 3 1 roll",
++  "      colordelta or or or",
++  "    } ifelse",
++  "  } ifelse",
++  "  {",
++  "    1 add",
++  "    4 index 3 index add 0.5 mul exch 4 index 3 index add 0.5 mul exch",
++  "    6 index 6 index 4 index 4 index 4 index funcSH",
++  "    2 index 6 index 6 index 4 index 4 index funcSH",
++  "    6 index 2 index 4 index 6 index 4 index funcSH",
++  "    5 3 roll 3 2 roll funcSH pop pop",
++  "  } {",
++  "    pop 3 index 2 index add 0.5 mul 3 index  2 index add 0.5 mul",
++  "~23n",
++  "    funcCol sc",
++  "~23s",
++  "    funcCol aload pop k",
++  "~23sn",
++  "    dup 4 index exch mat transform m",
++  "    3 index 3 index mat transform l",
++  "    1 index 3 index mat transform l",
++  "    mat transform l pop pop h f*",
++  "  } ifelse",
++  "} def",
++  "/axialCol {",
++  "  dup 0 lt {",
++  "    pop t0",
++  "  } {",
++  "    dup 1 gt {",
++  "      pop t1",
++  "    } {",
++  "      dt mul t0 add",
++  "    } ifelse",
++  "  } ifelse",
++  "  func n array astore",
++  "} def",
++  "/axialSH {",
++  "  dup 0 eq {",
++  "    true",
++  "  } {",
++  "    dup 8 eq {",
++  "      false",
++  "    } {",
++  "      2 index axialCol 2 index axialCol colordelta",
++  "    } ifelse",
++  "  } ifelse",
++  "  {",
++  "    1 add 3 1 roll 2 copy add 0.5 mul",
++  "    dup 4 3 roll exch 4 index axialSH",
++  "    exch 3 2 roll axialSH",
++  "  } {",
++  "    pop 2 copy add 0.5 mul",
++  "~23n",
++  "    axialCol sc",
++  "~23s",
++  "    axialCol aload pop k",
++  "~23sn",
++  "    exch dup dx mul x0 add exch dy mul y0 add",
++  "    3 2 roll dup dx mul x0 add exch dy mul y0 add",
++  "    dx abs dy abs ge {",
++  "      2 copy yMin sub dy mul dx div add yMin m",
++  "      yMax sub dy mul dx div add yMax l",
++  "      2 copy yMax sub dy mul dx div add yMax l",
++  "      yMin sub dy mul dx div add yMin l",
++  "      h f*",
++  "    } {",
++  "      exch 2 copy xMin sub dx mul dy div add xMin exch m",
++  "      xMax sub dx mul dy div add xMax exch l",
++  "      exch 2 copy xMax sub dx mul dy div add xMax exch l",
++  "      xMin sub dx mul dy div add xMin exch l",
++  "      h f*",
++  "    } ifelse",
++  "  } ifelse",
++  "} def",
++  "/radialCol {",
++  "  dup t0 lt {",
++  "    pop t0",
++  "  } {",
++  "    dup t1 gt {",
++  "      pop t1",
++  "    } if",
++  "  } ifelse",
++  "  func n array astore",
++  "} def",
++  "/radialSH {",
++  "  dup 0 eq {",
++  "    true",
++  "  } {",
++  "    dup 8 eq {",
++  "      false",
++  "    } {",
++  "      2 index dt mul t0 add radialCol",
++  "      2 index dt mul t0 add radialCol colordelta",
++  "    } ifelse",
++  "  } ifelse",
++  "  {",
++  "    1 add 3 1 roll 2 copy add 0.5 mul",
++  "    dup 4 3 roll exch 4 index radialSH",
++  "    exch 3 2 roll radialSH",
++  "  } {",
++  "    pop 2 copy add 0.5 mul dt mul t0 add",
++  "~23n",
++  "    radialCol sc",
++  "~23s",
++  "    radialCol aload pop k",
++  "~23sn",
++  "    encl {",
++  "      exch dup dx mul x0 add exch dup dy mul y0 add exch dr mul r0 add",
++  "      0 360 arc h",
++  "      dup dx mul x0 add exch dup dy mul y0 add exch dr mul r0 add",
++  "      360 0 arcn h f",
++  "    } {",
++  "      2 copy",
++  "      dup dx mul x0 add exch dup dy mul y0 add exch dr mul r0 add",
++  "      a1 a2 arcn",
++  "      dup dx mul x0 add exch dup dy mul y0 add exch dr mul r0 add",
++  "      a2 a1 arcn h",
++  "      dup dx mul x0 add exch dup dy mul y0 add exch dr mul r0 add",
++  "      a1 a2 arc",
++  "      dup dx mul x0 add exch dup dy mul y0 add exch dr mul r0 add",
++  "      a2 a1 arc h f",
++  "    } ifelse",
++  "  } ifelse",
++  "} def",
++  "~123sn",
++  "end",
++  NULL
++};
++
++static char *cmapProlog[] = {
++  "/CIDInit /ProcSet findresource begin",
++  "10 dict begin",
++  "  begincmap",
++  "  /CMapType 1 def",
++  "  /CMapName /Identity-H def",
++  "  /CIDSystemInfo 3 dict dup begin",
++  "    /Registry (Adobe) def",
++  "    /Ordering (Identity) def",
++  "    /Supplement 0 def",
++  "  end def",
++  "  1 begincodespacerange",
++  "    <0000> <ffff>",
++  "  endcodespacerange",
++  "  0 usefont",
++  "  1 begincidrange",
++  "    <0000> <ffff> 0",
++  "  endcidrange",
++  "  endcmap",
++  "  currentdict CMapName exch /CMap defineresource pop",
++  "end",
++  "10 dict begin",
++  "  begincmap",
++  "  /CMapType 1 def",
++  "  /CMapName /Identity-V def",
++  "  /CIDSystemInfo 3 dict dup begin",
++  "    /Registry (Adobe) def",
++  "    /Ordering (Identity) def",
++  "    /Supplement 0 def",
++  "  end def",
++  "  /WMode 1 def",
++  "  1 begincodespacerange",
++  "    <0000> <ffff>",
++  "  endcodespacerange",
++  "  0 usefont",
++  "  1 begincidrange",
++  "    <0000> <ffff> 0",
++  "  endcidrange",
++  "  endcmap",
++  "  currentdict CMapName exch /CMap defineresource pop",
++  "end",
++  "end",
++  NULL
++};
++
++//------------------------------------------------------------------------
++// Fonts
++//------------------------------------------------------------------------
++
++struct PSSubstFont {
++  char *psName;			// PostScript name
++  double mWidth;		// width of 'm' character
++};
++
++static char *psFonts[] = {
++  "Courier",
++  "Courier-Bold",
++  "Courier-Oblique",
++  "Courier-BoldOblique",
++  "Helvetica",
++  "Helvetica-Bold",
++  "Helvetica-Oblique",
++  "Helvetica-BoldOblique",
++  "Symbol",
++  "Times-Roman",
++  "Times-Bold",
++  "Times-Italic",
++  "Times-BoldItalic",
++  "ZapfDingbats",
++  NULL
++};
++
++static PSSubstFont psSubstFonts[] = {
++  {"Helvetica",             0.833},
++  {"Helvetica-Oblique",     0.833},
++  {"Helvetica-Bold",        0.889},
++  {"Helvetica-BoldOblique", 0.889},
++  {"Times-Roman",           0.788},
++  {"Times-Italic",          0.722},
++  {"Times-Bold",            0.833},
++  {"Times-BoldItalic",      0.778},
++  {"Courier",               0.600},
++  {"Courier-Oblique",       0.600},
++  {"Courier-Bold",          0.600},
++  {"Courier-BoldOblique",   0.600}
++};
++
++// Info for 8-bit fonts
++struct PSFont8Info {
++  Ref fontID;
++  Gushort *codeToGID;		// code-to-GID mapping for TrueType fonts
++};
++
++// Encoding info for substitute 16-bit font
++struct PSFont16Enc {
++  Ref fontID;
++  GString *enc;
++};
++
++//------------------------------------------------------------------------
++// process colors
++//------------------------------------------------------------------------
++
++#define psProcessCyan     1
++#define psProcessMagenta  2
++#define psProcessYellow   4
++#define psProcessBlack    8
++#define psProcessCMYK    15
++
++//------------------------------------------------------------------------
++// PSOutCustomColor
++//------------------------------------------------------------------------
++
++class PSOutCustomColor {
++public:
++
++  PSOutCustomColor(double cA, double mA,
++		   double yA, double kA, GString *nameA);
++  ~PSOutCustomColor();
++
++  double c, m, y, k;
++  GString *name;
++  PSOutCustomColor *next;
++};
++
++PSOutCustomColor::PSOutCustomColor(double cA, double mA,
++				   double yA, double kA, GString *nameA) {
++  c = cA;
++  m = mA;
++  y = yA;
++  k = kA;
++  name = nameA;
++  next = NULL;
++}
++
++PSOutCustomColor::~PSOutCustomColor() {
++  delete name;
++}
++
++//------------------------------------------------------------------------
++
++struct PSOutImgClipRect {
++  int x0, x1, y0, y1;
++};
++
++//------------------------------------------------------------------------
++// DeviceNRecoder
++//------------------------------------------------------------------------
++
++class DeviceNRecoder: public FilterStream {
++public:
++
++  DeviceNRecoder(Stream *strA, int widthA, int heightA,
++		 GfxImageColorMap *colorMapA);
++  virtual ~DeviceNRecoder();
++  virtual StreamKind getKind() { return strWeird; }
++  virtual void reset();
++  virtual int getChar()
++    { return (bufIdx >= bufSize && !fillBuf()) ? EOF : buf[bufIdx++]; }
++  virtual int lookChar()
++    { return (bufIdx >= bufSize && !fillBuf()) ? EOF : buf[bufIdx]; }
++  virtual GString *getPSFilter(int /*psLevel*/, char * /*indent*/) { return NULL; }
++  virtual GBool isBinary(GBool /*last*/ = gTrue) { return gTrue; }
++  virtual GBool isEncoder() { return gTrue; }
++
++private:
++
++  GBool fillBuf();
++
++  int width, height;
++  GfxImageColorMap *colorMap;
++  Function *func;
++  ImageStream *imgStr;
++  int buf[gfxColorMaxComps];
++  int pixelIdx;
++  int bufIdx;
++  int bufSize;
++};
++
++DeviceNRecoder::DeviceNRecoder(Stream *strA, int widthA, int heightA,
++			       GfxImageColorMap *colorMapA):
++    FilterStream(strA) {
++  width = widthA;
++  height = heightA;
++  colorMap = colorMapA;
++  imgStr = NULL;
++  pixelIdx = 0;
++  bufIdx = gfxColorMaxComps;
++  bufSize = ((GfxDeviceNColorSpace *)colorMap->getColorSpace())->
++              getAlt()->getNComps();
++  func = ((GfxDeviceNColorSpace *)colorMap->getColorSpace())->
++           getTintTransformFunc();
++}
++
++DeviceNRecoder::~DeviceNRecoder() {
++  if (imgStr) {
++    delete imgStr;
++  }
++}
++
++void DeviceNRecoder::reset() {
++  imgStr = new ImageStream(str, width, colorMap->getNumPixelComps(),
++			   colorMap->getBits());
++  imgStr->reset();
++}
++
++GBool DeviceNRecoder::fillBuf() {
++  Guchar pixBuf[gfxColorMaxComps];
++  GfxColor color;
++  double x[gfxColorMaxComps], y[gfxColorMaxComps];
++  int i;
++
++  if (pixelIdx >= width * height) {
++    return gFalse;
++  }
++  imgStr->getPixel(pixBuf);
++  colorMap->getColor(pixBuf, &color);
++  for (i = 0;
++       i < ((GfxDeviceNColorSpace *)colorMap->getColorSpace())->getNComps();
++       ++i) {
++    x[i] = colToDbl(color.c[i]);
++  }
++  func->transform(x, y);
++  for (i = 0; i < bufSize; ++i) {
++    buf[i] = (int)(y[i] * 255 + 0.5);
++  }
++  bufIdx = 0;
++  ++pixelIdx;
++  return gTrue;
++}
++
++//------------------------------------------------------------------------
++// PSOutputDev
++//------------------------------------------------------------------------
++
++extern "C" {
++typedef void (*SignalFunc)(int);
++}
++
++static void outputToFile(void *stream, char *data, int len) {
++  fwrite(data, 1, len, (FILE *)stream);
++}
++
++PSOutputDev::PSOutputDev(char *fileName, char *pstitle, XRef *xrefA, Catalog *catalog,
++			 int firstPage, int lastPage, PSOutMode modeA,
++			 int imgLLXA, int imgLLYA, int imgURXA, int imgURYA,
++			 GBool forceRasterizeA,
++			 GBool manualCtrlA) {
++  FILE *f;
++  PSFileType fileTypeA;
++
++  underlayCbk = NULL;
++  underlayCbkData = NULL;
++  overlayCbk = NULL;
++  overlayCbkData = NULL;
++
++  fontIDs = NULL;
++  fontFileIDs = NULL;
++  fontFileNames = NULL;
++  font8Info = NULL;
++  font16Enc = NULL;
++  imgIDs = NULL;
++  formIDs = NULL;
++  xobjStack = NULL;
++  embFontList = NULL;
++  customColors = NULL;
++  haveTextClip = gFalse;
++  t3String = NULL;
++  
++  forceRasterize = forceRasterizeA;
++
++  // open file or pipe
++  if (!strcmp(fileName, "-")) {
++    fileTypeA = psStdout;
++    f = stdout;
++  } else if (fileName[0] == '|') {
++    fileTypeA = psPipe;
++#ifdef HAVE_POPEN
++#ifndef WIN32
++    signal(SIGPIPE, (SignalFunc)SIG_IGN);
++#endif
++    if (!(f = popen(fileName + 1, "w"))) {
++      error(-1, "Couldn't run print command '%s'", fileName);
++      ok = gFalse;
++      return;
++    }
++#else
++    error(-1, "Print commands are not supported ('%s')", fileName);
++    ok = gFalse;
++    return;
++#endif
++  } else {
++    fileTypeA = psFile;
++    if (!(f = fopen(fileName, "w"))) {
++      error(-1, "Couldn't open PostScript file '%s'", fileName);
++      ok = gFalse;
++      return;
++    }
++  }
++
++  init(outputToFile, f, fileTypeA, pstitle,
++       xrefA, catalog, firstPage, lastPage, modeA,
++       imgLLXA, imgLLYA, imgURXA, imgURYA, manualCtrlA);
++}
++
++void PSOutputDev::init(PSOutputFunc outputFuncA, void *outputStreamA,
++		       PSFileType fileTypeA, char *pstitle, XRef *xrefA, Catalog *catalog,
++		       int firstPage, int lastPage, PSOutMode modeA,
++		       int imgLLXA, int imgLLYA, int imgURXA, int imgURYA,
++		       GBool manualCtrlA) {
++  Page *page;
++  PDFRectangle *box;
++
++  // initialize
++  setlocale(LC_NUMERIC,"POSIX");
++  ok = gTrue;
++  outputFunc = outputFuncA;
++  outputStream = outputStreamA;
++  fileType = fileTypeA;
++  xref = xrefA;
++  level = globalParams->getPSLevel();
++  mode = modeA;
++  paperWidth = globalParams->getPSPaperWidth();
++  paperHeight = globalParams->getPSPaperHeight();
++  imgLLX = imgLLXA;
++  imgLLY = imgLLYA;
++  imgURX = imgURXA;
++  imgURY = imgURYA;
++  if (imgLLX == 0 && imgURX == 0 && imgLLY == 0 && imgURY == 0) {
++    globalParams->getPSImageableArea(&imgLLX, &imgLLY, &imgURX, &imgURY);
++  }
++  if (paperWidth < 0 || paperHeight < 0) {
++    // this check is needed in case the document has zero pages
++    if (firstPage > 0 && firstPage <= catalog->getNumPages()) {
++      page = catalog->getPage(firstPage);
++      paperWidth = (int)ceil(page->getMediaWidth());
++      paperHeight = (int)ceil(page->getMediaHeight());
++    } else {
++      paperWidth = 1;
++      paperHeight = 1;
++    }
++    imgLLX = imgLLY = 0;
++    imgURX = paperWidth;
++    imgURY = paperHeight;
++  }
++  preload = globalParams->getPSPreload();
++  manualCtrl = manualCtrlA;
++  if (mode == psModeForm) {
++    lastPage = firstPage;
++  }
++  processColors = 0;
++  inType3Char = gFalse;
++
++#if OPI_SUPPORT
++  // initialize OPI nesting levels
++  opi13Nest = 0;
++  opi20Nest = 0;
++#endif
++
++  tx0 = ty0 = -1;
++  xScale0 = yScale0 = 0;
++  rotate0 = -1;
++  clipLLX0 = clipLLY0 = 0;
++  clipURX0 = clipURY0 = -1;
++
++  // initialize fontIDs, fontFileIDs, and fontFileNames lists
++  fontIDSize = 64;
++  fontIDLen = 0;
++  fontIDs = (Ref *)gmallocn(fontIDSize, sizeof(Ref));
++  fontFileIDSize = 64;
++  fontFileIDLen = 0;
++  fontFileIDs = (Ref *)gmallocn(fontFileIDSize, sizeof(Ref));
++  fontFileNameSize = 64;
++  fontFileNameLen = 0;
++  fontFileNames = (GString **)gmallocn(fontFileNameSize, sizeof(GString *));
++  psFileNames = (GString **)gmallocn(fontFileNameSize, sizeof(GString *));
++  nextTrueTypeNum = 0;
++  font8InfoLen = 0;
++  font8InfoSize = 0;
++  font16EncLen = 0;
++  font16EncSize = 0;
++  imgIDLen = 0;
++  imgIDSize = 0;
++  formIDLen = 0;
++  formIDSize = 0;
++
++  xobjStack = new GList();
++  numSaves = 0;
++  numTilingPatterns = 0;
++  nextFunc = 0;
++
++  // initialize embedded font resource comment list
++  embFontList = new GString();
++
++  if (!manualCtrl) {
++    // this check is needed in case the document has zero pages
++    if (firstPage > 0 && firstPage <= catalog->getNumPages()) {
++      writeHeader(firstPage, lastPage,
++		  catalog->getPage(firstPage)->getMediaBox(),
++		  catalog->getPage(firstPage)->getCropBox(),
++		  catalog->getPage(firstPage)->getRotate(), pstitle);
++    } else {
++      box = new PDFRectangle(0, 0, 1, 1);
++      writeHeader(firstPage, lastPage, box, box, 0, pstitle);
++      delete box;
++    }
++    if (mode != psModeForm) {
++      writePS("%%BeginProlog\n");
++    }
++    writeXpdfProcset();
++    if (mode != psModeForm) {
++      writePS("%%EndProlog\n");
++      writePS("%%BeginSetup\n");
++    }
++    writeDocSetup(catalog, firstPage, lastPage);
++    if (mode != psModeForm) {
++      writePS("%%EndSetup\n");
++    }
++  }
++
++  // initialize sequential page number
++  seqPage = 1;
++}
++
++PSOutputDev::~PSOutputDev() {
++  PSOutCustomColor *cc;
++  int i;
++
++  if (ok) {
++    if (!manualCtrl) {
++      writePS("%%Trailer\n");
++      writeTrailer();
++      if (mode != psModeForm) {
++	writePS("%%EOF\n");
++      }
++    }
++    if (fileType == psFile) {
++#ifdef MACOS
++      ICS_MapRefNumAndAssign((short)((FILE *)outputStream)->handle);
++#endif
++      fclose((FILE *)outputStream);
++    }
++#ifdef HAVE_POPEN
++    else if (fileType == psPipe) {
++      pclose((FILE *)outputStream);
++#ifndef WIN32
++      signal(SIGPIPE, (SignalFunc)SIG_DFL);
++#endif
++    }
++#endif
++  }
++  if (embFontList) {
++    delete embFontList;
++  }
++  if (fontIDs) {
++    gfree(fontIDs);
++  }
++  if (fontFileIDs) {
++    gfree(fontFileIDs);
++  }
++  if (fontFileNames) {
++    for (i = 0; i < fontFileNameLen; ++i) {
++      delete fontFileNames[i];
++    }
++    gfree(fontFileNames);
++  }
++  if (font8Info) {
++    for (i = 0; i < font8InfoLen; ++i) {
++      gfree(font8Info[i].codeToGID);
++    }
++    gfree(font8Info);
++  }
++  if (psFileNames) {
++    for (i = 0; i < fontFileNameLen; ++i) {
++      if (psFileNames[i])
++        delete psFileNames[i];
++    }
++    gfree(psFileNames);
++  }
++  if (font16Enc) {
++    for (i = 0; i < font16EncLen; ++i) {
++      delete font16Enc[i].enc;
++    }
++    gfree(font16Enc);
++  }
++  gfree(imgIDs);
++  gfree(formIDs);
++  if (xobjStack) {
++    delete xobjStack;
++  }
++  while (customColors) {
++    cc = customColors;
++    customColors = cc->next;
++    delete cc;
++  }
++}
++
++void PSOutputDev::writeHeader(int firstPage, int lastPage,
++			      PDFRectangle *mediaBox, PDFRectangle *cropBox,
++			      int pageRotate, char *pstitle) {
++  Object info, obj1;
++  double x1, y1, x2, y2;
++
++  switch (mode) {
++  case psModePS:
++    writePS("%!PS-Adobe-3.0\n");
++    break;
++  case psModeEPS:
++    writePS("%!PS-Adobe-3.0 EPSF-3.0\n");
++    break;
++  case psModeForm:
++    writePS("%!PS-Adobe-3.0 Resource-Form\n");
++    break;
++  }
++
++  xref->getDocInfo(&info);
++  if (info.isDict() && info.dictLookup("Creator", &obj1)->isString()) {
++    writePS("%%Creator: ");
++    writePSTextLine(obj1.getString());
++  }
++  obj1.free();
++  info.free();
++  if(pstitle) {
++    writePSFmt("%%Title: {0:s}\n", pstitle);
++  }
++  writePSFmt("%%LanguageLevel: {0:d}\n",
++	     (level == psLevel1 || level == psLevel1Sep) ? 1 :
++	     (level == psLevel2 || level == psLevel2Sep) ? 2 : 3);
++  if (level == psLevel1Sep || level == psLevel2Sep || level == psLevel3Sep) {
++    writePS("%%DocumentProcessColors: (atend)\n");
++    writePS("%%DocumentCustomColors: (atend)\n");
++  }
++  writePS("%%DocumentSuppliedResources: (atend)\n");
++
++  switch (mode) {
++  case psModePS:
++    writePSFmt("%%DocumentMedia: plain {0:d} {1:d} 0 () ()\n",
++	       paperWidth, paperHeight);
++    writePSFmt("%%BoundingBox: 0 0 {0:d} {1:d}\n", paperWidth, paperHeight);
++    writePSFmt("%%Pages: {0:d}\n", lastPage - firstPage + 1);
++    writePS("%%EndComments\n");
++    writePS("%%BeginDefaults\n");
++    writePS("%%PageMedia: plain\n");
++    writePS("%%EndDefaults\n");
++    break;
++  case psModeEPS:
++    epsX1 = cropBox->x1;
++    epsY1 = cropBox->y1;
++    epsX2 = cropBox->x2;
++    epsY2 = cropBox->y2;
++    if (pageRotate == 0 || pageRotate == 180) {
++      x1 = epsX1;
++      y1 = epsY1;
++      x2 = epsX2;
++      y2 = epsY2;
++    } else { // pageRotate == 90 || pageRotate == 270
++      x1 = 0;
++      y1 = 0;
++      x2 = epsY2 - epsY1;
++      y2 = epsX2 - epsX1;
++    }
++    writePSFmt("%%BoundingBox: {0:d} {1:d} {2:d} {3:d}\n",
++	       (int)floor(x1), (int)floor(y1), (int)ceil(x2), (int)ceil(y2));
++    if (floor(x1) != ceil(x1) || floor(y1) != ceil(y1) ||
++	floor(x2) != ceil(x2) || floor(y2) != ceil(y2)) {
++      writePSFmt("%%HiResBoundingBox: {0:.4g} {1:.4g} {2:.4g} {3:.4g}\n",
++		 x1, y1, x2, y2);
++    }
++    writePS("%%EndComments\n");
++    break;
++  case psModeForm:
++    writePS("%%EndComments\n");
++    writePS("32 dict dup begin\n");
++    writePSFmt("/BBox [{0:d} {1:d} {2:d} {3:d}] def\n",
++	       (int)floor(mediaBox->x1), (int)floor(mediaBox->y1),
++	       (int)ceil(mediaBox->x2), (int)ceil(mediaBox->y2));
++    writePS("/FormType 1 def\n");
++    writePS("/Matrix [1 0 0 1 0 0] def\n");
++    break;
++  }
++}
++
++void PSOutputDev::writeXpdfProcset() {
++  GBool lev1, lev2, lev3, sep, nonSep;
++  char **p;
++  char *q;
++
++  writePSFmt("%%BeginResource: procset xpdf {0:s} 0\n", xpdfVersion);
++  writePSFmt("%%Copyright: {0:s}\n", xpdfCopyright);
++  lev1 = lev2 = lev3 = sep = nonSep = gTrue;
++  for (p = prolog; *p; ++p) {
++    if ((*p)[0] == '~') {
++      lev1 = lev2 = lev3 = sep = nonSep = gFalse;
++      for (q = *p + 1; *q; ++q) {
++	switch (*q) {
++	case '1': lev1 = gTrue; break;
++	case '2': lev2 = gTrue; break;
++	case '3': lev3 = gTrue; break;
++	case 's': sep = gTrue; break;
++	case 'n': nonSep = gTrue; break;
++	}
++      }
++    } else if ((level == psLevel1 && lev1 && nonSep) ||
++	       (level == psLevel1Sep && lev1 && sep) ||
++	       (level == psLevel2 && lev2 && nonSep) ||
++	       (level == psLevel2Sep && lev2 && sep) ||
++	       (level == psLevel3 && lev3 && nonSep) ||
++	       (level == psLevel3Sep && lev3 && sep)) {
++      writePSFmt("{0:s}\n", *p);
++    }
++  }
++  writePS("%%EndResource\n");
++
++  if (level >= psLevel3) {
++    for (p = cmapProlog; *p; ++p) {
++      writePSFmt("{0:s}\n", *p);
++    }
++  }
++}
++
++void PSOutputDev::writeDocSetup(Catalog *catalog,
++				int firstPage, int lastPage) {
++  Page *page;
++  Dict *resDict;
++  Annots *annots;
++  Object obj1, obj2;
++  int pg, i;
++
++  if (mode == psModeForm) {
++    // swap the form and xpdf dicts
++    writePS("xpdf end begin dup begin\n");
++  } else {
++    writePS("xpdf begin\n");
++  }
++  for (pg = firstPage; pg <= lastPage; ++pg) {
++    page = catalog->getPage(pg);
++    if ((resDict = page->getResourceDict())) {
++      setupResources(resDict);
++    }
++    annots = new Annots(xref, catalog, page->getAnnots(&obj1));
++    obj1.free();
++    for (i = 0; i < annots->getNumAnnots(); ++i) {
++      if (annots->getAnnot(i)->getAppearance(&obj1)->isStream()) {
++	obj1.streamGetDict()->lookup("Resources", &obj2);
++	if (obj2.isDict()) {
++	  setupResources(obj2.getDict());
++	}
++	obj2.free();
++      }
++      obj1.free();
++    }
++    delete annots;
++  }
++  if (mode != psModeForm) {
++    if (mode != psModeEPS && !manualCtrl) {
++      writePSFmt("{0:d} {1:d} {2:s} pdfSetup\n",
++		 paperWidth, paperHeight,
++		 globalParams->getPSDuplex() ? "true" : "false");
++    }
++#if OPI_SUPPORT
++    if (globalParams->getPSOPI()) {
++      writePS("/opiMatrix matrix currentmatrix def\n");
++    }
++#endif
++  }
++}
++
++void PSOutputDev::writePageTrailer() {
++  if (mode != psModeForm) {
++    writePS("pdfEndPage\n");
++  }
++}
++
++void PSOutputDev::writeTrailer() {
++  PSOutCustomColor *cc;
++
++  if (mode == psModeForm) {
++    writePS("/Foo exch /Form defineresource pop\n");
++  } else {
++    writePS("end\n");
++    writePS("%%DocumentSuppliedResources:\n");
++    writePS(embFontList->getCString());
++    if (level == psLevel1Sep || level == psLevel2Sep ||
++	level == psLevel3Sep) {
++      writePS("%%DocumentProcessColors:");
++      if (processColors & psProcessCyan) {
++	writePS(" Cyan");
++	 }
++      if (processColors & psProcessMagenta) {
++	writePS(" Magenta");
++      }
++      if (processColors & psProcessYellow) {
++	writePS(" Yellow");
++      }
++      if (processColors & psProcessBlack) {
++	writePS(" Black");
++      }
++      writePS("\n");
++      writePS("%%DocumentCustomColors:");
++      for (cc = customColors; cc; cc = cc->next) {
++	writePSFmt(" ({0:s})", cc->name->getCString());
++      }
++      writePS("\n");
++      writePS("%%CMYKCustomColor:\n");
++      for (cc = customColors; cc; cc = cc->next) {
++	writePSFmt("%%+ {0:.4g} {1:.4g} {2:.4g} {3:.4g} ({4:t})\n",
++		   cc->c, cc->m, cc->y, cc->k, cc->name);
++      }
++    }
++  }
++}
++
++void PSOutputDev::setupResources(Dict *resDict) {
++  Object xObjDict, xObjRef, xObj, patDict, patRef, pat, resObj;
++  Ref ref0, ref1;
++  GBool skip;
++  int i, j;
++
++  setupFonts(resDict);
++  setupImages(resDict);
++  setupForms(resDict);
++
++  //----- recursively scan XObjects
++  resDict->lookup("XObject", &xObjDict);
++  if (xObjDict.isDict()) {
++    for (i = 0; i < xObjDict.dictGetLength(); ++i) {
++
++      // avoid infinite recursion on XObjects
++      skip = gFalse;
++      if ((xObjDict.dictGetValNF(i, &xObjRef)->isRef())) {
++	ref0 = xObjRef.getRef();
++	for (j = 0; j < xobjStack->getLength(); ++j) {
++	  ref1 = *(Ref *)xobjStack->get(j);
++	  if (ref1.num == ref0.num && ref1.gen == ref0.gen) {
++	    skip = gTrue;
++	    break;
++	  }
++	}
++	if (!skip) {
++	  xobjStack->append(&ref0);
++	}
++      }
++      if (!skip) {
++
++	// process the XObject's resource dictionary
++	xObjDict.dictGetVal(i, &xObj);
++	if (xObj.isStream()) {
++	  xObj.streamGetDict()->lookup("Resources", &resObj);
++	  if (resObj.isDict()) {
++	    setupResources(resObj.getDict());
++	  }
++	  resObj.free();
++	}
++	xObj.free();
++      }
++
++      if (xObjRef.isRef() && !skip) {
++	xobjStack->del(xobjStack->getLength() - 1);
++      }
++      xObjRef.free();
++    }
++  }
++  xObjDict.free();
++
++  //----- recursively scan Patterns
++  resDict->lookup("Pattern", &patDict);
++  if (patDict.isDict()) {
++    inType3Char = gTrue;
++    for (i = 0; i < patDict.dictGetLength(); ++i) {
++
++      // avoid infinite recursion on Patterns
++      skip = gFalse;
++      if ((patDict.dictGetValNF(i, &patRef)->isRef())) {
++	ref0 = patRef.getRef();
++	for (j = 0; j < xobjStack->getLength(); ++j) {
++	  ref1 = *(Ref *)xobjStack->get(j);
++	  if (ref1.num == ref0.num && ref1.gen == ref0.gen) {
++	    skip = gTrue;
++	    break;
++	  }
++	}
++	if (!skip) {
++	  xobjStack->append(&ref0);
++	}
++      }
++      if (!skip) {
++
++	// process the Pattern's resource dictionary
++	patDict.dictGetVal(i, &pat);
++	if (pat.isStream()) {
++	  pat.streamGetDict()->lookup("Resources", &resObj);
++	  if (resObj.isDict()) {
++	    setupResources(resObj.getDict());
++	  }
++	  resObj.free();
++	}
++	pat.free();
++      }
++
++      if (patRef.isRef() && !skip) {
++	xobjStack->del(xobjStack->getLength() - 1);
++      }
++      patRef.free();
++    }
++    inType3Char = gFalse;
++  }
++  patDict.free();
++}
++
++void PSOutputDev::setupFonts(Dict *resDict) {
++  Object obj1, obj2;
++  Ref r;
++  GfxFontDict *gfxFontDict;
++  GfxFont *font;
++  int i;
++
++  if (forceRasterize) return;
++
++  gfxFontDict = NULL;
++  resDict->lookupNF("Font", &obj1);
++  if (obj1.isRef()) {
++    obj1.fetch(xref, &obj2);
++    if (obj2.isDict()) {
++      r = obj1.getRef();
++      gfxFontDict = new GfxFontDict(xref, &r, obj2.getDict());
++    }
++    obj2.free();
++  } else if (obj1.isDict()) {
++    gfxFontDict = new GfxFontDict(xref, NULL, obj1.getDict());
++  }
++  if (gfxFontDict) {
++    for (i = 0; i < gfxFontDict->getNumFonts(); ++i) {
++      if ((font = gfxFontDict->getFont(i))) {
++	setupFont(font, resDict);
++      }
++    }
++    delete gfxFontDict;
++  }
++  obj1.free();
++}
++
++void PSOutputDev::setupFont(GfxFont *font, Dict *parentResDict) {
++  Ref fontFileID;
++  GString *name;
++  PSFontParam *fontParam;
++  GString *psName;
++  char buf[16];
++  GBool subst;
++  UnicodeMap *uMap;
++  char *charName;
++  double xs, ys;
++  int code;
++  double w1, w2;
++  double *fm;
++  int i, j;
++  DisplayFontParam *dfp;
++
++  // check if font is already set up
++  for (i = 0; i < fontIDLen; ++i) {
++    if (fontIDs[i].num == font->getID()->num &&
++	fontIDs[i].gen == font->getID()->gen) {
++      return;
++    }
++  }
++
++  // add entry to fontIDs list
++  if (fontIDLen >= fontIDSize) {
++    fontIDSize += 64;
++    fontIDs = (Ref *)greallocn(fontIDs, fontIDSize, sizeof(Ref));
++  }
++  fontIDs[fontIDLen++] = *font->getID();
++
++  xs = ys = 1;
++  subst = gFalse;
++
++  // check for resident 8-bit font
++  if (font->getName() &&
++      (fontParam = globalParams->getPSFont(font->getName()))) {
++    psName = new GString(fontParam->psFontName->getCString());
++
++  // check for embedded Type 1 font
++  } else if (globalParams->getPSEmbedType1() &&
++	     font->getType() == fontType1 &&
++	     font->getEmbeddedFontID(&fontFileID)) {
++    psName = filterPSName(font->getEmbeddedFontName());
++    setupEmbeddedType1Font(&fontFileID, psName);
++
++  // check for embedded Type 1C font
++  } else if (globalParams->getPSEmbedType1() &&
++	     font->getType() == fontType1C &&
++	     font->getEmbeddedFontID(&fontFileID)) {
++    // use the PDF font name because the embedded font name might
++    // not include the subset prefix
++    psName = filterPSName(font->getOrigName());
++    setupEmbeddedType1CFont(font, &fontFileID, psName);
++
++  // check for embedded OpenType - Type 1C font
++  } else if (globalParams->getPSEmbedType1() &&
++	     font->getType() == fontType1COT &&
++	     font->getEmbeddedFontID(&fontFileID)) {
++    // use the PDF font name because the embedded font name might
++    // not include the subset prefix
++    psName = filterPSName(font->getOrigName());
++    setupEmbeddedOpenTypeT1CFont(font, &fontFileID, psName);
++
++  // check for external Type 1 font file
++  } else if (globalParams->getPSEmbedType1() &&
++	     font->getType() == fontType1 &&
++	     font->getExtFontFile()) {
++    // this assumes that the PS font name matches the PDF font name
++    psName = font->getName()->copy();
++    setupExternalType1Font(font->getExtFontFile(), psName);
++
++  // check for embedded TrueType font
++  } else if (globalParams->getPSEmbedTrueType() &&
++	     (font->getType() == fontTrueType ||
++	      font->getType() == fontTrueTypeOT) &&
++	     font->getEmbeddedFontID(&fontFileID)) {
++    psName = filterPSName(font->getEmbeddedFontName());
++    setupEmbeddedTrueTypeFont(font, &fontFileID, psName);
++
++  // check for external TrueType font file
++  } else if (globalParams->getPSEmbedTrueType() &&
++	     font->getType() == fontTrueType &&
++	     font->getExtFontFile()) {
++    psName = setupExternalTrueTypeFont(font);
++
++  // check for embedded CID PostScript font
++  } else if (globalParams->getPSEmbedCIDPostScript() &&
++	     font->getType() == fontCIDType0C &&
++	     font->getEmbeddedFontID(&fontFileID)) {
++    psName = filterPSName(font->getEmbeddedFontName());
++    setupEmbeddedCIDType0Font(font, &fontFileID, psName);
++
++  // check for embedded CID TrueType font
++  } else if (globalParams->getPSEmbedCIDTrueType() &&
++	     (font->getType() == fontCIDType2 ||
++	      font->getType() == fontCIDType2OT) &&
++	     font->getEmbeddedFontID(&fontFileID)) {
++    psName = filterPSName(font->getEmbeddedFontName());
++    //~ should check to see if font actually uses vertical mode
++    setupEmbeddedCIDTrueTypeFont(font, &fontFileID, psName, gTrue);
++
++  // check for embedded OpenType - CID CFF font
++  } else if (globalParams->getPSEmbedCIDPostScript() &&
++	     font->getType() == fontCIDType0COT &&
++	     font->getEmbeddedFontID(&fontFileID)) {
++    psName = filterPSName(font->getEmbeddedFontName());
++    setupEmbeddedOpenTypeCFFFont(font, &fontFileID, psName);
++
++  // check for Type 3 font
++  } else if (font->getType() == fontType3) {
++    psName = GString::format("T3_{0:d}_{1:d}",
++			     font->getID()->num, font->getID()->gen);
++    setupType3Font(font, psName, parentResDict);
++  // check for external CID TrueType font file
++  } else if (globalParams->getPSEmbedCIDTrueType() &&
++	     font->getType() == fontCIDType2 &&
++	     font->getExtFontFile()) {
++    psName = setupExternalCIDTrueTypeFont(font, font->getExtFontFile());
++  // do 8-bit font substitution
++  } else if (!font->isCIDFont()) {
++    subst = gTrue;
++    name = font->getName();
++    psName = NULL;
++    if (name) {
++      for (i = 0; psFonts[i]; ++i) {
++	if (name->cmp(psFonts[i]) == 0) {
++	  psName = new GString(psFonts[i]);
++	  break;
++	}
++      }
++    }
++    if (!psName) {
++      if (font->isFixedWidth()) {
++	i = 8;
++      } else if (font->isSerif()) {
++	i = 4;
++      } else {
++	i = 0;
++      }
++      if (font->isBold()) {
++	i += 2;
++      }
++      if (font->isItalic()) {
++	i += 1;
++      }
++      psName = new GString(psSubstFonts[i].psName);
++      for (code = 0; code < 256; ++code) {
++	if ((charName = ((Gfx8BitFont *)font)->getCharName(code)) &&
++	    charName[0] == 'm' && charName[1] == '\0') {
++	  break;
++	}
++      }
++      if (code < 256) {
++	w1 = ((Gfx8BitFont *)font)->getWidth(code);
++      } else {
++	w1 = 0;
++      }
++      w2 = psSubstFonts[i].mWidth;
++      xs = w1 / w2;
++      if (xs < 0.1) {
++	xs = 1;
++      }
++      if (font->getType() == fontType3) {
++	// This is a hack which makes it possible to substitute for some
++	// Type 3 fonts.  The problem is that it's impossible to know what
++	// the base coordinate system used in the font is without actually
++	// rendering the font.
++	ys = xs;
++	fm = font->getFontMatrix();
++	if (fm[0] != 0) {
++	  ys *= fm[3] / fm[0];
++	}
++      } else {
++	ys = 1;
++      }
++    }
++
++  // do 16-bit font substitution
++  } else if ((fontParam = globalParams->
++	        getPSFont16(font->getName(),
++			    ((GfxCIDFont *)font)->getCollection(),
++			    font->getWMode()))) {
++    subst = gTrue;
++    psName = fontParam->psFontName->copy();
++    if (font16EncLen >= font16EncSize) {
++      font16EncSize += 16;
++      font16Enc = (PSFont16Enc *)greallocn(font16Enc,
++					   font16EncSize, sizeof(PSFont16Enc));
++    }
++    font16Enc[font16EncLen].fontID = *font->getID();
++    font16Enc[font16EncLen].enc = fontParam->encoding->copy();
++    if ((uMap = globalParams->getUnicodeMap(font16Enc[font16EncLen].enc))) {
++      uMap->decRefCnt();
++      ++font16EncLen;
++    } else {
++      error(-1, "Couldn't find Unicode map for 16-bit font encoding '%s'",
++	    font16Enc[font16EncLen].enc->getCString());
++    }
++
++    // try the display font for embedding
++  } else if (globalParams->getPSEmbedCIDTrueType() &&
++	     ((GfxCIDFont *)font)->getCollection() &&
++	     (dfp = globalParams->
++	      getDisplayCIDFont(font->getName(),
++				((GfxCIDFont *)font)->getCollection())) &&
++	     dfp->kind == displayFontTT) {
++    psName = setupExternalCIDTrueTypeFont(font, dfp->tt.fileName, dfp->tt.faceIndex);
++
++  // give up - can't do anything with this font
++  } else {
++    error(-1, "Couldn't find a font to substitute for '%s' ('%s' character collection)",
++	  font->getName() ? font->getName()->getCString() : "(unnamed)",
++	  ((GfxCIDFont *)font)->getCollection()
++	    ? ((GfxCIDFont *)font)->getCollection()->getCString()
++	    : "(unknown)");
++    return;
++  }
++
++  // generate PostScript code to set up the font
++  if (font->isCIDFont()) {
++    if (level == psLevel3 || level == psLevel3Sep) {
++      writePSFmt("/F{0:d}_{1:d} /{2:t} {3:d} pdfMakeFont16L3\n",
++		 font->getID()->num, font->getID()->gen, psName,
++		 font->getWMode());
++    } else {
++      writePSFmt("/F{0:d}_{1:d} /{2:t} {3:d} pdfMakeFont16\n",
++		 font->getID()->num, font->getID()->gen, psName,
++		 font->getWMode());
++    }
++  } else {
++    writePSFmt("/F{0:d}_{1:d} /{2:t} {3:.4g} {4:.4g}\n",
++	       font->getID()->num, font->getID()->gen, psName, xs, ys);
++    for (i = 0; i < 256; i += 8) {
++      writePS((char *)((i == 0) ? "[ " : "  "));
++      for (j = 0; j < 8; ++j) {
++	if (font->getType() == fontTrueType &&
++	    !subst &&
++	    !((Gfx8BitFont *)font)->getHasEncoding()) {
++	  sprintf(buf, "c%02x", i+j);
++	  charName = buf;
++	} else {
++	  charName = ((Gfx8BitFont *)font)->getCharName(i+j);
++	  // this is a kludge for broken PDF files that encode char 32
++	  // as .notdef
++	  if (i+j == 32 && charName && !strcmp(charName, ".notdef")) {
++	    charName = "space";
++	  }
++	}
++	writePS("/");
++	writePSName(charName ? charName : (char *)".notdef");
++	// the empty name is legal in PDF and PostScript, but PostScript
++	// uses a double-slash (//...) for "immediately evaluated names",
++	// so we need to add a space character here
++	if (charName && !charName[0]) {
++	  writePS(" ");
++	}
++      }
++      writePS((i == 256-8) ? (char *)"]\n" : (char *)"\n");
++    }
++    writePS("pdfMakeFont\n");
++  }
++
++  delete psName;
++}
++
++void PSOutputDev::setupEmbeddedType1Font(Ref *id, GString *psName) {
++  static char hexChar[17] = "0123456789abcdef";
++  Object refObj, strObj, obj1, obj2, obj3;
++  Dict *dict;
++  int length1, length2, length3;
++  int c;
++  int start[4];
++  GBool binMode;
++  int i;
++
++  // check if font is already embedded
++  for (i = 0; i < fontFileIDLen; ++i) {
++    if (fontFileIDs[i].num == id->num &&
++	fontFileIDs[i].gen == id->gen)
++      return;
++  }
++
++  // add entry to fontFileIDs list
++  if (fontFileIDLen >= fontFileIDSize) {
++    fontFileIDSize += 64;
++    fontFileIDs = (Ref *)greallocn(fontFileIDs, fontFileIDSize, sizeof(Ref));
++  }
++  fontFileIDs[fontFileIDLen++] = *id;
++
++  // get the font stream and info
++  refObj.initRef(id->num, id->gen);
++  refObj.fetch(xref, &strObj);
++  refObj.free();
++  if (!strObj.isStream()) {
++    error(-1, "Embedded font file object is not a stream");
++    goto err1;
++  }
++  if (!(dict = strObj.streamGetDict())) {
++    error(-1, "Embedded font stream is missing its dictionary");
++    goto err1;
++  }
++  dict->lookup("Length1", &obj1);
++  dict->lookup("Length2", &obj2);
++  dict->lookup("Length3", &obj3);
++  if (!obj1.isInt() || !obj2.isInt() || !obj3.isInt()) {
++    error(-1, "Missing length fields in embedded font stream dictionary");
++    obj1.free();
++    obj2.free();
++    obj3.free();
++    goto err1;
++  }
++  length1 = obj1.getInt();
++  length2 = obj2.getInt();
++  length3 = obj3.getInt();
++  obj1.free();
++  obj2.free();
++  obj3.free();
++
++  // beginning comment
++  writePSFmt("%%BeginResource: font {0:t}\n", psName);
++  embFontList->append("%%+ font ");
++  embFontList->append(psName->getCString());
++  embFontList->append("\n");
++
++  // copy ASCII portion of font
++  strObj.streamReset();
++  for (i = 0; i < length1 && (c = strObj.streamGetChar()) != EOF; ++i) {
++    writePSChar(c);
++  }
++
++  // figure out if encrypted portion is binary or ASCII
++  binMode = gFalse;
++  for (i = 0; i < 4; ++i) {
++    start[i] = strObj.streamGetChar();
++    if (start[i] == EOF) {
++      error(-1, "Unexpected end of file in embedded font stream");
++      goto err1;
++    }
++    if (!((start[i] >= '0' && start[i] <= '9') ||
++	  (start[i] >= 'A' && start[i] <= 'F') ||
++	  (start[i] >= 'a' && start[i] <= 'f')))
++      binMode = gTrue;
++  }
++
++  // convert binary data to ASCII
++  if (binMode) {
++    for (i = 0; i < 4; ++i) {
++      writePSChar(hexChar[(start[i] >> 4) & 0x0f]);
++      writePSChar(hexChar[start[i] & 0x0f]);
++    }
++#if 0 // this causes trouble for various PostScript printers
++    // if Length2 is incorrect (too small), font data gets chopped, so
++    // we take a few extra characters from the trailer just in case
++    length2 += length3 >= 8 ? 8 : length3;
++#endif
++    while (i < length2) {
++      if ((c = strObj.streamGetChar()) == EOF) {
++	break;
++      }
++      writePSChar(hexChar[(c >> 4) & 0x0f]);
++      writePSChar(hexChar[c & 0x0f]);
++      if (++i % 32 == 0) {
++	writePSChar('\n');
++      }
++    }
++    if (i % 32 > 0) {
++      writePSChar('\n');
++    }
++
++  // already in ASCII format -- just copy it
++  } else {
++    for (i = 0; i < 4; ++i) {
++      writePSChar(start[i]);
++    }
++    for (i = 4; i < length2; ++i) {
++      if ((c = strObj.streamGetChar()) == EOF) {
++	break;
++      }
++      writePSChar(c);
++    }
++  }
++
++  // write padding and "cleartomark"
++  for (i = 0; i < 8; ++i) {
++    writePS("00000000000000000000000000000000"
++	    "00000000000000000000000000000000\n");
++  }
++  writePS("cleartomark\n");
++
++  // ending comment
++  writePS("%%EndResource\n");
++
++ err1:
++  strObj.streamClose();
++  strObj.free();
++}
++
++//~ This doesn't handle .pfb files or binary eexec data (which only
++//~ happens in pfb files?).
++void PSOutputDev::setupExternalType1Font(GString *fileName, GString *psName) {
++  FILE *fontFile;
++  int c;
++  int i;
++
++  // check if font is already embedded
++  for (i = 0; i < fontFileNameLen; ++i) {
++    if (!fontFileNames[i]->cmp(fileName)) {
++      return;
++    }
++  }
++
++  // add entry to fontFileNames list
++  if (fontFileNameLen >= fontFileNameSize) {
++    fontFileNameSize += 64;
++    fontFileNames = (GString **)greallocn(fontFileNames,
++					  fontFileNameSize, sizeof(GString *));
++    psFileNames = (GString **)greallocn(psFileNames,
++					  fontFileNameSize, sizeof(GString *));
++  }
++  fontFileNames[fontFileNameLen] = fileName->copy();
++  psFileNames[fontFileNameLen] = psName->copy();
++  fontFileNameLen++;
++
++  // beginning comment
++  writePSFmt("%%BeginResource: font {0:t}\n", psName);
++  embFontList->append("%%+ font ");
++  embFontList->append(psName->getCString());
++  embFontList->append("\n");
++
++  // copy the font file
++  if (!(fontFile = fopen(fileName->getCString(), "rb"))) {
++    error(-1, "Couldn't open external font file");
++    return;
++  }
++  while ((c = fgetc(fontFile)) != EOF) {
++    writePSChar(c);
++  }
++  fclose(fontFile);
++
++  // ending comment
++  writePS("%%EndResource\n");
++}
++
++void PSOutputDev::setupEmbeddedType1CFont(GfxFont *font, Ref *id,
++					  GString *psName) {
++  char *fontBuf;
++  int fontLen;
++  FoFiType1C *ffT1C;
++  int i;
++
++  // check if font is already embedded
++  for (i = 0; i < fontFileIDLen; ++i) {
++    if (fontFileIDs[i].num == id->num &&
++	fontFileIDs[i].gen == id->gen)
++      return;
++  }
++
++  // add entry to fontFileIDs list
++  if (fontFileIDLen >= fontFileIDSize) {
++    fontFileIDSize += 64;
++    fontFileIDs = (Ref *)greallocn(fontFileIDs, fontFileIDSize, sizeof(Ref));
++  }
++  fontFileIDs[fontFileIDLen++] = *id;
++
++  // beginning comment
++  writePSFmt("%%BeginResource: font {0:t}\n", psName);
++  embFontList->append("%%+ font ");
++  embFontList->append(psName->getCString());
++  embFontList->append("\n");
++
++  // convert it to a Type 1 font
++  fontBuf = font->readEmbFontFile(xref, &fontLen);
++  if ((ffT1C = FoFiType1C::make(fontBuf, fontLen))) {
++    ffT1C->convertToType1(psName->getCString(), NULL, gTrue,
++			  outputFunc, outputStream);
++    delete ffT1C;
++  }
++  gfree(fontBuf);
++
++  // ending comment
++  writePS("%%EndResource\n");
++}
++
++void PSOutputDev::setupEmbeddedOpenTypeT1CFont(GfxFont *font, Ref *id,
++					       GString *psName) {
++  char *fontBuf;
++  int fontLen;
++  FoFiTrueType *ffTT;
++  int i;
++
++  // check if font is already embedded
++  for (i = 0; i < fontFileIDLen; ++i) {
++    if (fontFileIDs[i].num == id->num &&
++	fontFileIDs[i].gen == id->gen)
++      return;
++  }
++
++  // add entry to fontFileIDs list
++  if (fontFileIDLen >= fontFileIDSize) {
++    fontFileIDSize += 64;
++    fontFileIDs = (Ref *)greallocn(fontFileIDs, fontFileIDSize, sizeof(Ref));
++  }
++  fontFileIDs[fontFileIDLen++] = *id;
++
++  // beginning comment
++  writePSFmt("%%BeginResource: font {0:t}\n", psName);
++  embFontList->append("%%+ font ");
++  embFontList->append(psName->getCString());
++  embFontList->append("\n");
++
++  // convert it to a Type 1 font
++  fontBuf = font->readEmbFontFile(xref, &fontLen);
++  if ((ffTT = FoFiTrueType::make(fontBuf, fontLen))) {
++    if (ffTT->isOpenTypeCFF()) {
++      ffTT->convertToType1(psName->getCString(), NULL, gTrue,
++			   outputFunc, outputStream);
++    }
++    delete ffTT;
++  }
++  gfree(fontBuf);
++
++  // ending comment
++  writePS("%%EndResource\n");
++}
++
++void PSOutputDev::setupEmbeddedTrueTypeFont(GfxFont *font, Ref *id,
++					    GString *psName) {
++  char *fontBuf;
++  int fontLen;
++  FoFiTrueType *ffTT;
++  Gushort *codeToGID;
++  int i;
++
++  // check if font is already embedded
++  for (i = 0; i < fontFileIDLen; ++i) {
++    if (fontFileIDs[i].num == id->num &&
++	fontFileIDs[i].gen == id->gen) {
++      psName->appendf("_{0:d}", nextTrueTypeNum++);
++      break;
++    }
++  }
++
++  // add entry to fontFileIDs list
++  if (i == fontFileIDLen) {
++    if (fontFileIDLen >= fontFileIDSize) {
++      fontFileIDSize += 64;
++      fontFileIDs = (Ref *)greallocn(fontFileIDs, fontFileIDSize, sizeof(Ref));
++    }
++    fontFileIDs[fontFileIDLen++] = *id;
++  }
++
++  // beginning comment
++  writePSFmt("%%BeginResource: font {0:t}\n", psName);
++  embFontList->append("%%+ font ");
++  embFontList->append(psName->getCString());
++  embFontList->append("\n");
++
++  // convert it to a Type 42 font
++  fontBuf = font->readEmbFontFile(xref, &fontLen);
++  if ((ffTT = FoFiTrueType::make(fontBuf, fontLen))) {
++    codeToGID = ((Gfx8BitFont *)font)->getCodeToGIDMap(ffTT);
++    ffTT->convertToType42(psName->getCString(),
++			  ((Gfx8BitFont *)font)->getHasEncoding()
++			    ? ((Gfx8BitFont *)font)->getEncoding()
++			    : (char **)NULL,
++			  codeToGID, outputFunc, outputStream);
++    if (codeToGID) {
++      if (font8InfoLen >= font8InfoSize) {
++	font8InfoSize += 16;
++	font8Info = (PSFont8Info *)greallocn(font8Info,
++					     font8InfoSize,
++					     sizeof(PSFont8Info));
++      }
++      font8Info[font8InfoLen].fontID = *font->getID();
++      font8Info[font8InfoLen].codeToGID = codeToGID;
++      ++font8InfoLen;
++    }
++    delete ffTT;
++  }
++  gfree(fontBuf);
++
++  // ending comment
++  writePS("%%EndResource\n");
++}
++
++GString *PSOutputDev::setupExternalTrueTypeFont(GfxFont *font) {
++  GString *fileName;
++  char *fontBuf;
++  int fontLen;
++  FoFiTrueType *ffTT;
++  Gushort *codeToGID;
++  GString *psName;
++  int i;
++
++  // check if font is already embedded
++  fileName = font->getExtFontFile();
++  for (i = 0; i < fontFileNameLen; ++i) {
++    if (!fontFileNames[i]->cmp(fileName)) {
++      return psFileNames[i]->copy();
++    }
++  }
++
++  psName = filterPSName(font->getName());
++  // add entry to fontFileNames list
++  if (i == fontFileNameLen) {
++    if (fontFileNameLen >= fontFileNameSize) {
++      fontFileNameSize += 64;
++      fontFileNames =
++	(GString **)greallocn(fontFileNames,
++			      fontFileNameSize, sizeof(GString *));
++      psFileNames =
++	(GString **)greallocn(psFileNames,
++			      fontFileNameSize, sizeof(GString *));
++    }
++    fontFileNames[fontFileNameLen] = fileName->copy();
++    psFileNames[fontFileNameLen] = psName->copy();
++    fontFileNameLen++;
++  }
++
++  // beginning comment
++  writePSFmt("%%BeginResource: font {0:t}\n", psName);
++  embFontList->append("%%+ font ");
++  embFontList->append(psName->getCString());
++  embFontList->append("\n");
++
++  // convert it to a Type 42 font
++  fontBuf = font->readExtFontFile(&fontLen);
++  if ((ffTT = FoFiTrueType::make(fontBuf, fontLen))) {
++    codeToGID = ((Gfx8BitFont *)font)->getCodeToGIDMap(ffTT);
++    ffTT->convertToType42(psName->getCString(),
++			  ((Gfx8BitFont *)font)->getHasEncoding()
++			    ? ((Gfx8BitFont *)font)->getEncoding()
++			    : (char **)NULL,
++			  codeToGID, outputFunc, outputStream);
++    if (codeToGID) {
++      if (font8InfoLen >= font8InfoSize) {
++	font8InfoSize += 16;
++	font8Info = (PSFont8Info *)greallocn(font8Info,
++					     font8InfoSize,
++					     sizeof(PSFont8Info));
++      }
++      font8Info[font8InfoLen].fontID = *font->getID();
++      font8Info[font8InfoLen].codeToGID = codeToGID;
++      ++font8InfoLen;
++    }
++    delete ffTT;
++  }
++  gfree(fontBuf);
++
++  // ending comment
++  writePS("%%EndResource\n");
++  return psName;
++}
++
++GString *PSOutputDev::setupExternalCIDTrueTypeFont(GfxFont *font, GString *fileName, int faceIndex) {
++  FoFiTrueType *ffTT;
++  Gushort *codeToGID;
++  GString *psName;
++  int i;
++  GString *myFileName;
++
++  myFileName = fileName->copy();
++  if (faceIndex > 0) {
++    char tmp[32];
++    sprintf(tmp, ",%d", faceIndex);
++    myFileName->append(tmp);
++  }
++  // check if font is already embedded
++  for (i = 0; i < fontFileNameLen; ++i) {
++    if (!fontFileNames[i]->cmp(myFileName)) {
++      delete myFileName;
++      return psFileNames[i]->copy();
++    }
++  }
++
++  psName = filterPSName(font->getName());
++  // add entry to fontFileNames list
++  if (i == fontFileNameLen) {
++    if (fontFileNameLen >= fontFileNameSize) {
++      fontFileNameSize += 64;
++      fontFileNames =
++	(GString **)grealloc(fontFileNames,
++			     fontFileNameSize * sizeof(GString *));
++      psFileNames =
++	(GString **)grealloc(psFileNames,
++			     fontFileNameSize * sizeof(GString *));
++    }
++  }
++  fontFileNames[fontFileNameLen] = myFileName;
++  psFileNames[fontFileNameLen] = psName->copy();
++  fontFileNameLen++;
++
++  // beginning comment
++  writePSFmt("%%%%BeginResource: font %s\n", psName->getCString());
++  embFontList->append("%%+ font ");
++  embFontList->append(psName->getCString());
++  embFontList->append("\n");
++
++  // convert it to a CID type2 font
++  if ((ffTT = FoFiTrueType::load(fileName->getCString(), faceIndex))) {
++      int n = ((GfxCIDFont *)font)->getCIDToGIDLen();
++      if (n) {
++	codeToGID = (Gushort *)gmalloc(n * sizeof(Gushort));
++	memcpy(codeToGID, ((GfxCIDFont *)font)->getCIDToGID(), n * sizeof(Gushort));
++      } else {
++	codeToGID = ((GfxCIDFont *)font)->getCodeToGIDMap(ffTT, &n);
++      }
++      if (globalParams->getPSLevel() >= psLevel3) {
++	// Level 3: use a CID font
++	ffTT->convertToCIDType2(psName->getCString(),
++				codeToGID, n, gTrue,
++				outputFunc, outputStream);
++      } else {
++	// otherwise: use a non-CID composite font
++	ffTT->convertToType0(psName->getCString(),
++			     codeToGID, n, gTrue,
++			     outputFunc, outputStream);
++      }
++      gfree(codeToGID);
++      delete ffTT;
++  }
++
++  // ending comment
++  writePS("%%EndResource\n");
++  return psName;
++}
++
++void PSOutputDev::setupEmbeddedCIDType0Font(GfxFont *font, Ref *id,
++					    GString *psName) {
++  char *fontBuf;
++  int fontLen;
++  FoFiType1C *ffT1C;
++  int i;
++
++  // check if font is already embedded
++  for (i = 0; i < fontFileIDLen; ++i) {
++    if (fontFileIDs[i].num == id->num &&
++	fontFileIDs[i].gen == id->gen)
++      return;
++  }
++
++  // add entry to fontFileIDs list
++  if (fontFileIDLen >= fontFileIDSize) {
++    fontFileIDSize += 64;
++    fontFileIDs = (Ref *)greallocn(fontFileIDs, fontFileIDSize, sizeof(Ref));
++  }
++  fontFileIDs[fontFileIDLen++] = *id;
++
++  // beginning comment
++  writePSFmt("%%BeginResource: font {0:t}\n", psName);
++  embFontList->append("%%+ font ");
++  embFontList->append(psName->getCString());
++  embFontList->append("\n");
++
++  // convert it to a Type 0 font
++  fontBuf = font->readEmbFontFile(xref, &fontLen);
++  if ((ffT1C = FoFiType1C::make(fontBuf, fontLen))) {
++    if (globalParams->getPSLevel() >= psLevel3) {
++      // Level 3: use a CID font
++      ffT1C->convertToCIDType0(psName->getCString(), outputFunc, outputStream);
++    } else {
++      // otherwise: use a non-CID composite font
++      ffT1C->convertToType0(psName->getCString(), outputFunc, outputStream);
++    }
++    delete ffT1C;
++  }
++  gfree(fontBuf);
++
++  // ending comment
++  writePS("%%EndResource\n");
++}
++
++void PSOutputDev::setupEmbeddedCIDTrueTypeFont(GfxFont *font, Ref *id,
++					       GString *psName,
++					       GBool needVerticalMetrics) {
++  char *fontBuf;
++  int fontLen;
++  FoFiTrueType *ffTT;
++  int i;
++
++  // check if font is already embedded
++  for (i = 0; i < fontFileIDLen; ++i) {
++    if (fontFileIDs[i].num == id->num &&
++	fontFileIDs[i].gen == id->gen) {
++      psName->appendf("_{0:d}", nextTrueTypeNum++);
++      break;
++    }
++  }
++
++  // add entry to fontFileIDs list
++  if (fontFileIDLen >= fontFileIDSize) {
++    fontFileIDSize += 64;
++    fontFileIDs = (Ref *)greallocn(fontFileIDs, fontFileIDSize, sizeof(Ref));
++  }
++  fontFileIDs[fontFileIDLen++] = *id;
++
++  // beginning comment
++  writePSFmt("%%BeginResource: font {0:t}\n", psName);
++  embFontList->append("%%+ font ");
++  embFontList->append(psName->getCString());
++  embFontList->append("\n");
++
++  // convert it to a Type 0 font
++  fontBuf = font->readEmbFontFile(xref, &fontLen);
++  if ((ffTT = FoFiTrueType::make(fontBuf, fontLen))) {
++    if (globalParams->getPSLevel() >= psLevel3) {
++      // Level 3: use a CID font
++      ffTT->convertToCIDType2(psName->getCString(),
++			      ((GfxCIDFont *)font)->getCIDToGID(),
++			      ((GfxCIDFont *)font)->getCIDToGIDLen(),
++			      needVerticalMetrics,
++			      outputFunc, outputStream);
++    } else {
++      // otherwise: use a non-CID composite font
++      ffTT->convertToType0(psName->getCString(),
++			   ((GfxCIDFont *)font)->getCIDToGID(),
++			   ((GfxCIDFont *)font)->getCIDToGIDLen(),
++			   needVerticalMetrics,
++			   outputFunc, outputStream);
++    }
++    delete ffTT;
++  }
++  gfree(fontBuf);
++
++  // ending comment
++  writePS("%%EndResource\n");
++}
++
++void PSOutputDev::setupEmbeddedOpenTypeCFFFont(GfxFont *font, Ref *id,
++					       GString *psName) {
++  char *fontBuf;
++  int fontLen;
++  FoFiTrueType *ffTT;
++  int i;
++
++  // check if font is already embedded
++  for (i = 0; i < fontFileIDLen; ++i) {
++    if (fontFileIDs[i].num == id->num &&
++	fontFileIDs[i].gen == id->gen)
++      return;
++  }
++
++  // add entry to fontFileIDs list
++  if (fontFileIDLen >= fontFileIDSize) {
++    fontFileIDSize += 64;
++    fontFileIDs = (Ref *)greallocn(fontFileIDs, fontFileIDSize, sizeof(Ref));
++  }
++  fontFileIDs[fontFileIDLen++] = *id;
++
++  // beginning comment
++  writePSFmt("%%BeginResource: font {0:t}\n", psName);
++  embFontList->append("%%+ font ");
++  embFontList->append(psName->getCString());
++  embFontList->append("\n");
++
++  // convert it to a Type 0 font
++  fontBuf = font->readEmbFontFile(xref, &fontLen);
++  if ((ffTT = FoFiTrueType::make(fontBuf, fontLen))) {
++    if (ffTT->isOpenTypeCFF()) {
++      if (globalParams->getPSLevel() >= psLevel3) {
++	// Level 3: use a CID font
++	ffTT->convertToCIDType0(psName->getCString(),
++				outputFunc, outputStream);
++      } else {
++	// otherwise: use a non-CID composite font
++	ffTT->convertToType0(psName->getCString(), outputFunc, outputStream);
++      }
++    }
++    delete ffTT;
++  }
++  gfree(fontBuf);
++
++  // ending comment
++  writePS("%%EndResource\n");
++}
++
++void PSOutputDev::setupType3Font(GfxFont *font, GString *psName,
++				 Dict *parentResDict) {
++  Dict *resDict;
++  Dict *charProcs;
++  Object charProc;
++  Gfx *gfx;
++  PDFRectangle box;
++  double *m;
++  GString *buf;
++  int i;
++
++  // set up resources used by font
++  if ((resDict = ((Gfx8BitFont *)font)->getResources())) {
++    inType3Char = gTrue;
++    setupResources(resDict);
++    inType3Char = gFalse;
++  } else {
++    resDict = parentResDict;
++  }
++
++  // beginning comment
++  writePSFmt("%%BeginResource: font {0:t}\n", psName);
++  embFontList->append("%%+ font ");
++  embFontList->append(psName->getCString());
++  embFontList->append("\n");
++
++  // font dictionary
++  writePS("8 dict begin\n");
++  writePS("/FontType 3 def\n");
++  m = font->getFontMatrix();
++  writePSFmt("/FontMatrix [{0:.4g} {1:.4g} {2:.4g} {3:.4g} {4:.4g} {5:.4g}] def\n",
++	     m[0], m[1], m[2], m[3], m[4], m[5]);
++  m = font->getFontBBox();
++  writePSFmt("/FontBBox [{0:.4g} {1:.4g} {2:.4g} {3:.4g}] def\n",
++	     m[0], m[1], m[2], m[3]);
++  writePS("/Encoding 256 array def\n");
++  writePS("  0 1 255 { Encoding exch /.notdef put } for\n");
++  writePS("/BuildGlyph {\n");
++  writePS("  exch /CharProcs get exch\n");
++  writePS("  2 copy known not { pop /.notdef } if\n");
++  writePS("  get exec\n");
++  writePS("} bind def\n");
++  writePS("/BuildChar {\n");
++  writePS("  1 index /Encoding get exch get\n");
++  writePS("  1 index /BuildGlyph get exec\n");
++  writePS("} bind def\n");
++  if ((charProcs = ((Gfx8BitFont *)font)->getCharProcs())) {
++    writePSFmt("/CharProcs {0:d} dict def\n", charProcs->getLength());
++    writePS("CharProcs begin\n");
++    box.x1 = m[0];
++    box.y1 = m[1];
++    box.x2 = m[2];
++    box.y2 = m[3];
++    gfx = new Gfx(xref, this, resDict, &box, NULL);
++    inType3Char = gTrue;
++    for (i = 0; i < charProcs->getLength(); ++i) {
++      t3Cacheable = gFalse;
++      t3NeedsRestore = gFalse;
++      writePS("/");
++      writePSName(charProcs->getKey(i));
++      writePS(" {\n");
++      gfx->display(charProcs->getVal(i, &charProc));
++      charProc.free();
++      if (t3String) {
++	if (t3Cacheable) {
++	  buf = GString::format("{0:.4g} {1:.4g} {2:.4g} {3:.4g} {4:.4g} {5:.4g} setcachedevice\n",
++				t3WX, t3WY, t3LLX, t3LLY, t3URX, t3URY);
++	} else {
++	  buf = GString::format("{0:.4g} {1:.4g} setcharwidth\n", t3WX, t3WY);
++	}
++	(*outputFunc)(outputStream, buf->getCString(), buf->getLength());
++	delete buf;
++	(*outputFunc)(outputStream, t3String->getCString(),
++		      t3String->getLength());
++	delete t3String;
++	t3String = NULL;
++      }
++      if (t3NeedsRestore) {
++	(*outputFunc)(outputStream, "Q\n", 2);
++      }
++      writePS("} def\n");
++    }
++    inType3Char = gFalse;
++    delete gfx;
++    writePS("end\n");
++  }
++  writePS("currentdict end\n");
++  writePSFmt("/{0:t} exch definefont pop\n", psName);
++
++  // ending comment
++  writePS("%%EndResource\n");
++}
++
++void PSOutputDev::setupImages(Dict *resDict) {
++  Object xObjDict, xObj, xObjRef, subtypeObj;
++  int i;
++
++  if (!(mode == psModeForm || inType3Char || preload)) {
++    return;
++  }
++
++  resDict->lookup("XObject", &xObjDict);
++  if (xObjDict.isDict()) {
++    for (i = 0; i < xObjDict.dictGetLength(); ++i) {
++      xObjDict.dictGetValNF(i, &xObjRef);
++      xObjDict.dictGetVal(i, &xObj);
++      if (xObj.isStream()) {
++	xObj.streamGetDict()->lookup("Subtype", &subtypeObj);
++	if (subtypeObj.isName("Image")) {
++	  if (xObjRef.isRef()) {
++	    setupImage(xObjRef.getRef(), xObj.getStream());
++	  } else {
++	    error(-1, "Image in resource dict is not an indirect reference");
++	  }
++	}
++	subtypeObj.free();
++      }
++      xObj.free();
++      xObjRef.free();
++    }
++  }
++  xObjDict.free();
++}
++
++void PSOutputDev::setupImage(Ref id, Stream *str) {
++  GBool useRLE, useCompressed, useASCIIHex;
++  GString *s;
++  int c;
++  int size, line, col, i;
++
++  // check if image is already setup
++  for (i = 0; i < imgIDLen; ++i) {
++    if (imgIDs[i].num == id.num && imgIDs[i].gen == id.gen) {
++      return;
++    }
++  }
++
++  // add entry to imgIDs list
++  if (imgIDLen >= imgIDSize) {
++    if (imgIDSize == 0) {
++      imgIDSize = 64;
++    } else {
++      imgIDSize *= 2;
++    }
++    imgIDs = (Ref *)greallocn(imgIDs, imgIDSize, sizeof(Ref));
++  }
++  imgIDs[imgIDLen++] = id;
++
++  // filters
++  //~ this does not correctly handle the DeviceN color space
++  //~   -- need to use DeviceNRecoder
++  if (level < psLevel2) {
++    useRLE = gFalse;
++    useCompressed = gFalse;
++    useASCIIHex = gTrue;
++  } else {
++    s = str->getPSFilter(level < psLevel3 ? 2 : 3, "");
++    if (s) {
++      useRLE = gFalse;
++      useCompressed = gTrue;
++      delete s;
++    } else {
++      useRLE = gTrue;
++      useCompressed = gFalse;
++    }
++    useASCIIHex = level == psLevel1 || level == psLevel1Sep ||
++                  globalParams->getPSASCIIHex();
++  }
++  if (useCompressed) {
++    str = str->getUndecodedStream();
++  }
++  if (useRLE) {
++    str = new RunLengthEncoder(str);
++  }
++  if (useASCIIHex) {
++    str = new ASCIIHexEncoder(str);
++  } else {
++    str = new ASCII85Encoder(str);
++  }
++
++  // compute image data size
++  str->reset();
++  col = size = 0;
++  do {
++    do {
++      c = str->getChar();
++    } while (c == '\n' || c == '\r');
++    if (c == (useASCIIHex ? '>' : '~') || c == EOF) {
++      break;
++    }
++    if (c == 'z') {
++      ++col;
++    } else {
++      ++col;
++      for (i = 1; i <= (useASCIIHex ? 1 : 4); ++i) {
++	do {
++	  c = str->getChar();
++	} while (c == '\n' || c == '\r');
++	if (c == (useASCIIHex ? '>' : '~') || c == EOF) {
++	  break;
++	}
++	++col;
++      }
++    }
++    if (col > 225) {
++      ++size;
++      col = 0;
++    }
++  } while (c != (useASCIIHex ? '>' : '~') && c != EOF);
++  // add one entry for the final line of data; add another entry
++  // because the RunLengthDecode filter may read past the end
++  ++size;
++  if (useRLE) {
++    ++size;
++  }
++  writePSFmt("{0:d} array dup /ImData_{1:d}_{2:d} exch def\n",
++	     size, id.num, id.gen);
++  str->close();
++
++  // write the data into the array
++  str->reset();
++  line = col = 0;
++  writePS((char *)(useASCIIHex ? "dup 0 <" : "dup 0 <~"));
++  do {
++    do {
++      c = str->getChar();
++    } while (c == '\n' || c == '\r');
++    if (c == (useASCIIHex ? '>' : '~') || c == EOF) {
++      break;
++    }
++    if (c == 'z') {
++      writePSChar(c);
++      ++col;
++    } else {
++      writePSChar(c);
++      ++col;
++      for (i = 1; i <= (useASCIIHex ? 1 : 4); ++i) {
++	do {
++	  c = str->getChar();
++	} while (c == '\n' || c == '\r');
++	if (c == (useASCIIHex ? '>' : '~') || c == EOF) {
++	  break;
++	}
++	writePSChar(c);
++	++col;
++      }
++    }
++    // each line is: "dup nnnnn <~...data...~> put<eol>"
++    // so max data length = 255 - 20 = 235
++    // chunks are 1 or 4 bytes each, so we have to stop at 232
++    // but make it 225 just to be safe
++    if (col > 225) {
++      writePS((char *)(useASCIIHex ? "> put\n" : "~> put\n"));
++      ++line;
++      writePSFmt((char *)(useASCIIHex ? "dup {0:d} <" : "dup {0:d} <~"), line);
++      col = 0;
++    }
++  } while (c != (useASCIIHex ? '>' : '~') && c != EOF);
++  writePS((char *)(useASCIIHex ? "> put\n" : "~> put\n"));
++  if (useRLE) {
++    ++line;
++    writePSFmt("{0:d} <> put\n", line);
++  } else {
++    writePS("pop\n");
++  }
++  str->close();
++
++  delete str;
++}
++
++void PSOutputDev::setupForms(Dict *resDict) {
++  Object xObjDict, xObj, xObjRef, subtypeObj;
++  int i;
++
++  if (!preload) {
++    return;
++  }
++
++  resDict->lookup("XObject", &xObjDict);
++  if (xObjDict.isDict()) {
++    for (i = 0; i < xObjDict.dictGetLength(); ++i) {
++      xObjDict.dictGetValNF(i, &xObjRef);
++      xObjDict.dictGetVal(i, &xObj);
++      if (xObj.isStream()) {
++	xObj.streamGetDict()->lookup("Subtype", &subtypeObj);
++	if (subtypeObj.isName("Form")) {
++	  if (xObjRef.isRef()) {
++	    setupForm(xObjRef.getRef(), &xObj);
++	  } else {
++	    error(-1, "Form in resource dict is not an indirect reference");
++	  }
++	}
++	subtypeObj.free();
++      }
++      xObj.free();
++      xObjRef.free();
++    }
++  }
++  xObjDict.free();
++}
++
++void PSOutputDev::setupForm(Ref id, Object *strObj) {
++  Dict *dict, *resDict;
++  Object matrixObj, bboxObj, resObj, obj1;
++  double m[6], bbox[4];
++  PDFRectangle box;
++  Gfx *gfx;
++  int i;
++
++  // check if form is already defined
++  for (i = 0; i < formIDLen; ++i) {
++    if (formIDs[i].num == id.num && formIDs[i].gen == id.gen) {
++      return;
++    }
++  }
++
++  // add entry to formIDs list
++  if (formIDLen >= formIDSize) {
++    if (formIDSize == 0) {
++      formIDSize = 64;
++    } else {
++      formIDSize *= 2;
++    }
++    formIDs = (Ref *)greallocn(formIDs, formIDSize, sizeof(Ref));
++  }
++  formIDs[formIDLen++] = id;
++
++  dict = strObj->streamGetDict();
++
++  // get bounding box
++  dict->lookup("BBox", &bboxObj);
++  if (!bboxObj.isArray()) {
++    bboxObj.free();
++    error(-1, "Bad form bounding box");
++    return;
++  }
++  for (i = 0; i < 4; ++i) {
++    bboxObj.arrayGet(i, &obj1);
++    bbox[i] = obj1.getNum();
++    obj1.free();
++  }
++  bboxObj.free();
++
++  // get matrix
++  dict->lookup("Matrix", &matrixObj);
++  if (matrixObj.isArray()) {
++    for (i = 0; i < 6; ++i) {
++      matrixObj.arrayGet(i, &obj1);
++      m[i] = obj1.getNum();
++      obj1.free();
++    }
++  } else {
++    m[0] = 1; m[1] = 0;
++    m[2] = 0; m[3] = 1;
++    m[4] = 0; m[5] = 0;
++  }
++  matrixObj.free();
++
++  // get resources
++  dict->lookup("Resources", &resObj);
++  resDict = resObj.isDict() ? resObj.getDict() : (Dict *)NULL;
++
++  writePSFmt("/f_{0:d}_{1:d} {{\n", id.num, id.gen);
++  writePS("q\n");
++  writePSFmt("[{0:.4g} {1:.4g} {2:.4g} {3:.4g} {4:.4g} {5:.4g}] cm\n",
++	     m[0], m[1], m[2], m[3], m[4], m[5]);
++
++  box.x1 = bbox[0];
++  box.y1 = bbox[1];
++  box.x2 = bbox[2];
++  box.y2 = bbox[3];
++  gfx = new Gfx(xref, this, resDict, &box, &box);
++  gfx->display(strObj);
++  delete gfx;
++
++  writePS("Q\n");
++  writePS("} def\n");
++
++  resObj.free();
++}
++
++GBool PSOutputDev::checkPageSlice(Page *page, double /*hDPI*/, double /*vDPI*/,
++				  int rotateA, GBool useMediaBox, GBool crop,
++				  int sliceX, int sliceY,
++				  int sliceW, int sliceH,
++				  GBool printing, Catalog *catalog,
++				  GBool (*abortCheckCbk)(void *data),
++				  void *abortCheckCbkData) {
++#if HAVE_SPLASH
++  PreScanOutputDev *scan;
++  GBool rasterize;
++  SplashOutputDev *splashOut;
++  SplashColor paperColor;
++  PDFRectangle box;
++  GfxState *state;
++  SplashBitmap *bitmap;
++  Stream *str0, *str;
++  Object obj;
++  Guchar *p;
++  Guchar col[4];
++  double m0, m1, m2, m3, m4, m5;
++  int c, w, h, x, y, comp, i;
++
++  if (!forceRasterize) {
++    scan = new PreScanOutputDev();
++    page->displaySlice(scan, 72, 72, rotateA, useMediaBox, crop,
++		     sliceX, sliceY, sliceW, sliceH,
++		     printing, catalog, abortCheckCbk, abortCheckCbkData);
++    rasterize = scan->usesTransparency();
++    delete scan;
++  } else {
++    rasterize = true;
++  }
++  if (!rasterize) {
++    return gTrue;
++  }
++
++  // rasterize the page
++  if (level == psLevel1) {
++    paperColor[0] = 0xff;
++    splashOut = new SplashOutputDev(splashModeMono8, 1, gFalse,
++				    paperColor, gTrue, gFalse);
++#if SPLASH_CMYK
++  } else if (level == psLevel1Sep) {
++    paperColor[0] = paperColor[1] = paperColor[2] = paperColor[3] = 0;
++    splashOut = new SplashOutputDev(splashModeCMYK8, 1, gFalse,
++				    paperColor, gTrue, gFalse);
++#endif
++  } else {
++    paperColor[0] = paperColor[1] = paperColor[2] = 0xff;
++    splashOut = new SplashOutputDev(splashModeRGB8, 1, gFalse,
++				    paperColor, gTrue, gFalse);
++  }
++  splashOut->startDoc(xref);
++  page->displaySlice(splashOut, splashDPI, splashDPI, rotateA,
++		     useMediaBox, crop,
++		     sliceX, sliceY, sliceW, sliceH,
++		     printing, catalog, abortCheckCbk, abortCheckCbkData);
++
++  // start the PS page
++  page->makeBox(splashDPI, splashDPI, rotateA, useMediaBox, gFalse,
++		sliceX, sliceY, sliceW, sliceH, &box, &crop);
++  rotateA += page->getRotate();
++  if (rotateA >= 360) {
++    rotateA -= 360;
++  } else if (rotateA < 0) {
++    rotateA += 360;
++  }
++  state = new GfxState(splashDPI, splashDPI, &box, rotateA, gFalse);
++  startPage(page->getNum(), state);
++  delete state;
++  switch (rotateA) {
++  case 0:
++  default:  // this should never happen
++    m0 = box.x2 - box.x1;
++    m1 = 0;
++    m2 = 0;
++    m3 = box.y2 - box.y1;
++    m4 = box.x1;
++    m5 = box.y1;
++    break;
++  case 90:
++    m0 = 0;
++    m1 = box.y2 - box.y1;
++    m2 = -(box.x2 - box.x1);
++    m3 = 0;
++    m4 = box.x2;
++    m5 = box.y1;
++    break;
++  case 180:
++    m0 = -(box.x2 - box.x1);
++    m1 = 0;
++    m2 = 0;
++    m3 = -(box.y2 - box.y1);
++    m4 = box.x2;
++    m5 = box.y2;
++    break;
++  case 270:
++    m0 = 0;
++    m1 = -(box.y2 - box.y1);
++    m2 = box.x2 - box.x1;
++    m3 = 0;
++    m4 = box.x1;
++    m5 = box.y2;
++    break;
++  }
++
++  //~ need to add the process colors
++
++  // draw the rasterized image
++  bitmap = splashOut->getBitmap();
++  w = bitmap->getWidth();
++  h = bitmap->getHeight();
++  writePS("gsave\n");
++  writePSFmt("[{0:.4g} {1:.4g} {2:.4g} {3:.4g} {4:.4g} {5:.4g}] concat\n",
++	     m0, m1, m2, m3, m4, m5);
++  switch (level) {
++  case psLevel1:
++    writePSFmt("{0:d} {1:d} 8 [{2:d} 0 0 {3:d} 0 {4:d}] pdfIm1\n",
++	       w, h, w, -h, h);
++    p = bitmap->getDataPtr();
++    i = 0;
++    for (y = 0; y < h; ++y) {
++      for (x = 0; x < w; ++x) {
++	writePSFmt("{0:02x}", *p++);
++	if (++i == 32) {
++	  writePSChar('\n');
++	  i = 0;
++	}
++      }
++    }
++    if (i != 0) {
++      writePSChar('\n');
++    }
++    break;
++  case psLevel1Sep:
++    writePSFmt("{0:d} {1:d} 8 [{2:d} 0 0 {3:d} 0 {4:d}] pdfIm1Sep\n",
++	       w, h, w, -h, h);
++    p = bitmap->getDataPtr();
++    i = 0;
++    col[0] = col[1] = col[2] = col[3] = 0;
++    for (y = 0; y < h; ++y) {
++      for (comp = 0; comp < 4; ++comp) {
++	for (x = 0; x < w; ++x) {
++	  writePSFmt("{0:02x}", p[4*x + comp]);
++	  col[comp] |= p[4*x + comp];
++	  if (++i == 32) {
++	    writePSChar('\n');
++	    i = 0;
++	  }
++	}
++      }
++      p += bitmap->getRowSize();
++    }
++    if (i != 0) {
++      writePSChar('\n');
++    }
++    if (col[0]) {
++      processColors |= psProcessCyan;
++    }
++    if (col[1]) {
++      processColors |= psProcessMagenta;
++    }
++    if (col[2]) {
++      processColors |= psProcessYellow;
++    }
++    if (col[3]) {
++      processColors |= psProcessBlack;
++    }
++    break;
++  case psLevel2:
++  case psLevel2Sep:
++  case psLevel3:
++  case psLevel3Sep:
++    writePS("/DeviceRGB setcolorspace\n");
++    writePS("<<\n  /ImageType 1\n");
++    writePSFmt("  /Width {0:d}\n", bitmap->getWidth());
++    writePSFmt("  /Height {0:d}\n", bitmap->getHeight());
++    writePSFmt("  /ImageMatrix [{0:d} 0 0 {1:d} 0 {2:d}]\n", w, -h, h);
++    writePS("  /BitsPerComponent 8\n");
++    writePS("  /Decode [0 1 0 1 0 1]\n");
++    writePS("  /DataSource currentfile\n");
++    if (globalParams->getPSASCIIHex()) {
++      writePS("    /ASCIIHexDecode filter\n");
++    } else {
++      writePS("    /ASCII85Decode filter\n");
++    }
++    writePS("    /RunLengthDecode filter\n");
++    writePS(">>\n");
++    writePS("image\n");
++    obj.initNull();
++    str0 = new MemStream((char *)bitmap->getDataPtr(), 0, w * h * 3, &obj);
++    str = new RunLengthEncoder(str0);
++    if (globalParams->getPSASCIIHex()) {
++      str = new ASCIIHexEncoder(str);
++    } else {
++      str = new ASCII85Encoder(str);
++    }
++    str->reset();
++    while ((c = str->getChar()) != EOF) {
++      writePSChar(c);
++    }
++    str->close();
++    delete str;
++    delete str0;
++    processColors |= psProcessCMYK;
++    break;
++  }
++  delete splashOut;
++  writePS("grestore\n");
++
++  // finish the PS page
++  endPage();
++
++  return gFalse;
++#else
++  return gTrue;
++#endif
++}
++
++void PSOutputDev::startPage(int pageNum, GfxState *state) {
++  int x1, y1, x2, y2, width, height;
++  int imgWidth, imgHeight, imgWidth2, imgHeight2;
++  GBool landscape;
++
++
++  if (mode == psModePS) {
++    writePSFmt("%%Page: {0:d} {1:d}\n", pageNum, seqPage);
++    writePS("%%BeginPageSetup\n");
++  }
++
++  // underlays
++  if (underlayCbk) {
++    (*underlayCbk)(this, underlayCbkData);
++  }
++  if (overlayCbk) {
++    saveState(NULL);
++  }
++
++  switch (mode) {
++
++  case psModePS:
++    // rotate, translate, and scale page
++    imgWidth = imgURX - imgLLX;
++    imgHeight = imgURY - imgLLY;
++    x1 = (int)floor(state->getX1());
++    y1 = (int)floor(state->getY1());
++    x2 = (int)ceil(state->getX2());
++    y2 = (int)ceil(state->getY2());
++    width = x2 - x1;
++    height = y2 - y1;
++    tx = ty = 0;
++    // rotation and portrait/landscape mode
++    if (rotate0 >= 0) {
++      rotate = (360 - rotate0) % 360;
++      landscape = gFalse;
++    } else {
++      rotate = (360 - state->getRotate()) % 360;
++      if (rotate == 0 || rotate == 180) {
++	if (width > height && width > imgWidth) {
++	  rotate += 90;
++	  landscape = gTrue;
++	} else {
++	  landscape = gFalse;
++	}
++      } else { // rotate == 90 || rotate == 270
++	if (height > width && height > imgWidth) {
++	  rotate = 270 - rotate;
++	  landscape = gTrue;
++	} else {
++	  landscape = gFalse;
++	}
++      }
++    }
++    writePSFmt("%%PageOrientation: {0:s}\n",
++	       landscape ? "Landscape" : "Portrait");
++    writePS("pdfStartPage\n");
++    if (rotate == 0) {
++      imgWidth2 = imgWidth;
++      imgHeight2 = imgHeight;
++    } else if (rotate == 90) {
++      writePS("90 rotate\n");
++      ty = -imgWidth;
++      imgWidth2 = imgHeight;
++      imgHeight2 = imgWidth;
++    } else if (rotate == 180) {
++      writePS("180 rotate\n");
++      imgWidth2 = imgWidth;
++      imgHeight2 = imgHeight;
++      tx = -imgWidth;
++      ty = -imgHeight;
++    } else { // rotate == 270
++      writePS("270 rotate\n");
++      tx = -imgHeight;
++      imgWidth2 = imgHeight;
++      imgHeight2 = imgWidth;
++    }
++    // shrink or expand
++    if (xScale0 > 0 && yScale0 > 0) {
++      xScale = xScale0;
++      yScale = yScale0;
++    } else if ((globalParams->getPSShrinkLarger() &&
++	 (width > imgWidth2 || height > imgHeight2)) ||
++	(globalParams->getPSExpandSmaller() &&
++	 (width < imgWidth2 && height < imgHeight2))) {
++      xScale = (double)imgWidth2 / (double)width;
++      yScale = (double)imgHeight2 / (double)height;
++      if (yScale < xScale) {
++	xScale = yScale;
++      } else {
++	yScale = xScale;
++      }
++    } else {
++      xScale = yScale = 1;
++    }
++    // deal with odd bounding boxes or clipping
++    if (clipLLX0 < clipURX0 && clipLLY0 < clipURY0) {
++      tx -= xScale * clipLLX0;
++      ty -= yScale * clipLLY0;
++    } else {
++      tx -= xScale * x1;
++      ty -= yScale * y1;
++    }
++    // center
++    if (tx0 >= 0 && ty0 >= 0) {
++      tx += rotate == 0 ? tx0 : ty0;
++      ty += rotate == 0 ? ty0 : -tx0;
++    } else if (globalParams->getPSCenter()) {
++      if (clipLLX0 < clipURX0 && clipLLY0 < clipURY0) {
++	tx += (imgWidth2 - xScale * (clipURX0 - clipLLX0)) / 2;
++	ty += (imgHeight2 - yScale * (clipURY0 - clipLLY0)) / 2;
++      } else {
++	tx += (imgWidth2 - xScale * width) / 2;
++	ty += (imgHeight2 - yScale * height) / 2;
++      }
++    }
++    tx += rotate == 0 ? imgLLX : imgLLY;
++    ty += rotate == 0 ? imgLLY : -imgLLX;
++    if (tx != 0 || ty != 0) {
++      writePSFmt("{0:.4g} {1:.4g} translate\n", tx, ty);
++    }
++    if (xScale != 1 || yScale != 1) {
++      writePSFmt("{0:.4f} {1:.4f} scale\n", xScale, yScale);
++    }
++    if (clipLLX0 < clipURX0 && clipLLY0 < clipURY0) {
++      writePSFmt("{0:.4g} {1:.4g} {2:.4g} {3:.4g} re W\n",
++		 clipLLX0, clipLLY0, clipURX0 - clipLLX0, clipURY0 - clipLLY0);
++    } else {
++      writePSFmt("{0:d} {1:d} {2:d} {3:d} re W\n", x1, y1, x2 - x1, y2 - y1);
++    }
++
++    writePS("%%EndPageSetup\n");
++    ++seqPage;
++    break;
++
++  case psModeEPS:
++    writePS("pdfStartPage\n");
++    tx = ty = 0;
++    rotate = (360 - state->getRotate()) % 360;
++    if (rotate == 0) {
++    } else if (rotate == 90) {
++      writePS("90 rotate\n");
++      tx = -epsX1;
++      ty = -epsY2;
++    } else if (rotate == 180) {
++      writePS("180 rotate\n");
++      tx = -(epsX1 + epsX2);
++      ty = -(epsY1 + epsY2);
++    } else { // rotate == 270
++      writePS("270 rotate\n");
++      tx = -epsX2;
++      ty = -epsY1;
++    }
++    if (tx != 0 || ty != 0) {
++      writePSFmt("{0:.4g} {1:.4g} translate\n", tx, ty);
++    }
++    xScale = yScale = 1;
++    break;
++
++  case psModeForm:
++    writePS("/PaintProc {\n");
++    writePS("begin xpdf begin\n");
++    writePS("pdfStartPage\n");
++    tx = ty = 0;
++    xScale = yScale = 1;
++    rotate = 0;
++    break;
++  }
++}
++
++void PSOutputDev::endPage() {
++  if (overlayCbk) {
++    restoreState(NULL);
++    (*overlayCbk)(this, overlayCbkData);
++  }
++
++
++  if (mode == psModeForm) {
++    writePS("pdfEndPage\n");
++    writePS("end end\n");
++    writePS("} def\n");
++    writePS("end end\n");
++  } else {
++    if (!manualCtrl) {
++      writePS("showpage\n");
++    }
++    writePS("%%PageTrailer\n");
++    writePageTrailer();
++  }
++}
++
++void PSOutputDev::saveState(GfxState * /*state*/) {
++  writePS("q\n");
++  ++numSaves;
++}
++
++void PSOutputDev::restoreState(GfxState * /*state*/) {
++  writePS("Q\n");
++  --numSaves;
++}
++
++void PSOutputDev::updateCTM(GfxState * /*state*/, double m11, double m12,
++			    double m21, double m22, double m31, double m32) {
++  writePSFmt("[{0:.4g} {1:.4g} {2:.4g} {3:.4g} {4:.4g} {5:.4g}] cm\n",
++	     m11, m12, m21, m22, m31, m32);
++}
++
++void PSOutputDev::updateLineDash(GfxState *state) {
++  double *dash;
++  double start;
++  int length, i;
++
++  state->getLineDash(&dash, &length, &start);
++  writePS("[");
++  for (i = 0; i < length; ++i) {
++    writePSFmt("{0:.4g}{1:w}",
++	       dash[i] < 0 ? 0 : dash[i],
++	       (i == length-1) ? 0 : 1);
++  }
++  writePSFmt("] {0:.4g} d\n", start);
++}
++
++void PSOutputDev::updateFlatness(GfxState *state) {
++  writePSFmt("{0:d} i\n", state->getFlatness());
++}
++
++void PSOutputDev::updateLineJoin(GfxState *state) {
++  writePSFmt("{0:d} j\n", state->getLineJoin());
++}
++
++void PSOutputDev::updateLineCap(GfxState *state) {
++  writePSFmt("{0:d} J\n", state->getLineCap());
++}
++
++void PSOutputDev::updateMiterLimit(GfxState *state) {
++  writePSFmt("{0:.4g} M\n", state->getMiterLimit());
++}
++
++void PSOutputDev::updateLineWidth(GfxState *state) {
++  writePSFmt("{0:.4g} w\n", state->getLineWidth());
++}
++
++void PSOutputDev::updateFillColorSpace(GfxState *state) {
++  switch (level) {
++  case psLevel1:
++  case psLevel1Sep:
++    break;
++  case psLevel2:
++  case psLevel3:
++    if (state->getFillColorSpace()->getMode() != csPattern) {
++      dumpColorSpaceL2(state->getFillColorSpace(), gTrue, gFalse, gFalse);
++      writePS(" cs\n");
++    }
++    break;
++  case psLevel2Sep:
++  case psLevel3Sep:
++    break;
++  }
++}
++
++void PSOutputDev::updateStrokeColorSpace(GfxState *state) {
++  switch (level) {
++  case psLevel1:
++  case psLevel1Sep:
++    break;
++  case psLevel2:
++  case psLevel3:
++    if (state->getStrokeColorSpace()->getMode() != csPattern) {
++      dumpColorSpaceL2(state->getStrokeColorSpace(), gTrue, gFalse, gFalse);
++      writePS(" CS\n");
++    }
++    break;
++  case psLevel2Sep:
++  case psLevel3Sep:
++    break;
++  }
++}
++
++void PSOutputDev::updateFillColor(GfxState *state) {
++  GfxColor color;
++  GfxColor *colorPtr;
++  GfxGray gray;
++  GfxCMYK cmyk;
++  GfxSeparationColorSpace *sepCS;
++  double c, m, y, k;
++  int i;
++
++  switch (level) {
++  case psLevel1:
++    state->getFillGray(&gray);
++    writePSFmt("{0:.4g} g\n", colToDbl(gray));
++    break;
++  case psLevel1Sep:
++    state->getFillCMYK(&cmyk);
++    c = colToDbl(cmyk.c);
++    m = colToDbl(cmyk.m);
++    y = colToDbl(cmyk.y);
++    k = colToDbl(cmyk.k);
++    writePSFmt("{0:.4g} {1:.4g} {2:.4g} {3:.4g} k\n", c, m, y, k);
++    addProcessColor(c, m, y, k);
++    break;
++  case psLevel2:
++  case psLevel3:
++    if (state->getFillColorSpace()->getMode() != csPattern) {
++      colorPtr = state->getFillColor();
++      writePS("[");
++      for (i = 0; i < state->getFillColorSpace()->getNComps(); ++i) {
++	if (i > 0) {
++	  writePS(" ");
++	}
++	writePSFmt("{0:.4g}", colToDbl(colorPtr->c[i]));
++      }
++      writePS("] sc\n");
++    }
++    break;
++  case psLevel2Sep:
++  case psLevel3Sep:
++    if (state->getFillColorSpace()->getMode() == csSeparation) {
++      sepCS = (GfxSeparationColorSpace *)state->getFillColorSpace();
++      color.c[0] = gfxColorComp1;
++      sepCS->getCMYK(&color, &cmyk);
++      writePSFmt("{0:.4g} {1:.4g} {2:.4g} {3:.4g} {4:.4g} ({5:t}) ck\n",
++		 colToDbl(state->getFillColor()->c[0]),
++		 colToDbl(cmyk.c), colToDbl(cmyk.m),
++		 colToDbl(cmyk.y), colToDbl(cmyk.k),
++		 sepCS->getName());
++      addCustomColor(sepCS);
++    } else {
++      state->getFillCMYK(&cmyk);
++      c = colToDbl(cmyk.c);
++      m = colToDbl(cmyk.m);
++      y = colToDbl(cmyk.y);
++      k = colToDbl(cmyk.k);
++      writePSFmt("{0:.4g} {1:.4g} {2:.4g} {3:.4g} k\n", c, m, y, k);
++      addProcessColor(c, m, y, k);
++    }
++    break;
++  }
++  t3Cacheable = gFalse;
++}
++
++void PSOutputDev::updateStrokeColor(GfxState *state) {
++  GfxColor color;
++  GfxColor *colorPtr;
++  GfxGray gray;
++  GfxCMYK cmyk;
++  GfxSeparationColorSpace *sepCS;
++  double c, m, y, k;
++  int i;
++
++  switch (level) {
++  case psLevel1:
++    state->getStrokeGray(&gray);
++    writePSFmt("{0:.4g} G\n", colToDbl(gray));
++    break;
++  case psLevel1Sep:
++    state->getStrokeCMYK(&cmyk);
++    c = colToDbl(cmyk.c);
++    m = colToDbl(cmyk.m);
++    y = colToDbl(cmyk.y);
++    k = colToDbl(cmyk.k);
++    writePSFmt("{0:.4g} {1:.4g} {2:.4g} {3:.4g} K\n", c, m, y, k);
++    addProcessColor(c, m, y, k);
++    break;
++  case psLevel2:
++  case psLevel3:
++    if (state->getStrokeColorSpace()->getMode() != csPattern) {
++      colorPtr = state->getStrokeColor();
++      writePS("[");
++      for (i = 0; i < state->getStrokeColorSpace()->getNComps(); ++i) {
++	if (i > 0) {
++	  writePS(" ");
++	}
++	writePSFmt("{0:.4g}", colToDbl(colorPtr->c[i]));
++      }
++      writePS("] SC\n");
++    }
++    break;
++  case psLevel2Sep:
++  case psLevel3Sep:
++    if (state->getStrokeColorSpace()->getMode() == csSeparation) {
++      sepCS = (GfxSeparationColorSpace *)state->getStrokeColorSpace();
++      color.c[0] = gfxColorComp1;
++      sepCS->getCMYK(&color, &cmyk);
++      writePSFmt("{0:.4g} {1:.4g} {2:.4g} {3:.4g} {4:.4g} ({5:t}) CK\n",
++		 colToDbl(state->getStrokeColor()->c[0]),
++		 colToDbl(cmyk.c), colToDbl(cmyk.m),
++		 colToDbl(cmyk.y), colToDbl(cmyk.k),
++		 sepCS->getName());
++      addCustomColor(sepCS);
++    } else {
++      state->getStrokeCMYK(&cmyk);
++      c = colToDbl(cmyk.c);
++      m = colToDbl(cmyk.m);
++      y = colToDbl(cmyk.y);
++      k = colToDbl(cmyk.k);
++      writePSFmt("{0:.4g} {1:.4g} {2:.4g} {3:.4g} K\n", c, m, y, k);
++      addProcessColor(c, m, y, k);
++    }
++    break;
++  }
++  t3Cacheable = gFalse;
++}
++
++void PSOutputDev::addProcessColor(double c, double m, double y, double k) {
++  if (c > 0) {
++    processColors |= psProcessCyan;
++  }
++  if (m > 0) {
++    processColors |= psProcessMagenta;
++  }
++  if (y > 0) {
++    processColors |= psProcessYellow;
++  }
++  if (k > 0) {
++    processColors |= psProcessBlack;
++  }
++}
++
++void PSOutputDev::addCustomColor(GfxSeparationColorSpace *sepCS) {
++  PSOutCustomColor *cc;
++  GfxColor color;
++  GfxCMYK cmyk;
++
++  for (cc = customColors; cc; cc = cc->next) {
++    if (!cc->name->cmp(sepCS->getName())) {
++      return;
++    }
++  }
++  color.c[0] = gfxColorComp1;
++  sepCS->getCMYK(&color, &cmyk);
++  cc = new PSOutCustomColor(colToDbl(cmyk.c), colToDbl(cmyk.m),
++			    colToDbl(cmyk.y), colToDbl(cmyk.k),
++			    sepCS->getName()->copy());
++  cc->next = customColors;
++  customColors = cc;
++}
++
++void PSOutputDev::updateFillOverprint(GfxState *state) {
++  if (level >= psLevel2) {
++    writePSFmt("{0:s} op\n", state->getFillOverprint() ? "true" : "false");
++  }
++}
++
++void PSOutputDev::updateStrokeOverprint(GfxState *state) {
++  if (level >= psLevel2) {
++    writePSFmt("{0:s} OP\n", state->getStrokeOverprint() ? "true" : "false");
++  }
++}
++
++void PSOutputDev::updateTransfer(GfxState *state) {
++  Function **funcs;
++  int i;
++
++  funcs = state->getTransfer();
++  if (funcs[0] && funcs[1] && funcs[2] && funcs[3]) {
++    if (level >= psLevel2) {
++      for (i = 0; i < 4; ++i) {
++	cvtFunction(funcs[i]);
++      }
++      writePS("setcolortransfer\n");
++    } else {
++      cvtFunction(funcs[3]);
++      writePS("settransfer\n");
++    }
++  } else if (funcs[0]) {
++    cvtFunction(funcs[0]);
++    writePS("settransfer\n");
++  } else {
++    writePS("{} settransfer\n");
++  }
++}
++
++void PSOutputDev::updateFont(GfxState *state) {
++  if (state->getFont()) {
++    writePSFmt("/F{0:d}_{1:d} {2:.4g} Tf\n",
++	       state->getFont()->getID()->num, state->getFont()->getID()->gen,
++	       fabs(state->getFontSize()) < 0.00001 ? 0.00001
++	                                            : state->getFontSize());
++  }
++}
++
++void PSOutputDev::updateTextMat(GfxState *state) {
++  double *mat;
++
++  mat = state->getTextMat();
++  if (fabs(mat[0] * mat[3] - mat[1] * mat[2]) < 0.00001) {
++    // avoid a singular (or close-to-singular) matrix
++    writePSFmt("[0.00001 0 0 0.00001 {0:.4g} {1:.4g}] Tm\n", mat[4], mat[5]);
++  } else {
++    writePSFmt("[{0:.4g} {1:.4g} {2:.4g} {3:.4g} {4:.4g} {5:.4g}] Tm\n",
++	       mat[0], mat[1], mat[2], mat[3], mat[4], mat[5]);
++  }
++}
++
++void PSOutputDev::updateCharSpace(GfxState *state) {
++  writePSFmt("{0:.4g} Tc\n", state->getCharSpace());
++}
++
++void PSOutputDev::updateRender(GfxState *state) {
++  int rm;
++
++  rm = state->getRender();
++  writePSFmt("{0:d} Tr\n", rm);
++  rm &= 3;
++  if (rm != 0 && rm != 3) {
++    t3Cacheable = gFalse;
++  }
++}
++
++void PSOutputDev::updateRise(GfxState *state) {
++  writePSFmt("{0:.4g} Ts\n", state->getRise());
++}
++
++void PSOutputDev::updateWordSpace(GfxState *state) {
++  writePSFmt("{0:.4g} Tw\n", state->getWordSpace());
++}
++
++void PSOutputDev::updateHorizScaling(GfxState *state) {
++  double h;
++
++  h = state->getHorizScaling();
++  if (fabs(h) < 0.01) {
++    h = 0.01;
++  }
++  writePSFmt("{0:.4g} Tz\n", h);
++}
++
++void PSOutputDev::updateTextPos(GfxState *state) {
++  writePSFmt("{0:.4g} {1:.4g} Td\n", state->getLineX(), state->getLineY());
++}
++
++void PSOutputDev::updateTextShift(GfxState *state, double shift) {
++  if (state->getFont()->getWMode()) {
++    writePSFmt("{0:.4g} TJmV\n", shift);
++  } else {
++    writePSFmt("{0:.4g} TJm\n", shift);
++  }
++}
++
++void PSOutputDev::stroke(GfxState *state) {
++  doPath(state->getPath());
++  if (t3String) {
++    // if we're construct a cacheable Type 3 glyph, we need to do
++    // everything in the fill color
++    writePS("Sf\n");
++  } else {
++    writePS("S\n");
++  }
++}
++
++void PSOutputDev::fill(GfxState *state) {
++  doPath(state->getPath());
++  writePS("f\n");
++}
++
++void PSOutputDev::eoFill(GfxState *state) {
++  doPath(state->getPath());
++  writePS("f*\n");
++}
++
++void PSOutputDev::tilingPatternFill(GfxState * /*state*/, Object *str,
++				    int paintType, Dict *resDict,
++				    double *mat, double *bbox,
++				    int x0, int y0, int x1, int y1,
++				    double xStep, double yStep) {
++  PDFRectangle box;
++  Gfx *gfx;
++
++  // define a Type 3 font
++  writePS("8 dict begin\n");
++  writePS("/FontType 3 def\n");
++  writePS("/FontMatrix [1 0 0 1 0 0] def\n");
++  writePSFmt("/FontBBox [{0:.4g} {1:.4g} {2:.4g} {3:.4g}] def\n",
++	     bbox[0], bbox[1], bbox[2], bbox[3]);
++  writePS("/Encoding 256 array def\n");
++  writePS("  0 1 255 { Encoding exch /.notdef put } for\n");
++  writePS("  Encoding 120 /x put\n");
++  writePS("/BuildGlyph {\n");
++  writePS("  exch /CharProcs get exch\n");
++  writePS("  2 copy known not { pop /.notdef } if\n");
++  writePS("  get exec\n");
++  writePS("} bind def\n");
++  writePS("/BuildChar {\n");
++  writePS("  1 index /Encoding get exch get\n");
++  writePS("  1 index /BuildGlyph get exec\n");
++  writePS("} bind def\n");
++  writePS("/CharProcs 1 dict def\n");
++  writePS("CharProcs begin\n");
++  box.x1 = bbox[0];
++  box.y1 = bbox[1];
++  box.x2 = bbox[2];
++  box.y2 = bbox[3];
++  gfx = new Gfx(xref, this, resDict, &box, NULL);
++  writePS("/x {\n");
++  if (paintType == 2) {
++    writePSFmt("{0:.4g} 0 {1:.4g} {2:.4g} {3:.4g} {4:.4g} setcachedevice\n",
++	       xStep, bbox[0], bbox[1], bbox[2], bbox[3]);
++  } else {
++    if (x1 - 1 <= x0) {
++      writePS("1 0 setcharwidth\n");
++    } else {
++      writePSFmt("{0:.4g} 0 setcharwidth\n", xStep);
++    }
++  }
++  inType3Char = gTrue;
++  ++numTilingPatterns;
++  gfx->display(str);
++  --numTilingPatterns;
++  inType3Char = gFalse;
++  writePS("} def\n");
++  delete gfx;
++  writePS("end\n");
++  writePS("currentdict end\n");
++  writePSFmt("/xpdfTile{0:d} exch definefont pop\n", numTilingPatterns);
++
++  // draw the tiles
++  writePSFmt("/xpdfTile{0:d} findfont setfont\n", numTilingPatterns);
++  writePSFmt("gsave [{0:.4g} {1:.4g} {2:.4g} {3:.4g} {4:.4g} {5:.4g}] concat\n",
++	     mat[0], mat[1], mat[2], mat[3], mat[4], mat[5]);
++  writePSFmt("{0:d} 1 {1:d} {{ {2:.4g} exch {3:.4g} mul m {4:d} 1 {5:d} {{ pop (x) show }} for }} for\n",
++	     y0, y1 - 1, x0 * xStep, yStep, x0, x1 - 1);
++  writePS("grestore\n");
++}
++
++GBool PSOutputDev::functionShadedFill(GfxState * /*state*/,
++				      GfxFunctionShading *shading) {
++  double x0, y0, x1, y1;
++  double *mat;
++  int i;
++
++  if (level == psLevel2Sep || level == psLevel3Sep) {
++    if (shading->getColorSpace()->getMode() != csDeviceCMYK) {
++      return gFalse;
++    }
++    processColors |= psProcessCMYK;
++  }
++
++  shading->getDomain(&x0, &y0, &x1, &y1);
++  mat = shading->getMatrix();
++  writePSFmt("/mat [{0:.4g} {1:.4g} {2:.4g} {3:.4g} {4:.4g} {5:.4g}] def\n",
++	     mat[0], mat[1], mat[2], mat[3], mat[4], mat[5]);
++  writePSFmt("/n {0:d} def\n", shading->getColorSpace()->getNComps());
++  if (shading->getNFuncs() == 1) {
++    writePS("/func ");
++    cvtFunction(shading->getFunc(0));
++    writePS("def\n");
++  } else {
++    writePS("/func {\n");
++    for (i = 0; i < shading->getNFuncs(); ++i) {
++      if (i < shading->getNFuncs() - 1) {
++	writePS("2 copy\n");
++      }
++      cvtFunction(shading->getFunc(i));
++      writePS("exec\n");
++      if (i < shading->getNFuncs() - 1) {
++	writePS("3 1 roll\n");
++      }
++    }
++    writePS("} def\n");
++  }
++  writePSFmt("{0:.4g} {1:.4g} {2:.4g} {3:.4g} 0 funcSH\n", x0, y0, x1, y1);
++
++  return gTrue;
++}
++
++GBool PSOutputDev::axialShadedFill(GfxState *state, GfxAxialShading *shading) {
++  double xMin, yMin, xMax, yMax;
++  double x0, y0, x1, y1, dx, dy, mul;
++  double tMin, tMax, t, t0, t1;
++  int i;
++
++  if (level == psLevel2Sep || level == psLevel3Sep) {
++    if (shading->getColorSpace()->getMode() != csDeviceCMYK) {
++      return gFalse;
++    }
++    processColors |= psProcessCMYK;
++  }
++
++  // 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;
++  if (fabs(dx) < 0.01 && fabs(dy) < 0.01) {
++    return gTrue;
++  } else {
++    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();
++
++  // generate the PS code
++  writePSFmt("/t0 {0:.4g} def\n", t0);
++  writePSFmt("/t1 {0:.4g} def\n", t1);
++  writePSFmt("/dt {0:.4g} def\n", t1 - t0);
++  writePSFmt("/x0 {0:.4g} def\n", x0);
++  writePSFmt("/y0 {0:.4g} def\n", y0);
++  writePSFmt("/dx {0:.4g} def\n", x1 - x0);
++  writePSFmt("/x1 {0:.4g} def\n", x1);
++  writePSFmt("/y1 {0:.4g} def\n", y1);
++  writePSFmt("/dy {0:.4g} def\n", y1 - y0);
++  writePSFmt("/xMin {0:.4g} def\n", xMin);
++  writePSFmt("/yMin {0:.4g} def\n", yMin);
++  writePSFmt("/xMax {0:.4g} def\n", xMax);
++  writePSFmt("/yMax {0:.4g} def\n", yMax);
++  writePSFmt("/n {0:d} def\n", shading->getColorSpace()->getNComps());
++  if (shading->getNFuncs() == 1) {
++    writePS("/func ");
++    cvtFunction(shading->getFunc(0));
++    writePS("def\n");
++  } else {
++    writePS("/func {\n");
++    for (i = 0; i < shading->getNFuncs(); ++i) {
++      if (i < shading->getNFuncs() - 1) {
++	writePS("dup\n");
++      }
++      cvtFunction(shading->getFunc(i));
++      writePS("exec\n");
++      if (i < shading->getNFuncs() - 1) {
++	writePS("exch\n");
++      }
++    }
++    writePS("} def\n");
++  }
++  writePSFmt("{0:.4g} {1:.4g} 0 axialSH\n", tMin, tMax);
++
++  return gTrue;
++}
++
++GBool PSOutputDev::radialShadedFill(GfxState *state,
++				    GfxRadialShading *shading) {
++  double xMin, yMin, xMax, yMax;
++  double x0, y0, r0, x1, y1, r1, t0, t1;
++  double xa, ya, ra;
++  double sz, xz, yz, sMin, sMax, sa, ta;
++  double theta, alpha, a1, a2;
++  GBool enclosed;
++  int i;
++
++  if (level == psLevel2Sep || level == psLevel3Sep) {
++    if (shading->getColorSpace()->getMode() != csDeviceCMYK) {
++      return gFalse;
++    }
++    processColors |= psProcessCMYK;
++  }
++
++  // get the shading info
++  shading->getCoords(&x0, &y0, &r0, &x1, &y1, &r1);
++  t0 = shading->getDomain0();
++  t1 = shading->getDomain1();
++
++  // Compute the point at which r(s) = 0; check for the enclosed
++  // circles case; and compute the angles for the tangent lines.
++  if (r0 == r1) {
++    enclosed = x0 == x1 && y0 == y1;
++    theta = 0;
++    sz = 0; // make gcc happy
++  } else {
++    sz = -r0 / (r1 - r0);
++    xz = x0 + sz * (x1 - x0);
++    yz = y0 + sz * (y1 - y0);
++    enclosed = (xz - x0) * (xz - x0) + (yz - y0) * (yz - y0) <= r0 * r0;
++    theta = asin(r0 / sqrt((x0 - xz) * (x0 - xz) + (y0 - yz) * (y0 - yz)));
++    if (r0 > r1) {
++      theta = -theta;
++    }
++  }
++  if (enclosed) {
++    a1 = 0;
++    a2 = 360;
++  } else {
++    alpha = atan2(y1 - y0, x1 - x0);
++    a1 = (180 / M_PI) * (alpha + theta) + 90;
++    a2 = (180 / M_PI) * (alpha - theta) - 90;
++    while (a2 < a1) {
++      a2 += 360;
++    }
++  }
++
++  // compute the (possibly extended) s range
++  state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax);
++  if (enclosed) {
++    sMin = 0;
++    sMax = 1;
++  } else {
++    sMin = 1;
++    sMax = 0;
++    // solve for x(s) + r(s) = xMin
++    if ((x1 + r1) - (x0 + r0) != 0) {
++      sa = (xMin - (x0 + r0)) / ((x1 + r1) - (x0 + r0));
++      if (sa < sMin) {
++	sMin = sa;
++      } else if (sa > sMax) {
++	sMax = sa;
++      }
++    }
++    // solve for x(s) - r(s) = xMax
++    if ((x1 - r1) - (x0 - r0) != 0) {
++      sa = (xMax - (x0 - r0)) / ((x1 - r1) - (x0 - r0));
++      if (sa < sMin) {
++	sMin = sa;
++      } else if (sa > sMax) {
++	sMax = sa;
++      }
++    }
++    // solve for y(s) + r(s) = yMin
++    if ((y1 + r1) - (y0 + r0) != 0) {
++      sa = (yMin - (y0 + r0)) / ((y1 + r1) - (y0 + r0));
++      if (sa < sMin) {
++	sMin = sa;
++      } else if (sa > sMax) {
++	sMax = sa;
++      }
++    }
++    // solve for y(s) - r(s) = yMax
++    if ((y1 - r1) - (y0 - r0) != 0) {
++      sa = (yMax - (y0 - r0)) / ((y1 - r1) - (y0 - r0));
++      if (sa < sMin) {
++	sMin = sa;
++      } else if (sa > sMax) {
++	sMax = sa;
++      }
++    }
++    // check against sz
++    if (r0 < r1) {
++      if (sMin < sz) {
++	sMin = sz;
++      }
++    } else if (r0 > r1) {
++      if (sMax > sz) {
++	sMax = sz;
++      }
++    }
++    // check the 'extend' flags
++    if (!shading->getExtend0() && sMin < 0) {
++      sMin = 0;
++    }
++    if (!shading->getExtend1() && sMax > 1) {
++      sMax = 1;
++    }
++  }
++
++  // generate the PS code
++  writePSFmt("/x0 {0:.4g} def\n", x0);
++  writePSFmt("/x1 {0:.4g} def\n", x1);
++  writePSFmt("/dx {0:.4g} def\n", x1 - x0);
++  writePSFmt("/y0 {0:.4g} def\n", y0);
++  writePSFmt("/y1 {0:.4g} def\n", y1);
++  writePSFmt("/dy {0:.4g} def\n", y1 - y0);
++  writePSFmt("/r0 {0:.4g} def\n", r0);
++  writePSFmt("/r1 {0:.4g} def\n", r1);
++  writePSFmt("/dr {0:.4g} def\n", r1 - r0);
++  writePSFmt("/t0 {0:.4g} def\n", t0);
++  writePSFmt("/t1 {0:.4g} def\n", t1);
++  writePSFmt("/dt {0:.4g} def\n", t1 - t0);
++  writePSFmt("/n {0:d} def\n", shading->getColorSpace()->getNComps());
++  writePSFmt("/encl {0:s} def\n", enclosed ? "true" : "false");
++  writePSFmt("/a1 {0:.4g} def\n", a1);
++  writePSFmt("/a2 {0:.4g} def\n", a2);
++  if (shading->getNFuncs() == 1) {
++    writePS("/func ");
++    cvtFunction(shading->getFunc(0));
++    writePS("def\n");
++  } else {
++    writePS("/func {\n");
++    for (i = 0; i < shading->getNFuncs(); ++i) {
++      if (i < shading->getNFuncs() - 1) {
++	writePS("dup\n");
++      }
++      cvtFunction(shading->getFunc(i));
++      writePS("exec\n");
++      if (i < shading->getNFuncs() - 1) {
++	writePS("exch\n");
++      }
++    }
++    writePS("} def\n");
++  }
++  writePSFmt("{0:.4g} {1:.4g} 0 radialSH\n", sMin, sMax);
++
++  // extend the 'enclosed' case
++  if (enclosed) {
++    // extend the smaller circle
++    if ((shading->getExtend0() && r0 <= r1) ||
++	(shading->getExtend1() && r1 < r0)) {
++      if (r0 <= r1) {
++	ta = t0;
++	ra = r0;
++	xa = x0;
++	ya = y0;
++      } else {
++	ta = t1;
++	ra = r1;
++	xa = x1;
++	ya = y1;
++      }
++      if (level == psLevel2Sep || level == psLevel3Sep) {
++	writePSFmt("{0:.4g} radialCol aload pop k\n", ta);
++      } else {
++	writePSFmt("{0:.4g} radialCol sc\n", ta);
++      }
++      writePSFmt("{0:.4g} {1:.4g} {2:.4g} 0 360 arc h f*\n", xa, ya, ra);
++    }
++
++    // extend the larger circle
++    if ((shading->getExtend0() && r0 > r1) ||
++	(shading->getExtend1() && r1 >= r0)) {
++      if (r0 > r1) {
++	ta = t0;
++	ra = r0;
++	xa = x0;
++	ya = y0;
++      } else {
++	ta = t1;
++	ra = r1;
++	xa = x1;
++	ya = y1;
++      }
++      if (level == psLevel2Sep || level == psLevel3Sep) {
++	writePSFmt("{0:.4g} radialCol aload pop k\n", ta);
++      } else {
++	writePSFmt("{0:.4g} radialCol sc\n", ta);
++      }
++      writePSFmt("{0:.4g} {1:.4g} {2:.4g} 0 360 arc h\n", xa, ya, ra);
++      writePSFmt("{0:.4g} {1:.4g} m {2:.4g} {3:.4g} l {4:.4g} {5:.4g} l {6:.4g} {7:.4g} l h f*\n",
++		 xMin, yMin, xMin, yMax, xMax, yMax, xMax, yMin);
++    }
++  }
++
++  return gTrue;
++}
++
++void PSOutputDev::clip(GfxState *state) {
++  doPath(state->getPath());
++  writePS("W\n");
++}
++
++void PSOutputDev::eoClip(GfxState *state) {
++  doPath(state->getPath());
++  writePS("W*\n");
++}
++
++void PSOutputDev::clipToStrokePath(GfxState *state) {
++  doPath(state->getPath());
++  writePS("Ws\n");
++}
++
++void PSOutputDev::doPath(GfxPath *path) {
++  GfxSubpath *subpath;
++  double x0, y0, x1, y1, x2, y2, x3, y3, x4, y4;
++  int n, m, i, j;
++
++  n = path->getNumSubpaths();
++
++  if (n == 1 && path->getSubpath(0)->getNumPoints() == 5) {
++    subpath = path->getSubpath(0);
++    x0 = subpath->getX(0);
++    y0 = subpath->getY(0);
++    x4 = subpath->getX(4);
++    y4 = subpath->getY(4);
++    if (x4 == x0 && y4 == y0) {
++      x1 = subpath->getX(1);
++      y1 = subpath->getY(1);
++      x2 = subpath->getX(2);
++      y2 = subpath->getY(2);
++      x3 = subpath->getX(3);
++      y3 = subpath->getY(3);
++      if (x0 == x1 && x2 == x3 && y0 == y3 && y1 == y2) {
++	writePSFmt("{0:.4g} {1:.4g} {2:.4g} {3:.4g} re\n",
++		   x0 < x2 ? x0 : x2, y0 < y1 ? y0 : y1,
++		   fabs(x2 - x0), fabs(y1 - y0));
++	return;
++      } else if (x0 == x3 && x1 == x2 && y0 == y1 && y2 == y3) {
++	writePSFmt("{0:.4g} {1:.4g} {2:.4g} {3:.4g} re\n",
++		   x0 < x1 ? x0 : x1, y0 < y2 ? y0 : y2,
++		   fabs(x1 - x0), fabs(y2 - y0));
++	return;
++      }
++    }
++  }
++
++  for (i = 0; i < n; ++i) {
++    subpath = path->getSubpath(i);
++    m = subpath->getNumPoints();
++    writePSFmt("{0:.4g} {1:.4g} m\n", subpath->getX(0), subpath->getY(0));
++    j = 1;
++    while (j < m) {
++      if (subpath->getCurve(j)) {
++	writePSFmt("{0:.4g} {1:.4g} {2:.4g} {3:.4g} {4:.4g} {5:.4g} c\n",
++		   subpath->getX(j), subpath->getY(j),
++		   subpath->getX(j+1), subpath->getY(j+1),
++		   subpath->getX(j+2), subpath->getY(j+2));
++	j += 3;
++      } else {
++	writePSFmt("{0:.4g} {1:.4g} l\n", subpath->getX(j), subpath->getY(j));
++	++j;
++      }
++    }
++    if (subpath->isClosed()) {
++      writePS("h\n");
++    }
++  }
++}
++
++void PSOutputDev::drawString(GfxState *state, GString *s) {
++  GfxFont *font;
++  int wMode;
++  Gushort *codeToGID;
++  GString *s2;
++  double dx, dy, dx2, dy2, originX, originY;
++  char *p;
++  UnicodeMap *uMap;
++  CharCode code;
++  Unicode u[8];
++  char buf[8];
++  int len, nChars, uLen, n, m, i, j;
++
++  // check for invisible text -- this is used by Acrobat Capture
++  if (state->getRender() == 3) {
++    return;
++  }
++
++  // ignore empty strings
++  if (s->getLength() == 0) {
++    return;
++  }
++
++  // get the font
++  if (!(font = state->getFont())) {
++    return;
++  }
++  wMode = font->getWMode();
++
++  // check for a subtitute 16-bit font
++  uMap = NULL;
++  codeToGID = NULL;
++  if (font->isCIDFont()) {
++    for (i = 0; i < font16EncLen; ++i) {
++      if (font->getID()->num == font16Enc[i].fontID.num &&
++	  font->getID()->gen == font16Enc[i].fontID.gen) {
++	uMap = globalParams->getUnicodeMap(font16Enc[i].enc);
++	break;
++      }
++    }
++
++  // check for a code-to-GID map
++  } else {
++    for (i = 0; i < font8InfoLen; ++i) {
++      if (font->getID()->num == font8Info[i].fontID.num &&
++	  font->getID()->gen == font8Info[i].fontID.gen) {
++	codeToGID = font8Info[i].codeToGID;
++	break;
++      }
++    }
++  }
++
++  // compute width of chars in string, ignoring char spacing and word
++  // spacing -- the Tj operator will adjust for the metrics of the
++  // font that's actually used
++  dx = dy = 0;
++  nChars = 0;
++  p = s->getCString();
++  len = s->getLength();
++  s2 = new GString();
++  while (len > 0) {
++    n = font->getNextChar(p, len, &code,
++			  u, (int)(sizeof(u) / sizeof(Unicode)), &uLen,
++			  &dx2, &dy2, &originX, &originY);
++    if (font->isCIDFont()) {
++      if (uMap) {
++	for (i = 0; i < uLen; ++i) {
++	  m = uMap->mapUnicode(u[i], buf, (int)sizeof(buf));
++	  for (j = 0; j < m; ++j) {
++	    s2->append(buf[j]);
++	  }
++	}
++	//~ this really needs to get the number of chars in the target
++	//~ encoding - which may be more than the number of Unicode
++	//~ chars
++	nChars += uLen;
++      } else {
++	s2->append((char)((code >> 8) & 0xff));
++	s2->append((char)(code & 0xff));
++	++nChars;
++      }
++    } else {
++      if (!codeToGID || codeToGID[code]) {
++	s2->append((char)code);
++      }
++    }
++    dx += dx2;
++    dy += dy2;
++    p += n;
++    len -= n;
++  }
++  dx *= state->getFontSize() * state->getHorizScaling();
++  dy *= state->getFontSize();
++  if (uMap) {
++    uMap->decRefCnt();
++  }
++
++  if (s2->getLength() > 0) {
++    writePSString(s2);
++    if (font->isCIDFont()) {
++      if (wMode) {
++	writePSFmt(" {0:d} {1:.4g} Tj16V\n", nChars, dy);
++      } else {
++	writePSFmt(" {0:d} {1:.4g} Tj16\n", nChars, dx);
++      }
++    } else {
++      writePSFmt(" {0:.4g} Tj\n", dx);
++    }
++  }
++  delete s2;
++
++  if (state->getRender() & 4) {
++    haveTextClip = gTrue;
++  }
++}
++
++void PSOutputDev::endTextObject(GfxState * /*state*/) {
++  if (haveTextClip) {
++    writePS("Tclip\n");
++    haveTextClip = gFalse;
++  }
++}
++
++void PSOutputDev::drawImageMask(GfxState * /*state*/, Object *ref, Stream *str,
++				int width, int height, GBool invert,
++				GBool inlineImg) {
++  int len;
++
++  len = height * ((width + 7) / 8);
++  switch (level) {
++  case psLevel1:
++  case psLevel1Sep:
++    doImageL1(ref, NULL, invert, inlineImg, str, width, height, len);
++    break;
++  case psLevel2:
++  case psLevel2Sep:
++    doImageL2(ref, NULL, invert, inlineImg, str, width, height, len,
++	      NULL, NULL, 0, 0, gFalse);
++    break;
++  case psLevel3:
++  case psLevel3Sep:
++    doImageL3(ref, NULL, invert, inlineImg, str, width, height, len,
++	      NULL, NULL, 0, 0, gFalse);
++    break;
++  }
++}
++
++void PSOutputDev::drawImage(GfxState * /*state*/, Object *ref, Stream *str,
++			    int width, int height, GfxImageColorMap *colorMap,
++			    int *maskColors, GBool inlineImg) {
++  int len;
++
++  len = height * ((width * colorMap->getNumPixelComps() *
++		   colorMap->getBits() + 7) / 8);
++  switch (level) {
++  case psLevel1:
++    doImageL1(ref, colorMap, gFalse, inlineImg, str, width, height, len);
++    break;
++  case psLevel1Sep:
++    //~ handle indexed, separation, ... color spaces
++    doImageL1Sep(colorMap, gFalse, inlineImg, str, width, height, len);
++    break;
++  case psLevel2:
++  case psLevel2Sep:
++    doImageL2(ref, colorMap, gFalse, inlineImg, str,
++	      width, height, len, maskColors, NULL, 0, 0, gFalse);
++    break;
++  case psLevel3:
++  case psLevel3Sep:
++    doImageL3(ref, colorMap, gFalse, inlineImg, str,
++	      width, height, len, maskColors, NULL, 0, 0, gFalse);
++    break;
++  }
++  t3Cacheable = gFalse;
++}
++
++void PSOutputDev::drawMaskedImage(GfxState * /*state*/, Object *ref, Stream *str,
++				  int width, int height,
++				  GfxImageColorMap *colorMap,
++				  Stream *maskStr,
++				  int maskWidth, int maskHeight,
++				  GBool maskInvert) {
++  int len;
++
++  len = height * ((width * colorMap->getNumPixelComps() *
++		   colorMap->getBits() + 7) / 8);
++  switch (level) {
++  case psLevel1:
++    doImageL1(ref, colorMap, gFalse, gFalse, str, width, height, len);
++    break;
++  case psLevel1Sep:
++    //~ handle indexed, separation, ... color spaces
++    doImageL1Sep(colorMap, gFalse, gFalse, str, width, height, len);
++    break;
++  case psLevel2:
++  case psLevel2Sep:
++    doImageL2(ref, colorMap, gFalse, gFalse, str, width, height, len,
++	      NULL, maskStr, maskWidth, maskHeight, maskInvert);
++    break;
++  case psLevel3:
++  case psLevel3Sep:
++    doImageL3(ref, colorMap, gFalse, gFalse, str, width, height, len,
++	      NULL, maskStr, maskWidth, maskHeight, maskInvert);
++    break;
++  }
++  t3Cacheable = gFalse;
++}
++
++void PSOutputDev::doImageL1(Object *ref, GfxImageColorMap *colorMap,
++			    GBool invert, GBool inlineImg,
++			    Stream *str, int width, int height, int len) {
++  ImageStream *imgStr;
++  Guchar pixBuf[gfxColorMaxComps];
++  GfxGray gray;
++  int col, x, y, c, i;
++
++  if ((inType3Char || preload) && !colorMap) {
++    if (inlineImg) {
++      // create an array
++      str = new FixedLengthEncoder(str, len);
++      str = new ASCIIHexEncoder(str);
++      str->reset();
++      col = 0;
++      writePS("[<");
++      do {
++	do {
++	  c = str->getChar();
++	} while (c == '\n' || c == '\r');
++	if (c == '>' || c == EOF) {
++	  break;
++	}
++	writePSChar(c);
++	++col;
++	// each line is: "<...data...><eol>"
++	// so max data length = 255 - 4 = 251
++	// but make it 240 just to be safe
++	// chunks are 2 bytes each, so we need to stop on an even col number
++	if (col == 240) {
++	  writePS(">\n<");
++	  col = 0;
++	}
++      } while (c != '>' && c != EOF);
++      writePS(">]\n");
++      writePS("0\n");
++      str->close();
++      delete str;
++    } else {
++      // set up to use the array already created by setupImages()
++      writePSFmt("ImData_{0:d}_{1:d} 0\n", ref->getRefNum(), ref->getRefGen());
++    }
++  }
++
++  // image/imagemask command
++  if ((inType3Char || preload) && !colorMap) {
++    writePSFmt("{0:d} {1:d} {2:s} [{3:d} 0 0 {4:d} 0 {5:d}] pdfImM1a\n",
++	       width, height, invert ? "true" : "false",
++	       width, -height, height);
++  } else if (colorMap) {
++    writePSFmt("{0:d} {1:d} 8 [{2:d} 0 0 {3:d} 0 {4:d}] pdfIm1\n",
++	       width, height,
++	       width, -height, height);
++  } else {
++    writePSFmt("{0:d} {1:d} {2:s} [{3:d} 0 0 {4:d} 0 {5:d}] pdfImM1\n",
++	       width, height, invert ? "true" : "false",
++	       width, -height, height);
++  }
++
++  // image data
++  if (!((inType3Char || preload) && !colorMap)) {
++
++    if (colorMap) {
++
++      // set up to process the data stream
++      imgStr = new ImageStream(str, width, colorMap->getNumPixelComps(),
++			       colorMap->getBits());
++      imgStr->reset();
++
++      // process the data stream
++      i = 0;
++      for (y = 0; y < height; ++y) {
++
++	// write the line
++	for (x = 0; x < width; ++x) {
++	  imgStr->getPixel(pixBuf);
++	  colorMap->getGray(pixBuf, &gray);
++	  writePSFmt("{0:02x}", colToByte(gray));
++	  if (++i == 32) {
++	    writePSChar('\n');
++	    i = 0;
++	  }
++	}
++      }
++      if (i != 0) {
++	writePSChar('\n');
++      }
++      str->close();
++      delete imgStr;
++
++    // imagemask
++    } else {
++      str->reset();
++      i = 0;
++      for (y = 0; y < height; ++y) {
++	for (x = 0; x < width; x += 8) {
++	  writePSFmt("{0:02x}", str->getChar() & 0xff);
++	  if (++i == 32) {
++	    writePSChar('\n');
++	    i = 0;
++	  }
++	}
++      }
++      if (i != 0) {
++	writePSChar('\n');
++      }
++      str->close();
++    }
++  }
++}
++
++void PSOutputDev::doImageL1Sep(GfxImageColorMap *colorMap,
++			       GBool /*invert*/, GBool /*inlineImg*/,
++			       Stream *str, int width, int height, int /*len*/) {
++  ImageStream *imgStr;
++  Guchar *lineBuf;
++  Guchar pixBuf[gfxColorMaxComps];
++  GfxCMYK cmyk;
++  int x, y, i, comp;
++
++  // width, height, matrix, bits per component
++  writePSFmt("{0:d} {1:d} 8 [{2:d} 0 0 {3:d} 0 {4:d}] pdfIm1Sep\n",
++	     width, height,
++	     width, -height, height);
++
++  // allocate a line buffer
++  lineBuf = (Guchar *)gmalloc(4 * width);
++
++  // set up to process the data stream
++  imgStr = new ImageStream(str, width, colorMap->getNumPixelComps(),
++			   colorMap->getBits());
++  imgStr->reset();
++
++  // process the data stream
++  i = 0;
++  for (y = 0; y < height; ++y) {
++
++    // read the line
++    for (x = 0; x < width; ++x) {
++      imgStr->getPixel(pixBuf);
++      colorMap->getCMYK(pixBuf, &cmyk);
++      lineBuf[4*x+0] = colToByte(cmyk.c);
++      lineBuf[4*x+1] = colToByte(cmyk.m);
++      lineBuf[4*x+2] = colToByte(cmyk.y);
++      lineBuf[4*x+3] = colToByte(cmyk.k);
++      addProcessColor(colToDbl(cmyk.c), colToDbl(cmyk.m),
++		      colToDbl(cmyk.y), colToDbl(cmyk.k));
++    }
++
++    // write one line of each color component
++    for (comp = 0; comp < 4; ++comp) {
++      for (x = 0; x < width; ++x) {
++	writePSFmt("{0:02x}", lineBuf[4*x + comp]);
++	if (++i == 32) {
++	  writePSChar('\n');
++	  i = 0;
++	}
++      }
++    }
++  }
++
++  if (i != 0) {
++    writePSChar('\n');
++  }
++
++  str->close();
++  delete imgStr;
++  gfree(lineBuf);
++}
++
++void PSOutputDev::doImageL2(Object *ref, GfxImageColorMap *colorMap,
++			    GBool invert, GBool inlineImg,
++			    Stream *str, int width, int height, int len,
++			    int *maskColors, Stream *maskStr,
++			    int maskWidth, int maskHeight, GBool maskInvert) {
++  Stream *str2;
++  ImageStream *imgStr;
++  Guchar *line;
++  PSOutImgClipRect *rects0, *rects1, *rectsTmp, *rectsOut;
++  int rects0Len, rects1Len, rectsSize, rectsOutLen, rectsOutSize;
++  GBool emitRect, addRect, extendRect;
++  GString *s;
++  int n, numComps;
++  GBool useRLE, useASCII, useASCIIHex, useCompressed;
++  GfxSeparationColorSpace *sepCS;
++  GfxColor color;
++  GfxCMYK cmyk;
++  int c;
++  int col, i, j, x0, x1, y, maskXor;
++
++  // color key masking
++  if (maskColors && colorMap && !inlineImg) {
++    // can't read the stream twice for inline images -- but masking
++    // isn't allowed with inline images anyway
++    numComps = colorMap->getNumPixelComps();
++    imgStr = new ImageStream(str, width, numComps, colorMap->getBits());
++    imgStr->reset();
++    rects0Len = rects1Len = rectsOutLen = 0;
++    rectsSize = rectsOutSize = 64;
++    rects0 = (PSOutImgClipRect *)gmallocn(rectsSize, sizeof(PSOutImgClipRect));
++    rects1 = (PSOutImgClipRect *)gmallocn(rectsSize, sizeof(PSOutImgClipRect));
++    rectsOut = (PSOutImgClipRect *)gmallocn(rectsOutSize,
++					    sizeof(PSOutImgClipRect));
++    for (y = 0; y < height; ++y) {
++      if (!(line = imgStr->getLine())) {
++	break;
++      }
++      i = 0;
++      rects1Len = 0;
++      for (x0 = 0; x0 < width; ++x0) {
++	for (j = 0; j < numComps; ++j) {
++	  if (line[x0*numComps+j] < maskColors[2*j] ||
++	      line[x0*numComps+j] > maskColors[2*j+1]) {
++	    break;
++	  }
++	}
++	if (j < numComps) {
++	  break;
++	}
++      }
++      for (x1 = x0; x1 < width; ++x1) {
++	for (j = 0; j < numComps; ++j) {
++	  if (line[x1*numComps+j] < maskColors[2*j] ||
++	      line[x1*numComps+j] > maskColors[2*j+1]) {
++	    break;
++	  }
++	}
++	if (j == numComps) {
++	  break;
++	}
++      }
++      while (x0 < width || i < rects0Len) {
++	emitRect = addRect = extendRect = gFalse;
++	if (x0 >= width) {
++	  emitRect = gTrue;
++	} else if (i >= rects0Len) {
++	  addRect = gTrue;
++	} else if (rects0[i].x0 < x0) {
++	  emitRect = gTrue;
++	} else if (x0 < rects0[i].x0) {
++	  addRect = gTrue;
++	} else if (rects0[i].x1 == x1) {
++	  extendRect = gTrue;
++	} else {
++	  emitRect = addRect = gTrue;
++	}
++	if (emitRect) {
++	  if (rectsOutLen == rectsOutSize) {
++	    rectsOutSize *= 2;
++	    rectsOut = (PSOutImgClipRect *)greallocn(rectsOut, rectsOutSize,
++						     sizeof(PSOutImgClipRect));
++	  }
++	  rectsOut[rectsOutLen].x0 = rects0[i].x0;
++	  rectsOut[rectsOutLen].x1 = rects0[i].x1;
++	  rectsOut[rectsOutLen].y0 = height - y - 1;
++	  rectsOut[rectsOutLen].y1 = height - rects0[i].y0 - 1;
++	  ++rectsOutLen;
++	  ++i;
++	}
++	if (addRect || extendRect) {
++	  if (rects1Len == rectsSize) {
++	    rectsSize *= 2;
++	    rects0 = (PSOutImgClipRect *)greallocn(rects0, rectsSize,
++						   sizeof(PSOutImgClipRect));
++	    rects1 = (PSOutImgClipRect *)greallocn(rects1, rectsSize,
++						   sizeof(PSOutImgClipRect));
++	  }
++	  rects1[rects1Len].x0 = x0;
++	  rects1[rects1Len].x1 = x1;
++	  if (addRect) {
++	    rects1[rects1Len].y0 = y;
++	  }
++	  if (extendRect) {
++	    rects1[rects1Len].y0 = rects0[i].y0;
++	    ++i;
++	  }
++	  ++rects1Len;
++	  for (x0 = x1; x0 < width; ++x0) {
++	    for (j = 0; j < numComps; ++j) {
++	      if (line[x0*numComps+j] < maskColors[2*j] ||
++		  line[x0*numComps+j] > maskColors[2*j+1]) {
++		break;
++	      }
++	    }
++	    if (j < numComps) {
++	      break;
++	    }
++	  }
++	  for (x1 = x0; x1 < width; ++x1) {
++	    for (j = 0; j < numComps; ++j) {
++	      if (line[x1*numComps+j] < maskColors[2*j] ||
++		  line[x1*numComps+j] > maskColors[2*j+1]) {
++		break;
++	      }
++	    }
++	    if (j == numComps) {
++	      break;
++	    }
++	  }
++	}
++      }
++      rectsTmp = rects0;
++      rects0 = rects1;
++      rects1 = rectsTmp;
++      i = rects0Len;
++      rects0Len = rects1Len;
++      rects1Len = i;
++    }
++    for (i = 0; i < rects0Len; ++i) {
++      if (rectsOutLen == rectsOutSize) {
++	rectsOutSize *= 2;
++	rectsOut = (PSOutImgClipRect *)greallocn(rectsOut, rectsOutSize,
++						 sizeof(PSOutImgClipRect));
++      }
++      rectsOut[rectsOutLen].x0 = rects0[i].x0;
++      rectsOut[rectsOutLen].x1 = rects0[i].x1;
++      rectsOut[rectsOutLen].y0 = height - y - 1;
++      rectsOut[rectsOutLen].y1 = height - rects0[i].y0 - 1;
++      ++rectsOutLen;
++    }
++    writePSFmt("{0:d} array 0\n", rectsOutLen * 4);
++    for (i = 0; i < rectsOutLen; ++i) {
++      writePSFmt("[{0:d} {1:d} {2:d} {3:d}] pr\n",
++		 rectsOut[i].x0, rectsOut[i].y0,
++		 rectsOut[i].x1 - rectsOut[i].x0,
++		 rectsOut[i].y1 - rectsOut[i].y0);
++    }
++    writePSFmt("pop {0:d} {1:d} pdfImClip\n", width, height);
++    gfree(rectsOut);
++    gfree(rects0);
++    gfree(rects1);
++    delete imgStr;
++    str->close();
++
++  // explicit masking
++  } else if (maskStr) {
++    imgStr = new ImageStream(maskStr, maskWidth, 1, 1);
++    imgStr->reset();
++    rects0Len = rects1Len = rectsOutLen = 0;
++    rectsSize = rectsOutSize = 64;
++    rects0 = (PSOutImgClipRect *)gmallocn(rectsSize, sizeof(PSOutImgClipRect));
++    rects1 = (PSOutImgClipRect *)gmallocn(rectsSize, sizeof(PSOutImgClipRect));
++    rectsOut = (PSOutImgClipRect *)gmallocn(rectsOutSize,
++					    sizeof(PSOutImgClipRect));
++    maskXor = maskInvert ? 1 : 0;
++    for (y = 0; y < maskHeight; ++y) {
++      if (!(line = imgStr->getLine())) {
++	break;
++      }
++      i = 0;
++      rects1Len = 0;
++      for (x0 = 0; x0 < maskWidth && (line[x0] ^ maskXor); ++x0) ;
++      for (x1 = x0; x1 < maskWidth && !(line[x1] ^ maskXor); ++x1) ;
++      while (x0 < maskWidth || i < rects0Len) {
++	emitRect = addRect = extendRect = gFalse;
++	if (x0 >= maskWidth) {
++	  emitRect = gTrue;
++	} else if (i >= rects0Len) {
++	  addRect = gTrue;
++	} else if (rects0[i].x0 < x0) {
++	  emitRect = gTrue;
++	} else if (x0 < rects0[i].x0) {
++	  addRect = gTrue;
++	} else if (rects0[i].x1 == x1) {
++	  extendRect = gTrue;
++	} else {
++	  emitRect = addRect = gTrue;
++	}
++	if (emitRect) {
++	  if (rectsOutLen == rectsOutSize) {
++	    rectsOutSize *= 2;
++	    rectsOut = (PSOutImgClipRect *)greallocn(rectsOut, rectsOutSize,
++						     sizeof(PSOutImgClipRect));
++	  }
++	  rectsOut[rectsOutLen].x0 = rects0[i].x0;
++	  rectsOut[rectsOutLen].x1 = rects0[i].x1;
++	  rectsOut[rectsOutLen].y0 = maskHeight - y - 1;
++	  rectsOut[rectsOutLen].y1 = maskHeight - rects0[i].y0 - 1;
++	  ++rectsOutLen;
++	  ++i;
++	}
++	if (addRect || extendRect) {
++	  if (rects1Len == rectsSize) {
++	    rectsSize *= 2;
++	    rects0 = (PSOutImgClipRect *)greallocn(rects0, rectsSize,
++						   sizeof(PSOutImgClipRect));
++	    rects1 = (PSOutImgClipRect *)greallocn(rects1, rectsSize,
++						   sizeof(PSOutImgClipRect));
++	  }
++	  rects1[rects1Len].x0 = x0;
++	  rects1[rects1Len].x1 = x1;
++	  if (addRect) {
++	    rects1[rects1Len].y0 = y;
++	  }
++	  if (extendRect) {
++	    rects1[rects1Len].y0 = rects0[i].y0;
++	    ++i;
++	  }
++	  ++rects1Len;
++	  for (x0 = x1; x0 < maskWidth && (line[x0] ^ maskXor); ++x0) ;
++	  for (x1 = x0; x1 < maskWidth && !(line[x1] ^ maskXor); ++x1) ;
++	}
++      }
++      rectsTmp = rects0;
++      rects0 = rects1;
++      rects1 = rectsTmp;
++      i = rects0Len;
++      rects0Len = rects1Len;
++      rects1Len = i;
++    }
++    for (i = 0; i < rects0Len; ++i) {
++      if (rectsOutLen == rectsOutSize) {
++	rectsOutSize *= 2;
++	rectsOut = (PSOutImgClipRect *)greallocn(rectsOut, rectsOutSize,
++						 sizeof(PSOutImgClipRect));
++      }
++      rectsOut[rectsOutLen].x0 = rects0[i].x0;
++      rectsOut[rectsOutLen].x1 = rects0[i].x1;
++      rectsOut[rectsOutLen].y0 = maskHeight - y - 1;
++      rectsOut[rectsOutLen].y1 = maskHeight - rects0[i].y0 - 1;
++      ++rectsOutLen;
++    }
++    writePSFmt("{0:d} array 0\n", rectsOutLen * 4);
++    for (i = 0; i < rectsOutLen; ++i) {
++      writePSFmt("[{0:d} {1:d} {2:d} {3:d}] pr\n",
++		 rectsOut[i].x0, rectsOut[i].y0,
++		 rectsOut[i].x1 - rectsOut[i].x0,
++		 rectsOut[i].y1 - rectsOut[i].y0);
++    }
++    writePSFmt("pop {0:d} {1:d} pdfImClip\n", maskWidth, maskHeight);
++    gfree(rectsOut);
++    gfree(rects0);
++    gfree(rects1);
++    delete imgStr;
++    maskStr->close();
++  }
++
++  // color space
++  if (colorMap) {
++    dumpColorSpaceL2(colorMap->getColorSpace(), gFalse, gTrue, gFalse);
++    writePS(" setcolorspace\n");
++  }
++
++  useASCIIHex = globalParams->getPSASCIIHex();
++
++  // set up the image data
++  if (mode == psModeForm || inType3Char || preload) {
++    if (inlineImg) {
++      // create an array
++      str2 = new FixedLengthEncoder(str, len);
++      str2 = new RunLengthEncoder(str2);
++      if (useASCIIHex) {
++	str2 = new ASCIIHexEncoder(str2);
++      } else {
++	str2 = new ASCII85Encoder(str2);
++      }
++      str2->reset();
++      col = 0;
++      writePS((char *)(useASCIIHex ? "[<" : "[<~"));
++      do {
++	do {
++	  c = str2->getChar();
++	} while (c == '\n' || c == '\r');
++	if (c == (useASCIIHex ? '>' : '~') || c == EOF) {
++	  break;
++	}
++	if (c == 'z') {
++	  writePSChar(c);
++	  ++col;
++	} else {
++	  writePSChar(c);
++	  ++col;
++	  for (i = 1; i <= (useASCIIHex ? 1 : 4); ++i) {
++	    do {
++	      c = str2->getChar();
++	    } while (c == '\n' || c == '\r');
++	    if (c == (useASCIIHex ? '>' : '~') || c == EOF) {
++	      break;
++	    }
++	    writePSChar(c);
++	    ++col;
++	  }
++	}
++	// each line is: "<~...data...~><eol>"
++	// so max data length = 255 - 6 = 249
++	// chunks are 1 or 5 bytes each, so we have to stop at 245
++	// but make it 240 just to be safe
++	if (col > 240) {
++	  writePS((char *)(useASCIIHex ? ">\n<" : "~>\n<~"));
++	  col = 0;
++	}
++      } while (c != (useASCIIHex ? '>' : '~') && c != EOF);
++      writePS((char *)(useASCIIHex ? ">\n" : "~>\n"));
++      // add an extra entry because the RunLengthDecode filter may
++      // read past the end
++      writePS("<>]\n");
++      writePS("0\n");
++      str2->close();
++      delete str2;
++    } else {
++      // set up to use the array already created by setupImages()
++      writePSFmt("ImData_{0:d}_{1:d} 0\n", ref->getRefNum(), ref->getRefGen());
++    }
++  }
++
++  // image dictionary
++  writePS("<<\n  /ImageType 1\n");
++
++  // width, height, matrix, bits per component
++  writePSFmt("  /Width {0:d}\n", width);
++  writePSFmt("  /Height {0:d}\n", height);
++  writePSFmt("  /ImageMatrix [{0:d} 0 0 {1:d} 0 {2:d}]\n",
++	     width, -height, height);
++  if (colorMap && colorMap->getColorSpace()->getMode() == csDeviceN) {
++    writePS("  /BitsPerComponent 8\n");
++  } else {
++    writePSFmt("  /BitsPerComponent {0:d}\n",
++	       colorMap ? colorMap->getBits() : 1);
++  }
++
++  // decode 
++  if (colorMap) {
++    writePS("  /Decode [");
++    if ((level == psLevel2Sep || level == psLevel3Sep) &&
++	colorMap->getColorSpace()->getMode() == csSeparation) {
++      // this matches up with the code in the pdfImSep operator
++      n = (1 << colorMap->getBits()) - 1;
++      writePSFmt("{0:.4g} {1:.4g}", colorMap->getDecodeLow(0) * n,
++		 colorMap->getDecodeHigh(0) * n);
++    } else if (colorMap->getColorSpace()->getMode() == csDeviceN) {
++      numComps = ((GfxDeviceNColorSpace *)colorMap->getColorSpace())->
++	           getAlt()->getNComps();
++      for (i = 0; i < numComps; ++i) {
++	if (i > 0) {
++	  writePS(" ");
++	}
++	writePS("0 1");
++      }
++    } else {
++      numComps = colorMap->getNumPixelComps();
++      for (i = 0; i < numComps; ++i) {
++	if (i > 0) {
++	  writePS(" ");
++	}
++	writePSFmt("{0:.4g} {1:.4g}",
++		   colorMap->getDecodeLow(i), colorMap->getDecodeHigh(i));
++      }
++    }
++    writePS("]\n");
++  } else {
++    writePSFmt("  /Decode [{0:d} {1:d}]\n", invert ? 1 : 0, invert ? 0 : 1);
++  }
++
++  // data source
++  if (mode == psModeForm || inType3Char || preload) {
++    writePS("  /DataSource { 2 copy get exch 1 add exch }\n");
++  } else {
++    writePS("  /DataSource currentfile\n");
++  }
++
++  // filters
++  s = str->getPSFilter(level < psLevel2 ? 1 : level < psLevel3 ? 2 : 3,
++		       "    ");
++  if ((colorMap && colorMap->getColorSpace()->getMode() == csDeviceN) ||
++      inlineImg || !s) {
++    useRLE = gTrue;
++    useASCII = !(mode == psModeForm || inType3Char || preload);
++    useCompressed = gFalse;
++  } else {
++    useRLE = gFalse;
++    useASCII = str->isBinary() &&
++               !(mode == psModeForm || inType3Char || preload);
++    useCompressed = gTrue;
++  }
++  if (useASCII) {
++    writePSFmt("    /ASCII{0:s}Decode filter\n",
++	       useASCIIHex ? "Hex" : "85");
++  }
++  if (useRLE) {
++    writePS("    /RunLengthDecode filter\n");
++  }
++  if (useCompressed) {
++    writePS(s->getCString());
++  }
++  if (s) {
++    delete s;
++  }
++
++  if (mode == psModeForm || inType3Char || preload) {
++
++    // end of image dictionary
++    writePSFmt(">>\n{0:s}\n", colorMap ? "image" : "imagemask");
++
++    // get rid of the array and index
++    writePS("pop pop\n");
++
++  } else {
++
++    // cut off inline image streams at appropriate length
++    if (inlineImg) {
++      str = new FixedLengthEncoder(str, len);
++    } else if (useCompressed) {
++      str = str->getUndecodedStream();
++    }
++
++    // recode DeviceN data
++    if (colorMap && colorMap->getColorSpace()->getMode() == csDeviceN) {
++      str = new DeviceNRecoder(str, width, height, colorMap);
++    }
++
++    // add RunLengthEncode and ASCIIHex/85 encode filters
++    if (useRLE) {
++      str = new RunLengthEncoder(str);
++    }
++    if (useASCII) {
++      if (useASCIIHex) {
++	str = new ASCIIHexEncoder(str);
++      } else {
++	str = new ASCII85Encoder(str);
++      }
++    }
++
++    // end of image dictionary
++    writePS(">>\n");
++#if OPI_SUPPORT
++    if (opi13Nest) {
++      if (inlineImg) {
++	// this can't happen -- OPI dictionaries are in XObjects
++	error(-1, "Internal: OPI in inline image");
++	n = 0;
++      } else {
++	// need to read the stream to count characters -- the length
++	// is data-dependent (because of ASCII and RLE filters)
++	str->reset();
++	n = 0;
++	while ((c = str->getChar()) != EOF) {
++	  ++n;
++	}
++	str->close();
++      }
++      // +6/7 for "pdfIm\n" / "pdfImM\n"
++      // +8 for newline + trailer
++      n += colorMap ? 14 : 15;
++      writePSFmt("%%BeginData: {0:d} Hex Bytes\n", n);
++    }
++#endif
++    if ((level == psLevel2Sep || level == psLevel3Sep) && colorMap &&
++	colorMap->getColorSpace()->getMode() == csSeparation) {
++      color.c[0] = gfxColorComp1;
++      sepCS = (GfxSeparationColorSpace *)colorMap->getColorSpace();
++      sepCS->getCMYK(&color, &cmyk);
++      writePSFmt("{0:.4g} {1:.4g} {2:.4g} {3:.4g} ({4:t}) pdfImSep\n",
++		 colToDbl(cmyk.c), colToDbl(cmyk.m),
++		 colToDbl(cmyk.y), colToDbl(cmyk.k),
++		 sepCS->getName());
++    } else {
++      writePSFmt("{0:s}\n", colorMap ? "pdfIm" : "pdfImM");
++    }
++
++    // copy the stream data
++    str->reset();
++    while ((c = str->getChar()) != EOF) {
++      writePSChar(c);
++    }
++    str->close();
++
++    // add newline and trailer to the end
++    writePSChar('\n');
++    writePS("%-EOD-\n");
++#if OPI_SUPPORT
++    if (opi13Nest) {
++      writePS("%%EndData\n");
++    }
++#endif
++
++    // delete encoders
++    if (useRLE || useASCII || inlineImg) {
++      delete str;
++    }
++  }
++
++  if ((maskColors && colorMap && !inlineImg) || maskStr) {
++    writePS("pdfImClipEnd\n");
++  }
++}
++
++//~ this doesn't currently support OPI
++void PSOutputDev::doImageL3(Object *ref, GfxImageColorMap *colorMap,
++			    GBool invert, GBool inlineImg,
++			    Stream *str, int width, int height, int len,
++			    int *maskColors, Stream *maskStr,
++			    int maskWidth, int maskHeight, GBool maskInvert) {
++  Stream *str2;
++  GString *s;
++  int n, numComps;
++  GBool useRLE, useASCII, useASCIIHex, useCompressed;
++  GBool maskUseRLE, maskUseASCII, maskUseCompressed;
++  GfxSeparationColorSpace *sepCS;
++  GfxColor color;
++  GfxCMYK cmyk;
++  int c;
++  int col, i;
++
++  useASCIIHex = globalParams->getPSASCIIHex();
++  useRLE = useASCII = useCompressed = gFalse; // make gcc happy
++  maskUseRLE = maskUseASCII = maskUseCompressed = gFalse; // make gcc happy
++
++  // color space
++  if (colorMap) {
++    dumpColorSpaceL2(colorMap->getColorSpace(), gFalse, gTrue, gFalse);
++    writePS(" setcolorspace\n");
++  }
++
++  // set up the image data
++  if (mode == psModeForm || inType3Char || preload) {
++    if (inlineImg) {
++      // create an array
++      str2 = new FixedLengthEncoder(str, len);
++      str2 = new RunLengthEncoder(str2);
++      if (useASCIIHex) {
++	str2 = new ASCIIHexEncoder(str2);
++      } else {
++	str2 = new ASCII85Encoder(str2);
++      }
++      str2->reset();
++      col = 0;
++      writePS((char *)(useASCIIHex ? "[<" : "[<~"));
++      do {
++	do {
++	  c = str2->getChar();
++	} while (c == '\n' || c == '\r');
++	if (c == (useASCIIHex ? '>' : '~') || c == EOF) {
++	  break;
++	}
++	if (c == 'z') {
++	  writePSChar(c);
++	  ++col;
++	} else {
++	  writePSChar(c);
++	  ++col;
++	  for (i = 1; i <= (useASCIIHex ? 1 : 4); ++i) {
++	    do {
++	      c = str2->getChar();
++	    } while (c == '\n' || c == '\r');
++	    if (c == (useASCIIHex ? '>' : '~') || c == EOF) {
++	      break;
++	    }
++	    writePSChar(c);
++	    ++col;
++	  }
++	}
++	// each line is: "<~...data...~><eol>"
++	// so max data length = 255 - 6 = 249
++	// chunks are 1 or 5 bytes each, so we have to stop at 245
++	// but make it 240 just to be safe
++	if (col > 240) {
++	  writePS((char *)(useASCIIHex ? ">\n<" : "~>\n<~"));
++	  col = 0;
++	}
++      } while (c != (useASCIIHex ? '>' : '~') && c != EOF);
++      writePS((char *)(useASCIIHex ? ">\n" : "~>\n"));
++      // add an extra entry because the RunLengthDecode filter may
++      // read past the end
++      writePS("<>]\n");
++      writePS("0\n");
++      str2->close();
++      delete str2;
++    } else {
++      // set up to use the array already created by setupImages()
++      writePSFmt("ImData_{0:d}_{1:d} 0\n", ref->getRefNum(), ref->getRefGen());
++    }
++  }
++
++  // explicit masking
++  if (maskStr) {
++    writePS("<<\n  /ImageType 3\n");
++    writePS("  /InterleaveType 3\n");
++    writePS("  /DataDict\n");
++  }
++
++  // image (data) dictionary
++  writePSFmt("<<\n  /ImageType {0:d}\n", (maskColors && colorMap) ? 4 : 1);
++
++  // color key masking
++  if (maskColors && colorMap) {
++    writePS("  /MaskColor [\n");
++    numComps = colorMap->getNumPixelComps();
++    for (i = 0; i < 2 * numComps; i += 2) {
++      writePSFmt("    {0:d} {1:d}\n", maskColors[i], maskColors[i+1]);
++    }
++    writePS("  ]\n");
++  }
++
++  // width, height, matrix, bits per component
++  writePSFmt("  /Width {0:d}\n", width);
++  writePSFmt("  /Height {0:d}\n", height);
++  writePSFmt("  /ImageMatrix [{0:d} 0 0 {1:d} 0 {2:d}]\n",
++	     width, -height, height);
++  if (colorMap && colorMap->getColorSpace()->getMode() == csDeviceN) {
++    writePS("  /BitsPerComponent 8\n");
++  } else {
++    writePSFmt("  /BitsPerComponent {0:d}\n",
++	       colorMap ? colorMap->getBits() : 1);
++  }
++
++  // decode 
++  if (colorMap) {
++    writePS("  /Decode [");
++    if ((level == psLevel2Sep || level == psLevel3Sep) &&
++	colorMap->getColorSpace()->getMode() == csSeparation) {
++      // this matches up with the code in the pdfImSep operator
++      n = (1 << colorMap->getBits()) - 1;
++      writePSFmt("{0:.4g} {1:.4g}", colorMap->getDecodeLow(0) * n,
++		 colorMap->getDecodeHigh(0) * n);
++    } else if (colorMap->getColorSpace()->getMode() == csDeviceN) {
++      numComps = ((GfxDeviceNColorSpace *)colorMap->getColorSpace())->
++	           getAlt()->getNComps();
++      for (i = 0; i < numComps; ++i) {
++	if (i > 0) {
++	  writePS(" ");
++	}
++	writePS("0 1");
++      }
++    } else {
++      numComps = colorMap->getNumPixelComps();
++      for (i = 0; i < numComps; ++i) {
++	if (i > 0) {
++	  writePS(" ");
++	}
++	writePSFmt("{0:.4g} {1:.4g}", colorMap->getDecodeLow(i),
++		   colorMap->getDecodeHigh(i));
++      }
++    }
++    writePS("]\n");
++  } else {
++    writePSFmt("  /Decode [{0:d} {1:d}]\n", invert ? 1 : 0, invert ? 0 : 1);
++  }
++
++  // data source
++  if (mode == psModeForm || inType3Char || preload) {
++    writePS("  /DataSource { 2 copy get exch 1 add exch }\n");
++  } else {
++    writePS("  /DataSource currentfile\n");
++  }
++
++  // filters
++  s = str->getPSFilter(level < psLevel2 ? 1 : level < psLevel3 ? 2 : 3,
++		       "    ");
++  if ((colorMap && colorMap->getColorSpace()->getMode() == csDeviceN) ||
++      inlineImg || !s) {
++    useRLE = gTrue;
++    useASCII = !(mode == psModeForm || inType3Char || preload);
++    useCompressed = gFalse;
++  } else {
++    useRLE = gFalse;
++    useASCII = str->isBinary() &&
++               !(mode == psModeForm || inType3Char || preload);
++    useCompressed = gTrue;
++  }
++  if (useASCII) {
++    writePSFmt("    /ASCII{0:s}Decode filter\n",
++	       useASCIIHex ? "Hex" : "85");
++  }
++  if (useRLE) {
++    writePS("    /RunLengthDecode filter\n");
++  }
++  if (useCompressed) {
++    writePS(s->getCString());
++  }
++  if (s) {
++    delete s;
++  }
++
++  // end of image (data) dictionary
++  writePS(">>\n");
++
++  // explicit masking
++  if (maskStr) {
++    writePS("  /MaskDict\n");
++    writePS("<<\n");
++    writePS("  /ImageType 1\n");
++    writePSFmt("  /Width {0:d}\n", maskWidth);
++    writePSFmt("  /Height {0:d}\n", maskHeight);
++    writePSFmt("  /ImageMatrix [{0:d} 0 0 {1:d} 0 {2:d}]\n",
++	       maskWidth, -maskHeight, maskHeight);
++    writePS("  /BitsPerComponent 1\n");
++    writePSFmt("  /Decode [{0:d} {1:d}]\n",
++	       maskInvert ? 1 : 0, maskInvert ? 0 : 1);
++
++    // mask data source
++    writePS("  /DataSource currentfile\n");
++    s = maskStr->getPSFilter(3, "    ");
++    if (!s) {
++      maskUseRLE = gTrue;
++      maskUseASCII = gTrue;
++      maskUseCompressed = gFalse;
++    } else {
++      maskUseRLE = gFalse;
++      maskUseASCII = maskStr->isBinary();
++      maskUseCompressed = gTrue;
++    }
++    if (maskUseASCII) {
++      writePSFmt("    /ASCII{0:s}Decode filter\n",
++		 useASCIIHex ? "Hex" : "85");
++    }
++    if (maskUseRLE) {
++      writePS("    /RunLengthDecode filter\n");
++    }
++    if (maskUseCompressed) {
++      writePS(s->getCString());
++    }
++    if (s) {
++      delete s;
++    }
++
++    writePS(">>\n");
++    writePS(">>\n");
++  }
++
++  if (mode == psModeForm || inType3Char || preload) {
++
++    // image command
++    writePSFmt("{0:s}\n", colorMap ? "image" : "imagemask");
++
++  } else {
++
++    if ((level == psLevel2Sep || level == psLevel3Sep) && colorMap &&
++	colorMap->getColorSpace()->getMode() == csSeparation) {
++      color.c[0] = gfxColorComp1;
++      sepCS = (GfxSeparationColorSpace *)colorMap->getColorSpace();
++      sepCS->getCMYK(&color, &cmyk);
++      writePSFmt("{0:.4g} {1:.4g} {2:.4g} {3:.4g} ({4:t}) pdfImSep\n",
++		 colToDbl(cmyk.c), colToDbl(cmyk.m),
++		 colToDbl(cmyk.y), colToDbl(cmyk.k),
++		 sepCS->getName());
++    } else {
++      writePSFmt("{0:s}\n", colorMap ? "pdfIm" : "pdfImM");
++    }
++
++  }
++
++  // explicit masking
++  if (maskStr) {
++
++    if (maskUseCompressed) {
++      maskStr = maskStr->getUndecodedStream();
++    }
++
++    // add RunLengthEncode and ASCIIHex/85 encode filters
++    if (maskUseRLE) {
++      maskStr = new RunLengthEncoder(maskStr);
++    }
++    if (maskUseASCII) {
++      if (useASCIIHex) {
++	maskStr = new ASCIIHexEncoder(maskStr);
++      } else {
++	maskStr = new ASCII85Encoder(maskStr);
++      }
++    }
++
++    // copy the stream data
++    maskStr->reset();
++    while ((c = maskStr->getChar()) != EOF) {
++      writePSChar(c);
++    }
++    maskStr->close();
++    writePSChar('\n');
++
++    // delete encoders
++    if (maskUseRLE || maskUseASCII) {
++      delete maskStr;
++    }
++  }
++
++  // get rid of the array and index
++  if (mode == psModeForm || inType3Char || preload) {
++    writePS("pop pop\n");
++
++  // image data
++  } else {
++
++    // cut off inline image streams at appropriate length
++    if (inlineImg) {
++      str = new FixedLengthEncoder(str, len);
++    } else if (useCompressed) {
++      str = str->getUndecodedStream();
++    }
++
++    // recode DeviceN data
++    if (colorMap && colorMap->getColorSpace()->getMode() == csDeviceN) {
++      str = new DeviceNRecoder(str, width, height, colorMap);
++    }
++
++    // add RunLengthEncode and ASCIIHex/85 encode filters
++    if (useRLE) {
++      str = new RunLengthEncoder(str);
++    }
++    if (useASCII) {
++      if (useASCIIHex) {
++	str = new ASCIIHexEncoder(str);
++      } else {
++	str = new ASCII85Encoder(str);
++      }
++    }
++
++    // copy the stream data
++    str->reset();
++    while ((c = str->getChar()) != EOF) {
++      writePSChar(c);
++    }
++    str->close();
++
++    // add newline and trailer to the end
++    writePSChar('\n');
++    writePS("%-EOD-\n");
++
++    // delete encoders
++    if (useRLE || useASCII || inlineImg) {
++      delete str;
++    }
++  }
++}
++
++void PSOutputDev::dumpColorSpaceL2(GfxColorSpace *colorSpace,
++				   GBool genXform, GBool updateColors,
++				   GBool map01) {
++  GfxCalGrayColorSpace *calGrayCS;
++  GfxCalRGBColorSpace *calRGBCS;
++  GfxLabColorSpace *labCS;
++  GfxIndexedColorSpace *indexedCS;
++  GfxSeparationColorSpace *separationCS;
++  GfxDeviceNColorSpace *deviceNCS;
++  GfxColorSpace *baseCS;
++  Guchar *lookup, *p;
++  double x[gfxColorMaxComps], y[gfxColorMaxComps];
++  double low[gfxColorMaxComps], range[gfxColorMaxComps];
++  GfxColor color;
++  GfxCMYK cmyk;
++  Function *func;
++  int n, numComps, numAltComps;
++  int byte;
++  int i, j, k;
++
++  switch (colorSpace->getMode()) {
++
++  case csDeviceGray:
++    writePS("/DeviceGray");
++    if (genXform) {
++      writePS(" {}");
++    }
++    if (updateColors) {
++      processColors |= psProcessBlack;
++    }
++    break;
++
++  case csCalGray:
++    calGrayCS = (GfxCalGrayColorSpace *)colorSpace;
++    writePS("[/CIEBasedA <<\n");
++    writePSFmt(" /DecodeA {{{0:.4g} exp}} bind\n", calGrayCS->getGamma());
++    writePSFmt(" /MatrixA [{0:.4g} {1:.4g} {2:.4g}]\n",
++	       calGrayCS->getWhiteX(), calGrayCS->getWhiteY(),
++	       calGrayCS->getWhiteZ());
++    writePSFmt(" /WhitePoint [{0:.4g} {1:.4g} {2:.4g}]\n",
++	       calGrayCS->getWhiteX(), calGrayCS->getWhiteY(),
++	       calGrayCS->getWhiteZ());
++    writePSFmt(" /BlackPoint [{0:.4g} {1:.4g} {2:.4g}]\n",
++	       calGrayCS->getBlackX(), calGrayCS->getBlackY(),
++	       calGrayCS->getBlackZ());
++    writePS(">>]");
++    if (genXform) {
++      writePS(" {}");
++    }
++    if (updateColors) {
++      processColors |= psProcessBlack;
++    }
++    break;
++
++  case csDeviceRGB:
++    writePS("/DeviceRGB");
++    if (genXform) {
++      writePS(" {}");
++    }
++    if (updateColors) {
++      processColors |= psProcessCMYK;
++    }
++    break;
++
++  case csCalRGB:
++    calRGBCS = (GfxCalRGBColorSpace *)colorSpace;
++    writePS("[/CIEBasedABC <<\n");
++    writePSFmt(" /DecodeABC [{{{0:.4g} exp}} bind {{{1:.4g} exp}} bind {{{2:.4g} exp}} bind]\n",
++	       calRGBCS->getGammaR(), calRGBCS->getGammaG(),
++	       calRGBCS->getGammaB());
++    writePSFmt(" /MatrixABC [{0:.4g} {1:.4g} {2:.4g} {3:.4g} {4:.4g} {5:.4g} {6:.4g} {7:.4g} {8:.4g}]\n",
++	       calRGBCS->getMatrix()[0], calRGBCS->getMatrix()[1],
++	       calRGBCS->getMatrix()[2], calRGBCS->getMatrix()[3],
++	       calRGBCS->getMatrix()[4], calRGBCS->getMatrix()[5],
++	       calRGBCS->getMatrix()[6], calRGBCS->getMatrix()[7],
++	       calRGBCS->getMatrix()[8]);
++    writePSFmt(" /WhitePoint [{0:.4g} {1:.4g} {2:.4g}]\n",
++	       calRGBCS->getWhiteX(), calRGBCS->getWhiteY(),
++	       calRGBCS->getWhiteZ());
++    writePSFmt(" /BlackPoint [{0:.4g} {1:.4g} {2:.4g}]\n",
++	       calRGBCS->getBlackX(), calRGBCS->getBlackY(),
++	       calRGBCS->getBlackZ());
++    writePS(">>]");
++    if (genXform) {
++      writePS(" {}");
++    }
++    if (updateColors) {
++      processColors |= psProcessCMYK;
++    }
++    break;
++
++  case csDeviceCMYK:
++    writePS("/DeviceCMYK");
++    if (genXform) {
++      writePS(" {}");
++    }
++    if (updateColors) {
++      processColors |= psProcessCMYK;
++    }
++    break;
++
++  case csLab:
++    labCS = (GfxLabColorSpace *)colorSpace;
++    writePS("[/CIEBasedABC <<\n");
++    if (map01) {
++      writePS(" /RangeABC [0 1 0 1 0 1]\n");
++      writePSFmt(" /DecodeABC [{{100 mul 16 add 116 div}} bind {{{0:.4g} mul {1:.4g} add}} bind {{{2:.4g} mul {3:.4g} add}} bind]\n",
++		 (labCS->getAMax() - labCS->getAMin()) / 500.0,
++		 labCS->getAMin() / 500.0,
++		 (labCS->getBMax() - labCS->getBMin()) / 200.0,
++		 labCS->getBMin() / 200.0);
++    } else {
++      writePSFmt(" /RangeABC [0 100 {0:.4g} {1:.4g} {2:.4g} {3:.4g}]\n",
++		 labCS->getAMin(), labCS->getAMax(),
++		 labCS->getBMin(), labCS->getBMax());
++      writePS(" /DecodeABC [{16 add 116 div} bind {500 div} bind {200 div} bind]\n");
++    }
++    writePS(" /MatrixABC [1 1 1 1 0 0 0 0 -1]\n");
++    writePS(" /DecodeLMN\n");
++    writePS("   [{dup 6 29 div ge {dup dup mul mul}\n");
++    writePSFmt("     {{4 29 div sub 108 841 div mul }} ifelse {0:.4g} mul}} bind\n",
++	       labCS->getWhiteX());
++    writePS("    {dup 6 29 div ge {dup dup mul mul}\n");
++    writePSFmt("     {{4 29 div sub 108 841 div mul }} ifelse {0:.4g} mul}} bind\n",
++	       labCS->getWhiteY());
++    writePS("    {dup 6 29 div ge {dup dup mul mul}\n");
++    writePSFmt("     {{4 29 div sub 108 841 div mul }} ifelse {0:.4g} mul}} bind]\n",
++	       labCS->getWhiteZ());
++    writePSFmt(" /WhitePoint [{0:.4g} {1:.4g} {2:.4g}]\n",
++	       labCS->getWhiteX(), labCS->getWhiteY(), labCS->getWhiteZ());
++    writePSFmt(" /BlackPoint [{0:.4g} {1:.4g} {2:.4g}]\n",
++	       labCS->getBlackX(), labCS->getBlackY(), labCS->getBlackZ());
++    writePS(">>]");
++    if (genXform) {
++      writePS(" {}");
++    }
++    if (updateColors) {
++      processColors |= psProcessCMYK;
++    }
++    break;
++
++  case csICCBased:
++    // there is no transform function to the alternate color space, so
++    // we can use it directly
++    dumpColorSpaceL2(((GfxICCBasedColorSpace *)colorSpace)->getAlt(),
++		     genXform, updateColors, gFalse);
++    break;
++
++  case csIndexed:
++    indexedCS = (GfxIndexedColorSpace *)colorSpace;
++    baseCS = indexedCS->getBase();
++    writePS("[/Indexed ");
++    dumpColorSpaceL2(baseCS, gFalse, gFalse, gTrue);
++    n = indexedCS->getIndexHigh();
++    numComps = baseCS->getNComps();
++    lookup = indexedCS->getLookup();
++    writePSFmt(" {0:d} <\n", n);
++    if (baseCS->getMode() == csDeviceN) {
++      func = ((GfxDeviceNColorSpace *)baseCS)->getTintTransformFunc();
++      baseCS->getDefaultRanges(low, range, indexedCS->getIndexHigh());
++      if (((GfxDeviceNColorSpace *)baseCS)->getAlt()->getMode() == csLab) {
++	labCS = (GfxLabColorSpace *)((GfxDeviceNColorSpace *)baseCS)->getAlt();
++      } else {
++	labCS = NULL;
++      }
++      numAltComps = ((GfxDeviceNColorSpace *)baseCS)->getAlt()->getNComps();
++      p = lookup;
++      for (i = 0; i <= n; i += 8) {
++	writePS("  ");
++	for (j = i; j < i+8 && j <= n; ++j) {
++	  for (k = 0; k < numComps; ++k) {
++	    x[k] = low[k] + (*p++ / 255.0) * range[k];
++	  }
++	  func->transform(x, y);
++	  if (labCS) {
++	    y[0] /= 100.0;
++	    y[1] = (y[1] - labCS->getAMin()) /
++	           (labCS->getAMax() - labCS->getAMin());
++	    y[2] = (y[2] - labCS->getBMin()) /
++	           (labCS->getBMax() - labCS->getBMin());
++	  }
++	  for (k = 0; k < numAltComps; ++k) {
++	    byte = (int)(y[k] * 255 + 0.5);
++	    if (byte < 0) {
++	      byte = 0;
++	    } else if (byte > 255) {
++	      byte = 255;
++	    }
++	    writePSFmt("{0:02x}", byte);
++	  }
++	  if (updateColors) {
++	    color.c[0] = dblToCol(j);
++	    indexedCS->getCMYK(&color, &cmyk);
++	    addProcessColor(colToDbl(cmyk.c), colToDbl(cmyk.m),
++			    colToDbl(cmyk.y), colToDbl(cmyk.k));
++	  }
++	}
++	writePS("\n");
++      }
++    } else {
++      for (i = 0; i <= n; i += 8) {
++	writePS("  ");
++	for (j = i; j < i+8 && j <= n; ++j) {
++	  for (k = 0; k < numComps; ++k) {
++	    writePSFmt("{0:02x}", lookup[j * numComps + k]);
++	  }
++	  if (updateColors) {
++	    color.c[0] = dblToCol(j);
++	    indexedCS->getCMYK(&color, &cmyk);
++	    addProcessColor(colToDbl(cmyk.c), colToDbl(cmyk.m),
++			    colToDbl(cmyk.y), colToDbl(cmyk.k));
++	  }
++	}
++	writePS("\n");
++      }
++    }
++    writePS(">]");
++    if (genXform) {
++      writePS(" {}");
++    }
++    break;
++
++  case csSeparation:
++    separationCS = (GfxSeparationColorSpace *)colorSpace;
++    writePS("[/Separation ");
++    writePSString(separationCS->getName());
++    writePS(" ");
++    dumpColorSpaceL2(separationCS->getAlt(), gFalse, gFalse, gFalse);
++    writePS("\n");
++    cvtFunction(separationCS->getFunc());
++    writePS("]");
++    if (genXform) {
++      writePS(" {}");
++    }
++    if (updateColors) {
++      addCustomColor(separationCS);
++    }
++    break;
++
++  case csDeviceN:
++    // DeviceN color spaces are a Level 3 PostScript feature.
++    deviceNCS = (GfxDeviceNColorSpace *)colorSpace;
++    dumpColorSpaceL2(deviceNCS->getAlt(), gFalse, updateColors, map01);
++    if (genXform) {
++      writePS(" ");
++      cvtFunction(deviceNCS->getTintTransformFunc());
++    }
++    break;
++
++  case csPattern:
++    //~ unimplemented
++    break;
++  }
++}
++
++#if OPI_SUPPORT
++void PSOutputDev::opiBegin(GfxState *state, Dict *opiDict) {
++  Object dict;
++
++  if (globalParams->getPSOPI()) {
++    opiDict->lookup("2.0", &dict);
++    if (dict.isDict()) {
++      opiBegin20(state, dict.getDict());
++      dict.free();
++    } else {
++      dict.free();
++      opiDict->lookup("1.3", &dict);
++      if (dict.isDict()) {
++	opiBegin13(state, dict.getDict());
++      }
++      dict.free();
++    }
++  }
++}
++
++void PSOutputDev::opiBegin20(GfxState *state, Dict *dict) {
++  Object obj1, obj2, obj3, obj4;
++  double width, height, left, right, top, bottom;
++  int w, h;
++  int i;
++
++  writePS("%%BeginOPI: 2.0\n");
++  writePS("%%Distilled\n");
++
++  dict->lookup("F", &obj1);
++  if (getFileSpec(&obj1, &obj2)) {
++    writePSFmt("%%ImageFileName: {0:t}\n", obj2.getString());
++    obj2.free();
++  }
++  obj1.free();
++
++  dict->lookup("MainImage", &obj1);
++  if (obj1.isString()) {
++    writePSFmt("%%MainImage: {0:t}\n", obj1.getString());
++  }
++  obj1.free();
++
++  //~ ignoring 'Tags' entry
++  //~ need to use writePSString() and deal with >255-char lines
++
++  dict->lookup("Size", &obj1);
++  if (obj1.isArray() && obj1.arrayGetLength() == 2) {
++    obj1.arrayGet(0, &obj2);
++    width = obj2.getNum();
++    obj2.free();
++    obj1.arrayGet(1, &obj2);
++    height = obj2.getNum();
++    obj2.free();
++    writePSFmt("%%ImageDimensions: {0:.4g} {1:.4g}\n", width, height);
++  }
++  obj1.free();
++
++  dict->lookup("CropRect", &obj1);
++  if (obj1.isArray() && obj1.arrayGetLength() == 4) {
++    obj1.arrayGet(0, &obj2);
++    left = obj2.getNum();
++    obj2.free();
++    obj1.arrayGet(1, &obj2);
++    top = obj2.getNum();
++    obj2.free();
++    obj1.arrayGet(2, &obj2);
++    right = obj2.getNum();
++    obj2.free();
++    obj1.arrayGet(3, &obj2);
++    bottom = obj2.getNum();
++    obj2.free();
++    writePSFmt("%%ImageCropRect: {0:.4g} {1:.4g} {2:.4g} {3:.4g}\n",
++	       left, top, right, bottom);
++  }
++  obj1.free();
++
++  dict->lookup("Overprint", &obj1);
++  if (obj1.isBool()) {
++    writePSFmt("%%ImageOverprint: {0:s}\n", obj1.getBool() ? "true" : "false");
++  }
++  obj1.free();
++
++  dict->lookup("Inks", &obj1);
++  if (obj1.isName()) {
++    writePSFmt("%%ImageInks: {0:s}\n", obj1.getName());
++  } else if (obj1.isArray() && obj1.arrayGetLength() >= 1) {
++    obj1.arrayGet(0, &obj2);
++    if (obj2.isName()) {
++      writePSFmt("%%ImageInks: {0:s} {1:d}",
++		 obj2.getName(), (obj1.arrayGetLength() - 1) / 2);
++      for (i = 1; i+1 < obj1.arrayGetLength(); i += 2) {
++	obj1.arrayGet(i, &obj3);
++	obj1.arrayGet(i+1, &obj4);
++	if (obj3.isString() && obj4.isNum()) {
++	  writePS(" ");
++	  writePSString(obj3.getString());
++	  writePSFmt(" {0:.4g}", obj4.getNum());
++	}
++	obj3.free();
++	obj4.free();
++      }
++      writePS("\n");
++    }
++    obj2.free();
++  }
++  obj1.free();
++
++  writePS("gsave\n");
++
++  writePS("%%BeginIncludedImage\n");
++
++  dict->lookup("IncludedImageDimensions", &obj1);
++  if (obj1.isArray() && obj1.arrayGetLength() == 2) {
++    obj1.arrayGet(0, &obj2);
++    w = obj2.getInt();
++    obj2.free();
++    obj1.arrayGet(1, &obj2);
++    h = obj2.getInt();
++    obj2.free();
++    writePSFmt("%%IncludedImageDimensions: {0:d} {1:d}\n", w, h);
++  }
++  obj1.free();
++
++  dict->lookup("IncludedImageQuality", &obj1);
++  if (obj1.isNum()) {
++    writePSFmt("%%IncludedImageQuality: {0:.4g}\n", obj1.getNum());
++  }
++  obj1.free();
++
++  ++opi20Nest;
++}
++
++void PSOutputDev::opiBegin13(GfxState *state, Dict *dict) {
++  Object obj1, obj2;
++  int left, right, top, bottom, samples, bits, width, height;
++  double c, m, y, k;
++  double llx, lly, ulx, uly, urx, ury, lrx, lry;
++  double tllx, tlly, tulx, tuly, turx, tury, tlrx, tlry;
++  double horiz, vert;
++  int i, j;
++
++  writePS("save\n");
++  writePS("/opiMatrix2 matrix currentmatrix def\n");
++  writePS("opiMatrix setmatrix\n");
++
++  dict->lookup("F", &obj1);
++  if (getFileSpec(&obj1, &obj2)) {
++    writePSFmt("%ALDImageFileName: {0:t}\n", obj2.getString());
++    obj2.free();
++  }
++  obj1.free();
++
++  dict->lookup("CropRect", &obj1);
++  if (obj1.isArray() && obj1.arrayGetLength() == 4) {
++    obj1.arrayGet(0, &obj2);
++    left = obj2.getInt();
++    obj2.free();
++    obj1.arrayGet(1, &obj2);
++    top = obj2.getInt();
++    obj2.free();
++    obj1.arrayGet(2, &obj2);
++    right = obj2.getInt();
++    obj2.free();
++    obj1.arrayGet(3, &obj2);
++    bottom = obj2.getInt();
++    obj2.free();
++    writePSFmt("%ALDImageCropRect: {0:d} {1:d} {2:d} {3:d}\n",
++	       left, top, right, bottom);
++  }
++  obj1.free();
++
++  dict->lookup("Color", &obj1);
++  if (obj1.isArray() && obj1.arrayGetLength() == 5) {
++    obj1.arrayGet(0, &obj2);
++    c = obj2.getNum();
++    obj2.free();
++    obj1.arrayGet(1, &obj2);
++    m = obj2.getNum();
++    obj2.free();
++    obj1.arrayGet(2, &obj2);
++    y = obj2.getNum();
++    obj2.free();
++    obj1.arrayGet(3, &obj2);
++    k = obj2.getNum();
++    obj2.free();
++    obj1.arrayGet(4, &obj2);
++    if (obj2.isString()) {
++      writePSFmt("%ALDImageColor: {0:.4g} {1:.4g} {2:.4g} {3:.4g} ",
++		 c, m, y, k);
++      writePSString(obj2.getString());
++      writePS("\n");
++    }
++    obj2.free();
++  }
++  obj1.free();
++
++  dict->lookup("ColorType", &obj1);
++  if (obj1.isName()) {
++    writePSFmt("%ALDImageColorType: {0:s}\n", obj1.getName());
++  }
++  obj1.free();
++
++  //~ ignores 'Comments' entry
++  //~ need to handle multiple lines
++
++  dict->lookup("CropFixed", &obj1);
++  if (obj1.isArray()) {
++    obj1.arrayGet(0, &obj2);
++    ulx = obj2.getNum();
++    obj2.free();
++    obj1.arrayGet(1, &obj2);
++    uly = obj2.getNum();
++    obj2.free();
++    obj1.arrayGet(2, &obj2);
++    lrx = obj2.getNum();
++    obj2.free();
++    obj1.arrayGet(3, &obj2);
++    lry = obj2.getNum();
++    obj2.free();
++    writePSFmt("%ALDImageCropFixed: {0:.4g} {1:.4g} {2:.4g} {3:.4g}\n",
++	       ulx, uly, lrx, lry);
++  }
++  obj1.free();
++
++  dict->lookup("GrayMap", &obj1);
++  if (obj1.isArray()) {
++    writePS("%ALDImageGrayMap:");
++    for (i = 0; i < obj1.arrayGetLength(); i += 16) {
++      if (i > 0) {
++	writePS("\n%%+");
++      }
++      for (j = 0; j < 16 && i+j < obj1.arrayGetLength(); ++j) {
++	obj1.arrayGet(i+j, &obj2);
++	writePSFmt(" {0:d}", obj2.getInt());
++	obj2.free();
++      }
++    }
++    writePS("\n");
++  }
++  obj1.free();
++
++  dict->lookup("ID", &obj1);
++  if (obj1.isString()) {
++    writePSFmt("%ALDImageID: {0:t}\n", obj1.getString());
++  }
++  obj1.free();
++
++  dict->lookup("ImageType", &obj1);
++  if (obj1.isArray() && obj1.arrayGetLength() == 2) {
++    obj1.arrayGet(0, &obj2);
++    samples = obj2.getInt();
++    obj2.free();
++    obj1.arrayGet(1, &obj2);
++    bits = obj2.getInt();
++    obj2.free();
++    writePSFmt("%ALDImageType: {0:d} {1:d}\n", samples, bits);
++  }
++  obj1.free();
++
++  dict->lookup("Overprint", &obj1);
++  if (obj1.isBool()) {
++    writePSFmt("%ALDImageOverprint: {0:s}\n",
++	       obj1.getBool() ? "true" : "false");
++  }
++  obj1.free();
++
++  dict->lookup("Position", &obj1);
++  if (obj1.isArray() && obj1.arrayGetLength() == 8) {
++    obj1.arrayGet(0, &obj2);
++    llx = obj2.getNum();
++    obj2.free();
++    obj1.arrayGet(1, &obj2);
++    lly = obj2.getNum();
++    obj2.free();
++    obj1.arrayGet(2, &obj2);
++    ulx = obj2.getNum();
++    obj2.free();
++    obj1.arrayGet(3, &obj2);
++    uly = obj2.getNum();
++    obj2.free();
++    obj1.arrayGet(4, &obj2);
++    urx = obj2.getNum();
++    obj2.free();
++    obj1.arrayGet(5, &obj2);
++    ury = obj2.getNum();
++    obj2.free();
++    obj1.arrayGet(6, &obj2);
++    lrx = obj2.getNum();
++    obj2.free();
++    obj1.arrayGet(7, &obj2);
++    lry = obj2.getNum();
++    obj2.free();
++    opiTransform(state, llx, lly, &tllx, &tlly);
++    opiTransform(state, ulx, uly, &tulx, &tuly);
++    opiTransform(state, urx, ury, &turx, &tury);
++    opiTransform(state, lrx, lry, &tlrx, &tlry);
++    writePSFmt("%ALDImagePosition: {0:.4g} {1:.4g} {2:.4g} {3:.4g} {4:.4g} {5:.4g} {6:.4g} {7:.4g}\n",
++	       tllx, tlly, tulx, tuly, turx, tury, tlrx, tlry);
++    obj2.free();
++  }
++  obj1.free();
++
++  dict->lookup("Resolution", &obj1);
++  if (obj1.isArray() && obj1.arrayGetLength() == 2) {
++    obj1.arrayGet(0, &obj2);
++    horiz = obj2.getNum();
++    obj2.free();
++    obj1.arrayGet(1, &obj2);
++    vert = obj2.getNum();
++    obj2.free();
++    writePSFmt("%ALDImageResoution: {0:.4g} {1:.4g}\n", horiz, vert);
++    obj2.free();
++  }
++  obj1.free();
++
++  dict->lookup("Size", &obj1);
++  if (obj1.isArray() && obj1.arrayGetLength() == 2) {
++    obj1.arrayGet(0, &obj2);
++    width = obj2.getInt();
++    obj2.free();
++    obj1.arrayGet(1, &obj2);
++    height = obj2.getInt();
++    obj2.free();
++    writePSFmt("%ALDImageDimensions: {0:d} {1:d}\n", width, height);
++  }
++  obj1.free();
++
++  //~ ignoring 'Tags' entry
++  //~ need to use writePSString() and deal with >255-char lines
++
++  dict->lookup("Tint", &obj1);
++  if (obj1.isNum()) {
++    writePSFmt("%ALDImageTint: {0:.4g}\n", obj1.getNum());
++  }
++  obj1.free();
++
++  dict->lookup("Transparency", &obj1);
++  if (obj1.isBool()) {
++    writePSFmt("%ALDImageTransparency: {0:s}\n",
++	       obj1.getBool() ? "true" : "false");
++  }
++  obj1.free();
++
++  writePS("%%BeginObject: image\n");
++  writePS("opiMatrix2 setmatrix\n");
++  ++opi13Nest;
++}
++
++// Convert PDF user space coordinates to PostScript default user space
++// coordinates.  This has to account for both the PDF CTM and the
++// PSOutputDev page-fitting transform.
++void PSOutputDev::opiTransform(GfxState *state, double x0, double y0,
++			       double *x1, double *y1) {
++  double t;
++
++  state->transform(x0, y0, x1, y1);
++  *x1 += tx;
++  *y1 += ty;
++  if (rotate == 90) {
++    t = *x1;
++    *x1 = -*y1;
++    *y1 = t;
++  } else if (rotate == 180) {
++    *x1 = -*x1;
++    *y1 = -*y1;
++  } else if (rotate == 270) {
++    t = *x1;
++    *x1 = *y1;
++    *y1 = -t;
++  }
++  *x1 *= xScale;
++  *y1 *= yScale;
++}
++
++void PSOutputDev::opiEnd(GfxState *state, Dict *opiDict) {
++  Object dict;
++
++  if (globalParams->getPSOPI()) {
++    opiDict->lookup("2.0", &dict);
++    if (dict.isDict()) {
++      writePS("%%EndIncludedImage\n");
++      writePS("%%EndOPI\n");
++      writePS("grestore\n");
++      --opi20Nest;
++      dict.free();
++    } else {
++      dict.free();
++      opiDict->lookup("1.3", &dict);
++      if (dict.isDict()) {
++	writePS("%%EndObject\n");
++	writePS("restore\n");
++	--opi13Nest;
++      }
++      dict.free();
++    }
++  }
++}
++
++GBool PSOutputDev::getFileSpec(Object *fileSpec, Object *fileName) {
++  if (fileSpec->isString()) {
++    fileSpec->copy(fileName);
++    return gTrue;
++  }
++  if (fileSpec->isDict()) {
++    fileSpec->dictLookup("DOS", fileName);
++    if (fileName->isString()) {
++      return gTrue;
++    }
++    fileName->free();
++    fileSpec->dictLookup("Mac", fileName);
++    if (fileName->isString()) {
++      return gTrue;
++    }
++    fileName->free();
++    fileSpec->dictLookup("Unix", fileName);
++    if (fileName->isString()) {
++      return gTrue;
++    }
++    fileName->free();
++    fileSpec->dictLookup("F", fileName);
++    if (fileName->isString()) {
++      return gTrue;
++    }
++    fileName->free();
++  }
++  return gFalse;
++}
++#endif // OPI_SUPPORT
++
++void PSOutputDev::type3D0(GfxState * /*state*/, double wx, double wy) {
++  writePSFmt("{0:.4g} {1:.4g} setcharwidth\n", wx, wy);
++  writePS("q\n");
++  t3NeedsRestore = gTrue;
++}
++
++void PSOutputDev::type3D1(GfxState * /*state*/, double wx, double wy,
++			  double llx, double lly, double urx, double ury) {
++  t3WX = wx;
++  t3WY = wy;
++  t3LLX = llx;
++  t3LLY = lly;
++  t3URX = urx;
++  t3URY = ury;
++  t3String = new GString();
++  writePS("q\n");
++  t3Cacheable = gTrue;
++  t3NeedsRestore = gTrue;
++}
++
++void PSOutputDev::drawForm(Ref id) {
++  writePSFmt("f_{0:d}_{1:d}\n", id.num, id.gen);
++}
++
++void PSOutputDev::psXObject(Stream *psStream, Stream *level1Stream) {
++  Stream *str;
++  int c;
++
++  if ((level == psLevel1 || level == psLevel1Sep) && level1Stream) {
++    str = level1Stream;
++  } else {
++    str = psStream;
++  }
++  str->reset();
++  while ((c = str->getChar()) != EOF) {
++    writePSChar(c);
++  }
++  str->close();
++}
++
++//~ can nextFunc be reset to 0 -- maybe at the start of each page?
++//~   or maybe at the start of each color space / pattern?
++void PSOutputDev::cvtFunction(Function *func) {
++  SampledFunction *func0;
++  ExponentialFunction *func2;
++  StitchingFunction *func3;
++  PostScriptFunction *func4;
++  int thisFunc, m, n, nSamples, i, j, k;
++
++  switch (func->getType()) {
++
++  case -1:			// identity
++    writePS("{}\n");
++    break;
++
++  case 0:			// sampled
++    func0 = (SampledFunction *)func;
++    thisFunc = nextFunc++;
++    m = func0->getInputSize();
++    n = func0->getOutputSize();
++    nSamples = n;
++    for (i = 0; i < m; ++i) {
++      nSamples *= func0->getSampleSize(i);
++    }
++    writePSFmt("/xpdfSamples{0:d} [\n", thisFunc);
++    for (i = 0; i < nSamples; ++i) {
++      writePSFmt("{0:.4g}\n", func0->getSamples()[i]);
++    }
++    writePS("] def\n");
++    writePSFmt("{{ {0:d} array {1:d} array {2:d} 2 roll\n", 2*m, m, m+2);
++    // [e01] [efrac] x0 x1 ... xm-1
++    for (i = m-1; i >= 0; --i) {
++      // [e01] [efrac] x0 x1 ... xi
++      writePSFmt("{0:.4g} sub {1:.4g} mul {2:.4g} add\n",
++	      func0->getDomainMin(i),
++	      (func0->getEncodeMax(i) - func0->getEncodeMin(i)) /
++	        (func0->getDomainMax(i) - func0->getDomainMin(i)),
++	      func0->getEncodeMin(i));
++      // [e01] [efrac] x0 x1 ... xi-1 xi'
++      writePSFmt("dup 0 lt {{ pop 0 }} {{ dup {0:d} gt {{ pop {1:d} }} if }} ifelse\n",
++		 func0->getSampleSize(i) - 1, func0->getSampleSize(i) - 1);
++      // [e01] [efrac] x0 x1 ... xi-1 xi'
++      writePS("dup floor cvi exch dup ceiling cvi exch 2 index sub\n");
++      // [e01] [efrac] x0 x1 ... xi-1 floor(xi') ceiling(xi') xi'-floor(xi')
++      writePSFmt("{0:d} index {1:d} 3 2 roll put\n", i+3, i);
++      // [e01] [efrac] x0 x1 ... xi-1 floor(xi') ceiling(xi')
++      writePSFmt("{0:d} index {1:d} 3 2 roll put\n", i+3, 2*i+1);
++      // [e01] [efrac] x0 x1 ... xi-1 floor(xi')
++      writePSFmt("{0:d} index {1:d} 3 2 roll put\n", i+2, 2*i);
++      // [e01] [efrac] x0 x1 ... xi-1
++    }
++    // [e01] [efrac]
++    for (i = 0; i < n; ++i) {
++      // [e01] [efrac] y(0) ... y(i-1)
++      for (j = 0; j < (1<<m); ++j) {
++	// [e01] [efrac] y(0) ... y(i-1) s(0) s(1) ... s(j-1)
++	writePSFmt("xpdfSamples{0:d}\n", thisFunc);
++	k = m - 1;
++	writePSFmt("{0:d} index {1:d} get\n", i+j+2, 2 * k + ((j >> k) & 1));
++	for (k = m - 2; k >= 0; --k) {
++	  writePSFmt("{0:d} mul {1:d} index {2:d} get add\n",
++		     func0->getSampleSize(k),
++		     i + j + 3,
++		     2 * k + ((j >> k) & 1));
++	}
++	if (n > 1) {
++	  writePSFmt("{0:d} mul {1:d} add ", n, i);
++	}
++	writePS("get\n");
++      }
++      // [e01] [efrac] y(0) ... y(i-1) s(0) s(1) ... s(2^m-1)
++      for (j = 0; j < m; ++j) {
++	// [e01] [efrac] y(0) ... y(i-1) s(0) s(1) ... s(2^(m-j)-1)
++	for (k = 0; k < (1 << (m - j)); k += 2) {
++	  // [e01] [efrac] y(0) ... y(i-1) <k/2 s' values> <2^(m-j)-k s values>
++	  writePSFmt("{0:d} index {1:d} get dup\n",
++		     i + k/2 + (1 << (m-j)) - k, j);
++	  writePS("3 2 roll mul exch 1 exch sub 3 2 roll mul add\n");
++	  writePSFmt("{0:d} 1 roll\n", k/2 + (1 << m-j) - k - 1);
++	}
++	// [e01] [efrac] s'(0) s'(1) ... s(2^(m-j-1)-1)
++      }
++      // [e01] [efrac] y(0) ... y(i-1) s
++      writePSFmt("{0:.4g} mul {1:.4g} add\n",
++		 func0->getDecodeMax(i) - func0->getDecodeMin(i),
++		 func0->getDecodeMin(i));
++      writePSFmt("dup {0:.4g} lt {{ pop {1:.4g} }} {{ dup {2:.4g} gt {{ pop {3:.4g} }} if }} ifelse\n",
++		 func0->getRangeMin(i), func0->getRangeMin(i),
++		 func0->getRangeMax(i), func0->getRangeMax(i));
++      // [e01] [efrac] y(0) ... y(i-1) y(i)
++    }
++    // [e01] [efrac] y(0) ... y(n-1)
++    writePSFmt("{0:d} {1:d} roll pop pop }}\n", n+2, n);
++    break;
++
++  case 2:			// exponential
++    func2 = (ExponentialFunction *)func;
++    n = func2->getOutputSize();
++    writePSFmt("{{ dup {0:.4g} lt {{ pop {1:.4g} }} {{ dup {2:.4g} gt {{ pop {3:.4g} }} if }} ifelse\n",
++	       func2->getDomainMin(0), func2->getDomainMin(0),
++	       func2->getDomainMax(0), func2->getDomainMax(0));
++    // x
++    for (i = 0; i < n; ++i) {
++      // x y(0) .. y(i-1)
++      writePSFmt("{0:d} index {1:.4g} exp {2:.4g} mul {3:.4g} add\n",
++		 i, func2->getE(), func2->getC1()[i] - func2->getC0()[i],
++		 func2->getC0()[i]);
++      if (func2->getHasRange()) {
++	writePSFmt("dup {0:.4g} lt {{ pop {1:.4g} }} {{ dup {2:.4g} gt {{ pop {3:.4g} }} if }} ifelse\n",
++		   func2->getRangeMin(i), func2->getRangeMin(i),
++		   func2->getRangeMax(i), func2->getRangeMax(i));
++      }
++    }
++    // x y(0) .. y(n-1)
++    writePSFmt("{0:d} {1:d} roll pop }}\n", n+1, n);
++    break;
++
++  case 3:			// stitching
++    func3 = (StitchingFunction *)func;
++    thisFunc = nextFunc++;
++    for (i = 0; i < func3->getNumFuncs(); ++i) {
++      cvtFunction(func3->getFunc(i));
++      writePSFmt("/xpdfFunc{0:d}_{1:d} exch def\n", thisFunc, i);
++    }
++    writePSFmt("{{ dup {0:.4g} lt {{ pop {1:.4g} }} {{ dup {2:.4g} gt {{ pop {3:.4g} }} if }} ifelse\n",
++	       func3->getDomainMin(0), func3->getDomainMin(0),
++	       func3->getDomainMax(0), func3->getDomainMax(0));
++    for (i = 0; i < func3->getNumFuncs() - 1; ++i) {
++      writePSFmt("dup {0:.4g} lt {{ {1:.4g} sub {2:.4g} mul {3:.4g} add xpdfFunc{4:d}_{5:d} }} {{\n",
++		 func3->getBounds()[i+1],
++		 func3->getBounds()[i],
++		 func3->getScale()[i],
++		 func3->getEncode()[2*i],
++		 thisFunc, i);
++    }
++    writePSFmt("{0:.4g} sub {1:.4g} mul {2:.4g} add xpdfFunc{3:d}_{4:d}\n",
++	       func3->getBounds()[i],
++	       func3->getScale()[i],
++	       func3->getEncode()[2*i],
++	       thisFunc, i);
++    for (i = 0; i < func3->getNumFuncs() - 1; ++i) {
++      writePS("} ifelse\n");
++    }
++    writePS("}\n");
++    break;
++
++  case 4:			// PostScript
++    func4 = (PostScriptFunction *)func;
++    writePS(func4->getCodeString()->getCString());
++    writePS("\n");
++    break;
++  }
++}
++
++void PSOutputDev::writePSChar(char c) {
++  if (t3String) {
++    t3String->append(c);
++  } else {
++    (*outputFunc)(outputStream, &c, 1);
++  }
++}
++
++void PSOutputDev::writePS(char *s) {
++  if (t3String) {
++    t3String->append(s);
++  } else {
++    (*outputFunc)(outputStream, s, strlen(s));
++  }
++}
++
++void PSOutputDev::writePSFmt(const char *fmt, ...) {
++  va_list args;
++  GString *buf;
++
++  va_start(args, fmt);
++  if (t3String) {
++    t3String->appendfv((char *)fmt, args);
++  } else {
++    buf = GString::formatv((char *)fmt, args);
++    (*outputFunc)(outputStream, buf->getCString(), buf->getLength());
++    delete buf;
++  }
++  va_end(args);
++}
++
++void PSOutputDev::writePSString(GString *s) {
++  Guchar *p;
++  int n, line;
++  char buf[8];
++
++  writePSChar('(');
++  line = 1;
++  for (p = (Guchar *)s->getCString(), n = s->getLength(); n; ++p, --n) {
++    if (line >= 64) {
++      writePSChar('\\');
++      writePSChar('\n');
++      line = 0;
++    }
++    if (*p == '(' || *p == ')' || *p == '\\') {
++      writePSChar('\\');
++      writePSChar((char)*p);
++      line += 2;
++    } else if (*p < 0x20 || *p >= 0x80) {
++      sprintf(buf, "\\%03o", *p);
++      writePS(buf);
++      line += 4;
++    } else {
++      writePSChar((char)*p);
++      ++line;
++    }
++  }
++  writePSChar(')');
++}
++
++void PSOutputDev::writePSName(char *s) {
++  char *p;
++  char c;
++
++  p = s;
++  while ((c = *p++)) {
++    if (c <= (char)0x20 || c >= (char)0x7f ||
++	c == '(' || c == ')' || c == '<' || c == '>' ||
++	c == '[' || c == ']' || c == '{' || c == '}' ||
++	c == '/' || c == '%') {
++      writePSFmt("#{0:02x}", c & 0xff);
++    } else {
++      writePSChar(c);
++    }
++  }
++}
++
++GString *PSOutputDev::filterPSName(GString *name) {
++  GString *name2;
++  char buf[8];
++  int i;
++  char c;
++
++  name2 = new GString();
++
++  // ghostscript chokes on names that begin with out-of-limits
++  // numbers, e.g., 1e4foo is handled correctly (as a name), but
++  // 1e999foo generates a limitcheck error
++  c = name->getChar(0);
++  if (c >= '0' && c <= '9') {
++    name2->append('f');
++  }
++
++  for (i = 0; i < name->getLength(); ++i) {
++    c = name->getChar(i);
++    if (c <= (char)0x20 || c >= (char)0x7f ||
++	c == '(' || c == ')' || c == '<' || c == '>' ||
++	c == '[' || c == ']' || c == '{' || c == '}' ||
++	c == '/' || c == '%') {
++      sprintf(buf, "#%02x", c & 0xff);
++      name2->append(buf);
++    } else {
++      name2->append(c);
++    }
++  }
++  return name2;
++}
++
++// Write a DSC-compliant <textline>.
++void PSOutputDev::writePSTextLine(GString *s) {
++  int i, j, step;
++  int c;
++
++  // - DSC comments must be printable ASCII; control chars and
++  //   backslashes have to be escaped (we do cheap Unicode-to-ASCII
++  //   conversion by simply ignoring the high byte)
++  // - lines are limited to 255 chars (we limit to 200 here to allow
++  //   for the keyword, which was emitted by the caller)
++  // - lines that start with a left paren are treated as <text>
++  //   instead of <textline>, so we escape a leading paren
++  if (s->getLength() >= 2 &&
++      (s->getChar(0) & 0xff) == 0xfe &&
++      (s->getChar(1) & 0xff) == 0xff) {
++    i = 3;
++    step = 2;
++  } else {
++    i = 0;
++    step = 1;
++  }
++  for (j = 0; i < s->getLength() && j < 200; i += step) {
++    c = s->getChar(i) & 0xff;
++    if (c == '\\') {
++      writePS("\\\\");
++      j += 2;
++    } else if (c < 0x20 || c > 0x7e || (j == 0 && c == '(')) {
++      writePSFmt("\\{0:03o}", c);
++      j += 4;
++    } else {
++      writePSChar(c);
++      ++j;
++    }
++  }
++  writePS("\n");
++}
+diff -Naur xpdf.orig/xpdf/Stream.cc xpdf/xpdf/Stream.cc
+--- xpdf.orig/xpdf/Stream.cc	2010-03-22 23:55:22.000000000 +0900
++++ xpdf/xpdf/Stream.cc	2010-03-23 00:14:18.000000000 +0900
+@@ -323,6 +323,10 @@
+   } else {
+     imgLineSize = nVals;
+   }
++  if (width > INT_MAX / nComps) {
++    // force a call to gmallocn(-1,...), which will throw an exception
++    imgLineSize = -1;
++  }
+   imgLine = (Guchar *)gmallocn(imgLineSize, sizeof(Guchar));
+   imgIdx = nVals;
+ }
+diff -Naur xpdf.orig/xpdf/XRef.cc xpdf/xpdf/XRef.cc
+--- xpdf.orig/xpdf/XRef.cc	2010-03-22 23:55:22.000000000 +0900
++++ xpdf/xpdf/XRef.cc	2010-03-23 00:14:18.000000000 +0900
+@@ -52,6 +52,8 @@
+   // generation 0.
+   ObjectStream(XRef *xref, int objStrNumA);
+ 
++  GBool isOk() { return ok; }
++
+   ~ObjectStream();
+ 
+   // Return the object number of this object stream.
+@@ -67,6 +69,7 @@
+   int nObjects;			// number of objects in the stream
+   Object *objs;			// the objects (length = nObjects)
+   int *objNums;			// the object numbers (length = nObjects)
++  GBool ok;
+ };
+ 
+ ObjectStream::ObjectStream(XRef *xref, int objStrNumA) {
+@@ -80,6 +83,7 @@
+   nObjects = 0;
+   objs = NULL;
+   objNums = NULL;
++  ok = gFalse;
+ 
+   if (!xref->fetch(objStrNum, 0, &objStr)->isStream()) {
+     goto err1;
+@@ -105,6 +109,13 @@
+     goto err1;
+   }
+ 
++  // this is an arbitrary limit to avoid integer overflow problems
++  // in the 'new Object[nObjects]' call (Acrobat apparently limits
++  // object streams to 100-200 objects)
++  if (nObjects > 1000000) {
++    error(-1, "Too many objects in an object stream");
++    goto err1;
++  }
+   objs = new Object[nObjects];
+   objNums = (int *)gmallocn(nObjects, sizeof(int));
+   offsets = (int *)gmallocn(nObjects, sizeof(int));
+@@ -161,10 +172,10 @@
+   }
+ 
+   gfree(offsets);
++  ok = gTrue;
+ 
+  err1:
+   objStr.free();
+-  return;
+ }
+ 
+ ObjectStream::~ObjectStream() {
+@@ -837,6 +848,11 @@
+ 	delete objStr;
+       }
+       objStr = new ObjectStream(this, e->offset);
++      if (!objStr->isOk()) {
++	delete objStr;
++	objStr = NULL;
++	goto err;
++      }
+     }
+     objStr->getObject(e->gen, num, obj);
+     break;
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/macports-changes/attachments/20100322/897009df/attachment-0001.html>


More information about the macports-changes mailing list