]> www.fi.muni.cz Git - evince.git/blobdiff - pdf/xpdf/PSOutputDev.cc
Synched with Xpdf 0.92
[evince.git] / pdf / xpdf / PSOutputDev.cc
index edff5cddfa323089bdd7e3353a10735f6e86621c..53cf39daababcc245433b4b548720e7a287f3051 100644 (file)
 #include "Catalog.h"
 #include "Page.h"
 #include "Stream.h"
+#include "FormWidget.h"
 #include "PSOutputDev.h"
 
+#if JAPANESE_SUPPORT
+#include "Japan12ToRKSJ.h"
+#endif
+
+#ifdef MACOS
+// needed for setting type/creator of MacOS files
+#include "ICSupport.h"
+#endif
+
 //------------------------------------------------------------------------
 // Parameters
 //------------------------------------------------------------------------
 // Generate Level 1 PostScript?
 GBool psOutLevel1 = gFalse;
 
+// Generate Level 1 separable PostScript?
+GBool psOutLevel1Sep = gFalse;
+
+// Generate Encapsulated PostScript?
+GBool psOutEPS = gFalse;
+
+#if OPI_SUPPORT
+// Generate OPI comments?
+GBool psOutOPI = gFalse;
+#endif
+
 int paperWidth = defPaperWidth;
 int paperHeight = defPaperHeight;
 
@@ -72,20 +93,26 @@ static char *prolog[] = {
   "  /pdfHorizScaling 1 def",
   "} def",
   "/pdfEndPage { end } def",
-  "/sCol { pdfLastStroke not {",
-  "          pdfStroke aload length",
-  "          1 eq { setgray } { setrgbcolor} ifelse",
-  "          /pdfLastStroke true def /pdfLastFill false def",
-  "        } if } def",
-  "/fCol { pdfLastFill not {",
-  "          pdfFill aload length",
-  "          1 eq { setgray } { setrgbcolor } ifelse",
-  "          /pdfLastFill true def /pdfLastStroke false def",
-  "        } if } def",
+  "/sCol {",
+  "  pdfLastStroke not {",
+  "    pdfStroke aload length",
+  "    dup 1 eq { pop setgray }",
+  "      { 3 eq { setrgbcolor } { setcmykcolor } ifelse } ifelse",
+  "    /pdfLastStroke true def /pdfLastFill false def",
+  "  } if",
+  "} def",
+  "/fCol {",
+  "  pdfLastFill not {",
+  "    pdfFill aload length",
+  "    dup 1 eq { pop setgray }",
+  "      { 3 eq { setrgbcolor } { setcmykcolor } ifelse } ifelse",
+  "    /pdfLastFill true def /pdfLastStroke false def",
+  "  } if",
+  "} def",
   "% build a font",
   "/pdfMakeFont {",
-  "  3 2 roll findfont",
-  "  3 2 roll 1 matrix scale makefont",
+  "  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",
@@ -93,6 +120,7 @@ static char *prolog[] = {
   "  end",
   "  definefont pop",
   "} def",
+  "/pdfMakeFont16 { findfont definefont pop } def",
   "% graphics state operators",
   "/q { gsave pdfDictSize dict begin } def",
   "/Q { end grestore } def",
@@ -105,12 +133,16 @@ static char *prolog[] = {
   "/w { setlinewidth } def",
   "% color operators",
   "/g { dup 1 array astore /pdfFill exch def setgray",
-  "    /pdfLastFill true def /pdfLastStroke false def } def",
+  "     /pdfLastFill true def /pdfLastStroke false def } def",
   "/G { dup 1 array astore /pdfStroke exch def setgray",
   "     /pdfLastStroke true def /pdfLastFill false def } def",
   "/rg { 3 copy 3 array astore /pdfFill exch def setrgbcolor",
-  "     /pdfLastFill true def /pdfLastStroke false def } def",
+  "      /pdfLastFill true def /pdfLastStroke false def } def",
   "/RG { 3 copy 3 array astore /pdfStroke exch def setrgbcolor",
+  "      /pdfLastStroke true def /pdfLastFill false def } def",
+  "/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",
   "% path segment operators",
   "/m { moveto } def",
@@ -118,6 +150,7 @@ static char *prolog[] = {
   "/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",
   "/f { fCol fill } def",
@@ -155,6 +188,17 @@ static char *prolog[] = {
   "  /pdfImBuf1 4 index string def",
   "  { currentfile pdfImBuf1 readhexstring pop } image",
   "} def",
+  "/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",
   "/pdfImM1 {",
   "  /pdfImBuf1 4 index 7 add 8 idiv string def",
   "  { currentfile pdfImBuf1 readhexstring pop } imagemask",
@@ -233,8 +277,11 @@ PSOutputDev::PSOutputDev(char *fileName, Catalog *catalog,
                         GBool embedType11, GBool doForm1) {
   Page *page;
   Dict *resDict;
+  FormWidgets *formWidgets;
   char **p;
   int pg;
+  Object obj1, obj2;
+  int i;
 
   // initialize
   embedType1 = embedType11;
@@ -242,6 +289,7 @@ PSOutputDev::PSOutputDev(char *fileName, Catalog *catalog,
   fontIDs = NULL;
   fontFileIDs = NULL;
   fontFileNames = NULL;
+  embFontList = NULL;
   f = NULL;
   if (doForm)
     lastPage = firstPage;
@@ -285,71 +333,151 @@ PSOutputDev::PSOutputDev(char *fileName, Catalog *catalog,
   fontFileIDs = (Ref *)gmalloc(fontFileIDSize * sizeof(Ref));
   fontFileNameSize = 64;
   fontFileNameLen = 0;
-  fontFileNames = (char **)gmalloc(fontFileNameSize * sizeof(char *));
+  fontFileNames = (GString **)gmalloc(fontFileNameSize * sizeof(GString *));
+
+  // initialize embedded font resource comment list
+  embFontList = new GString();
 
   // write header
   if (doForm) {
     writePS("%%!PS-Adobe-3.0 Resource-Form\n");
     writePS("%%%%Creator: xpdf/pdftops %s\n", xpdfVersion);
+    writePS("%%%%LanguageLevel: %d\n",
+           (psOutLevel1 || psOutLevel1Sep) ? 1 : 2);
+    if (psOutLevel1Sep) {
+      writePS("%%%%DocumentProcessColors: Cyan Magenta Yellow Black\n");
+    }
+    writePS("%%%%EndComments\n");
+    page = catalog->getPage(firstPage);
+    writePS("32 dict dup begin\n");
+    writePS("/BBox [%d %d %d %d] def\n",
+           (int)page->getX1(), (int)page->getY1(),
+           (int)page->getX2(), (int)page->getY2());
+    writePS("/FormType 1 def\n");
+    writePS("/Matrix [1 0 0 1 0 0] def\n");
+  } else if (psOutEPS) {
+    writePS("%%!PS-Adobe-3.0 EPSF-3.0\n");
+    writePS("%%%%Creator: xpdf/pdftops %s\n", xpdfVersion);
+    writePS("%%%%LanguageLevel: %d\n",
+           (psOutLevel1 || psOutLevel1Sep) ? 1 : 2);
+    if (psOutLevel1Sep) {
+      writePS("%%%%DocumentProcessColors: Cyan Magenta Yellow Black\n");
+    }
+    page = catalog->getPage(firstPage);
+    writePS("%%%%BoundingBox: %d %d %d %d\n",
+           (int)floor(page->getX1()), (int)floor(page->getY1()),
+           (int)ceil(page->getX2()), (int)ceil(page->getY2()));
+    if (floor(page->getX1()) != ceil(page->getX1()) ||
+       floor(page->getY1()) != ceil(page->getY1()) ||
+       floor(page->getX2()) != ceil(page->getX2()) ||
+       floor(page->getY2()) != ceil(page->getY2())) {
+      writePS("%%%%HiResBoundingBox: %g %g %g %g\n",
+             page->getX1(), page->getY1(),
+             page->getX2(), page->getY2());
+    }
+    writePS("%%%%DocumentSuppliedResources: (atend)\n");
     writePS("%%%%EndComments\n");
   } else {
     writePS("%%!PS-Adobe-3.0\n");
     writePS("%%%%Creator: xpdf/pdftops %s\n", xpdfVersion);
+    writePS("%%%%LanguageLevel: %d\n",
+           (psOutLevel1 || psOutLevel1Sep) ? 1 : 2);
+    if (psOutLevel1Sep) {
+      writePS("%%%%DocumentProcessColors: Cyan Magenta Yellow Black\n");
+    }
+    writePS("%%%%DocumentMedia: plain %d %d 0 () ()\n",
+           paperWidth, paperHeight);
     writePS("%%%%Pages: %d\n", lastPage - firstPage + 1);
     writePS("%%%%EndComments\n");
+    writePS("%%%%BeginDefaults\n");
+    writePS("%%%%PageMedia: plain\n");
+    writePS("%%%%EndDefaults\n");
   }
 
   // write prolog
-  if (!doForm)
+  if (!doForm) {
     writePS("%%%%BeginProlog\n");
-  writePS("%%%%BeginResource: xpdf %s\n", xpdfVersion);
-  for (p = prolog; *p; ++p)
+  }
+  writePS("%%%%BeginResource: procset xpdf %s 0\n", xpdfVersion);
+  for (p = prolog; *p; ++p) {
     writePS("%s\n", *p);
+  }
   writePS("%%%%EndResource\n");
-  if (!doForm)
+  if (!doForm) {
     writePS("%%%%EndProlog\n");
+  }
 
-  // set up fonts
-  if (!doForm)
+  // set up fonts and images
+  type3Warning = gFalse;
+  if (doForm) {
+    // swap the form and xpdf dicts
+    writePS("xpdf end begin dup begin\n");
+  } else {
     writePS("%%%%BeginSetup\n");
-  writePS("xpdf begin\n");
+    writePS("xpdf begin\n");
+  }
   for (pg = firstPage; pg <= lastPage; ++pg) {
-    if ((resDict = catalog->getPage(pg)->getResourceDict()))
-      setupFonts(resDict);
+    page = catalog->getPage(pg);
+    if ((resDict = page->getResourceDict())) {
+      setupResources(resDict);
+    }
+    formWidgets = new FormWidgets(page->getAnnots(&obj1));
+    obj1.free();
+    for (i = 0; i < formWidgets->getNumWidgets(); ++i) {
+      if (formWidgets->getWidget(i)->getAppearance(&obj1)->isStream()) {
+       obj1.streamGetDict()->lookup("Resources", &obj2);
+       if (obj2.isDict()) {
+         setupResources(obj2.getDict());
+       }
+       obj2.free();
+      }
+      obj1.free();
+    }
+    delete formWidgets;
   }
-  if (doForm) {
-    writePS("end\n");
-  } else {
-    writePS("%d %d pdfSetup\n", paperWidth, paperHeight);
+  if (!doForm) {
+#if OPI_SUPPORT
+    if (psOutOPI) {
+      writePS("/opiMatrix matrix currentmatrix def\n");
+    }
+#endif
+    if (!psOutEPS) {
+      writePS("%d %d pdfSetup\n", paperWidth, paperHeight);
+    }
     writePS("%%%%EndSetup\n");
   }
 
-  // write form header
-  if (doForm) {
-    page = catalog->getPage(firstPage);
-    writePS("4 dict dup begin\n");
-    writePS("/BBox [%d %d %d %d] def\n",
-           (int)page->getX1(), (int)page->getY1(),
-           (int)page->getX2(), (int)page->getY2());
-    writePS("/FormType 1 def\n");
-    writePS("/Matrix [1 0 0 1 0 0] def\n");
-  }
-
   // initialize sequential page number
   seqPage = 1;
+
+#if OPI_SUPPORT
+  // initialize OPI nesting levels
+  opi13Nest = 0;
+  opi20Nest = 0;
+#endif
 }
 
 PSOutputDev::~PSOutputDev() {
+  int i;
+
   if (f) {
     if (doForm) {
-      writePS("end\n");
       writePS("/Foo exch /Form defineresource pop\n");
+    } else if (psOutEPS) {
+      writePS("%%%%Trailer\n");
+      writePS("end\n");
+      writePS("%%%%DocumentSuppliedResources:\n");
+      writePS("%s", embFontList->getCString());
+      writePS("%%%%EOF\n");
     } else {
       writePS("%%%%Trailer\n");
       writePS("end\n");
       writePS("%%%%EOF\n");
     }
     if (fileType == psFile) {
+#ifdef MACOS
+      ICS_MapRefNumAndAssign((short)f->handle);
+#endif
       fclose(f);
     }
 #ifdef HAVE_POPEN
@@ -361,30 +489,29 @@ PSOutputDev::~PSOutputDev() {
     }
 #endif
   }
-  if (fontIDs)
+  if (embFontList) {
+    delete embFontList;
+  }
+  if (fontIDs) {
     gfree(fontIDs);
-  if (fontFileIDs)
+  }
+  if (fontFileIDs) {
     gfree(fontFileIDs);
-  if (fontFileNames)
+  }
+  if (fontFileNames) {
+    for (i = 0; i < fontFileNameLen; ++i) {
+      delete fontFileNames[i];
+    }
     gfree(fontFileNames);
+  }
 }
 
-void PSOutputDev::setupFonts(Dict *resDict) {
-  Object fontDict, xObjDict, xObj, resObj;
-  GfxFontDict *gfxFontDict;
-  GfxFont *font;
+void PSOutputDev::setupResources(Dict *resDict) {
+  Object xObjDict, xObj, resObj;
   int i;
 
-  resDict->lookup("Font", &fontDict);
-  if (fontDict.isDict()) {
-    gfxFontDict = new GfxFontDict(fontDict.getDict());
-    for (i = 0; i < gfxFontDict->getNumFonts(); ++i) {
-      font = gfxFontDict->getFont(i);
-      setupFont(font);
-    }
-    delete gfxFontDict;
-  }
-  fontDict.free();
+  setupFonts(resDict);
+  setupImages(resDict);
 
   resDict->lookup("XObject", &xObjDict);
   if (xObjDict.isDict()) {
@@ -392,8 +519,9 @@ void PSOutputDev::setupFonts(Dict *resDict) {
       xObjDict.dictGetVal(i, &xObj);
       if (xObj.isStream()) {
        xObj.streamGetDict()->lookup("Resources", &resObj);
-       if (resObj.isDict())
-         setupFonts(resObj.getDict());
+       if (resObj.isDict()) {
+         setupResources(resObj.getDict());
+       }
        resObj.free();
       }
       xObj.free();
@@ -402,12 +530,34 @@ void PSOutputDev::setupFonts(Dict *resDict) {
   xObjDict.free();
 }
 
+void PSOutputDev::setupFonts(Dict *resDict) {
+  Object fontDict;
+  GfxFontDict *gfxFontDict;
+  GfxFont *font;
+  int i;
+
+  resDict->lookup("Font", &fontDict);
+  if (fontDict.isDict()) {
+    gfxFontDict = new GfxFontDict(fontDict.getDict());
+    for (i = 0; i < gfxFontDict->getNumFonts(); ++i) {
+      font = gfxFontDict->getFont(i);
+      setupFont(font);
+    }
+    delete gfxFontDict;
+  }
+  fontDict.free();
+}
+
 void PSOutputDev::setupFont(GfxFont *font) {
   Ref fontFileID;
   GString *name;
   char *psName;
   char *charName;
-  double scale;
+  double xs, ys;
+  GBool do16Bit;
+  int code;
+  double w1, w2;
+  double *fm;
   int i, j;
 
   // check if font is already set up
@@ -424,33 +574,40 @@ void PSOutputDev::setupFont(GfxFont *font) {
   }
   fontIDs[fontIDLen++] = font->getID();
 
+  xs = ys = 1;
+  do16Bit = gFalse;
+
   // check for embedded Type 1 font
   if (embedType1 && font->getType() == fontType1 &&
       font->getEmbeddedFontID(&fontFileID)) {
-    setupEmbeddedType1Font(&fontFileID);
     psName = font->getEmbeddedFontName();
-    scale = 1;
+    setupEmbeddedType1Font(&fontFileID, psName);
 
   // check for external Type 1 font file
   } else if (embedType1 && font->getType() == fontType1 &&
             font->getExtFontFile()) {
-    setupEmbeddedType1Font(font->getExtFontFile());
     // this assumes that the PS font name matches the PDF font name
     psName = font->getName()->getCString();
-    scale = 1;
+    setupEmbeddedType1Font(font->getExtFontFile(), psName);
 
   // check for embedded Type 1C font
   } else if (embedType1 && font->getType() == fontType1C &&
             font->getEmbeddedFontID(&fontFileID)) {
-    setupEmbeddedType1CFont(font, &fontFileID);
     psName = font->getEmbeddedFontName();
-    scale = 1;
+    setupEmbeddedType1CFont(font, &fontFileID, psName);
+
+  } else if (font->is16Bit() && font->getCharSet16() == font16AdobeJapan12) {
+    psName = "Ryumin-Light-RKSJ";
+    do16Bit = gTrue;
 
   // do font substitution
   } else {
+    if (!type3Warning && font->getType() == fontType3) {
+      error(-1, "This document uses Type 3 fonts - some text may not be correctly printed");
+      type3Warning = gTrue;
+    }
     name = font->getName();
     psName = NULL;
-    scale = 1.0;
     if (name) {
       for (i = 0; psFonts[i].name; ++i) {
        if (name->cmp(psFonts[i].name) == 0) {
@@ -471,27 +628,52 @@ void PSOutputDev::setupFont(GfxFont *font) {
       if (font->isItalic())
        i += 1;
       psName = psSubstFonts[i].psName;
-      scale = font->getWidth('m') / psSubstFonts[i].mWidth;
-      if (scale < 0.1)
-       scale = 1;
+      if ((code = font->getCharCode("m")) >= 0) {
+       w1 = 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;
+      }
     }
   }
 
   // generate PostScript code to set up the font
-  writePS("/F%d_%d /%s %g\n",
-         font->getID().num, font->getID().gen, psName, scale);
-  for (i = 0; i < 256; i += 8) {
-    writePS((i == 0) ? "[ " : "  ");
-    for (j = 0; j < 8; ++j) {
-      charName = font->getCharName(i+j);
-      writePS("/%s", charName ? charName : ".notdef");
+  if (do16Bit) {
+    writePS("/F%d_%d /%s pdfMakeFont16\n",
+           font->getID().num, font->getID().gen, psName);
+  } else {
+    writePS("/F%d_%d /%s %g %g\n",
+           font->getID().num, font->getID().gen, psName, xs, ys);
+    for (i = 0; i < 256; i += 8) {
+      writePS((i == 0) ? "[ " : "  ");
+      for (j = 0; j < 8; ++j) {
+       charName = font->getCharName(i+j);
+       writePS("/%s", charName ? charName : ".notdef");
+      }
+      writePS((i == 256-8) ? "]\n" : "\n");
     }
-    writePS((i == 256-8) ? "]\n" : "\n");
+    writePS("pdfMakeFont\n");
   }
-  writePS("pdfMakeFont\n");
 }
 
-void PSOutputDev::setupEmbeddedType1Font(Ref *id) {
+void PSOutputDev::setupEmbeddedType1Font(Ref *id, char *psName) {
   static char hexChar[17] = "0123456789abcdef";
   Object refObj, strObj, obj1, obj2;
   Dict *dict;
@@ -540,6 +722,14 @@ void PSOutputDev::setupEmbeddedType1Font(Ref *id) {
   obj1.free();
   obj2.free();
 
+  // beginning comment
+  if (psOutEPS) {
+    writePS("%%%%BeginResource: font %s\n", psName);
+    embFontList->append("%%+ font ");
+    embFontList->append(psName);
+    embFontList->append("\n");
+  }
+
   // copy ASCII portion of font
   strObj.streamReset();
   for (i = 0; i < length1 && (c = strObj.streamGetChar()) != EOF; ++i)
@@ -593,42 +783,63 @@ void PSOutputDev::setupEmbeddedType1Font(Ref *id) {
            "00000000000000000000000000000000\n");
   writePS("cleartomark\n");
 
+  // ending comment
+  if (psOutEPS) {
+    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::setupEmbeddedType1Font(char *fileName) {
+void PSOutputDev::setupEmbeddedType1Font(GString *fileName, char *psName) {
   FILE *fontFile;
   int c;
   int i;
 
   // check if font is already embedded
   for (i = 0; i < fontFileNameLen; ++i) {
-    if (!strcmp(fontFileNames[i], fileName))
+    if (!fontFileNames[i]->cmp(fileName)) {
       return;
+    }
   }
 
   // add entry to fontFileNames list
   if (fontFileNameLen >= fontFileNameSize) {
     fontFileNameSize += 64;
-    fontFileNames = (char **)grealloc(fontFileNames,
-                                     fontFileNameSize * sizeof(char *));
+    fontFileNames = (GString **)grealloc(fontFileNames,
+                                        fontFileNameSize * sizeof(GString *));
+  }
+  fontFileNames[fontFileNameLen++] = fileName->copy();
+
+  // beginning comment
+  if (psOutEPS) {
+    writePS("%%%%BeginResource: font %s\n", psName);
+    embFontList->append("%%+ font ");
+    embFontList->append(psName);
+    embFontList->append("\n");
   }
-  fontFileNames[fontFileNameLen++] = fileName;
 
   // copy the font file
-  if (!(fontFile = fopen(fileName, "rb"))) {
+  if (!(fontFile = fopen(fileName->getCString(), "rb"))) {
     error(-1, "Couldn't open external font file");
     return;
   }
   while ((c = fgetc(fontFile)) != EOF)
     fputc(c, f);
   fclose(fontFile);
+
+  // ending comment
+  if (psOutEPS) {
+    writePS("%%%%EndResource\n");
+  }
 }
 
-void PSOutputDev::setupEmbeddedType1CFont(GfxFont *font, Ref *id) {
+void PSOutputDev::setupEmbeddedType1CFont(GfxFont *font, Ref *id,
+                                         char *psName) {
   char *fontBuf;
   int fontLen;
   Type1CFontConverter *cvt;
@@ -648,23 +859,160 @@ void PSOutputDev::setupEmbeddedType1CFont(GfxFont *font, Ref *id) {
   }
   fontFileIDs[fontFileIDLen++] = *id;
 
+  // beginning comment
+  if (psOutEPS) {
+    writePS("%%%%BeginResource: font %s\n", psName);
+    embFontList->append("%%+ font ");
+    embFontList->append(psName);
+    embFontList->append("\n");
+  }
+
   // convert it to a Type 1 font
   fontBuf = font->readEmbFontFile(&fontLen);
   cvt = new Type1CFontConverter(fontBuf, fontLen, f);
   cvt->convert();
   delete cvt;
   gfree(fontBuf);
+
+  // ending comment
+  if (psOutEPS) {
+    writePS("%%%%EndResource\n");
+  }
+}
+
+void PSOutputDev::setupImages(Dict *resDict) {
+  Object xObjDict, xObj, xObjRef, subtypeObj;
+  int i;
+
+  if (!doForm) {
+    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) {
+  int c;
+  int size, line, col, i;
+
+  // construct an encoder stream
+  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 == '~' || c == EOF) {
+      break;
+    }
+    if (c == 'z') {
+      ++col;
+    } else {
+      ++col;
+      for (i = 1; i <= 4; ++i) {
+       do {
+         c = str->getChar();
+       } while (c == '\n' || c == '\r');
+       if (c == '~' || c == EOF) {
+         break;
+       }
+       ++col;
+      }
+    }
+    if (col > 225) {
+      ++size;
+      col = 0;
+    }
+  } while (c != '~' && c != EOF);
+  ++size;
+  writePS("%d array dup /ImData_%d_%d exch def\n", size, id.num, id.gen);
+
+  // write the data into the array
+  str->reset();
+  line = col = 0;
+  writePS("dup 0 <~");
+  do {
+    do {
+      c = str->getChar();
+    } while (c == '\n' || c == '\r');
+    if (c == '~' || c == EOF) {
+      break;
+    }
+    if (c == 'z') {
+      fputc(c, f);
+      ++col;
+    } else {
+      fputc(c, f);
+      ++col;
+      for (i = 1; i <= 4; ++i) {
+       do {
+         c = str->getChar();
+       } while (c == '\n' || c == '\r');
+       if (c == '~' || c == EOF) {
+         break;
+       }
+       fputc(c, f);
+       ++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("~> put\n");
+      ++line;
+      writePS("dup %d <~", line);
+      col = 0;
+    }
+  } while (c != '~' && c != EOF);
+  writePS("~> put\n");
+  writePS("pop\n");
+
+  delete str;
 }
 
 void PSOutputDev::startPage(int pageNum, GfxState *state) {
   int x1, y1, x2, y2, width, height, t;
-  double xScale, yScale;
 
   if (doForm) {
 
     writePS("/PaintProc {\n");
     writePS("begin xpdf begin\n");
-    writePS("pdfSetup\n");
+    writePS("pdfStartPage\n");
+    tx = ty = 0;
+    xScale = yScale = 1;
+    landscape = gFalse;
+
+  } else if (psOutEPS) {
+
+    writePS("pdfStartPage\n");
+    tx = ty = 0;
+    xScale = yScale = 1;
+    landscape = gFalse;
 
   } else {
 
@@ -678,26 +1026,41 @@ void PSOutputDev::startPage(int pageNum, GfxState *state) {
     y2 = (int)(state->getY2() + 0.5);
     width = x2 - x1;
     height = y2 - y1;
-    if (width > height) {
+    if (width > height && width > paperWidth) {
+      landscape = gTrue;
       writePS("%%%%PageOrientation: Landscape\n");
       writePS("pdfStartPage\n");
       writePS("90 rotate\n");
-      writePS("%d %d translate\n", -x1, -(y1 + paperWidth));
+      tx = -x1;
+      ty = -(y1 + paperWidth);
       t = width;
       width = height;
       height = t;
     } else {
+      landscape = gFalse;
       writePS("%%%%PageOrientation: Portrait\n");
       writePS("pdfStartPage\n");
-      if (x1 != 0 || y1 != 0)
-       writePS("%d %d translate\n", -x1, -y1);
+      tx = -x1;
+      ty = -y1;
+    }
+    if (width < paperWidth) {
+      tx += (paperWidth - width) / 2;
+    }
+    if (height < paperHeight) {
+      ty += (paperHeight - height) / 2;
+    }
+    if (tx != 0 || ty != 0) {
+      writePS("%g %g translate\n", tx, ty);
     }
     if (width > paperWidth || height > paperHeight) {
       xScale = (double)paperWidth / (double)width;
       yScale = (double)paperHeight / (double)height;
-      if (yScale < xScale)
+      if (yScale < xScale) {
        xScale = yScale;
+      }
       writePS("%0.4f %0.4f scale\n", xScale, xScale);
+    } else {
+      xScale = yScale = 1;
     }
 
     writePS("%%%%EndPageSetup\n");
@@ -710,6 +1073,7 @@ void PSOutputDev::endPage() {
     writePS("pdfEndPage\n");
     writePS("end end\n");
     writePS("} def\n");
+    writePS("end end\n");
   } else {
     writePS("showpage\n");
     writePS("%%%%PageTrailer\n");
@@ -763,31 +1127,37 @@ void PSOutputDev::updateLineWidth(GfxState *state) {
 }
 
 void PSOutputDev::updateFillColor(GfxState *state) {
-  GfxColor *color;
-  double r, g, b;
-
-  color = state->getFillColor();
-  r = color->getR();
-  g = color->getG();
-  b = color->getB();
-  if (r == g && g == b)
-    writePS("%g g\n", r);
-  else
-    writePS("%g %g %g rg\n", r, g, b);
+  GfxRGB rgb;
+  GfxCMYK cmyk;
+
+  if (psOutLevel1Sep) {
+    state->getFillCMYK(&cmyk);
+    writePS("%g %g %g %g k\n", cmyk.c, cmyk.m, cmyk.y, cmyk.k);
+  } else {
+    state->getFillRGB(&rgb);
+    if (rgb.r == rgb.g && rgb.g == rgb.b) {
+      writePS("%g g\n", rgb.r);
+    } else {
+      writePS("%g %g %g rg\n", rgb.r, rgb.g, rgb.b);
+    }
+  }
 }
 
 void PSOutputDev::updateStrokeColor(GfxState *state) {
-  GfxColor *color;
-  double r, g, b;
-
-  color = state->getStrokeColor();
-  r = color->getR();
-  g = color->getG();
-  b = color->getB();
-  if (r == g && g == b)
-    writePS("%g G\n", r);
-  else
-    writePS("%g %g %g RG\n", r, g, b);
+  GfxRGB rgb;
+  GfxCMYK cmyk;
+
+  if (psOutLevel1Sep) {
+    state->getStrokeCMYK(&cmyk);
+    writePS("%g %g %g %g K\n", cmyk.c, cmyk.m, cmyk.y, cmyk.k);
+  } else {
+    state->getStrokeRGB(&rgb);
+    if (rgb.r == rgb.g && rgb.g == rgb.b) {
+      writePS("%g G\n", rgb.r);
+    } else {
+      writePS("%g %g %g RG\n", rgb.r, rgb.g, rgb.b);
+    }
+  }
 }
 
 void PSOutputDev::updateFont(GfxState *state) {
@@ -909,6 +1279,9 @@ void PSOutputDev::doPath(GfxPath *path) {
        ++j;
       }
     }
+    if (subpath->isClosed()) {
+      writePS("h\n");
+    }
   }
 }
 
@@ -921,37 +1294,83 @@ void PSOutputDev::drawString(GfxState *state, GString *s) {
   writePS(" %g Tj\n", state->getFont()->getWidth(s));
 }
 
-void PSOutputDev::drawImageMask(GfxState *state, Stream *str,
+void PSOutputDev::drawString16(GfxState *state, GString *s) {
+  int c1, c2;
+  double w;
+  int i;
+
+  // check for invisible text -- this is used by Acrobat Capture
+  if ((state->getRender() & 3) == 3)
+    return;
+
+  switch (state->getFont()->getCharSet16()) {
+
+  case font16AdobeJapan12:
+#if JAPANESE_SUPPORT
+    writePS("<");
+    w = 0;
+    for (i = 0; i < s->getLength(); i += 2) {
+      c1 = ((s->getChar(i) & 0xff) << 8) + (s->getChar(i+1) & 0xff);
+      if (c1 <= 8285) {
+       c2 = japan12ToRKSJ[c1];
+      } else {
+       c2 = 0x20;
+      }
+      if (c2 <= 0xff) {
+       writePS("%02x", c2);
+      } else {
+       writePS("%02x%02x", c2 >> 8, c2 & 0xff);
+      }
+      w += state->getFont()->getWidth16(c1);
+    }
+    writePS("> %g Tj\n", w);
+#endif
+    break;
+
+  case font16AdobeGB12:
+    break;
+
+  case font16AdobeCNS13:
+    break;
+  }
+}
+
+void PSOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
                                int width, int height, GBool invert,
                                GBool inlineImg) {
   int len;
 
   len = height * ((width + 7) / 8);
-  if (psOutLevel1)
+  if (psOutLevel1 || psOutLevel1Sep) {
     doImageL1(NULL, invert, inlineImg, str, width, height, len);
-  else
-    doImage(NULL, invert, inlineImg, str, width, height, len);
+  } else {
+    doImageL2(ref, NULL, invert, inlineImg, str, width, height, len);
+  }
 }
 
-void PSOutputDev::drawImage(GfxState *state, Stream *str, int width,
-                           int height, GfxImageColorMap *colorMap,
+void PSOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
+                           int width, int height, GfxImageColorMap *colorMap,
                            GBool inlineImg) {
   int len;
 
   len = height * ((width * colorMap->getNumPixelComps() *
                   colorMap->getBits() + 7) / 8);
-  if (psOutLevel1)
+  if (psOutLevel1) {
     doImageL1(colorMap, gFalse, inlineImg, str, width, height, len);
-  else
-    doImage(colorMap, gFalse, inlineImg, str, width, height, len);
+  } else if (psOutLevel1Sep) {
+    //~ handle indexed, separation, ... color spaces
+    doImageL1Sep(colorMap, gFalse, inlineImg, str, width, height, len);
+  } else {
+    doImageL2(ref, colorMap, gFalse, inlineImg, str, width, height, len);
+  }
 }
 
 void PSOutputDev::doImageL1(GfxImageColorMap *colorMap,
                            GBool invert, GBool inlineImg,
                            Stream *str, int width, int height, int len) {
   ImageStream *imgStr;
-  Guchar pixBuf[4];
-  GfxColor color;
+  Guchar pixBuf[gfxColorMaxComps];
+  double gray;
   int x, y, i;
 
   // width, height, matrix, bits per component
@@ -980,8 +1399,8 @@ void PSOutputDev::doImageL1(GfxImageColorMap *colorMap,
       // write the line
       for (x = 0; x < width; ++x) {
        imgStr->getPixel(pixBuf);
-       colorMap->getColor(pixBuf, &color);
-       fprintf(f, "%02x", (int)(color.getGray() * 255 + 0.5));
+       colorMap->getGray(pixBuf, &gray);
+       fprintf(f, "%02x", (int)(gray * 255 + 0.5));
        if (++i == 32) {
          fputc('\n', f);
          i = 0;
@@ -1010,50 +1429,80 @@ void PSOutputDev::doImageL1(GfxImageColorMap *colorMap,
   }
 }
 
-void PSOutputDev::doImage(GfxImageColorMap *colorMap,
-                         GBool invert, GBool inlineImg,
-                         Stream *str, int width, int height, int len) {
-  GfxColorSpace *colorSpace;
+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
+  writePS("%d %d 8 [%d 0 0 %d 0 %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] = (int)(255 * cmyk.c + 0.5);
+      lineBuf[4*x+1] = (int)(255 * cmyk.m + 0.5);
+      lineBuf[4*x+2] = (int)(255 * cmyk.y + 0.5);
+      lineBuf[4*x+3] = (int)(255 * cmyk.k + 0.5);
+    }
+
+    // write one line of each color component
+    for (comp = 0; comp < 4; ++comp) {
+      for (x = 0; x < width; ++x) {
+       fprintf(f, "%02x", lineBuf[4*x + comp]);
+       if (++i == 32) {
+         fputc('\n', f);
+         i = 0;
+       }
+      }
+    }
+  }
+
+  if (i != 0) {
+    fputc('\n', f);
+  }
+
+  delete imgStr;
+  gfree(lineBuf);
+}
+
+void PSOutputDev::doImageL2(Object *ref, GfxImageColorMap *colorMap,
+                           GBool invert, GBool inlineImg,
+                           Stream *str, int width, int height, int len) {
   GString *s;
   int n, numComps;
-  Guchar *color;
   GBool useRLE, useA85;
   int c;
-  int i, j, k;
+  int i;
 
   // color space
   if (colorMap) {
-    colorSpace = colorMap->getColorSpace();
-    if (colorSpace->isIndexed())
-      writePS("[/Indexed ");
-    switch (colorSpace->getMode()) {
-    case colorGray:
-      writePS("/DeviceGray ");
-      break;
-    case colorCMYK:
-      writePS("/DeviceCMYK ");
-      break;
-    case colorRGB:
-      writePS("/DeviceRGB ");
-      break;
-    }
-    if (colorSpace->isIndexed()) {
-      n = colorSpace->getIndexHigh();
-      numComps = colorSpace->getNumColorComps();
-      writePS("%d <\n", n);
-      for (i = 0; i <= n; i += 8) {
-       writePS("  ");
-       for (j = i; j < i+8 && j <= n; ++j) {
-         color = colorSpace->getLookupVal(j);
-         for (k = 0; k < numComps; ++k)
-           writePS("%02x", color[k]);
-       }
-       writePS("\n");
-      }
-      writePS("> ] setcolorspace\n");
-    } else {
-      writePS("setcolorspace\n");
-    }
+    dumpColorSpaceL2(colorMap->getColorSpace());
+    writePS(" setcolorspace\n");
+  }
+
+  // set up to use the array created by setupImages()
+  if (doForm && !inlineImg) {
+    writePS("ImData_%d_%d 0\n", ref->getRefNum(), ref->getRefGen());
   }
 
   // image dictionary
@@ -1069,11 +1518,20 @@ void PSOutputDev::doImage(GfxImageColorMap *colorMap,
   // decode 
   if (colorMap) {
     writePS("  /Decode [");
-    numComps = colorMap->getNumPixelComps();
-    for (i = 0; i < numComps; ++i) {
-      if (i > 0)
-       writePS(" ");
-      writePS("%g %g", colorMap->getDecodeLow(i), colorMap->getDecodeHigh(i));
+    if (colorMap->getColorSpace()->getMode() == csSeparation) {
+      //~ this is a kludge -- see comment in dumpColorSpaceL2
+      n = (1 << colorMap->getBits()) - 1;
+      writePS("%g %g", colorMap->getDecodeLow(0) * n,
+             colorMap->getDecodeHigh(0) * n);
+    } else {
+      numComps = colorMap->getNumPixelComps();
+      for (i = 0; i < numComps; ++i) {
+       if (i > 0) {
+         writePS(" ");
+       }
+       writePS("%g %g", colorMap->getDecodeLow(i),
+               colorMap->getDecodeHigh(i));
+      }
     }
     writePS("]\n");
   } else {
@@ -1082,20 +1540,33 @@ void PSOutputDev::doImage(GfxImageColorMap *colorMap,
 
   if (doForm) {
 
-    // data source
-    writePS("  /DataSource <~\n");
+    if (inlineImg) {
 
-    // write image data stream, using ASCII85 encode filter
-    str = new ASCII85Encoder(str);
-    str->reset();
-    while ((c = str->getChar()) != EOF)
-      fputc(c, f);
-    fputc('\n', f);
-    delete str;
+      // data source
+      writePS("  /DataSource <~\n");
+
+      // write image data stream, using ASCII85 encode filter
+      str = new FixedLengthEncoder(str, len);
+      str = new ASCII85Encoder(str);
+      str->reset();
+      while ((c = str->getChar()) != EOF) {
+       fputc(c, f);
+      }
+      fputc('\n', f);
+      delete str;
+
+    } else {
+      writePS("  /DataSource { 2 copy get exch 1 add exch }\n");
+    }
 
     // end of image dictionary
     writePS(">>\n%s\n", colorMap ? "image" : "imagemask");
 
+    // get rid of the array and index
+    if (!inlineImg) {
+      writePS("pop pop\n");
+    }
+
   } else {
 
     // data source
@@ -1117,11 +1588,6 @@ void PSOutputDev::doImage(GfxImageColorMap *colorMap,
     if (s)
       delete s;
 
-    // end of image dictionary
-    writePS(">>\n%s\n", colorMap ? "pdfIm" : "pdfImM");
-
-    // write image data stream
-
     // cut off inline image streams at appropriate length
     if (inlineImg)
       str = new FixedLengthEncoder(str, len);
@@ -1134,6 +1600,31 @@ void PSOutputDev::doImage(GfxImageColorMap *colorMap,
     if (useA85)
       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 A85 and RLE filters)
+       str->reset();
+       n = 0;
+       while ((c = str->getChar()) != EOF) {
+         ++n;
+       }
+      }
+      // +6/7 for "pdfIm\n" / "pdfImM\n"
+      // +8 for newline + trailer
+      n += colorMap ? 14 : 15;
+      writePS("%%%%BeginData: %d Hex Bytes\n", n);
+    }
+#endif
+    writePS("%s\n", colorMap ? "pdfIm" : "pdfImM");
+
     // copy the stream data
     str->reset();
     while ((c = str->getChar()) != EOF)
@@ -1142,6 +1633,11 @@ void PSOutputDev::doImage(GfxImageColorMap *colorMap,
     // add newline and trailer to the end
     fputc('\n', f);
     fputs("%-EOD-\n", f);
+#if OPI_SUPPORT
+    if (opi13Nest) {
+      writePS("%%%%EndData\n");
+    }
+#endif
 
     // delete encoders
     if (useRLE || useA85)
@@ -1149,7 +1645,571 @@ void PSOutputDev::doImage(GfxImageColorMap *colorMap,
   }
 }
 
-void PSOutputDev::writePS(char *fmt, ...) {
+void PSOutputDev::dumpColorSpaceL2(GfxColorSpace *colorSpace) {
+  GfxCalGrayColorSpace *calGrayCS;
+  GfxCalRGBColorSpace *calRGBCS;
+  GfxLabColorSpace *labCS;
+  GfxIndexedColorSpace *indexedCS;
+  GfxSeparationColorSpace *separationCS;
+  Guchar *lookup;
+  double x[1], y[gfxColorMaxComps];
+  int n, numComps;
+  int i, j, k;
+
+  switch (colorSpace->getMode()) {
+
+  case csDeviceGray:
+    writePS("/DeviceGray");
+    break;
+
+  case csCalGray:
+    calGrayCS = (GfxCalGrayColorSpace *)colorSpace;
+    writePS("[/CIEBasedA <<\n");
+    writePS(" /DecodeA {%g exp} bind\n", calGrayCS->getGamma());
+    writePS(" /MatrixA [%g %g %g]\n",
+           calGrayCS->getWhiteX(), calGrayCS->getWhiteY(),
+           calGrayCS->getWhiteZ());
+    writePS(" /WhitePoint [%g %g %g]\n",
+           calGrayCS->getWhiteX(), calGrayCS->getWhiteY(),
+           calGrayCS->getWhiteZ());
+    writePS(" /BlackPoint [%g %g %g]\n",
+           calGrayCS->getBlackX(), calGrayCS->getBlackY(),
+           calGrayCS->getBlackZ());
+    writePS(">>]");
+    break;
+
+  case csDeviceRGB:
+    writePS("/DeviceRGB");
+    break;
+
+  case csCalRGB:
+    calRGBCS = (GfxCalRGBColorSpace *)colorSpace;
+    writePS("[/CIEBasedABC <<\n");
+    writePS(" /DecodeABC [{%g exp} bind {%g exp} bind {%g exp} bind]\n",
+           calRGBCS->getGammaR(), calRGBCS->getGammaG(),
+           calRGBCS->getGammaB());
+    writePS(" /MatrixABC [%g %g %g %g %g %g %g %g %g]\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]);
+    writePS(" /WhitePoint [%g %g %g]\n",
+           calRGBCS->getWhiteX(), calRGBCS->getWhiteY(),
+           calRGBCS->getWhiteZ());
+    writePS(" /BlackPoint [%g %g %g]\n",
+           calRGBCS->getBlackX(), calRGBCS->getBlackY(),
+           calRGBCS->getBlackZ());
+    writePS(">>]");
+    break;
+
+  case csDeviceCMYK:
+    writePS("/DeviceCMYK");
+    break;
+
+  case csLab:
+    labCS = (GfxLabColorSpace *)colorSpace;
+    writePS("[/CIEBasedABC <<\n");
+    writePS(" /RangeABC [0 100 %g %g %g %g]\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");
+    writePS("     {4 29 div sub 108 841 div mul } ifelse %g mul} bind\n",
+           labCS->getWhiteX());
+    writePS("    {dup 6 29 div ge {dup dup mul mul}\n");
+    writePS("     {4 29 div sub 108 841 div mul } ifelse %g mul} bind\n",
+           labCS->getWhiteY());
+    writePS("    {dup 6 29 div ge {dup dup mul mul}\n");
+    writePS("     {4 29 div sub 108 841 div mul } ifelse %g mul} bind]\n",
+           labCS->getWhiteZ());
+    writePS(" /WhitePoint [%g %g %g]\n",
+           labCS->getWhiteX(), labCS->getWhiteY(), labCS->getWhiteZ());
+    writePS(" /BlackPoint [%g %g %g]\n",
+           labCS->getBlackX(), labCS->getBlackY(), labCS->getBlackZ());
+    writePS(">>]");
+    break;
+
+  case csICCBased:
+    dumpColorSpaceL2(((GfxICCBasedColorSpace *)colorSpace)->getAlt());
+    break;
+
+  case csIndexed:
+    indexedCS = (GfxIndexedColorSpace *)colorSpace;
+    writePS("[/Indexed ");
+    dumpColorSpaceL2(indexedCS->getBase());
+    n = indexedCS->getIndexHigh();
+    numComps = indexedCS->getBase()->getNComps();
+    lookup = indexedCS->getLookup();
+    writePS(" %d <\n", n);
+    for (i = 0; i <= n; i += 8) {
+      writePS("  ");
+      for (j = i; j < i+8 && j <= n; ++j) {
+       for (k = 0; k < numComps; ++k) {
+         writePS("%02x", lookup[j * numComps + k]);
+       }
+      }
+      writePS("\n");
+    }
+    writePS(">]");
+    break;
+
+  case csSeparation:
+    //~ this is a kludge -- the correct thing would to ouput a
+    //~ separation color space, with the specified alternate color
+    //~ space and tint transform
+    separationCS = (GfxSeparationColorSpace *)colorSpace;
+    writePS(" [/Indexed ");
+    dumpColorSpaceL2(separationCS->getAlt());
+    writePS(" 255 <\n");
+    numComps = separationCS->getAlt()->getNComps();
+    for (i = 0; i <= 255; i += 8) {
+      writePS("  ");
+      for (j = i; j < i+8 && j <= 255; ++j) {
+       x[0] = (double)j / 255.0;
+       separationCS->getFunc()->transform(x, y);
+       for (k = 0; k < numComps; ++k) {
+         writePS("%02x", (int)(255 * y[k] + 0.5));
+       }
+      }
+      writePS("\n");
+    }
+    writePS(">]");
+    break;
+
+  case csDeviceN:
+    // DeviceN color spaces are a Level 3 PostScript feature.
+    dumpColorSpaceL2(((GfxDeviceNColorSpace *)colorSpace)->getAlt());
+    break;
+
+  case csPattern:
+    //~ unimplemented
+    break;
+
+  }
+}
+
+#if OPI_SUPPORT
+void PSOutputDev::opiBegin(GfxState *state, Dict *opiDict) {
+  Object dict;
+
+  if (psOutOPI) {
+    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)) {
+    writePS("%%%%ImageFileName: %s\n",
+           obj2.getString()->getCString());
+    obj2.free();
+  }
+  obj1.free();
+
+  dict->lookup("MainImage", &obj1);
+  if (obj1.isString()) {
+    writePS("%%%%MainImage: %s\n", obj1.getString()->getCString());
+  }
+  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();
+    writePS("%%%%ImageDimensions: %g %g\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();
+    writePS("%%%%ImageCropRect: %g %g %g %g\n", left, top, right, bottom);
+  }
+  obj1.free();
+
+  dict->lookup("Overprint", &obj1);
+  if (obj1.isBool()) {
+    writePS("%%%%ImageOverprint: %s\n", obj1.getBool() ? "true" : "false");
+  }
+  obj1.free();
+
+  dict->lookup("Inks", &obj1);
+  if (obj1.isName()) {
+    writePS("%%%%ImageInks: %s\n", obj1.getName());
+  } else if (obj1.isArray() && obj1.arrayGetLength() >= 1) {
+    obj1.arrayGet(0, &obj2);
+    if (obj2.isName()) {
+      writePS("%%%%ImageInks: %s %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());
+         writePS(" %g", 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();
+    writePS("%%%%IncludedImageDimensions: %d %d\n", w, h);
+  }
+  obj1.free();
+
+  dict->lookup("IncludedImageQuality", &obj1);
+  if (obj1.isNum()) {
+    writePS("%%%%IncludedImageQuality: %g\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)) {
+    writePS("%%ALDImageFileName: %s\n",
+           obj2.getString()->getCString());
+    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();
+    writePS("%%ALDImageCropRect: %d %d %d %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()) {
+      writePS("%%ALDImageColor: %g %g %g %g ", c, m, y, k);
+      writePSString(obj2.getString());
+      writePS("\n");
+    }
+    obj2.free();
+  }
+  obj1.free();
+
+  dict->lookup("ColorType", &obj1);
+  if (obj1.isName()) {
+    writePS("%%ALDImageColorType: %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();
+    writePS("%%ALDImageCropFixed: %g %g %g %g\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);
+       writePS(" %d", obj2.getInt());
+       obj2.free();
+      }
+    }
+    writePS("\n");
+  }
+  obj1.free();
+
+  dict->lookup("ID", &obj1);
+  if (obj1.isString()) {
+    writePS("%%ALDImageID: %s\n", obj1.getString()->getCString());
+  }
+  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();
+    writePS("%%ALDImageType: %d %d\n", samples, bits);
+  }
+  obj1.free();
+
+  dict->lookup("Overprint", &obj1);
+  if (obj1.isBool()) {
+    writePS("%%ALDImageOverprint: %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);
+    writePS("%%ALDImagePosition: %g %g %g %g %g %g %g %g\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();
+    writePS("%%ALDImageResoution: %g %g\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();
+    writePS("%%ALDImageDimensions: %d %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()) {
+    writePS("%%ALDImageTint: %g\n", obj1.getNum());
+  }
+  obj1.free();
+
+  dict->lookup("Transparency", &obj1);
+  if (obj1.isBool()) {
+    writePS("%%ALDImageTransparency: %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 (landscape) {
+    t = *x1;
+    *x1 = -*y1;
+    *y1 = t;
+  }
+  *x1 *= xScale;
+  *y1 *= yScale;
+}
+
+void PSOutputDev::opiEnd(GfxState *state, Dict *opiDict) {
+  Object dict;
+
+  if (psOutOPI) {
+    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::writePS(const char *fmt, ...) {
   va_list args;
 
   va_start(args, fmt);