]> www.fi.muni.cz Git - evince.git/commitdiff
kill traces of ltk, incorporate new sources
authorMartin Kretzschmar <mkretzschmar@src.gnome.org>
Mon, 31 Mar 2003 21:08:43 +0000 (21:08 +0000)
committerMartin Kretzschmar <mkretzschmar@src.gnome.org>
Mon, 31 Mar 2003 21:08:43 +0000 (21:08 +0000)
* xpdf/Makefile.am: kill traces of ltk, incorporate new sources

* Makefile.am, configure.in: don't build the ltk subdir

* ANNOUNCE, CHANGES, ChangeLog, README, aconf-dj.h, aconf-win32.h,
dj_make.bat, ms_make.bat, vms_make.com: update

* xpdf/LTKOutputDev.cc, xpdf/LTKOutputDev.h, xpdf/postscript.xbm,
xpdf/xpdf-flip.ltk, xpdf/xpdf-ltk.h, xpdf/xpdf-top.ltk,
xpdf/xpdf.ltk: remove.

* xpdf/Annot.cc, xpdf/Annot.h, xpdf/Array.cc, xpdf/Array.h,
xpdf/BuiltinFont.cc, xpdf/BuiltinFont.h,
xpdf/BuiltinFontTables.cc, xpdf/CMap.cc, xpdf/CMap.h,
xpdf/Catalog.cc, xpdf/Catalog.h, xpdf/CharCodeToUnicode.cc,
xpdf/CharCodeToUnicode.h, xpdf/Decrypt.cc, xpdf/Decrypt.h,
xpdf/Dict.cc, xpdf/Dict.h, xpdf/Error.cc, xpdf/Error.h,
xpdf/FTFont.cc, xpdf/FTFont.h, xpdf/FontFile.cc, xpdf/FontFile.h,
xpdf/Function.cc, xpdf/Function.h, xpdf/Gfx.cc, xpdf/Gfx.h,
xpdf/GfxFont.cc, xpdf/GfxFont.h, xpdf/GfxState.cc,
xpdf/GfxState.h, xpdf/GlobalParams.cc, xpdf/GlobalParams.h,
xpdf/ImageOutputDev.cc, xpdf/ImageOutputDev.h, xpdf/Lexer.cc,
xpdf/Lexer.h, xpdf/Link.cc, xpdf/Link.h, xpdf/NameToCharCode.cc,
xpdf/NameToCharCode.h, xpdf/NameToUnicodeTable.h, xpdf/Object.cc,
xpdf/Object.h, xpdf/OutputDev.cc, xpdf/OutputDev.h,
xpdf/PBMOutputDev.cc, xpdf/PBMOutputDev.h, xpdf/PDFDoc.cc,
xpdf/PDFDoc.h, xpdf/PSOutputDev.cc, xpdf/PSOutputDev.h,
xpdf/PSTokenizer.cc, xpdf/PSTokenizer.h, xpdf/Page.cc,
xpdf/Page.h, xpdf/Parser.cc, xpdf/Parser.h, xpdf/SFont.cc,
xpdf/SFont.h, xpdf/Stream.cc, xpdf/Stream.h, xpdf/T1Font.cc,
xpdf/T1Font.h, xpdf/TTFont.cc, xpdf/TTFont.h,
xpdf/TextOutputDev.cc, xpdf/TextOutputDev.h, xpdf/UnicodeMap.cc,
xpdf/UnicodeMap.h, xpdf/XOutputDev.cc, xpdf/XOutputDev.h,
xpdf/XRef.cc, xpdf/XRef.h, xpdf/config.h, xpdf/pdffonts.cc,
xpdf/pdfimages.cc, xpdf/pdfinfo.cc, xpdf/pdftopbm.cc,
xpdf/pdftops.cc, xpdf/pdftotext.cc, xpdf/vms_make.com,
xpdf/xpdf.cc: update.

* goo/GHash.cc, goo/GHash.h, goo/GList.cc, goo/GList.h,
goo/GString.cc, goo/GString.h: mostly Mac OS X gcc fixage.

* doc/pdffonts.1, doc/pdffonts.cat, doc/pdffonts.hlp,
doc/pdfimages.1, doc/pdfimages.cat, doc/pdfimages.hlp,
doc/pdfinfo.1, doc/pdfinfo.cat, doc/pdfinfo.hlp, doc/pdftopbm.1,
doc/pdftopbm.cat, doc/pdftopbm.hlp, doc/pdftops.1,
doc/pdftops.cat, doc/pdftops.hlp, doc/pdftotext.1,
doc/pdftotext.cat, doc/pdftotext.hlp, doc/xpdf.1, doc/xpdf.cat,
doc/xpdf.hlp, doc/xpdfrc.5, doc/xpdfrc.cat, doc/xpdfrc.hlp:
update docs.

62 files changed:
pdf/goo/GString.cc
pdf/goo/GString.h
pdf/xpdf/Array.cc
pdf/xpdf/Array.h
pdf/xpdf/Catalog.cc
pdf/xpdf/Catalog.h
pdf/xpdf/Dict.cc
pdf/xpdf/Dict.h
pdf/xpdf/Error.cc
pdf/xpdf/Error.h
pdf/xpdf/FontFile.cc
pdf/xpdf/Function.cc
pdf/xpdf/Gfx.cc
pdf/xpdf/Gfx.h
pdf/xpdf/GfxFont.cc
pdf/xpdf/GfxFont.h
pdf/xpdf/GfxState.cc
pdf/xpdf/GfxState.h
pdf/xpdf/ImageOutputDev.cc
pdf/xpdf/ImageOutputDev.h
pdf/xpdf/LTKOutputDev.cc [deleted file]
pdf/xpdf/LTKOutputDev.h [deleted file]
pdf/xpdf/Lexer.cc
pdf/xpdf/Lexer.h
pdf/xpdf/Link.cc
pdf/xpdf/Link.h
pdf/xpdf/Makefile.am
pdf/xpdf/Object.cc
pdf/xpdf/Object.h
pdf/xpdf/OutputDev.cc
pdf/xpdf/OutputDev.h
pdf/xpdf/PBMOutputDev.cc
pdf/xpdf/PBMOutputDev.h
pdf/xpdf/PDFDoc.cc
pdf/xpdf/PDFDoc.h
pdf/xpdf/PSOutputDev.cc
pdf/xpdf/PSOutputDev.h
pdf/xpdf/Page.cc
pdf/xpdf/Page.h
pdf/xpdf/Parser.cc
pdf/xpdf/Parser.h
pdf/xpdf/Stream.cc
pdf/xpdf/Stream.h
pdf/xpdf/TextOutputDev.cc
pdf/xpdf/TextOutputDev.h
pdf/xpdf/XOutputDev.cc
pdf/xpdf/XOutputDev.h
pdf/xpdf/XRef.cc
pdf/xpdf/XRef.h
pdf/xpdf/pdfimages.cc
pdf/xpdf/pdfinfo.cc
pdf/xpdf/pdftopbm.cc
pdf/xpdf/pdftops.cc
pdf/xpdf/pdftotext.cc
pdf/xpdf/postscript.xbm [deleted file]
pdf/xpdf/vms_make.com
pdf/xpdf/xpdf-flip.ltk [deleted file]
pdf/xpdf/xpdf-ltk.h [deleted file]
pdf/xpdf/xpdf-top.ltk [deleted file]
pdf/xpdf/xpdf.cc
pdf/xpdf/xpdf.ltk [deleted file]
pdf/xpdf/xpdfconfig.h

index 3bf626aa41e99321d12da3c07b13e11b35daad7e..25e0e05737934cb1291a00a1d597487d97aa5fd1 100644 (file)
@@ -8,11 +8,12 @@
 //
 //========================================================================
 
-#ifdef __GNUC__
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
 #pragma implementation
 #endif
 
-#include <aconf.h>
 #include <stdlib.h>
 #include <stddef.h>
 #include <string.h>
@@ -203,8 +204,12 @@ GString *GString::del(int i, int n) {
   int j;
 
   if (n > 0) {
-    for (j = i; j <= length - n; ++j)
+    if (i + n > length) {
+      n = length - i;
+    }
+    for (j = i; j <= length - n; ++j) {
       s[j] = s[j + n];
+    }
     resize(length -= n);
   }
   return this;
index 93796cb7901a9a15720943105d044874f546390c..d22cc196848a88955c91ab92585b33089f6baf43 100644 (file)
@@ -11,7 +11,9 @@
 #ifndef GSTRING_H
 #define GSTRING_H
 
-#ifdef __GNUC__
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
 #pragma interface
 #endif
 
index fbdde49da9089e6d8181d87cf46f1d0138fa7167..9c6cb3415a5911531de6470379d941a0c07190c0 100644 (file)
@@ -6,11 +6,12 @@
 //
 //========================================================================
 
-#ifdef __GNUC__
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
 #pragma implementation
 #endif
 
-#include <aconf.h>
 #include <stddef.h>
 #include "gmem.h"
 #include "Object.h"
index a118f6852b01c9cb048fdbcc1dd06e32e2b94ec5..09dbe2bcfbd36f1c02a2dcf3627d5bf8ed876ea5 100644 (file)
@@ -9,7 +9,9 @@
 #ifndef ARRAY_H
 #define ARRAY_H
 
-#ifdef __GNUC__
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
 #pragma interface
 #endif
 
index 1212e2eb3469ab4afe628fddfc21c0fb913b66ad..efea82862e43ebbd92f91229cc16986d9fc1d7ef 100644 (file)
@@ -6,11 +6,12 @@
 //
 //========================================================================
 
-#ifdef __GNUC__
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
 #pragma implementation
 #endif
 
-#include <aconf.h>
 #include <stddef.h>
 #include "gmem.h"
 #include "Object.h"
@@ -26,7 +27,7 @@
 // Catalog
 //------------------------------------------------------------------------
 
-Catalog::Catalog(XRef *xrefA, GBool printCommands) {
+Catalog::Catalog(XRef *xrefA) {
   Object catDict, pagesDict;
   Object obj, obj2;
   int numPages0;
@@ -69,7 +70,7 @@ Catalog::Catalog(XRef *xrefA, GBool printCommands) {
     pageRefs[i].num = -1;
     pageRefs[i].gen = -1;
   }
-  numPages = readPageTree(pagesDict.getDict(), NULL, 0, printCommands);
+  numPages = readPageTree(pagesDict.getDict(), NULL, 0);
   if (numPages != numPages0) {
     error(-1, "Page count in top-level pages object is incorrect");
   }
@@ -100,6 +101,9 @@ Catalog::Catalog(XRef *xrefA, GBool printCommands) {
   // get the structure tree root
   catDict.dictLookup("StructTreeRoot", &structTreeRoot);
 
+  // get the outline dictionary
+  catDict.dictLookup("Outlines", &outline);
+
   catDict.free();
   return;
 
@@ -133,6 +137,7 @@ Catalog::~Catalog() {
   }
   metadata.free();
   structTreeRoot.free();
+  outline.free();
 }
 
 GString *Catalog::readMetadata() {
@@ -159,8 +164,7 @@ GString *Catalog::readMetadata() {
   return s;
 }
 
-int Catalog::readPageTree(Dict *pagesDict, PageAttrs *attrs, int start,
-                         GBool printCommands) {
+int Catalog::readPageTree(Dict *pagesDict, PageAttrs *attrs, int start) {
   Object kids;
   Object kid;
   Object kidRef;
@@ -179,7 +183,7 @@ int Catalog::readPageTree(Dict *pagesDict, PageAttrs *attrs, int start,
     kids.arrayGet(i, &kid);
     if (kid.isDict("Page")) {
       attrs2 = new PageAttrs(attrs1, kid.getDict());
-      page = new Page(xref, start+1, kid.getDict(), attrs2, printCommands);
+      page = new Page(xref, start+1, kid.getDict(), attrs2);
       if (!page->isOk()) {
        ++start;
        goto err3;
@@ -205,7 +209,7 @@ int Catalog::readPageTree(Dict *pagesDict, PageAttrs *attrs, int start,
     // This should really be isDict("Pages"), but I've seen at least one
     // PDF file where the /Type entry is missing.
     } else if (kid.isDict()) {
-      if ((start = readPageTree(kid.getDict(), attrs1, start, printCommands))
+      if ((start = readPageTree(kid.getDict(), attrs1, start))
          < 0)
        goto err2;
     } else {
@@ -276,6 +280,10 @@ LinkDest *Catalog::findDest(GString *name) {
     error(-1, "Bad named destination value");
   }
   obj1.free();
+  if (dest && !dest->isOk()) {
+    delete dest;
+    dest = NULL;
+  }
 
   return dest;
 }
index afad803fc88b689060dd4406f5a137d5897c85ee..7f89a611a30b4750674032320d323eec6bf3f52f 100644 (file)
@@ -9,7 +9,9 @@
 #ifndef CATALOG_H
 #define CATALOG_H
 
-#ifdef __GNUC__
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
 #pragma interface
 #endif
 
@@ -28,7 +30,7 @@ class Catalog {
 public:
 
   // Constructor.
-  Catalog(XRef *xrefA, GBool printCommands = gFalse);
+  Catalog(XRef *xrefA);
 
   // Destructor.
   ~Catalog();
@@ -63,6 +65,8 @@ public:
   // NULL if <name> is not a destination.
   LinkDest *findDest(GString *name);
 
+  Object *getOutline() { return &outline; }
+
 private:
 
   XRef *xref;                  // the xref table for this PDF file
@@ -75,10 +79,10 @@ private:
   GString *baseURI;            // base URI for URI-type links
   Object metadata;             // metadata stream
   Object structTreeRoot;       // structure tree root dictionary
+  Object outline;              // outline dictionary
   GBool ok;                    // true if catalog is valid
 
-  int readPageTree(Dict *pages, PageAttrs *attrs, int start,
-                  GBool printCommands);
+  int readPageTree(Dict *pages, PageAttrs *attrs, int start);
   Object *findDestInTree(Object *tree, GString *name, Object *obj);
 };
 
index 5eb077e0937cab2186de3417d37230bd5c010d12..ad4ec3ef36df5421fdffaad3608684cccb0dc2dd 100644 (file)
@@ -6,11 +6,12 @@
 //
 //========================================================================
 
-#ifdef __GNUC__
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
 #pragma implementation
 #endif
 
-#include <aconf.h>
 #include <stddef.h>
 #include <string.h>
 #include "gmem.h"
index b9945144f18730315fdbc1e3a2c56d35ab595535..e87ed9034cec7183c58eee6b1888e8c66a9d8f11 100644 (file)
@@ -9,7 +9,9 @@
 #ifndef DICT_H
 #define DICT_H
 
-#ifdef __GNUC__
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
 #pragma interface
 #endif
 
index 3eae5c9c117d72df5d55e5b10c072b15e6cd8e25..4b2d120a616dec6544e933a0209ebd230bf41027 100644 (file)
@@ -6,11 +6,12 @@
 //
 //========================================================================
 
-#ifdef __GNUC__
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
 #pragma implementation
 #endif
 
-#include <aconf.h>
 #include <stdio.h>
 #include <stddef.h>
 #include <stdarg.h>
index 77801c5912a26b497240b188d58b2f19e22cf6f3..c924065d323b1664fbf6de4c1023f1d9ec19c6cd 100644 (file)
@@ -9,7 +9,9 @@
 #ifndef ERROR_H
 #define ERROR_H
 
-#ifdef __GNUC__
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
 #pragma interface
 #endif
 
index 74769096011090880ca9b331c2be6a018ec6e2b7..6860e4acec3791cfd92e0d01f89f540abfc6865d 100644 (file)
@@ -12,6 +12,7 @@
 #pragma implementation
 #endif
 
+#include <locale.h>
 #include <math.h>
 #include <stdlib.h>
 #include <stddef.h>
@@ -2214,7 +2215,11 @@ double Type1CFontFile::getNum(Guchar **ptr, GBool *isFP) {
       }
     } while (i < 64);
     buf[i] = '\0';
-    x = atof(buf);
+    {
+      char *theLocale = setlocale(LC_NUMERIC, "C");
+      x = atof(buf);
+      setlocale(LC_NUMERIC, theLocale);
+    }
     *isFP = gTrue;
   } else if (b0 == 31) {
     x = 0;
index 82bbdce2c0cbca26d188fdc4581697ad473cff27..cafb63f32314dfd87285ee0e8d75450ba821116b 100644 (file)
@@ -12,6 +12,7 @@
 #pragma implementation
 #endif
 
+#include <locale.h>
 #include <stdlib.h>
 #include <string.h>
 #include <ctype.h>
@@ -1072,7 +1073,11 @@ GBool PostScriptFunction::parseCode(Stream *str, int *codePtr) {
       resizeCode(*codePtr);
       if (isReal) {
        code[*codePtr].type = psReal;
-       code[*codePtr].real = atof(tok->getCString());
+        {
+          char *theLocale = setlocale(LC_NUMERIC, "C");
+          code[*codePtr].real = atof(tok->getCString());
+          setlocale(LC_NUMERIC, theLocale);
+        }
       } else {
        code[*codePtr].type = psInt;
        code[*codePtr].intg = atoi(tok->getCString());
index 0b00f9116d26d764a783057ade84fbb401c24e0c..2717a04f249688fced62bc7415b39ef01c872cfc 100644 (file)
@@ -6,16 +6,18 @@
 //
 //========================================================================
 
-#ifdef __GNUC__
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
 #pragma implementation
 #endif
 
-#include <aconf.h>
 #include <stdio.h>
 #include <stddef.h>
 #include <string.h>
 #include <math.h>
 #include "gmem.h"
+#include "GlobalParams.h"
 #include "CharTypes.h"
 #include "Object.h"
 #include "Array.h"
@@ -381,12 +383,13 @@ GBool GfxResources::lookupGState(char *name, Object *obj) {
 
 Gfx::Gfx(XRef *xrefA, OutputDev *outA, int pageNum, Dict *resDict, double dpi,
         PDFRectangle *box, GBool crop, PDFRectangle *cropBox, int rotate,
-        GBool printCommandsA) {
+        GBool (*abortCheckCbkA)(void *data),
+        void *abortCheckCbkDataA) {
   int i;
 
   xref = xrefA;
   subPage = gFalse;
-  printCommands = printCommandsA;
+  printCommands = globalParams->getPrintCommands();
 
   // start the resource stack
   res = new GfxResources(xref, resDict, NULL);
@@ -403,6 +406,8 @@ Gfx::Gfx(XRef *xrefA, OutputDev *outA, int pageNum, Dict *resDict, double dpi,
   for (i = 0; i < 6; ++i) {
     baseMatrix[i] = state->getCTM()[i];
   }
+  abortCheckCbk = abortCheckCbkA;
+  abortCheckCbkData = abortCheckCbkDataA;
 
   // set crop box
   if (crop) {
@@ -418,12 +423,14 @@ Gfx::Gfx(XRef *xrefA, OutputDev *outA, int pageNum, Dict *resDict, double dpi,
 }
 
 Gfx::Gfx(XRef *xrefA, OutputDev *outA, Dict *resDict,
-        PDFRectangle *box, GBool crop, PDFRectangle *cropBox) {
+        PDFRectangle *box, GBool crop, PDFRectangle *cropBox,
+        GBool (*abortCheckCbkA)(void *data),
+        void *abortCheckCbkDataA) {
   int i;
 
   xref = xrefA;
   subPage = gTrue;
-  printCommands = gFalse;
+  printCommands = globalParams->getPrintCommands();
 
   // start the resource stack
   res = new GfxResources(xref, resDict, NULL);
@@ -437,6 +444,8 @@ Gfx::Gfx(XRef *xrefA, OutputDev *outA, Dict *resDict,
   for (i = 0; i < 6; ++i) {
     baseMatrix[i] = state->getCTM()[i];
   }
+  abortCheckCbk = abortCheckCbkA;
+  abortCheckCbkData = abortCheckCbkDataA;
 
   // set crop box
   if (crop) {
@@ -494,11 +503,11 @@ void Gfx::display(Object *obj, GBool topLevel) {
 void Gfx::go(GBool topLevel) {
   Object obj;
   Object args[maxArgs];
-  int numArgs;
-  int i;
+  int numArgs, i;
+  int lastAbortCheck;
 
   // scan a sequence of objects
-  updateLevel = 0;
+  updateLevel = lastAbortCheck = 0;
   numArgs = 0;
   parser->getObj(&obj);
   while (!obj.isEOF()) {
@@ -526,6 +535,16 @@ void Gfx::go(GBool topLevel) {
        updateLevel = 0;
       }
 
+      // check for an abort
+      if (abortCheckCbk) {
+       if (updateLevel - lastAbortCheck > 10) {
+         if ((*abortCheckCbk)(abortCheckCbkData)) {
+           break;
+         }
+         lastAbortCheck = updateLevel;
+       }
+      }
+
     // got an argument - save it
     } else if (numArgs < maxArgs) {
       args[numArgs++] = obj;
@@ -575,7 +594,7 @@ void Gfx::execOp(Object *cmd, Object args[], int numArgs) {
   int i;
 
   // find operator
-  name = cmd->getName();
+  name = cmd->getCmd();
   if (!(op = findOp(name))) {
     if (ignoreUndef == 0)
       error(getPos(), "Unknown operator '%s'", name);
index bdf56e8610503eff0c999e1a348298a29940873e..c3c57cf1806d5321574e5d8af3abd0a8413f6442 100644 (file)
@@ -9,7 +9,9 @@
 #ifndef GFX_H
 #define GFX_H
 
-#ifdef __GNUC__
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
 #pragma interface
 #endif
 
@@ -31,7 +33,7 @@ class GfxAxialShading;
 class GfxRadialShading;
 class GfxState;
 class Gfx;
-struct PDFRectangle;
+class PDFRectangle;
 
 //------------------------------------------------------------------------
 // Gfx
@@ -97,11 +99,14 @@ public:
   // Constructor for regular output.
   Gfx(XRef *xrefA, OutputDev *outA, int pageNum, Dict *resDict, double dpi,
       PDFRectangle *box, GBool crop, PDFRectangle *cropBox, int rotate,
-      GBool printCommandsA);
+      GBool (*abortCheckCbkA)(void *data) = NULL,
+      void *abortCheckCbkDataA = NULL);
 
   // Constructor for a sub-page object.
   Gfx(XRef *xrefA, OutputDev *outA, Dict *resDict,
-      PDFRectangle *box, GBool crop, PDFRectangle *cropBox);
+      PDFRectangle *box, GBool crop, PDFRectangle *cropBox,
+      GBool (*abortCheckCbkA)(void *data) = NULL,
+      void *abortCheckCbkDataA = NULL);
 
   ~Gfx();
 
@@ -134,6 +139,10 @@ private:
 
   Parser *parser;              // parser for page content stream(s)
 
+  GBool                                // callback to check for an abort
+    (*abortCheckCbk)(void *data);
+  void *abortCheckCbkData;
+
   static Operator opTab[];     // table of operators
 
   void go(GBool topLevel);
index 8dcd8e78d01e3c20ac71fd3a40ef9c350f3c449e..5acb8459e79e2c14abcc8e948e40c7434cfe3676 100644 (file)
@@ -6,11 +6,12 @@
 //
 //========================================================================
 
-#ifdef __GNUC__
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
 #pragma implementation
 #endif
 
-#include <aconf.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -564,8 +565,9 @@ Gfx8BitFont::Gfx8BitFont(XRef *xref, char *tagA, Ref idA, GString *nameA,
   if (!baseEnc) {
     if (builtinFont) {
       baseEnc = builtinFont->defaultBaseEnc;
+      hasEncoding = gTrue;
     } else if (type == fontTrueType) {
-      baseEnc = macRomanEncoding;
+      baseEnc = winAnsiEncoding;
     } else {
       baseEnc = standardEncoding;
     }
@@ -1240,21 +1242,31 @@ GString *GfxCIDFont::getCollection() {
 GfxFontDict::GfxFontDict(XRef *xref, Dict *fontDict) {
   int i;
   Object obj1, obj2;
+  Ref r;
 
   numFonts = fontDict->getLength();
   fonts = (GfxFont **)gmalloc(numFonts * sizeof(GfxFont *));
   for (i = 0; i < numFonts; ++i) {
     fontDict->getValNF(i, &obj1);
     obj1.fetch(xref, &obj2);
-    if (obj1.isRef() && obj2.isDict()) {
+    if (obj2.isDict()) {
+      if (obj1.isRef()) {
+       r = obj1.getRef();
+      } else {
+       // no indirect reference for this font, so invent a unique one
+       // (legal generation numbers are five digits, so any 6-digit
+       // number would be safe)
+       r.num = i;
+       r.gen = 999999;
+      }
       fonts[i] = GfxFont::makeFont(xref, fontDict->getKey(i),
-                                  obj1.getRef(), obj2.getDict());
+                                  r, obj2.getDict());
       if (fonts[i] && !fonts[i]->isOk()) {
        delete fonts[i];
        fonts[i] = NULL;
       }
     } else {
-      error(-1, "font resource is not a dictionary reference");
+      error(-1, "font resource is not a dictionary");
       fonts[i] = NULL;
     }
     obj1.free();
index c67ac29a40a9aa12965eb5ed9cef0138861751c1..bcabe5f36bee1f6a0075cdd4aec23c9c980f6fd1 100644 (file)
@@ -9,7 +9,9 @@
 #ifndef GFXFONT_H
 #define GFXFONT_H
 
-#ifdef __GNUC__
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
 #pragma interface
 #endif
 
index 94501a8c9b7b4974fc5ed7b0de10ce0347602d9c..d968ac1de86d886aeba6094a6a0fa02921eef965 100644 (file)
@@ -6,11 +6,12 @@
 //
 //========================================================================
 
-#ifdef __GNUC__
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
 #pragma implementation
 #endif
 
-#include <aconf.h>
 #include <stddef.h>
 #include <math.h>
 #include <string.h> // for memcpy()
@@ -852,12 +853,14 @@ GfxColorSpace *GfxIndexedColorSpace::parse(Array *arr) {
 void GfxIndexedColorSpace::getGray(GfxColor *color, double *gray) {
   Guchar *p;
   GfxColor color2;
+  double low[gfxColorMaxComps], range[gfxColorMaxComps];
   int n, i;
 
   n = base->getNComps();
+  base->getDefaultRanges(low, range, indexHigh);
   p = &lookup[(int)(color->c[0] + 0.5) * n];
   for (i = 0; i < n; ++i) {
-    color2.c[i] = p[i] / 255.0;
+    color2.c[i] = low[i] + (p[i] / 255.0) * range[i];
   }
   base->getGray(&color2, gray);
 }
@@ -865,12 +868,14 @@ void GfxIndexedColorSpace::getGray(GfxColor *color, double *gray) {
 void GfxIndexedColorSpace::getRGB(GfxColor *color, GfxRGB *rgb) {
   Guchar *p;
   GfxColor color2;
+  double low[gfxColorMaxComps], range[gfxColorMaxComps];
   int n, i;
 
   n = base->getNComps();
+  base->getDefaultRanges(low, range, indexHigh);
   p = &lookup[(int)(color->c[0] + 0.5) * n];
   for (i = 0; i < n; ++i) {
-    color2.c[i] = p[i] / 255.0;
+    color2.c[i] = low[i] + (p[i] / 255.0) * range[i];
   }
   base->getRGB(&color2, rgb);
 }
@@ -878,12 +883,14 @@ void GfxIndexedColorSpace::getRGB(GfxColor *color, GfxRGB *rgb) {
 void GfxIndexedColorSpace::getCMYK(GfxColor *color, GfxCMYK *cmyk) {
   Guchar *p;
   GfxColor color2;
+  double low[gfxColorMaxComps], range[gfxColorMaxComps];
   int n, i;
 
   n = base->getNComps();
+  base->getDefaultRanges(low, range, indexHigh);
   p = &lookup[(int)(color->c[0] + 0.5) * n];
   for (i = 0; i < n; ++i) {
-    color2.c[i] = p[i] / 255.0;
+    color2.c[i] = low[i] + (p[i] / 255.0) * range[i];
   }
   base->getCMYK(&color2, cmyk);
 }
@@ -1682,10 +1689,11 @@ GfxImageColorMap::GfxImageColorMap(int bitsA, Object *decode,
     nComps2 = colorSpace2->getNComps();
     lookup = (double *)gmalloc((indexHigh + 1) * nComps2 * sizeof(double));
     lookup2 = indexedCS->getLookup();
+    colorSpace2->getDefaultRanges(x, y, indexHigh);
     for (i = 0; i <= indexHigh; ++i) {
-      j = (int)(decodeLow[0] +(i * decodeRange[0]) / maxPixel + 0.5);
+      j = (int)(decodeLow[0] + (i * decodeRange[0]) / maxPixel + 0.5);
       for (k = 0; k < nComps2; ++k) {
-       lookup[i*nComps2 + k] = lookup2[i*nComps2 + k] / 255.0;
+       lookup[j*nComps2 + k] = x[k] + (lookup2[i*nComps2 + k] / 255.0) * y[k];
       }
     }
   } else if (colorSpace->getMode() == csSeparation) {
index b1f6f28ec9d32954e9e2a5f456496ac2f7835d81..e99735c63edf3bc94de5ac70136f8476ee9d7179 100644 (file)
@@ -9,7 +9,9 @@
 #ifndef GFXSTATE_H
 #define GFXSTATE_H
 
-#ifdef __GNUC__
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
 #pragma interface
 #endif
 
@@ -19,7 +21,7 @@
 
 class Array;
 class GfxFont;
-struct PDFRectangle;
+class PDFRectangle;
 
 //------------------------------------------------------------------------
 // GfxColor
@@ -406,7 +408,9 @@ public:
   virtual int getNComps() { return nComps; }
 
   // DeviceN-specific access.
+  GString *getColorantName(int i) { return names[i]; }
   GfxColorSpace *getAlt() { return alt; }
+  Function *getTintTransformFunc() { return func; }
 
 private:
 
index d6c2eb3e7fc8284393ff554c84e39e5edb58c48c..1a6d87e1f687102640e0dcc5fa3bedbe73abd688 100644 (file)
@@ -6,11 +6,12 @@
 //
 //========================================================================
 
-#ifdef __GNUC__
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
 #pragma implementation
 #endif
 
-#include <aconf.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <stddef.h>
@@ -98,7 +99,7 @@ void ImageOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
                               int *maskColors, GBool inlineImg) {
   FILE *f;
   ImageStream *imgStr;
-  Guchar pixBuf[4];
+  Guchar *p;
   GfxRGB rgb;
   int x, y;
   int c;
@@ -148,7 +149,7 @@ void ImageOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
     // copy the stream
     size = height * ((width + 7) / 8);
     for (i = 0; i < size; ++i) {
-      fputc(str->getChar(), f);
+      fputc(str->getChar() ^ 0xff, f);
     }
 
     str->close();
@@ -177,12 +178,13 @@ void ImageOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
     for (y = 0; y < height; ++y) {
 
       // write the line
+      p = imgStr->getLine();
       for (x = 0; x < width; ++x) {
-       imgStr->getPixel(pixBuf);
-       colorMap->getRGB(pixBuf, &rgb);
+       colorMap->getRGB(p, &rgb);
        fputc((int)(rgb.r * 255 + 0.5), f);
        fputc((int)(rgb.g * 255 + 0.5), f);
        fputc((int)(rgb.b * 255 + 0.5), f);
+       p += colorMap->getNumPixelComps();
       }
     }
     delete imgStr;
index 5828841145c378d496a3a1b43f0cd4f361350147..c3eb36e0e09d7902ee046ef67abc62cbf928fdb3 100644 (file)
@@ -9,7 +9,9 @@
 #ifndef IMAGEOUTPUTDEV_H
 #define IMAGEOUTPUTDEV_H
 
-#ifdef __GNUC__
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
 #pragma interface
 #endif
 
diff --git a/pdf/xpdf/LTKOutputDev.cc b/pdf/xpdf/LTKOutputDev.cc
deleted file mode 100644 (file)
index ba5a332..0000000
+++ /dev/null
@@ -1,72 +0,0 @@
-//========================================================================
-//
-// LTKOutputDev.cc
-//
-// Copyright 1998-2002 Glyph & Cog, LLC
-//
-//========================================================================
-
-#ifdef __GNUC__
-#pragma implementation
-#endif
-
-#include <aconf.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <stddef.h>
-#include <string.h>
-#include "gmem.h"
-#include "GString.h"
-#include "LTKWindow.h"
-#include "LTKScrollingCanvas.h"
-#include "Object.h"
-#include "Stream.h"
-#include "GfxState.h"
-#include "GfxFont.h"
-#include "Error.h"
-#include "LTKOutputDev.h"
-
-//------------------------------------------------------------------------
-
-LTKOutputDev::LTKOutputDev(LTKWindow *winA, GBool reverseVideoA,
-                          unsigned long paperColor, GBool installCmap,
-                          GBool rgbCubeSize, GBool incrementalUpdateA):
-  XOutputDev(winA->getDisplay(),
-            ((LTKScrollingCanvas *)winA->findWidget("canvas"))->getPixmap(),
-            0, winA->getColormap(), reverseVideoA, paperColor,
-            installCmap, rgbCubeSize)
-{
-  win = winA;
-  canvas = (LTKScrollingCanvas *)win->findWidget("canvas");
-  setPixmap(canvas->getPixmap(),
-           canvas->getRealWidth(), canvas->getRealHeight());
-  incrementalUpdate = incrementalUpdateA;
-}
-
-LTKOutputDev::~LTKOutputDev() {
-}
-
-void LTKOutputDev::startPage(int pageNum, GfxState *state) {
-  canvas->resize((int)(state->getPageWidth() + 0.5),
-                (int)(state->getPageHeight() + 0.5));
-  setPixmap(canvas->getPixmap(),
-           canvas->getRealWidth(), canvas->getRealHeight());
-  XOutputDev::startPage(pageNum, state);
-  if (incrementalUpdate) {
-    canvas->redraw();
-  }
-}
-
-void LTKOutputDev::endPage() {
-  if (!incrementalUpdate) {
-    canvas->redraw();
-  }
-  XOutputDev::endPage();
-}
-
-void LTKOutputDev::dump() {
-  if (incrementalUpdate) {
-    canvas->redraw();
-  }
-  XOutputDev::dump();
-}
diff --git a/pdf/xpdf/LTKOutputDev.h b/pdf/xpdf/LTKOutputDev.h
deleted file mode 100644 (file)
index 7b996dc..0000000
+++ /dev/null
@@ -1,52 +0,0 @@
-//========================================================================
-//
-// LTKOutputDev.h
-//
-// Copyright 1998-2002 Glyph & Cog, LLC
-//
-//========================================================================
-
-#ifndef LTKOUTPUTDEV_H
-#define LTKOUTPUTDEV_H
-
-#ifdef __GNUC__
-#pragma interface
-#endif
-
-#include <stddef.h>
-#include "config.h"
-#include "XOutputDev.h"
-
-class LTKApp;
-class LTKWindow;
-
-//------------------------------------------------------------------------
-
-class LTKOutputDev: public XOutputDev {
-public:
-
-  LTKOutputDev(LTKWindow *winA, GBool reverseVideoA,
-              unsigned long paperColor, GBool installCmap,
-              GBool rgbCubeSize, GBool incrementalUpdateA);
-
-  ~LTKOutputDev();
-
-  //----- initialization and control
-
-  // Start a page.
-  virtual void startPage(int pageNum, GfxState *state);
-
-  // End a page.
-  virtual void endPage();
-
-  // Dump page contents to display.
-  virtual void dump();
-
-private:
-
-  LTKWindow *win;              // window
-  LTKScrollingCanvas *canvas;  // drawing canvas
-  GBool incrementalUpdate;     // incrementally update the display?
-};
-
-#endif
index d037469113d31506e230ccea6cba54354c636277..982295ee3261bddfa1918e41010df5a45ea5cc84 100644 (file)
@@ -6,11 +6,12 @@
 //
 //========================================================================
 
-#ifdef __GNUC__
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
 #pragma implementation
 #endif
 
-#include <aconf.h>
 #include <stdlib.h>
 #include <stddef.h>
 #include <string.h>
index 8a01ab25e9ae96e80863e6e15d24d15e5e530543..2dfe31462ec3180c492111f9e1dc9c1a33790ac0 100644 (file)
@@ -9,7 +9,9 @@
 #ifndef LEXER_H
 #define LEXER_H
 
-#ifdef __GNUC__
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
 #pragma interface
 #endif
 
index eb9d033635bc67bc60a17f991efead4102357ef4..b16563a925de3957cb3dc6a87995e45fbcf06117 100644 (file)
@@ -6,11 +6,12 @@
 //
 //========================================================================
 
-#ifdef __GNUC__
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
 #pragma implementation
 #endif
 
-#include <aconf.h>
 #include <stddef.h>
 #include <string.h>
 #include "gmem.h"
 #include "Link.h"
 
 //------------------------------------------------------------------------
+// LinkAction
+//------------------------------------------------------------------------
+
+LinkAction *LinkAction::parseDest(Object *obj) {
+  LinkAction *action;
+
+  action = new LinkGoTo(obj);
+  if (!action->isOk()) {
+    delete action;
+    return NULL;
+  }
+  return action;
+}
+
+LinkAction *LinkAction::parseAction(Object *obj, GString *baseURI) {
+  LinkAction *action;
+  Object obj2, obj3, obj4;
+
+  if (!obj->isDict()) {
+    error(-1, "Bad annotation action");
+    return NULL;
+  }
+
+  obj->dictLookup("S", &obj2);
+
+  // GoTo action
+  if (obj2.isName("GoTo")) {
+    obj->dictLookup("D", &obj3);
+    action = new LinkGoTo(&obj3);
+    obj3.free();
+
+  // GoToR action
+  } else if (obj2.isName("GoToR")) {
+    obj->dictLookup("F", &obj3);
+    obj->dictLookup("D", &obj4);
+    action = new LinkGoToR(&obj3, &obj4);
+    obj3.free();
+    obj4.free();
+
+  // Launch action
+  } else if (obj2.isName("Launch")) {
+    action = new LinkLaunch(obj);
+
+  // URI action
+  } else if (obj2.isName("URI")) {
+    obj->dictLookup("URI", &obj3);
+    action = new LinkURI(&obj3, baseURI);
+    obj3.free();
+
+  // Named action
+  } else if (obj2.isName("Named")) {
+    obj->dictLookup("N", &obj3);
+    action = new LinkNamed(&obj3);
+    obj3.free();
+
+  // Movie action
+  } else if (obj2.isName("Movie")) {
+    obj->dictLookupNF("Annot", &obj3);
+    obj->dictLookup("T", &obj4);
+    action = new LinkMovie(&obj3, &obj4);
+    obj3.free();
+    obj4.free();
+
+  // unknown action
+  } else if (obj2.isName()) {
+    action = new LinkUnknown(obj2.getName());
+
+  // action is missing or wrong type
+  } else {
+    error(-1, "Bad annotation action");
+    action = NULL;
+  }
 
-static GString *getFileSpecName(Object *fileSpecObj);
+  obj2.free();
+
+  if (action && !action->isOk()) {
+    delete action;
+    return NULL;
+  }
+  return action;
+}
+
+GString *LinkAction::getFileSpecName(Object *fileSpecObj) {
+  GString *name;
+  Object obj1;
+
+  name = NULL;
+
+  // string
+  if (fileSpecObj->isString()) {
+    name = fileSpecObj->getString()->copy();
+
+  // dictionary
+  } else if (fileSpecObj->isDict()) {
+    if (!fileSpecObj->dictLookup("Unix", &obj1)->isString()) {
+      obj1.free();
+      fileSpecObj->dictLookup("F", &obj1);
+    }
+    if (obj1.isString())
+      name = obj1.getString()->copy();
+    else
+      error(-1, "Illegal file spec in link");
+    obj1.free();
+
+  // error
+  } else {
+    error(-1, "Illegal file spec in link");
+  }
+
+  return name;
+}
 
 //------------------------------------------------------------------------
 // LinkDest
@@ -37,6 +147,10 @@ LinkDest::LinkDest(Array *a) {
   ok = gFalse;
 
   // get page
+  if (a->getLength() < 2) {
+    error(-1, "Annotation destination array has wrong length");
+    return;
+  }
   a->getNF(0, &obj1);
   if (obj1.isInt()) {
     pageNum = obj1.getInt() + 1;
@@ -56,6 +170,10 @@ LinkDest::LinkDest(Array *a) {
 
   // XYZ link
   if (obj1.isName("XYZ")) {
+    if (a->getLength() != 5) {
+      error(-1, "Annotation destination array has wrong length");
+      goto err2;
+    }
     kind = destXYZ;
     a->get(2, &obj2);
     if (obj2.isNull()) {
@@ -93,10 +211,18 @@ LinkDest::LinkDest(Array *a) {
 
   // Fit link
   } else if (obj1.isName("Fit")) {
+    if (a->getLength() != 2) {
+      error(-1, "Annotation destination array has wrong length");
+      goto err2;
+    }
     kind = destFit;
 
   // FitH link
   } else if (obj1.isName("FitH")) {
+    if (a->getLength() != 3) {
+      error(-1, "Annotation destination array has wrong length");
+      goto err2;
+    }
     kind = destFitH;
     if (!a->get(2, &obj2)->isNum()) {
       error(-1, "Bad annotation destination position");
@@ -107,6 +233,10 @@ LinkDest::LinkDest(Array *a) {
 
   // FitV link
   } else if (obj1.isName("FitV")) {
+    if (a->getLength() != 3) {
+      error(-1, "Annotation destination array has wrong length");
+      goto err2;
+    }
     kind = destFitV;
     if (!a->get(2, &obj2)->isNum()) {
       error(-1, "Bad annotation destination position");
@@ -117,6 +247,10 @@ LinkDest::LinkDest(Array *a) {
 
   // FitR link
   } else if (obj1.isName("FitR")) {
+    if (a->getLength() != 6) {
+      error(-1, "Annotation destination array has wrong length");
+      goto err2;
+    }
     kind = destFitR;
     if (!a->get(2, &obj2)->isNum()) {
       error(-1, "Bad annotation destination position");
@@ -145,10 +279,18 @@ LinkDest::LinkDest(Array *a) {
 
   // FitB link
   } else if (obj1.isName("FitB")) {
+    if (a->getLength() != 2) {
+      error(-1, "Annotation destination array has wrong length");
+      goto err2;
+    }
     kind = destFitB;
 
   // FitBH link
   } else if (obj1.isName("FitBH")) {
+    if (a->getLength() != 3) {
+      error(-1, "Annotation destination array has wrong length");
+      goto err2;
+    }
     kind = destFitBH;
     if (!a->get(2, &obj2)->isNum()) {
       error(-1, "Bad annotation destination position");
@@ -159,6 +301,10 @@ LinkDest::LinkDest(Array *a) {
 
   // FitBV link
   } else if (obj1.isName("FitBV")) {
+    if (a->getLength() != 3) {
+      error(-1, "Annotation destination array has wrong length");
+      goto err2;
+    }
     kind = destFitBV;
     if (!a->get(2, &obj2)->isNum()) {
       error(-1, "Bad annotation destination position");
@@ -292,18 +438,33 @@ LinkLaunch::LinkLaunch(Object *actionObj) {
       fileName = getFileSpecName(&obj1);
     } else {
       obj1.free();
+#ifdef WIN32
+      if (actionObj->dictLookup("Win", &obj1)->isDict()) {
+       obj1.dictLookup("F", &obj2);
+       fileName = getFileSpecName(&obj2);
+       obj2.free();
+       if (obj1.dictLookup("P", &obj2)->isString()) {
+         params = obj2.getString()->copy();
+       }
+       obj2.free();
+      } else {
+       error(-1, "Bad launch-type link action");
+      }
+#else
       //~ This hasn't been defined by Adobe yet, so assume it looks
       //~ just like the Win dictionary until they say otherwise.
       if (actionObj->dictLookup("Unix", &obj1)->isDict()) {
        obj1.dictLookup("F", &obj2);
        fileName = getFileSpecName(&obj2);
        obj2.free();
-       if (obj1.dictLookup("P", &obj2)->isString())
+       if (obj1.dictLookup("P", &obj2)->isString()) {
          params = obj2.getString()->copy();
+       }
        obj2.free();
       } else {
        error(-1, "Bad launch-type link action");
       }
+#endif
     }
     obj1.free();
   }
@@ -377,6 +538,28 @@ LinkNamed::~LinkNamed() {
   }
 }
 
+//------------------------------------------------------------------------
+// LinkMovie
+//------------------------------------------------------------------------
+
+LinkMovie::LinkMovie(Object *annotObj, Object *titleObj) {
+  annotRef.num = -1;
+  title = NULL;
+  if (annotObj->isRef()) {
+    annotRef = annotObj->getRef();
+  } else if (titleObj->isString()) {
+    title = titleObj->getString()->copy();
+  } else {
+    error(-1, "Movie action is missing both the Annot and T keys");
+  }
+}
+
+LinkMovie::~LinkMovie() {
+  if (title) {
+    delete title;
+  }
+}
+
 //------------------------------------------------------------------------
 // LinkUnknown
 //------------------------------------------------------------------------
@@ -394,7 +577,7 @@ LinkUnknown::~LinkUnknown() {
 //------------------------------------------------------------------------
 
 Link::Link(Dict *dict, GString *baseURI) {
-  Object obj1, obj2, obj3, obj4;
+  Object obj1, obj2;
   double t;
 
   action = NULL;
@@ -457,66 +640,21 @@ Link::Link(Dict *dict, GString *baseURI) {
 
   // look for destination
   if (!dict->lookup("Dest", &obj1)->isNull()) {
-    action = new LinkGoTo(&obj1);
+    action = LinkAction::parseDest(&obj1);
 
   // look for action
   } else {
     obj1.free();
     if (dict->lookup("A", &obj1)->isDict()) {
-      obj1.dictLookup("S", &obj2);
-
-      // GoTo action
-      if (obj2.isName("GoTo")) {
-       obj1.dictLookup("D", &obj3);
-       action = new LinkGoTo(&obj3);
-       obj3.free();
-
-      // GoToR action
-      } else if (obj2.isName("GoToR")) {
-       obj1.dictLookup("F", &obj3);
-       obj1.dictLookup("D", &obj4);
-       action = new LinkGoToR(&obj3, &obj4);
-       obj3.free();
-       obj4.free();
-
-      // Launch action
-      } else if (obj2.isName("Launch")) {
-       action = new LinkLaunch(&obj1);
-
-      // URI action
-      } else if (obj2.isName("URI")) {
-       obj1.dictLookup("URI", &obj3);
-       action = new LinkURI(&obj3, baseURI);
-       obj3.free();
-
-      // Named action
-      } else if (obj2.isName("Named")) {
-       obj1.dictLookup("N", &obj3);
-       action = new LinkNamed(&obj3);
-       obj3.free();
-
-      // unknown action
-      } else if (obj2.isName()) {
-       action = new LinkUnknown(obj2.getName());
-
-      // action is missing or wrong type
-      } else {
-       error(-1, "Bad annotation action");
-       action = NULL;
-      }
-
-      obj2.free();
-
-    } else {
-      error(-1, "Missing annotation destination/action");
-      action = NULL;
+      action = LinkAction::parseAction(&obj1, baseURI);
     }
   }
   obj1.free();
 
   // check for bad action
-  if (action && action->isOk())
+  if (action) {
     ok = gTrue;
+  }
 
   return;
 
@@ -595,36 +733,3 @@ GBool Links::onLink(double x, double y) const {
   }
   return gFalse;
 }
-
-//------------------------------------------------------------------------
-
-// Extract a file name from a file specification (string or dictionary).
-static GString *getFileSpecName(Object *fileSpecObj) {
-  GString *name;
-  Object obj1;
-
-  name = NULL;
-
-  // string
-  if (fileSpecObj->isString()) {
-    name = fileSpecObj->getString()->copy();
-
-  // dictionary
-  } else if (fileSpecObj->isDict()) {
-    if (!fileSpecObj->dictLookup("Unix", &obj1)->isString()) {
-      obj1.free();
-      fileSpecObj->dictLookup("F", &obj1);
-    }
-    if (obj1.isString())
-      name = obj1.getString()->copy();
-    else
-      error(-1, "Illegal file spec in link");
-    obj1.free();
-
-  // error
-  } else {
-    error(-1, "Illegal file spec in link");
-  }
-
-  return name;
-}
index e5b15e327c351350be5172dd01b3fcd59a026ee9..e19c3c035b1764843318bab06bf7e2e3170b862b 100644 (file)
@@ -9,7 +9,9 @@
 #ifndef LINK_H
 #define LINK_H
 
-#ifdef __GNUC__
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
 #pragma interface
 #endif
 
@@ -29,6 +31,7 @@ enum LinkActionKind {
   actionLaunch,                        // launch app (or open document)
   actionURI,                   // URI
   actionNamed,                 // named action
+  actionMovie,                 // movie action
   actionUnknown                        // anything else
 };
 
@@ -43,6 +46,16 @@ public:
 
   // Check link action type.
   virtual LinkActionKind getKind() = 0;
+
+  // Parse a destination (old-style action) name, string, or array.
+  static LinkAction *parseDest(Object *obj);
+
+  // Parse an action dictionary.
+  static LinkAction *parseAction(Object *obj, GString *baseURI = NULL);
+
+  // Extract a file name from a file specification (string or
+  // dictionary).
+  static GString *getFileSpecName(Object *fileSpecObj);
 };
 
 //------------------------------------------------------------------------
@@ -239,6 +252,30 @@ private:
   GString *name;
 };
 
+//------------------------------------------------------------------------
+// LinkMovie
+//------------------------------------------------------------------------
+
+class LinkMovie: public LinkAction {
+public:
+
+  LinkMovie(Object *annotObj, Object *titleObj);
+
+  virtual ~LinkMovie();
+
+  virtual GBool isOk() { return annotRef.num >= 0 || title != NULL; }
+
+  virtual LinkActionKind getKind() { return actionMovie; }
+  GBool hasAnnotRef() { return annotRef.num >= 0; }
+  Ref *getAnnotRef() { return &annotRef; }
+  GString *getTitle() { return title; }
+
+private:
+
+  Ref annotRef;
+  GString *title;
+};
+
 //------------------------------------------------------------------------
 // LinkUnknown
 //------------------------------------------------------------------------
index 1ab6b318f1a2994aeab037bd52db5dd9148a496a..3e39038dd59e430016f3daa989edf4d25ba4c96b 100644 (file)
@@ -1,7 +1,6 @@
 
 INCLUDES =                                                     \
        -I$(top_srcdir)/goo                                     \
-       -I$(top_srcdir)/ltk                                     \
        -DDATADIR=\""$(datadir)"\"                              \
         -DGNOMELOCALEDIR=\""$(prefix)/$(DATADIRNAME)/locale"\" \
        -DGNOMEICONDIR=\""$(datadir)/pixmaps"\"                 \
@@ -67,6 +66,8 @@ common_sources =              \
        GfxState.h              \
        GlobalParams.cc         \
        GlobalParams.h          \
+       JBIG2Stream.cc          \
+       JBIG2Stream.h           \
        Lexer.cc                \
        Lexer.h                 \
        Link.cc                 \
@@ -76,10 +77,14 @@ common_sources =            \
        NameToUnicodeTable.h    \
        Object.cc               \
        Object.h                \
+       Outline.cc              \
+       Outline.h               \
        OutputDev.cc            \
        OutputDev.h             \
        PDFDoc.cc               \
        PDFDoc.h                \
+       PDFDocEncoding.cc       \
+       PDFDocEncoding.h        \
        PSTokenizer.cc          \
        PSTokenizer.h           \
        Page.cc                 \
@@ -152,8 +157,18 @@ gnome_pdf_viewer_LDADD =           \
 
 xpdf_SOURCES =                 \
        $(common_sources)       \
+       XPDFApp.cc              \
+       XPDFApp.h               \
+       XPDFCore.cc             \
+       XPDFCore.h              \
+       XPDFTree.cc             \
+       XPDFTree.h              \
+       XPDFViewer.cc           \
+       XPDFViewer.h            \
        XOutputDev.cc           \
-       LTKOutputDev.cc         \
+       XOutputDev.h            \
+       XPixmapOutputDev.cc     \
+       XPixmapOutputDev.h      \
        PSOutputDev.cc          \
        PSOutputDev.h           \
        xpdf.cc
@@ -161,7 +176,6 @@ xpdf_SOURCES =                      \
 xpdf_CFLAGS = $(X_CFLAGS)
 xpdf_LDADD =                           \
        $(top_builddir)/goo/libgoo.a    \
-       $(top_builddir)/ltk/libltk.a    \
        -lXpm                           \
        $(EXTRA_GNOME_LIBS)             \
        $(libpaper_LIBS)
@@ -201,11 +215,6 @@ xpdf_LDADD =                               \
 
 #pdftopbm_LDADD = ../goo/libgoo.a $(EXTRA_GNOME_LIBS)
 
-xpdf-ltk.h: xpdf.ltk
-       rm -f $@
-       $(top_srcdir)/ltk/ltkbuild <xpdf.ltk >$@.new
-       mv $@.new $@
-
 #bitmaps =             
 #      about.xbm               
 #      dblLeftArrow.xbm        
index 6d92c6a3403edaeaa343d3fc7f37f2e0fe6b20b1..a1c71af0f75eb909625edbcc5c629cd4d265cd31 100644 (file)
@@ -6,11 +6,12 @@
 //
 //========================================================================
 
-#ifdef __GNUC__
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
 #pragma implementation
 #endif
 
-#include <aconf.h>
 #include <stddef.h>
 #include "Object.h"
 #include "Array.h"
index 65d0be0d57031207dd1d1635325b28645ee2f6b1..d8a5fb545041d9b5873f4ab6c55fa19113132875 100644 (file)
@@ -9,7 +9,9 @@
 #ifndef OBJECT_H
 #define OBJECT_H
 
-#ifdef __GNUC__
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
 #pragma interface
 #endif
 
@@ -152,6 +154,7 @@ public:
   Ref getRef() { return ref; }
   int getRefNum() { return ref.num; }
   int getRefGen() { return ref.gen; }
+  char *getCmd() { return cmd; }
 
   // Array accessors.
   int arrayGetLength();
index 6d465423b36be0e3ec29aaa7f9fc148c313c473d..15e6a8674b5876215a4e2a2b9af02c49a95d6bbd 100644 (file)
@@ -6,11 +6,12 @@
 //
 //========================================================================
 
-#ifdef __GNUC__
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
 #pragma implementation
 #endif
 
-#include <aconf.h>
 #include <stddef.h>
 #include "Object.h"
 #include "Stream.h"
index ec99ffa301d6fb01381114c35fcae1b49219ef65..995c7e75f68dab629ace4251df4d606f8d249aff 100644 (file)
@@ -9,7 +9,9 @@
 #ifndef OUTPUTDEV_H
 #define OUTPUTDEV_H
 
-#ifdef __GNUC__
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
 #pragma interface
 #endif
 
index 57782447f993d87f7f015ad8310bbc5e9dd7e655..34432991da035d225497e79764558da0cf7622d3 100644 (file)
@@ -6,11 +6,12 @@
 //
 //========================================================================
 
-#ifdef __GNUC__
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
 #pragma implementation
 #endif
 
-#include <aconf.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <stddef.h>
@@ -82,10 +83,12 @@ void PBMOutputDev::killPBMOutputDev(PBMOutputDev *out) {
 PBMOutputDev::PBMOutputDev(Display *displayA, int screenA,
                           Pixmap pixmapA, Window dummyWinA,
                           int invertA, char *fileRootA):
-  XOutputDev(displayA, pixmapA, 1,
-            DefaultColormap(displayA, screenA), gFalse,
+  XOutputDev(displayA, screenA,
+            DefaultVisual(displayA, screenA),
+            DefaultColormap(displayA, screenA),
+            gFalse,
             WhitePixel(displayA, DefaultScreen(displayA)),
-            gFalse, 1)
+            gFalse, 1, 1)
 {
   display = displayA;
   screen = screenA;
index 5b28fc5a4de5b4cb01b62fa439281662f049bc42..6926ce06939a6bd7d2d4d636ea40fb02a5dc971f 100644 (file)
@@ -9,7 +9,9 @@
 #ifndef PBMOUTPUTDEV_H
 #define PBMOUTPUTDEV_H
 
-#ifdef __GNUC__
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
 #pragma interface
 #endif
 
index f3b7f799417c272f705b5b8ffeafd92e58afd09e..ff6cefafcaf45797b4ce56e97edf37d76cdff597 100644 (file)
@@ -6,11 +6,12 @@
 //
 //========================================================================
 
-#ifdef __GNUC__
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
 #pragma implementation
 #endif
 
-#include <aconf.h>
 #include <locale.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -18,6 +19,7 @@
 #include <string.h>
 #include "GString.h"
 #include "config.h"
+#include "GlobalParams.h"
 #include "Page.h"
 #include "Catalog.h"
 #include "Stream.h"
@@ -28,6 +30,9 @@
 #include "ErrorCodes.h"
 #include "Lexer.h"
 #include "Parser.h"
+#ifndef DISABLE_OUTLINE
+#include "Outline.h"
+#endif
 #include "PDFDoc.h"
 
 //------------------------------------------------------------------------
@@ -40,9 +45,9 @@
 //------------------------------------------------------------------------
 
 PDFDoc::PDFDoc(GString *fileNameA, GString *ownerPassword,
-              GString *userPassword, GBool printCommandsA) {
+              GString *userPassword) {
   Object obj;
-  GString *fileName2;
+  GString *fileName1, *fileName2;
 
   ok = gFalse;
   errCode = errNone;
@@ -52,19 +57,24 @@ PDFDoc::PDFDoc(GString *fileNameA, GString *ownerPassword,
   xref = NULL;
   catalog = NULL;
   links = NULL;
-  printCommands = printCommandsA;
+#ifndef DISABLE_OUTLINE
+  outline = NULL;
+#endif
 
-  // try to open file
   fileName = fileNameA;
+  fileName1 = fileName;
+
+
+  // try to open file
   fileName2 = NULL;
 #ifdef VMS
-  if (!(file = fopen(fileName->getCString(), "rb", "ctx=stm"))) {
-    error(-1, "Couldn't open file '%s'", fileName->getCString());
+  if (!(file = fopen(fileName1->getCString(), "rb", "ctx=stm"))) {
+    error(-1, "Couldn't open file '%s'", fileName1->getCString());
     errCode = errOpenFile;
     return;
   }
 #else
-  if (!(file = fopen(fileName->getCString(), "rb"))) {
+  if (!(file = fopen(fileName1->getCString(), "rb"))) {
     fileName2 = fileName->copy();
     fileName2->lowerCase();
     if (!(file = fopen(fileName2->getCString(), "rb"))) {
@@ -88,7 +98,7 @@ PDFDoc::PDFDoc(GString *fileNameA, GString *ownerPassword,
 }
 
 PDFDoc::PDFDoc(BaseStream *strA, GString *ownerPassword,
-              GString *userPassword, GBool printCommandsA) {
+              GString *userPassword) {
   ok = gFalse;
   errCode = errNone;
   fileName = NULL;
@@ -97,7 +107,9 @@ PDFDoc::PDFDoc(BaseStream *strA, GString *ownerPassword,
   xref = NULL;
   catalog = NULL;
   links = NULL;
-  printCommands = printCommandsA;
+#ifndef DISABLE_OUTLINE
+  outline = NULL;
+#endif
   ok = setup(ownerPassword, userPassword);
 }
 
@@ -114,18 +126,28 @@ GBool PDFDoc::setup(GString *ownerPassword, GString *userPassword) {
   }
 
   // read catalog
-  catalog = new Catalog(xref, printCommands);
+  catalog = new Catalog(xref);
   if (!catalog->isOk()) {
     error(-1, "Couldn't read page catalog");
     errCode = errBadCatalog;
     return gFalse;
   }
 
+#ifndef DISABLE_OUTLINE
+  // read outline
+  outline = new Outline(catalog->getOutline(), xref);
+#endif
+
   // done
   return gTrue;
 }
 
 PDFDoc::~PDFDoc() {
+#ifndef DISABLE_OUTLINE
+  if (outline) {
+    delete outline;
+  }
+#endif
   if (catalog) {
     delete catalog;
   }
@@ -182,10 +204,12 @@ void PDFDoc::checkHeader() {
 }
 
 void PDFDoc::displayPage(OutputDev *out, int page, double zoom,
-                        int rotate, GBool doLinks) {
+                        int rotate, GBool doLinks,
+                        GBool (*abortCheckCbk)(void *data),
+                        void *abortCheckCbkData) {
   Page *p;
 
-  if (printCommands) {
+  if (globalParams->getPrintCommands()) {
     printf("***** page %d *****\n", page);
   }
   p = catalog->getPage(page);
@@ -195,21 +219,38 @@ void PDFDoc::displayPage(OutputDev *out, int page, double zoom,
       links = NULL;
     }
     getLinks(p);
-    p->display(out, zoom, rotate, links, catalog);
+    p->display(out, zoom, rotate, links, catalog,
+              abortCheckCbk, abortCheckCbkData);
   } else {
-    p->display(out, zoom, rotate, NULL, catalog);
+    p->display(out, zoom, rotate, NULL, catalog,
+              abortCheckCbk, abortCheckCbkData);
   }
 }
 
 void PDFDoc::displayPages(OutputDev *out, int firstPage, int lastPage,
-                         int zoom, int rotate, GBool doLinks) {
+                         int zoom, int rotate, GBool doLinks,
+                         GBool (*abortCheckCbk)(void *data),
+                         void *abortCheckCbkData) {
   int page;
 
   for (page = firstPage; page <= lastPage; ++page) {
-    displayPage(out, page, zoom, rotate, doLinks);
+    displayPage(out, page, zoom, rotate, doLinks,
+               abortCheckCbk, abortCheckCbkData);
   }
 }
 
+void PDFDoc::displayPageSlice(OutputDev *out, int page, double zoom,
+                             int rotate, int sliceX, int sliceY,
+                             int sliceW, int sliceH,
+                             GBool (*abortCheckCbk)(void *data),
+                             void *abortCheckCbkData) {
+  Page *p;
+
+  p = catalog->getPage(page);
+  p->displaySlice(out, zoom, rotate, sliceX, sliceY, sliceW, sliceH,
+                 NULL, catalog, abortCheckCbk, abortCheckCbkData);
+}
+
 GBool PDFDoc::isLinearized() {
   Parser *parser;
   Object obj1, obj2, obj3, obj4, obj5;
index c12531e9387c76bf7c89c9a8e146cb1e9e5500e8..ff6e359ceb784f7a72d51df9645dd9707fc51c84 100644 (file)
@@ -9,7 +9,9 @@
 #ifndef PDFDOC_H
 #define PDFDOC_H
 
-#ifdef __GNUC__
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
 #pragma interface
 #endif
 
@@ -25,6 +27,7 @@ class OutputDev;
 class Links;
 class LinkAction;
 class LinkDest;
+class Outline;
 
 //------------------------------------------------------------------------
 // PDFDoc
@@ -34,9 +37,9 @@ class PDFDoc {
 public:
 
   PDFDoc(GString *fileNameA, GString *ownerPassword = NULL,
-        GString *userPassword = NULL, GBool printCommandsA = gFalse);
+        GString *userPassword = NULL);
   PDFDoc(BaseStream *strA, GString *ownerPassword = NULL,
-        GString *userPassword = NULL, GBool printCommandsA = gFalse);
+        GString *userPassword = NULL);
   ~PDFDoc();
 
   // Was PDF document successfully opened?
@@ -77,11 +80,22 @@ public:
 
   // Display a page.
   void displayPage(OutputDev *out, int page, double zoom,
-                  int rotate, GBool doLinks);
+                  int rotate, GBool doLinks,
+                  GBool (*abortCheckCbk)(void *data) = NULL,
+                  void *abortCheckCbkData = NULL);
 
   // Display a range of pages.
   void displayPages(OutputDev *out, int firstPage, int lastPage,
-                   int zoom, int rotate, GBool doLinks);
+                   int zoom, int rotate, GBool doLinks,
+                   GBool (*abortCheckCbk)(void *data) = NULL,
+                   void *abortCheckCbkData = NULL);
+
+  // Display part of a page.
+  void displayPageSlice(OutputDev *out, int page, double zoom,
+                       int rotate, int sliceX, int sliceY,
+                       int sliceW, int sliceH,
+                       GBool (*abortCheckCbk)(void *data) = NULL,
+                       void *abortCheckCbkData = NULL);
 
   // Find a page, given its object ID.  Returns page number, or 0 if
   // not found.
@@ -99,6 +113,11 @@ public:
   LinkDest *findDest(GString *name)
     { return catalog->findDest(name); }
 
+#ifndef DISABLE_OUTLINE
+  // Return the outline object.
+  Outline *getOutline() { return outline; }
+#endif
+
   // Is the file encrypted?
   GBool isEncrypted() { return xref->isEncrypted(); }
 
@@ -117,6 +136,7 @@ public:
 
   // Return the document's Info dictionary (if any).
   Object *getDocInfo(Object *obj) { return xref->getDocInfo(obj); }
+  Object *getDocInfoNF(Object *obj) { return xref->getDocInfoNF(obj); }
 
   // Return the PDF version specified by the file.
   double getPDFVersion() { return pdfVersion; }
@@ -124,6 +144,7 @@ public:
   // Save this file with another name.
   GBool saveAs(GString *name);
 
+
 private:
 
   GBool setup(GString *ownerPassword, GString *userPassword);
@@ -137,7 +158,10 @@ private:
   XRef *xref;
   Catalog *catalog;
   Links *links;
-  GBool printCommands;
+#ifndef DISABLE_OUTLINE
+  Outline *outline;
+#endif
+
 
   GBool ok;
   int errCode;
index 18359119617a158c28ea0a0b0245d0df0aadeac1..9844ab324fda9b63aabe0e1783fedaa711ce990f 100644 (file)
@@ -6,11 +6,12 @@
 //
 //========================================================================
 
-#ifdef __GNUC__
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
 #pragma implementation
 #endif
 
-#include <aconf.h>
 #include <stdio.h>
 #include <stddef.h>
 #include <stdarg.h>
@@ -54,7 +55,7 @@ static char *prolog[] = {
   "      /PageSize exch def",
   "      /ImagingBBox null def",
   "      /Policies 1 dict dup begin /PageSize 3 def end def",
-  "      /Duplex exch def",
+  "      { /Duplex true def } if",
   "    currentdict end setpagedevice",
   "  } {",
   "    pop pop",
@@ -268,7 +269,8 @@ static char *prolog[] = {
   "      } if",
   "      pdfTextRender 3 and dup 1 eq exch 2 eq or {",
   "        8 6 roll moveto",
-  "        sCol false awcp currentpoint stroke moveto",
+  "        currentfont /FontType get 3 eq { fCol } { sCol } ifelse",
+  "        false awcp currentpoint stroke moveto",
   "      } {",
   "        8 {pop} repeat",
   "      } ifelse",
@@ -488,50 +490,29 @@ 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, XRef *xrefA, Catalog *catalog,
                         int firstPage, int lastPage, PSOutMode modeA) {
-  Page *page;
-  PDFRectangle *box;
-  Dict *resDict;
-  Annots *annots;
-  char **p;
-  int pg;
-  Object obj1, obj2;
-  int i;
+  FILE *f;
+  PSFileType fileTypeA;
 
-  // initialize
-  xref = xrefA;
-  level = globalParams->getPSLevel();
-  mode = modeA;
-  paperWidth = globalParams->getPSPaperWidth();
-  paperHeight = globalParams->getPSPaperHeight();
   fontIDs = NULL;
   fontFileIDs = NULL;
   fontFileNames = NULL;
   font16Enc = NULL;
   embFontList = NULL;
-  f = NULL;
-  if (mode == psModeForm) {
-    lastPage = firstPage;
-  }
-  processColors = 0;
   customColors = NULL;
-  inType3Char = gFalse;
   t3String = NULL;
 
-#if OPI_SUPPORT
-  // initialize OPI nesting levels
-  opi13Nest = 0;
-  opi20Nest = 0;
-#endif
-
   // open file or pipe
-  ok = gTrue;
   if (!strcmp(fileName, "-")) {
-    fileType = psStdout;
+    fileTypeA = psStdout;
     f = stdout;
   } else if (fileName[0] == '|') {
-    fileType = psPipe;
+    fileTypeA = psPipe;
 #ifdef HAVE_POPEN
 #ifndef WIN32
     signal(SIGPIPE, (SignalFunc)SIG_IGN);
@@ -547,7 +528,7 @@ PSOutputDev::PSOutputDev(char *fileName, XRef *xrefA, Catalog *catalog,
     return;
 #endif
   } else {
-    fileType = psFile;
+    fileTypeA = psFile;
     if (!(f = fopen(fileName, "w"))) {
       error(-1, "Couldn't open PostScript file '%s'", fileName);
       ok = gFalse;
@@ -555,6 +536,59 @@ PSOutputDev::PSOutputDev(char *fileName, XRef *xrefA, Catalog *catalog,
     }
   }
 
+  init(outputToFile, f, fileTypeA,
+       xrefA, catalog, firstPage, lastPage, modeA);
+}
+
+PSOutputDev::PSOutputDev(PSOutputFunc outputFuncA, void *outputStreamA,
+                        XRef *xrefA, Catalog *catalog,
+                        int firstPage, int lastPage, PSOutMode modeA) {
+  fontIDs = NULL;
+  fontFileIDs = NULL;
+  fontFileNames = NULL;
+  font16Enc = NULL;
+  embFontList = NULL;
+  customColors = NULL;
+  t3String = NULL;
+
+  init(outputFuncA, outputStreamA, psGeneric,
+       xrefA, catalog, firstPage, lastPage, modeA);
+}
+
+void PSOutputDev::init(PSOutputFunc outputFuncA, void *outputStreamA,
+                      PSFileType fileTypeA, XRef *xrefA, Catalog *catalog,
+                      int firstPage, int lastPage, PSOutMode modeA) {
+  Page *page;
+  PDFRectangle *box;
+  Dict *resDict;
+  Annots *annots;
+  char **p;
+  int pg;
+  Object obj1, obj2;
+  int i;
+
+  // initialize
+  ok = gTrue;
+  outputFunc = outputFuncA;
+  outputStream = outputStreamA;
+  fileType = fileTypeA;
+  xref = xrefA;
+  level = globalParams->getPSLevel();
+  mode = modeA;
+  paperWidth = globalParams->getPSPaperWidth();
+  paperHeight = globalParams->getPSPaperHeight();
+  if (mode == psModeForm) {
+    lastPage = firstPage;
+  }
+  processColors = 0;
+  inType3Char = gFalse;
+
+#if OPI_SUPPORT
+  // initialize OPI nesting levels
+  opi13Nest = 0;
+  opi20Nest = 0;
+#endif
+
   // initialize fontIDs, fontFileIDs, and fontFileNames lists
   fontIDSize = 64;
   fontIDLen = 0;
@@ -574,66 +608,66 @@ PSOutputDev::PSOutputDev(char *fileName, XRef *xrefA, Catalog *catalog,
   // write header
   switch (mode) {
   case psModePS:
-    writePS("%%!PS-Adobe-3.0\n");
-    writePS("%%%%Creator: xpdf/pdftops %s\n", xpdfVersion);
-    writePS("%%%%LanguageLevel: %d\n",
-           (level == psLevel1 || level == psLevel1Sep) ? 1 :
-           (level == psLevel2 || level == psLevel2Sep) ? 2 : 3);
+    writePS("%!PS-Adobe-3.0\n");
+    writePSFmt("%%%%Creator: xpdf/pdftops %s\n", xpdfVersion);
+    writePSFmt("%%%%LanguageLevel: %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");
-    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");
+      writePS("%%DocumentProcessColors: (atend)\n");
+      writePS("%%DocumentCustomColors: (atend)\n");
+    }
+    writePS("%%DocumentSuppliedResources: (atend)\n");
+    writePSFmt("%%%%DocumentMedia: plain %d %d 0 () ()\n",
+              paperWidth, paperHeight);
+    writePSFmt("%%%%Pages: %d\n", lastPage - firstPage + 1);
+    writePS("%%EndComments\n");
+    writePS("%%BeginDefaults\n");
+    writePS("%%PageMedia: plain\n");
+    writePS("%%EndDefaults\n");
     break;
   case psModeEPS:
-    writePS("%%!PS-Adobe-3.0 EPSF-3.0\n");
-    writePS("%%%%Creator: xpdf/pdftops %s\n", xpdfVersion);
-    writePS("%%%%LanguageLevel: %d\n",
-           (level == psLevel1 || level == psLevel1Sep) ? 1 :
-           (level == psLevel2 || level == psLevel2Sep) ? 2 : 3);
+    writePS("%!PS-Adobe-3.0 EPSF-3.0\n");
+    writePSFmt("%%%%Creator: xpdf/pdftops %s\n", xpdfVersion);
+    writePSFmt("%%%%LanguageLevel: %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("%%DocumentProcessColors: (atend)\n");
+      writePS("%%DocumentCustomColors: (atend)\n");
     }
     page = catalog->getPage(firstPage);
     box = page->getBox();
-    writePS("%%%%BoundingBox: %d %d %d %d\n",
-           (int)floor(box->x1), (int)floor(box->y1),
-           (int)ceil(box->x2), (int)ceil(box->y2));
+    writePSFmt("%%%%BoundingBox: %d %d %d %d\n",
+              (int)floor(box->x1), (int)floor(box->y1),
+              (int)ceil(box->x2), (int)ceil(box->y2));
     if (floor(box->x1) != ceil(box->x1) ||
        floor(box->y1) != ceil(box->y1) ||
        floor(box->x2) != ceil(box->x2) ||
        floor(box->y2) != ceil(box->y2)) {
-      writePS("%%%%HiResBoundingBox: %g %g %g %g\n",
-             box->x1, box->y1, box->x2, box->y2);
+      writePSFmt("%%%%HiResBoundingBox: %g %g %g %g\n",
+                box->x1, box->y1, box->x2, box->y2);
     }
-    writePS("%%%%DocumentSuppliedResources: (atend)\n");
-    writePS("%%%%EndComments\n");
+    writePS("%%DocumentSuppliedResources: (atend)\n");
+    writePS("%%EndComments\n");
     break;
   case psModeForm:
-    writePS("%%!PS-Adobe-3.0 Resource-Form\n");
-    writePS("%%%%Creator: xpdf/pdftops %s\n", xpdfVersion);
-    writePS("%%%%LanguageLevel: %d\n",
-           (level == psLevel1 || level == psLevel1Sep) ? 1 :
-           (level == psLevel2 || level == psLevel2Sep) ? 2 : 3);
+    writePS("%!PS-Adobe-3.0 Resource-Form\n");
+    writePSFmt("%%%%Creator: xpdf/pdftops %s\n", xpdfVersion);
+    writePSFmt("%%%%LanguageLevel: %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("%%DocumentProcessColors: (atend)\n");
+      writePS("%%DocumentCustomColors: (atend)\n");
     }
-    writePS("%%%%DocumentSuppliedResources: (atend)\n");
-    writePS("%%%%EndComments\n");
+    writePS("%%DocumentSuppliedResources: (atend)\n");
+    writePS("%%EndComments\n");
     page = catalog->getPage(firstPage);
     box = page->getBox();
     writePS("32 dict dup begin\n");
-    writePS("/BBox [%d %d %d %d] def\n",
-           (int)box->x1, (int)box->y1, (int)box->x2, (int)box->y2);
+    writePSFmt("/BBox [%d %d %d %d] def\n",
+              (int)box->x1, (int)box->y1, (int)box->x2, (int)box->y2);
     writePS("/FormType 1 def\n");
     writePS("/Matrix [1 0 0 1 0 0] def\n");
     break;
@@ -641,20 +675,20 @@ PSOutputDev::PSOutputDev(char *fileName, XRef *xrefA, Catalog *catalog,
 
   // write prolog
   if (mode != psModeForm) {
-    writePS("%%%%BeginProlog\n");
+    writePS("%%BeginProlog\n");
   }
-  writePS("%%%%BeginResource: procset xpdf %s 0\n", xpdfVersion);
+  writePSFmt("%%%%BeginResource: procset xpdf %s 0\n", xpdfVersion);
   for (p = prolog; *p; ++p) {
-    writePS("%s\n", *p);
+    writePSFmt("%s\n", *p);
   }
-  writePS("%%%%EndResource\n");
+  writePS("%%EndResource\n");
   if (level >= psLevel3) {
     for (p = cmapProlog; *p; ++p) {
-      writePS("%s\n", *p);
+      writePSFmt("%s\n", *p);
     }
   }
   if (mode != psModeForm) {
-    writePS("%%%%EndProlog\n");
+    writePS("%%EndProlog\n");
   }
 
   // set up fonts and images
@@ -662,7 +696,7 @@ PSOutputDev::PSOutputDev(char *fileName, XRef *xrefA, Catalog *catalog,
     // swap the form and xpdf dicts
     writePS("xpdf end begin dup begin\n");
   } else {
-    writePS("%%%%BeginSetup\n");
+    writePS("%%BeginSetup\n");
     writePS("xpdf begin\n");
   }
   for (pg = firstPage; pg <= lastPage; ++pg) {
@@ -686,16 +720,16 @@ PSOutputDev::PSOutputDev(char *fileName, XRef *xrefA, Catalog *catalog,
   }
   if (mode != psModeForm) {
     if (mode != psModeEPS) {
-      writePS("%d %d %s pdfSetup\n",
-             paperWidth, paperHeight,
-             globalParams->getPSDuplex() ? "true" : "false");
+      writePSFmt("%d %d %s pdfSetup\n",
+                paperWidth, paperHeight,
+                globalParams->getPSDuplex() ? "true" : "false");
     }
 #if OPI_SUPPORT
     if (globalParams->getPSOPI()) {
       writePS("/opiMatrix matrix currentmatrix def\n");
     }
 #endif
-    writePS("%%%%EndSetup\n");
+    writePS("%%EndSetup\n");
   }
 
   // initialize sequential page number
@@ -706,17 +740,17 @@ PSOutputDev::~PSOutputDev() {
   PSOutCustomColor *cc;
   int i;
 
-  if (f) {
+  if (ok) {
     if (mode == psModeForm) {
       writePS("/Foo exch /Form defineresource pop\n");
     } else {
-      writePS("%%%%Trailer\n");
+      writePS("%%Trailer\n");
       writePS("end\n");
-      writePS("%%%%DocumentSuppliedResources:\n");
-      writePS("%s", embFontList->getCString());
+      writePS("%%DocumentSuppliedResources:\n");
+      writePS(embFontList->getCString());
       if (level == psLevel1Sep || level == psLevel2Sep ||
          level == psLevel3Sep) {
-         writePS("%%%%DocumentProcessColors:");
+         writePS("%%DocumentProcessColors:");
          if (processColors & psProcessCyan) {
           writePS(" Cyan");
         }
@@ -730,28 +764,28 @@ PSOutputDev::~PSOutputDev() {
           writePS(" Black");
         }
          writePS("\n");
-         writePS("%%%%DocumentCustomColors:");
+         writePS("%%DocumentCustomColors:");
         for (cc = customColors; cc; cc = cc->next) {
-          writePS(" (%s)", cc->name->getCString());
+          writePSFmt(" (%s)", cc->name->getCString());
         }
          writePS("\n");
-         writePS("%%%%CMYKCustomColor:\n");
+         writePS("%%CMYKCustomColor:\n");
         for (cc = customColors; cc; cc = cc->next) {
-          writePS("%%%%+ %g %g %g %g (%s)\n",
+          writePSFmt("%%%%+ %g %g %g %g (%s)\n",
                   cc->c, cc->m, cc->y, cc->k, cc->name->getCString());
         }
       }
-      writePS("%%%%EOF\n");
+      writePS("%%EOF\n");
     }
     if (fileType == psFile) {
 #ifdef MACOS
-      ICS_MapRefNumAndAssign((short)f->handle);
+      ICS_MapRefNumAndAssign((short)((FILE *)outputStream)->handle);
 #endif
-      fclose(f);
+      fclose((FILE *)outputStream);
     }
 #ifdef HAVE_POPEN
     else if (fileType == psPipe) {
-      pclose(f);
+      pclose((FILE *)outputStream);
 #ifndef WIN32
       signal(SIGPIPE, (SignalFunc)SIG_DFL);
 #endif
@@ -834,7 +868,7 @@ void PSOutputDev::setupFont(GfxFont *font, Dict *parentResDict) {
   PSFontParam *fontParam;
   GString *psNameStr;
   char *psName;
-  char type3Name[64];
+  char type3Name[64], buf[16];
   UnicodeMap *uMap;
   char *charName;
   double xs, ys;
@@ -1020,29 +1054,36 @@ void PSOutputDev::setupFont(GfxFont *font, Dict *parentResDict) {
   // generate PostScript code to set up the font
   if (font->isCIDFont()) {
     if (level == psLevel3 || level == psLevel3Sep) {
-      writePS("/F%d_%d /%s %d pdfMakeFont16L3\n",
-             font->getID()->num, font->getID()->gen, psName,
-             font->getWMode());
+      writePSFmt("/F%d_%d /%s %d pdfMakeFont16L3\n",
+                font->getID()->num, font->getID()->gen, psName,
+                font->getWMode());
     } else {
-      writePS("/F%d_%d /%s %d pdfMakeFont16\n",
-             font->getID()->num, font->getID()->gen, psName,
-             font->getWMode());
+      writePSFmt("/F%d_%d /%s %d pdfMakeFont16\n",
+                font->getID()->num, font->getID()->gen, psName,
+                font->getWMode());
     }
   } else {
-    writePS("/F%d_%d /%s %g %g\n",
-           font->getID()->num, font->getID()->gen, psName, xs, ys);
+    writePSFmt("/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) ? "[ " : "  ");
+      writePSFmt((i == 0) ? "[ " : "  ");
       for (j = 0; j < 8; ++j) {
-       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";
+       if (font->getType() == fontTrueType &&
+           !((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("/%s", charName ? charName : ".notdef");
+       writePS("/");
+       writePSName(charName ? charName : (char *)".notdef");
       }
-      writePS((i == 256-8) ? "]\n" : "\n");
+      writePS((i == 256-8) ? (char *)"]\n" : (char *)"\n");
     }
     writePS("pdfMakeFont\n");
   }
@@ -1102,7 +1143,7 @@ void PSOutputDev::setupEmbeddedType1Font(Ref *id, char *psName) {
   obj2.free();
 
   // beginning comment
-  writePS("%%%%BeginResource: font %s\n", psName);
+  writePSFmt("%%%%BeginResource: font %s\n", psName);
   embFontList->append("%%+ font ");
   embFontList->append(psName);
   embFontList->append("\n");
@@ -1168,7 +1209,7 @@ void PSOutputDev::setupEmbeddedType1Font(Ref *id, char *psName) {
   writePS("cleartomark\n");
 
   // ending comment
-  writePS("%%%%EndResource\n");
+  writePS("%%EndResource\n");
 
  err1:
   strObj.streamClose();
@@ -1198,7 +1239,7 @@ void PSOutputDev::setupExternalType1Font(GString *fileName, char *psName) {
   fontFileNames[fontFileNameLen++] = fileName->copy();
 
   // beginning comment
-  writePS("%%%%BeginResource: font %s\n", psName);
+  writePSFmt("%%%%BeginResource: font %s\n", psName);
   embFontList->append("%%+ font ");
   embFontList->append(psName);
   embFontList->append("\n");
@@ -1214,7 +1255,7 @@ void PSOutputDev::setupExternalType1Font(GString *fileName, char *psName) {
   fclose(fontFile);
 
   // ending comment
-  writePS("%%%%EndResource\n");
+  writePS("%%EndResource\n");
 }
 
 void PSOutputDev::setupEmbeddedType1CFont(GfxFont *font, Ref *id,
@@ -1239,7 +1280,7 @@ void PSOutputDev::setupEmbeddedType1CFont(GfxFont *font, Ref *id,
   fontFileIDs[fontFileIDLen++] = *id;
 
   // beginning comment
-  writePS("%%%%BeginResource: font %s\n", psName);
+  writePSFmt("%%%%BeginResource: font %s\n", psName);
   embFontList->append("%%+ font ");
   embFontList->append(psName);
   embFontList->append("\n");
@@ -1247,12 +1288,12 @@ void PSOutputDev::setupEmbeddedType1CFont(GfxFont *font, Ref *id,
   // convert it to a Type 1 font
   fontBuf = font->readEmbFontFile(xref, &fontLen);
   t1cFile = new Type1CFontFile(fontBuf, fontLen);
-  t1cFile->convertToType1(f);
+  t1cFile->convertToType1(outputFunc, outputStream);
   delete t1cFile;
   gfree(fontBuf);
 
   // ending comment
-  writePS("%%%%EndResource\n");
+  writePS("%%EndResource\n");
 }
 
 void PSOutputDev::setupEmbeddedTrueTypeFont(GfxFont *font, Ref *id,
@@ -1278,7 +1319,7 @@ void PSOutputDev::setupEmbeddedTrueTypeFont(GfxFont *font, Ref *id,
   fontFileIDs[fontFileIDLen++] = *id;
 
   // beginning comment
-  writePS("%%%%BeginResource: font %s\n", psName);
+  writePSFmt("%%%%BeginResource: font %s\n", psName);
   embFontList->append("%%+ font ");
   embFontList->append(psName);
   embFontList->append("\n");
@@ -1288,13 +1329,14 @@ void PSOutputDev::setupEmbeddedTrueTypeFont(GfxFont *font, Ref *id,
   ttFile = new TrueTypeFontFile(fontBuf, fontLen);
   ctu = ((Gfx8BitFont *)font)->getToUnicode();
   ttFile->convertToType42(psName, ((Gfx8BitFont *)font)->getEncoding(),
-                         ctu, ((Gfx8BitFont *)font)->getHasEncoding(), f);
+                         ctu, ((Gfx8BitFont *)font)->getHasEncoding(),
+                         outputFunc, outputStream);
   ctu->decRefCnt();
   delete ttFile;
   gfree(fontBuf);
 
   // ending comment
-  writePS("%%%%EndResource\n");
+  writePS("%%EndResource\n");
 }
 
 void PSOutputDev::setupExternalTrueTypeFont(GfxFont *font, char *psName) {
@@ -1322,7 +1364,7 @@ void PSOutputDev::setupExternalTrueTypeFont(GfxFont *font, char *psName) {
   fontFileNames[fontFileNameLen++] = fileName->copy();
 
   // beginning comment
-  writePS("%%%%BeginResource: font %s\n", psName);
+  writePSFmt("%%%%BeginResource: font %s\n", psName);
   embFontList->append("%%+ font ");
   embFontList->append(psName);
   embFontList->append("\n");
@@ -1332,13 +1374,14 @@ void PSOutputDev::setupExternalTrueTypeFont(GfxFont *font, char *psName) {
   ttFile = new TrueTypeFontFile(fontBuf, fontLen);
   ctu = ((Gfx8BitFont *)font)->getToUnicode();
   ttFile->convertToType42(psName, ((Gfx8BitFont *)font)->getEncoding(),
-                         ctu, ((Gfx8BitFont *)font)->getHasEncoding(), f);
+                         ctu, ((Gfx8BitFont *)font)->getHasEncoding(),
+                         outputFunc, outputStream);
   ctu->decRefCnt();
   delete ttFile;
   gfree(fontBuf);
 
   // ending comment
-  writePS("%%%%EndResource\n");
+  writePS("%%EndResource\n");
 }
 
 void PSOutputDev::setupEmbeddedCIDType0Font(GfxFont *font, Ref *id,
@@ -1363,7 +1406,7 @@ void PSOutputDev::setupEmbeddedCIDType0Font(GfxFont *font, Ref *id,
   fontFileIDs[fontFileIDLen++] = *id;
 
   // beginning comment
-  writePS("%%%%BeginResource: font %s\n", psName);
+  writePSFmt("%%%%BeginResource: font %s\n", psName);
   embFontList->append("%%+ font ");
   embFontList->append(psName);
   embFontList->append("\n");
@@ -1373,16 +1416,16 @@ void PSOutputDev::setupEmbeddedCIDType0Font(GfxFont *font, Ref *id,
   t1cFile = new Type1CFontFile(fontBuf, fontLen);
   if (globalParams->getPSLevel() >= psLevel3) {
     // Level 3: use a CID font
-    t1cFile->convertToCIDType0(psName, f);
+    t1cFile->convertToCIDType0(psName, outputFunc, outputStream);
   } else {
     // otherwise: use a non-CID composite font
-    t1cFile->convertToType0(psName, f);
+    t1cFile->convertToType0(psName, outputFunc, outputStream);
   }
   delete t1cFile;
   gfree(fontBuf);
 
   // ending comment
-  writePS("%%%%EndResource\n");
+  writePS("%%EndResource\n");
 }
 
 void PSOutputDev::setupEmbeddedCIDTrueTypeFont(GfxFont *font, Ref *id,
@@ -1407,7 +1450,7 @@ void PSOutputDev::setupEmbeddedCIDTrueTypeFont(GfxFont *font, Ref *id,
   fontFileIDs[fontFileIDLen++] = *id;
 
   // beginning comment
-  writePS("%%%%BeginResource: font %s\n", psName);
+  writePSFmt("%%%%BeginResource: font %s\n", psName);
   embFontList->append("%%+ font ");
   embFontList->append(psName);
   embFontList->append("\n");
@@ -1418,17 +1461,19 @@ void PSOutputDev::setupEmbeddedCIDTrueTypeFont(GfxFont *font, Ref *id,
   if (globalParams->getPSLevel() >= psLevel3) {
     ttFile->convertToCIDType2(psName,
                              ((GfxCIDFont *)font)->getCIDToGID(),
-                             ((GfxCIDFont *)font)->getCIDToGIDLen(), f);
+                             ((GfxCIDFont *)font)->getCIDToGIDLen(),
+                             outputFunc, outputStream);
   } else {
     // otherwise: use a non-CID composite font
     ttFile->convertToType0(psName, ((GfxCIDFont *)font)->getCIDToGID(),
-                          ((GfxCIDFont *)font)->getCIDToGIDLen(), f);
+                          ((GfxCIDFont *)font)->getCIDToGIDLen(),
+                          outputFunc, outputStream);
   }
   delete ttFile;
   gfree(fontBuf);
 
   // ending comment
-  writePS("%%%%EndResource\n");
+  writePS("%%EndResource\n");
 }
 
 void PSOutputDev::setupType3Font(GfxFont *font, char *psName,
@@ -1439,6 +1484,7 @@ void PSOutputDev::setupType3Font(GfxFont *font, char *psName,
   Gfx *gfx;
   PDFRectangle box;
   double *m;
+  char buf[256];
   int i;
 
   // set up resources used by font
@@ -1449,7 +1495,7 @@ void PSOutputDev::setupType3Font(GfxFont *font, char *psName,
   }
 
   // beginning comment
-  writePS("%%%%BeginResource: font %s\n", psName);
+  writePSFmt("%%%%BeginResource: font %s\n", psName);
   embFontList->append("%%+ font ");
   embFontList->append(psName);
   embFontList->append("\n");
@@ -1458,11 +1504,11 @@ void PSOutputDev::setupType3Font(GfxFont *font, char *psName,
   writePS("7 dict begin\n");
   writePS("/FontType 3 def\n");
   m = font->getFontMatrix();
-  writePS("/FontMatrix [%g %g %g %g %g %g] def\n",
-         m[0], m[1], m[2], m[3], m[4], m[5]);
+  writePSFmt("/FontMatrix [%g %g %g %g %g %g] def\n",
+            m[0], m[1], m[2], m[3], m[4], m[5]);
   m = font->getFontBBox();
-  writePS("/FontBBox [%g %g %g %g] def\n",
-         m[0], m[1], m[2], m[3]);
+  writePSFmt("/FontBBox [%g %g %g %g] 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");
@@ -1475,7 +1521,7 @@ void PSOutputDev::setupType3Font(GfxFont *font, char *psName,
   writePS("  1 index /BuildGlyph get exec\n");
   writePS("} bind def\n");
   if ((charProcs = ((Gfx8BitFont *)font)->getCharProcs())) {
-    writePS("/CharProcs %d dict def\n", charProcs->getLength());
+    writePSFmt("/CharProcs %d dict def\n", charProcs->getLength());
     writePS("CharProcs begin\n");
     box.x1 = m[0];
     box.y1 = m[1];
@@ -1483,22 +1529,27 @@ void PSOutputDev::setupType3Font(GfxFont *font, char *psName,
     box.y2 = m[3];
     gfx = new Gfx(xref, this, resDict, &box, gFalse, NULL);
     inType3Char = gTrue;
+    t3Cacheable = gFalse;
     for (i = 0; i < charProcs->getLength(); ++i) {
-      writePS("/%s {\n", charProcs->getKey(i));
+      writePS("/");
+      writePSName(charProcs->getKey(i));
+      writePS(" {\n");
       gfx->display(charProcs->getVal(i, &charProc));
       charProc.free();
       if (t3String) {
        if (t3Cacheable) {
-         fprintf(f, "%g %g %g %g %g %g setcachedevice\n",
+         sprintf(buf, "%g %g %g %g %g %g setcachedevice\n",
                  t3WX, t3WY, t3LLX, t3LLY, t3URX, t3URY);
        } else {
-         fprintf(f, "%g %g setcharwidth\n", t3WX, t3WY);
+         sprintf(buf, "%g %g setcharwidth\n", t3WX, t3WY);
        }
-       fputs(t3String->getCString(), f);
+       (*outputFunc)(outputStream, buf, strlen(buf));
+       (*outputFunc)(outputStream, t3String->getCString(),
+                     t3String->getLength());
        delete t3String;
        t3String = NULL;
       }
-      fputs("Q\n", f);
+      (*outputFunc)(outputStream, "Q\n", 2);
       writePS("} def\n");
     }
     inType3Char = gFalse;
@@ -1506,10 +1557,10 @@ void PSOutputDev::setupType3Font(GfxFont *font, char *psName,
     writePS("end\n");
   }
   writePS("currentdict end\n");
-  writePS("/%s exch definefont pop\n", psName);
+  writePSFmt("/%s exch definefont pop\n", psName);
 
   // ending comment
-  writePS("%%%%EndResource\n");
+  writePS("%%EndResource\n");
 }
 
 void PSOutputDev::setupImages(Dict *resDict) {
@@ -1584,7 +1635,7 @@ void PSOutputDev::setupImage(Ref id, Stream *str) {
     }
   } while (c != '~' && c != EOF);
   ++size;
-  writePS("%d array dup /ImData_%d_%d exch def\n", size, id.num, id.gen);
+  writePSFmt("%d array dup /ImData_%d_%d exch def\n", size, id.num, id.gen);
 
   // write the data into the array
   str->reset();
@@ -1621,7 +1672,7 @@ void PSOutputDev::setupImage(Ref id, Stream *str) {
     if (col > 225) {
       writePS("~> put\n");
       ++line;
-      writePS("dup %d <~", line);
+      writePSFmt("dup %d <~", line);
       col = 0;
     }
   } while (c != '~' && c != EOF);
@@ -1634,11 +1685,12 @@ void PSOutputDev::setupImage(Ref id, Stream *str) {
 void PSOutputDev::startPage(int pageNum, GfxState *state) {
   int x1, y1, x2, y2, width, height, t;
 
+
   switch (mode) {
 
   case psModePS:
-    writePS("%%%%Page: %d %d\n", pageNum, seqPage);
-    writePS("%%%%BeginPageSetup\n");
+    writePSFmt("%%%%Page: %d %d\n", pageNum, seqPage);
+    writePS("%%BeginPageSetup\n");
 
     // rotate, translate, and scale page
     x1 = (int)(state->getX1() + 0.5);
@@ -1649,8 +1701,8 @@ void PSOutputDev::startPage(int pageNum, GfxState *state) {
     height = y2 - y1;
     if (width > height && width > paperWidth) {
       landscape = gTrue;
-      writePS("%%%%PageOrientation: %s\n",
-             state->getCTM()[0] ? "Landscape" : "Portrait");
+      writePSFmt("%%%%PageOrientation: %s\n",
+                state->getCTM()[0] ? "Landscape" : "Portrait");
       writePS("pdfStartPage\n");
       writePS("90 rotate\n");
       tx = -x1;
@@ -1660,8 +1712,8 @@ void PSOutputDev::startPage(int pageNum, GfxState *state) {
       height = t;
     } else {
       landscape = gFalse;
-      writePS("%%%%PageOrientation: %s\n",
-             state->getCTM()[0] ? "Portrait" : "Landscape");
+      writePSFmt("%%%%PageOrientation: %s\n",
+                state->getCTM()[0] ? "Portrait" : "Landscape");
       writePS("pdfStartPage\n");
       tx = -x1;
       ty = -y1;
@@ -1673,7 +1725,7 @@ void PSOutputDev::startPage(int pageNum, GfxState *state) {
       ty += (paperHeight - height) / 2;
     }
     if (tx != 0 || ty != 0) {
-      writePS("%g %g translate\n", tx, ty);
+      writePSFmt("%g %g translate\n", tx, ty);
     }
     if (width > paperWidth || height > paperHeight) {
       xScale = (double)paperWidth / (double)width;
@@ -1683,12 +1735,12 @@ void PSOutputDev::startPage(int pageNum, GfxState *state) {
       } else {
        yScale = xScale;
       }
-      writePS("%0.4f %0.4f scale\n", xScale, xScale);
+      writePSFmt("%0.4f %0.4f scale\n", xScale, xScale);
     } else {
       xScale = yScale = 1;
     }
 
-    writePS("%%%%EndPageSetup\n");
+    writePS("%%EndPageSetup\n");
     ++seqPage;
     break;
 
@@ -1711,6 +1763,7 @@ void PSOutputDev::startPage(int pageNum, GfxState *state) {
 }
 
 void PSOutputDev::endPage() {
+
   if (mode == psModeForm) {
     writePS("pdfEndPage\n");
     writePS("end end\n");
@@ -1718,7 +1771,7 @@ void PSOutputDev::endPage() {
     writePS("end end\n");
   } else {
     writePS("showpage\n");
-    writePS("%%%%PageTrailer\n");
+    writePS("%%PageTrailer\n");
     writePS("pdfEndPage\n");
   }
 }
@@ -1733,7 +1786,7 @@ void PSOutputDev::restoreState(GfxState *state) {
 
 void PSOutputDev::updateCTM(GfxState *state, double m11, double m12,
                            double m21, double m22, double m31, double m32) {
-  writePS("[%g %g %g %g %g %g] cm\n", m11, m12, m21, m22, m31, m32);
+  writePSFmt("[%g %g %g %g %g %g] cm\n", m11, m12, m21, m22, m31, m32);
 }
 
 void PSOutputDev::updateLineDash(GfxState *state) {
@@ -1744,28 +1797,28 @@ void PSOutputDev::updateLineDash(GfxState *state) {
   state->getLineDash(&dash, &length, &start);
   writePS("[");
   for (i = 0; i < length; ++i)
-    writePS("%g%s", dash[i], (i == length-1) ? "" : " ");
-  writePS("] %g d\n", start);
+    writePSFmt("%g%s", dash[i], (i == length-1) ? "" : " ");
+  writePSFmt("] %g d\n", start);
 }
 
 void PSOutputDev::updateFlatness(GfxState *state) {
-  writePS("%d i\n", state->getFlatness());
+  writePSFmt("%d i\n", state->getFlatness());
 }
 
 void PSOutputDev::updateLineJoin(GfxState *state) {
-  writePS("%d j\n", state->getLineJoin());
+  writePSFmt("%d j\n", state->getLineJoin());
 }
 
 void PSOutputDev::updateLineCap(GfxState *state) {
-  writePS("%d J\n", state->getLineCap());
+  writePSFmt("%d J\n", state->getLineCap());
 }
 
 void PSOutputDev::updateMiterLimit(GfxState *state) {
-  writePS("%g M\n", state->getMiterLimit());
+  writePSFmt("%g M\n", state->getMiterLimit());
 }
 
 void PSOutputDev::updateLineWidth(GfxState *state) {
-  writePS("%g w\n", state->getLineWidth());
+  writePSFmt("%g w\n", state->getLineWidth());
 }
 
 void PSOutputDev::updateFillColor(GfxState *state) {
@@ -1778,23 +1831,24 @@ void PSOutputDev::updateFillColor(GfxState *state) {
   switch (level) {
   case psLevel1:
     state->getFillGray(&gray);
-    writePS("%g g\n", gray);
+    writePSFmt("%g g\n", gray);
     break;
   case psLevel1Sep:
     state->getFillCMYK(&cmyk);
-    writePS("%g %g %g %g k\n", cmyk.c, cmyk.m, cmyk.y, cmyk.k);
+    writePSFmt("%g %g %g %g k\n", cmyk.c, cmyk.m, cmyk.y, cmyk.k);
+    addProcessColor(cmyk.c, cmyk.m, cmyk.y, cmyk.k);
     break;
   case psLevel2:
   case psLevel3:
     if (state->getFillColorSpace()->getMode() == csDeviceCMYK) {
       state->getFillCMYK(&cmyk);
-      writePS("%g %g %g %g k\n", cmyk.c, cmyk.m, cmyk.y, cmyk.k);
+      writePSFmt("%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);
+       writePSFmt("%g g\n", rgb.r);
       } else {
-       writePS("%g %g %g rg\n", rgb.r, rgb.g, rgb.b);
+       writePSFmt("%g %g %g rg\n", rgb.r, rgb.g, rgb.b);
       }
     }
     break;
@@ -1804,14 +1858,14 @@ void PSOutputDev::updateFillColor(GfxState *state) {
       sepCS = (GfxSeparationColorSpace *)state->getFillColorSpace();
       color.c[0] = 1;
       sepCS->getCMYK(&color, &cmyk);
-      writePS("%g %g %g %g %g (%s) ck\n",
-             state->getFillColor()->c[0],
-             cmyk.c, cmyk.m, cmyk.y, cmyk.k,
-             sepCS->getName()->getCString());
+      writePSFmt("%g %g %g %g %g (%s) ck\n",
+                state->getFillColor()->c[0],
+                cmyk.c, cmyk.m, cmyk.y, cmyk.k,
+                sepCS->getName()->getCString());
       addCustomColor(sepCS);
     } else {
       state->getFillCMYK(&cmyk);
-      writePS("%g %g %g %g k\n", cmyk.c, cmyk.m, cmyk.y, cmyk.k);
+      writePSFmt("%g %g %g %g k\n", cmyk.c, cmyk.m, cmyk.y, cmyk.k);
       addProcessColor(cmyk.c, cmyk.m, cmyk.y, cmyk.k);
     }
     break;
@@ -1829,23 +1883,24 @@ void PSOutputDev::updateStrokeColor(GfxState *state) {
   switch (level) {
   case psLevel1:
     state->getStrokeGray(&gray);
-    writePS("%g G\n", gray);
+    writePSFmt("%g G\n", gray);
     break;
   case psLevel1Sep:
     state->getStrokeCMYK(&cmyk);
-    writePS("%g %g %g %g K\n", cmyk.c, cmyk.m, cmyk.y, cmyk.k);
+    writePSFmt("%g %g %g %g K\n", cmyk.c, cmyk.m, cmyk.y, cmyk.k);
+    addProcessColor(cmyk.c, cmyk.m, cmyk.y, cmyk.k);
     break;
   case psLevel2:
   case psLevel3:
     if (state->getStrokeColorSpace()->getMode() == csDeviceCMYK) {
       state->getStrokeCMYK(&cmyk);
-      writePS("%g %g %g %g K\n", cmyk.c, cmyk.m, cmyk.y, cmyk.k);
+      writePSFmt("%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);
+       writePSFmt("%g G\n", rgb.r);
       } else {
-       writePS("%g %g %g RG\n", rgb.r, rgb.g, rgb.b);
+       writePSFmt("%g %g %g RG\n", rgb.r, rgb.g, rgb.b);
       }
     }
     break;
@@ -1855,14 +1910,14 @@ void PSOutputDev::updateStrokeColor(GfxState *state) {
       sepCS = (GfxSeparationColorSpace *)state->getStrokeColorSpace();
       color.c[0] = 1;
       sepCS->getCMYK(&color, &cmyk);
-      writePS("%g %g %g %g %g (%s) CK\n",
-             state->getStrokeColor()->c[0],
-             cmyk.c, cmyk.m, cmyk.y, cmyk.k,
-             sepCS->getName()->getCString());
+      writePSFmt("%g %g %g %g %g (%s) CK\n",
+                state->getStrokeColor()->c[0],
+                cmyk.c, cmyk.m, cmyk.y, cmyk.k,
+                sepCS->getName()->getCString());
       addCustomColor(sepCS);
     } else {
       state->getStrokeCMYK(&cmyk);
-      writePS("%g %g %g %g K\n", cmyk.c, cmyk.m, cmyk.y, cmyk.k);
+      writePSFmt("%g %g %g %g K\n", cmyk.c, cmyk.m, cmyk.y, cmyk.k);
       addProcessColor(cmyk.c, cmyk.m, cmyk.y, cmyk.k);
     }
     break;
@@ -1905,9 +1960,9 @@ void PSOutputDev::addCustomColor(GfxSeparationColorSpace *sepCS) {
 
 void PSOutputDev::updateFont(GfxState *state) {
   if (state->getFont()) {
-    writePS("/F%d_%d %g Tf\n",
-           state->getFont()->getID()->num, state->getFont()->getID()->gen,
-           state->getFontSize());
+    writePSFmt("/F%d_%d %g Tf\n",
+              state->getFont()->getID()->num, state->getFont()->getID()->gen,
+              state->getFontSize());
   }
 }
 
@@ -1915,19 +1970,19 @@ void PSOutputDev::updateTextMat(GfxState *state) {
   double *mat;
 
   mat = state->getTextMat();
-  writePS("[%g %g %g %g %g %g] Tm\n",
-         mat[0], mat[1], mat[2], mat[3], mat[4], mat[5]);
+  writePSFmt("[%g %g %g %g %g %g] Tm\n",
+            mat[0], mat[1], mat[2], mat[3], mat[4], mat[5]);
 }
 
 void PSOutputDev::updateCharSpace(GfxState *state) {
-  writePS("%g Tc\n", state->getCharSpace());
+  writePSFmt("%g Tc\n", state->getCharSpace());
 }
 
 void PSOutputDev::updateRender(GfxState *state) {
   int rm;
 
   rm = state->getRender();
-  writePS("%d Tr\n", rm);
+  writePSFmt("%d Tr\n", rm);
   rm &= 3;
   if (rm != 0 && rm != 3) {
     t3Cacheable = gFalse;
@@ -1935,26 +1990,26 @@ void PSOutputDev::updateRender(GfxState *state) {
 }
 
 void PSOutputDev::updateRise(GfxState *state) {
-  writePS("%g Ts\n", state->getRise());
+  writePSFmt("%g Ts\n", state->getRise());
 }
 
 void PSOutputDev::updateWordSpace(GfxState *state) {
-  writePS("%g Tw\n", state->getWordSpace());
+  writePSFmt("%g Tw\n", state->getWordSpace());
 }
 
 void PSOutputDev::updateHorizScaling(GfxState *state) {
-  writePS("%g Tz\n", state->getHorizScaling());
+  writePSFmt("%g Tz\n", state->getHorizScaling());
 }
 
 void PSOutputDev::updateTextPos(GfxState *state) {
-  writePS("%g %g Td\n", state->getLineX(), state->getLineY());
+  writePSFmt("%g %g Td\n", state->getLineX(), state->getLineY());
 }
 
 void PSOutputDev::updateTextShift(GfxState *state, double shift) {
   if (state->getFont()->getWMode()) {
-    writePS("%g TJmV\n", shift);
+    writePSFmt("%g TJmV\n", shift);
   } else {
-    writePS("%g TJm\n", shift);
+    writePSFmt("%g TJm\n", shift);
   }
 }
 
@@ -2010,14 +2065,14 @@ void PSOutputDev::doPath(GfxPath *path) {
       x3 = subpath->getX(3);
       y3 = subpath->getY(3);
       if (x0 == x1 && x2 == x3 && y0 == y3 && y1 == y2) {
-       writePS("%g %g %g %g re\n",
-               x0 < x2 ? x0 : x2, y0 < y1 ? y0 : y1,
-               fabs(x2 - x0), fabs(y1 - y0));
+       writePSFmt("%g %g %g %g 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) {
-       writePS("%g %g %g %g re\n",
-               x0 < x1 ? x0 : x1, y0 < y2 ? y0 : y2,
-               fabs(x1 - x0), fabs(y2 - y0));
+       writePSFmt("%g %g %g %g re\n",
+                  x0 < x1 ? x0 : x1, y0 < y2 ? y0 : y2,
+                  fabs(x1 - x0), fabs(y2 - y0));
        return;
       }
     }
@@ -2026,16 +2081,16 @@ void PSOutputDev::doPath(GfxPath *path) {
   for (i = 0; i < n; ++i) {
     subpath = path->getSubpath(i);
     m = subpath->getNumPoints();
-    writePS("%g %g m\n", subpath->getX(0), subpath->getY(0));
+    writePSFmt("%g %g m\n", subpath->getX(0), subpath->getY(0));
     j = 1;
     while (j < m) {
       if (subpath->getCurve(j)) {
-       writePS("%g %g %g %g %g %g c\n", subpath->getX(j), subpath->getY(j),
-               subpath->getX(j+1), subpath->getY(j+1),
-               subpath->getX(j+2), subpath->getY(j+2));
+       writePSFmt("%g %g %g %g %g %g 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 {
-       writePS("%g %g l\n", subpath->getX(j), subpath->getY(j));
+       writePSFmt("%g %g l\n", subpath->getX(j), subpath->getY(j));
        ++j;
       }
     }
@@ -2134,12 +2189,12 @@ void PSOutputDev::drawString(GfxState *state, GString *s) {
     writePSString(s2);
     if (font->isCIDFont()) {
       if (wMode) {
-       writePS(" %d %g Tj16V\n", nChars, dy);
+       writePSFmt(" %d %g Tj16V\n", nChars, dy);
       } else {
-       writePS(" %d %g Tj16\n", nChars, dx);
+       writePSFmt(" %d %g Tj16\n", nChars, dx);
       }
     } else {
-      writePS(" %g Tj\n", dx);
+      writePSFmt(" %g Tj\n", dx);
     }
   }
   if (font->isCIDFont()) {
@@ -2195,13 +2250,13 @@ void PSOutputDev::doImageL1(GfxImageColorMap *colorMap,
 
   // width, height, matrix, bits per component
   if (colorMap) {
-    writePS("%d %d 8 [%d 0 0 %d 0 %d] pdfIm1\n",
-           width, height,
-           width, -height, height);
+    writePSFmt("%d %d 8 [%d 0 0 %d 0 %d] pdfIm1\n",
+              width, height,
+              width, -height, height);
   } else {
-    writePS("%d %d %s [%d 0 0 %d 0 %d] pdfImM1\n",
-           width, height, invert ? "true" : "false",
-           width, -height, height);
+    writePSFmt("%d %d %s [%d 0 0 %d 0 %d] pdfImM1\n",
+              width, height, invert ? "true" : "false",
+              width, -height, height);
   }
 
   // image
@@ -2220,7 +2275,7 @@ void PSOutputDev::doImageL1(GfxImageColorMap *colorMap,
       for (x = 0; x < width; ++x) {
        imgStr->getPixel(pixBuf);
        colorMap->getGray(pixBuf, &gray);
-       writePS("%02x", (int)(gray * 255 + 0.5));
+       writePSFmt("%02x", (int)(gray * 255 + 0.5));
        if (++i == 32) {
          writePSChar('\n');
          i = 0;
@@ -2238,7 +2293,7 @@ void PSOutputDev::doImageL1(GfxImageColorMap *colorMap,
     i = 0;
     for (y = 0; y < height; ++y) {
       for (x = 0; x < width; x += 8) {
-       writePS("%02x", str->getChar() & 0xff);
+       writePSFmt("%02x", str->getChar() & 0xff);
        if (++i == 32) {
          writePSChar('\n');
          i = 0;
@@ -2262,9 +2317,9 @@ void PSOutputDev::doImageL1Sep(GfxImageColorMap *colorMap,
   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);
+  writePSFmt("%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);
@@ -2286,12 +2341,13 @@ void PSOutputDev::doImageL1Sep(GfxImageColorMap *colorMap,
       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);
+      addProcessColor(cmyk.c, cmyk.m, cmyk.y, cmyk.k);
     }
 
     // write one line of each color component
     for (comp = 0; comp < 4; ++comp) {
       for (x = 0; x < width; ++x) {
-       writePS("%02x", lineBuf[4*x + comp]);
+       writePSFmt("%02x", lineBuf[4*x + comp]);
        if (++i == 32) {
          writePSChar('\n');
          i = 0;
@@ -2318,7 +2374,7 @@ void PSOutputDev::doImageL2(Object *ref, GfxImageColorMap *colorMap,
   GfxColor color;
   GfxCMYK cmyk;
   int c;
-  int i;
+  int line, col, i;
 
   // color space
   if (colorMap) {
@@ -2326,20 +2382,72 @@ void PSOutputDev::doImageL2(Object *ref, GfxImageColorMap *colorMap,
     writePS(" setcolorspace\n");
   }
 
-  // set up to use the array created by setupImages()
-  if ((mode == psModeForm || inType3Char) && !inlineImg) {
-    writePS("ImData_%d_%d 0\n", ref->getRefNum(), ref->getRefGen());
+  // set up the image data
+  if (mode == psModeForm || inType3Char) {
+    if (inlineImg) {
+      // create an array
+      str = new FixedLengthEncoder(str, len);
+      if (globalParams->getPSASCIIHex()) {
+       str = new ASCIIHexEncoder(str);
+      } else {
+       str = new ASCII85Encoder(str);
+      }
+      str->reset();
+      line = col = 0;
+      writePS("[<~");
+      do {
+       do {
+         c = str->getChar();
+       } while (c == '\n' || c == '\r');
+       if (c == '~' || c == EOF) {
+         break;
+       }
+       if (c == 'z') {
+         writePSChar(c);
+         ++col;
+       } else {
+         writePSChar(c);
+         ++col;
+         for (i = 1; i <= 4; ++i) {
+           do {
+             c = str->getChar();
+           } while (c == '\n' || c == '\r');
+           if (c == '~' || 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("~>\n");
+         ++line;
+         writePSFmt("<~", line);
+         col = 0;
+       }
+      } while (c != '~' && c != EOF);
+      writePS("~>]\n");
+      writePS("0\n");
+      delete str;
+    } else {
+      // set up to use the array already created by setupImages()
+      writePSFmt("ImData_%d_%d 0\n", ref->getRefNum(), ref->getRefGen());
+    }
   }
 
   // image dictionary
   writePS("<<\n  /ImageType 1\n");
 
   // width, height, matrix, bits per component
-  writePS("  /Width %d\n", width);
-  writePS("  /Height %d\n", height);
-  writePS("  /ImageMatrix [%d 0 0 %d 0 %d]\n", width, -height, height);
-  writePS("  /BitsPerComponent %d\n",
-         colorMap ? colorMap->getBits() : 1);
+  writePSFmt("  /Width %d\n", width);
+  writePSFmt("  /Height %d\n", height);
+  writePSFmt("  /ImageMatrix [%d 0 0 %d 0 %d]\n", width, -height, height);
+  writePSFmt("  /BitsPerComponent %d\n",
+            colorMap ? colorMap->getBits() : 1);
 
   // decode 
   if (colorMap) {
@@ -2347,55 +2455,33 @@ void PSOutputDev::doImageL2(Object *ref, GfxImageColorMap *colorMap,
     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);
+      writePSFmt("%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));
+       writePSFmt("%g %g", colorMap->getDecodeLow(i),
+                  colorMap->getDecodeHigh(i));
       }
     }
     writePS("]\n");
   } else {
-    writePS("  /Decode [%d %d]\n", invert ? 1 : 0, invert ? 0 : 1);
+    writePSFmt("  /Decode [%d %d]\n", invert ? 1 : 0, invert ? 0 : 1);
   }
 
   if (mode == psModeForm || inType3Char) {
 
-    if (inlineImg) {
-
-      // data source
-      writePS("  /DataSource <~\n");
-
-      // write image data stream, using ASCII85 encode filter
-      str = new FixedLengthEncoder(str, len);
-      if (globalParams->getPSASCIIHex()) {
-       str = new ASCIIHexEncoder(str);
-      } else {
-       str = new ASCII85Encoder(str);
-      }
-      str->reset();
-      while ((c = str->getChar()) != EOF) {
-       writePSChar(c);
-      }
-      writePSChar('\n');
-      delete str;
-
-    } else {
-      writePS("  /DataSource { 2 copy get exch 1 add exch }\n");
-    }
+    // data source
+    writePS("  /DataSource { 2 copy get exch 1 add exch }\n");
 
     // end of image dictionary
-    writePS(">>\n%s\n", colorMap ? "image" : "imagemask");
+    writePSFmt(">>\n%s\n", colorMap ? "image" : "imagemask");
 
     // get rid of the array and index
-    if (!inlineImg) {
-      writePS("pop pop\n");
-    }
+    writePS("pop pop\n");
 
   } else {
 
@@ -2412,14 +2498,14 @@ void PSOutputDev::doImageL2(Object *ref, GfxImageColorMap *colorMap,
       useCompressed = gTrue;
     }
     if (useASCII) {
-      writePS("    /ASCII%sDecode filter\n",
-             globalParams->getPSASCIIHex() ? "Hex" : "85");
+      writePSFmt("    /ASCII%sDecode filter\n",
+                globalParams->getPSASCIIHex() ? "Hex" : "85");
     }
     if (useRLE) {
       writePS("    /RunLengthDecode filter\n");
     }
     if (useCompressed) {
-      writePS("%s", s->getCString());
+      writePS(s->getCString());
     }
     if (s) {
       delete s;
@@ -2465,7 +2551,7 @@ void PSOutputDev::doImageL2(Object *ref, GfxImageColorMap *colorMap,
       // +6/7 for "pdfIm\n" / "pdfImM\n"
       // +8 for newline + trailer
       n += colorMap ? 14 : 15;
-      writePS("%%%%BeginData: %d Hex Bytes\n", n);
+      writePSFmt("%%%%BeginData: %d Hex Bytes\n", n);
     }
 #endif
     if ((level == psLevel2Sep || level == psLevel3Sep) && colorMap &&
@@ -2473,10 +2559,11 @@ void PSOutputDev::doImageL2(Object *ref, GfxImageColorMap *colorMap,
       color.c[0] = 1;
       sepCS = (GfxSeparationColorSpace *)colorMap->getColorSpace();
       sepCS->getCMYK(&color, &cmyk);
-      writePS("%g %g %g %g (%s) pdfImSep\n",
-             cmyk.c, cmyk.m, cmyk.y, cmyk.k, sepCS->getName()->getCString());
+      writePSFmt("%g %g %g %g (%s) pdfImSep\n",
+                cmyk.c, cmyk.m, cmyk.y, cmyk.k,
+                sepCS->getName()->getCString());
     } else {
-      writePS("%s\n", colorMap ? "pdfIm" : "pdfImM");
+      writePSFmt("%s\n", colorMap ? "pdfIm" : "pdfImM");
     }
 
     // copy the stream data
@@ -2488,10 +2575,10 @@ void PSOutputDev::doImageL2(Object *ref, GfxImageColorMap *colorMap,
 
     // add newline and trailer to the end
     writePSChar('\n');
-    writePS("%%-EOD-\n");
+    writePS("%-EOD-\n");
 #if OPI_SUPPORT
     if (opi13Nest) {
-      writePS("%%%%EndData\n");
+      writePS("%%EndData\n");
     }
 #endif
 
@@ -2508,11 +2595,14 @@ void PSOutputDev::dumpColorSpaceL2(GfxColorSpace *colorSpace) {
   GfxLabColorSpace *labCS;
   GfxIndexedColorSpace *indexedCS;
   GfxSeparationColorSpace *separationCS;
-  Guchar *lookup;
+  GfxColorSpace *baseCS;
+  Guchar *lookup, *p;
   double x[gfxColorMaxComps], y[gfxColorMaxComps];
   GfxColor color;
   GfxCMYK cmyk;
-  int n, numComps;
+  Function *func;
+  int n, numComps, numAltComps;
+  int byte;
   int i, j, k;
 
   switch (colorSpace->getMode()) {
@@ -2525,16 +2615,16 @@ void PSOutputDev::dumpColorSpaceL2(GfxColorSpace *colorSpace) {
   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());
+    writePSFmt(" /DecodeA {%g exp} bind\n", calGrayCS->getGamma());
+    writePSFmt(" /MatrixA [%g %g %g]\n",
+              calGrayCS->getWhiteX(), calGrayCS->getWhiteY(),
+              calGrayCS->getWhiteZ());
+    writePSFmt(" /WhitePoint [%g %g %g]\n",
+              calGrayCS->getWhiteX(), calGrayCS->getWhiteY(),
+              calGrayCS->getWhiteZ());
+    writePSFmt(" /BlackPoint [%g %g %g]\n",
+              calGrayCS->getBlackX(), calGrayCS->getBlackY(),
+              calGrayCS->getBlackZ());
     writePS(">>]");
     processColors |= psProcessBlack;
     break;
@@ -2547,21 +2637,21 @@ void PSOutputDev::dumpColorSpaceL2(GfxColorSpace *colorSpace) {
   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());
+    writePSFmt(" /DecodeABC [{%g exp} bind {%g exp} bind {%g exp} bind]\n",
+              calRGBCS->getGammaR(), calRGBCS->getGammaG(),
+              calRGBCS->getGammaB());
+    writePSFmt(" /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]);
+    writePSFmt(" /WhitePoint [%g %g %g]\n",
+              calRGBCS->getWhiteX(), calRGBCS->getWhiteY(),
+              calRGBCS->getWhiteZ());
+    writePSFmt(" /BlackPoint [%g %g %g]\n",
+              calRGBCS->getBlackX(), calRGBCS->getBlackY(),
+              calRGBCS->getBlackZ());
     writePS(">>]");
     processColors |= psProcessCMYK;
     break;
@@ -2574,25 +2664,25 @@ void PSOutputDev::dumpColorSpaceL2(GfxColorSpace *colorSpace) {
   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());
+    writePSFmt(" /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());
+    writePSFmt("     {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());
+    writePSFmt("     {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());
+    writePSFmt("     {4 29 div sub 108 841 div mul } ifelse %g mul} bind]\n",
+              labCS->getWhiteZ());
+    writePSFmt(" /WhitePoint [%g %g %g]\n",
+              labCS->getWhiteX(), labCS->getWhiteY(), labCS->getWhiteZ());
+    writePSFmt(" /BlackPoint [%g %g %g]\n",
+              labCS->getBlackX(), labCS->getBlackY(), labCS->getBlackZ());
     writePS(">>]");
     processColors |= psProcessCMYK;
     break;
@@ -2603,23 +2693,52 @@ void PSOutputDev::dumpColorSpaceL2(GfxColorSpace *colorSpace) {
 
   case csIndexed:
     indexedCS = (GfxIndexedColorSpace *)colorSpace;
+    baseCS = indexedCS->getBase();
     writePS("[/Indexed ");
-    dumpColorSpaceL2(indexedCS->getBase());
+    dumpColorSpaceL2(baseCS);
     n = indexedCS->getIndexHigh();
-    numComps = indexedCS->getBase()->getNComps();
+    numComps = baseCS->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]);
+    writePSFmt(" %d <\n", n);
+    if (baseCS->getMode() == csDeviceN) {
+      func = ((GfxDeviceNColorSpace *)baseCS)->getTintTransformFunc();
+      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] = *p++ / 255.0;
+         }
+         func->transform(x, y);
+         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("%02x", byte);
+         }
+         color.c[0] = j;
+         indexedCS->getCMYK(&color, &cmyk);
+         addProcessColor(cmyk.c, cmyk.m, cmyk.y, cmyk.k);
        }
-       color.c[0] = j;
-       indexedCS->getCMYK(&color, &cmyk);
-       addProcessColor(cmyk.c, cmyk.m, cmyk.y, 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("%02x", lookup[j * numComps + k]);
+         }
+         color.c[0] = j;
+         indexedCS->getCMYK(&color, &cmyk);
+         addProcessColor(cmyk.c, cmyk.m, cmyk.y, cmyk.k);
+       }
+       writePS("\n");
       }
-      writePS("\n");
     }
     writePS(">]");
     break;
@@ -2639,7 +2758,7 @@ void PSOutputDev::dumpColorSpaceL2(GfxColorSpace *colorSpace) {
        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));
+         writePSFmt("%02x", (int)(255 * y[k] + 0.5));
        }
       }
       writePS("\n");
@@ -2686,20 +2805,20 @@ void PSOutputDev::opiBegin20(GfxState *state, Dict *dict) {
   int w, h;
   int i;
 
-  writePS("%%%%BeginOPI: 2.0\n");
-  writePS("%%%%Distilled\n");
+  writePS("%%BeginOPI: 2.0\n");
+  writePS("%%Distilled\n");
 
   dict->lookup("F", &obj1);
   if (getFileSpec(&obj1, &obj2)) {
-    writePS("%%%%ImageFileName: %s\n",
-           obj2.getString()->getCString());
+    writePSFmt("%%%%ImageFileName: %s\n",
+              obj2.getString()->getCString());
     obj2.free();
   }
   obj1.free();
 
   dict->lookup("MainImage", &obj1);
   if (obj1.isString()) {
-    writePS("%%%%MainImage: %s\n", obj1.getString()->getCString());
+    writePSFmt("%%%%MainImage: %s\n", obj1.getString()->getCString());
   }
   obj1.free();
 
@@ -2714,7 +2833,7 @@ void PSOutputDev::opiBegin20(GfxState *state, Dict *dict) {
     obj1.arrayGet(1, &obj2);
     height = obj2.getNum();
     obj2.free();
-    writePS("%%%%ImageDimensions: %g %g\n", width, height);
+    writePSFmt("%%%%ImageDimensions: %g %g\n", width, height);
   }
   obj1.free();
 
@@ -2732,31 +2851,31 @@ void PSOutputDev::opiBegin20(GfxState *state, Dict *dict) {
     obj1.arrayGet(3, &obj2);
     bottom = obj2.getNum();
     obj2.free();
-    writePS("%%%%ImageCropRect: %g %g %g %g\n", left, top, right, bottom);
+    writePSFmt("%%%%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");
+    writePSFmt("%%%%ImageOverprint: %s\n", obj1.getBool() ? "true" : "false");
   }
   obj1.free();
 
   dict->lookup("Inks", &obj1);
   if (obj1.isName()) {
-    writePS("%%%%ImageInks: %s\n", obj1.getName());
+    writePSFmt("%%%%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);
+      writePSFmt("%%%%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());
+         writePSFmt(" %g", obj4.getNum());
        }
        obj3.free();
        obj4.free();
@@ -2769,7 +2888,7 @@ void PSOutputDev::opiBegin20(GfxState *state, Dict *dict) {
 
   writePS("gsave\n");
 
-  writePS("%%%%BeginIncludedImage\n");
+  writePS("%%BeginIncludedImage\n");
 
   dict->lookup("IncludedImageDimensions", &obj1);
   if (obj1.isArray() && obj1.arrayGetLength() == 2) {
@@ -2779,13 +2898,13 @@ void PSOutputDev::opiBegin20(GfxState *state, Dict *dict) {
     obj1.arrayGet(1, &obj2);
     h = obj2.getInt();
     obj2.free();
-    writePS("%%%%IncludedImageDimensions: %d %d\n", w, h);
+    writePSFmt("%%%%IncludedImageDimensions: %d %d\n", w, h);
   }
   obj1.free();
 
   dict->lookup("IncludedImageQuality", &obj1);
   if (obj1.isNum()) {
-    writePS("%%%%IncludedImageQuality: %g\n", obj1.getNum());
+    writePSFmt("%%%%IncludedImageQuality: %g\n", obj1.getNum());
   }
   obj1.free();
 
@@ -2807,8 +2926,8 @@ void PSOutputDev::opiBegin13(GfxState *state, Dict *dict) {
 
   dict->lookup("F", &obj1);
   if (getFileSpec(&obj1, &obj2)) {
-    writePS("%%ALDImageFileName: %s\n",
-           obj2.getString()->getCString());
+    writePSFmt("%%ALDImageFileName: %s\n",
+              obj2.getString()->getCString());
     obj2.free();
   }
   obj1.free();
@@ -2827,7 +2946,7 @@ void PSOutputDev::opiBegin13(GfxState *state, Dict *dict) {
     obj1.arrayGet(3, &obj2);
     bottom = obj2.getInt();
     obj2.free();
-    writePS("%%ALDImageCropRect: %d %d %d %d\n", left, top, right, bottom);
+    writePSFmt("%%ALDImageCropRect: %d %d %d %d\n", left, top, right, bottom);
   }
   obj1.free();
 
@@ -2847,7 +2966,7 @@ void PSOutputDev::opiBegin13(GfxState *state, Dict *dict) {
     obj2.free();
     obj1.arrayGet(4, &obj2);
     if (obj2.isString()) {
-      writePS("%%ALDImageColor: %g %g %g %g ", c, m, y, k);
+      writePSFmt("%%ALDImageColor: %g %g %g %g ", c, m, y, k);
       writePSString(obj2.getString());
       writePS("\n");
     }
@@ -2857,7 +2976,7 @@ void PSOutputDev::opiBegin13(GfxState *state, Dict *dict) {
 
   dict->lookup("ColorType", &obj1);
   if (obj1.isName()) {
-    writePS("%%ALDImageColorType: %s\n", obj1.getName());
+    writePSFmt("%%ALDImageColorType: %s\n", obj1.getName());
   }
   obj1.free();
 
@@ -2878,20 +2997,20 @@ void PSOutputDev::opiBegin13(GfxState *state, Dict *dict) {
     obj1.arrayGet(3, &obj2);
     lry = obj2.getNum();
     obj2.free();
-    writePS("%%ALDImageCropFixed: %g %g %g %g\n", ulx, uly, lrx, lry);
+    writePSFmt("%%ALDImageCropFixed: %g %g %g %g\n", ulx, uly, lrx, lry);
   }
   obj1.free();
 
   dict->lookup("GrayMap", &obj1);
   if (obj1.isArray()) {
-    writePS("%%ALDImageGrayMap:");
+    writePS("%ALDImageGrayMap:");
     for (i = 0; i < obj1.arrayGetLength(); i += 16) {
       if (i > 0) {
-       writePS("\n%%%%+");
+       writePS("\n%%+");
       }
       for (j = 0; j < 16 && i+j < obj1.arrayGetLength(); ++j) {
        obj1.arrayGet(i+j, &obj2);
-       writePS(" %d", obj2.getInt());
+       writePSFmt(" %d", obj2.getInt());
        obj2.free();
       }
     }
@@ -2901,7 +3020,7 @@ void PSOutputDev::opiBegin13(GfxState *state, Dict *dict) {
 
   dict->lookup("ID", &obj1);
   if (obj1.isString()) {
-    writePS("%%ALDImageID: %s\n", obj1.getString()->getCString());
+    writePSFmt("%%ALDImageID: %s\n", obj1.getString()->getCString());
   }
   obj1.free();
 
@@ -2913,13 +3032,13 @@ void PSOutputDev::opiBegin13(GfxState *state, Dict *dict) {
     obj1.arrayGet(1, &obj2);
     bits = obj2.getInt();
     obj2.free();
-    writePS("%%ALDImageType: %d %d\n", samples, bits);
+    writePSFmt("%%ALDImageType: %d %d\n", samples, bits);
   }
   obj1.free();
 
   dict->lookup("Overprint", &obj1);
   if (obj1.isBool()) {
-    writePS("%%ALDImageOverprint: %s\n", obj1.getBool() ? "true" : "false");
+    writePSFmt("%%ALDImageOverprint: %s\n", obj1.getBool() ? "true" : "false");
   }
   obj1.free();
 
@@ -2953,8 +3072,8 @@ void PSOutputDev::opiBegin13(GfxState *state, Dict *dict) {
     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);
+    writePSFmt("%%ALDImagePosition: %g %g %g %g %g %g %g %g\n",
+              tllx, tlly, tulx, tuly, turx, tury, tlrx, tlry);
     obj2.free();
   }
   obj1.free();
@@ -2967,7 +3086,7 @@ void PSOutputDev::opiBegin13(GfxState *state, Dict *dict) {
     obj1.arrayGet(1, &obj2);
     vert = obj2.getNum();
     obj2.free();
-    writePS("%%ALDImageResoution: %g %g\n", horiz, vert);
+    writePSFmt("%%ALDImageResoution: %g %g\n", horiz, vert);
     obj2.free();
   }
   obj1.free();
@@ -2980,7 +3099,7 @@ void PSOutputDev::opiBegin13(GfxState *state, Dict *dict) {
     obj1.arrayGet(1, &obj2);
     height = obj2.getInt();
     obj2.free();
-    writePS("%%ALDImageDimensions: %d %d\n", width, height);
+    writePSFmt("%%ALDImageDimensions: %d %d\n", width, height);
   }
   obj1.free();
 
@@ -2989,17 +3108,17 @@ void PSOutputDev::opiBegin13(GfxState *state, Dict *dict) {
 
   dict->lookup("Tint", &obj1);
   if (obj1.isNum()) {
-    writePS("%%ALDImageTint: %g\n", obj1.getNum());
+    writePSFmt("%%ALDImageTint: %g\n", obj1.getNum());
   }
   obj1.free();
 
   dict->lookup("Transparency", &obj1);
   if (obj1.isBool()) {
-    writePS("%%ALDImageTransparency: %s\n", obj1.getBool() ? "true" : "false");
+    writePSFmt("%%ALDImageTransparency: %s\n", obj1.getBool() ? "true" : "false");
   }
   obj1.free();
 
-  writePS("%%%%BeginObject: image\n");
+  writePS("%%BeginObject: image\n");
   writePS("opiMatrix2 setmatrix\n");
   ++opi13Nest;
 }
@@ -3029,8 +3148,8 @@ void PSOutputDev::opiEnd(GfxState *state, Dict *opiDict) {
   if (globalParams->getPSOPI()) {
     opiDict->lookup("2.0", &dict);
     if (dict.isDict()) {
-      writePS("%%%%EndIncludedImage\n");
-      writePS("%%%%EndOPI\n");
+      writePS("%%EndIncludedImage\n");
+      writePS("%%EndOPI\n");
       writePS("grestore\n");
       --opi20Nest;
       dict.free();
@@ -3038,7 +3157,7 @@ void PSOutputDev::opiEnd(GfxState *state, Dict *opiDict) {
       dict.free();
       opiDict->lookup("1.3", &dict);
       if (dict.isDict()) {
-       writePS("%%%%EndObject\n");
+       writePS("%%EndObject\n");
        writePS("restore\n");
        --opi13Nest;
       }
@@ -3079,7 +3198,7 @@ GBool PSOutputDev::getFileSpec(Object *fileSpec, Object *fileName) {
 #endif // OPI_SUPPORT
 
 void PSOutputDev::type3D0(GfxState *state, double wx, double wy) {
-  writePS("%g %g setcharwidth\n", wx, wy);
+  writePSFmt("%g %g setcharwidth\n", wx, wy);
   writePS("q\n");
 }
 
@@ -3112,18 +3231,34 @@ void PSOutputDev::psXObject(Stream *psStream, Stream *level1Stream) {
   str->close();
 }
 
-void PSOutputDev::writePS(const char *fmt, ...) {
+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;
   char buf[512];
 
   va_start(args, fmt);
+  vsprintf(buf, fmt, args);
+  va_end(args);
   if (t3String) {
-    vsprintf(buf, fmt, args);
     t3String->append(buf);
   } else {
-    vfprintf(f, fmt, args);
+    (*outputFunc)(outputStream, buf, strlen(buf));
   }
-  va_end(args);
 }
 
 void PSOutputDev::writePSString(GString *s) {
@@ -3137,11 +3272,11 @@ void PSOutputDev::writePSString(GString *s) {
       writePSChar('\\');
       writePSChar((char)*p);
     } else if (*p < 0x20 || *p >= 0x80) {
+      sprintf(buf, "\\%03o", *p);
       if (t3String) {
-       sprintf(buf, "\\%03o", *p);
        t3String->append(buf);
       } else {
-       fprintf(f, "\\%03o", *p);
+       (*outputFunc)(outputStream, buf, strlen(buf));
       }
     } else {
       writePSChar((char)*p);
@@ -3150,11 +3285,20 @@ void PSOutputDev::writePSString(GString *s) {
   writePSChar(')');
 }
 
-void PSOutputDev::writePSChar(char c) {
-  if (t3String) {
-    t3String->append(c);
-  } else {
-    fputc(c, f);
+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("#%02x", c & 0xff);
+    } else {
+      writePSChar(c);
+    }
   }
 }
 
index ba208dea439fcc4e4c00938e00ce6bb01dbe9bd9..fbfc3a5c3d536ab9c0d0caca07f6117ba0fd58d4 100644 (file)
@@ -9,7 +9,9 @@
 #ifndef PSOUTPUTDEV_H
 #define PSOUTPUTDEV_H
 
-#ifdef __GNUC__
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
 #pragma interface
 #endif
 
@@ -39,9 +41,12 @@ enum PSOutMode {
 enum PSFileType {
   psFile,                      // write to file
   psPipe,                      // write to pipe
-  psStdout                     // write to stdout
+  psStdout,                    // write to stdout
+  psGeneric                    // write to a generic stream
 };
 
+typedef void (*PSOutputFunc)(void *stream, char *data, int len);
+
 class PSOutputDev: public OutputDev {
 public:
 
@@ -49,6 +54,11 @@ public:
   PSOutputDev(char *fileName, XRef *xrefA, Catalog *catalog,
              int firstPage, int lastPage, PSOutMode modeA);
 
+  // Open a PSOutputDev that will write to a generic stream.
+  PSOutputDev(PSOutputFunc outputFuncA, void *outputStreamA,
+             XRef *xrefA, Catalog *catalog,
+             int firstPage, int lastPage, PSOutMode modeA);
+
   // Destructor -- writes the trailer and closes the file.
   virtual ~PSOutputDev();
 
@@ -137,8 +147,12 @@ public:
   //----- PostScript XObjects
   virtual void psXObject(Stream *psStream, Stream *level1Stream);
 
+
 private:
 
+  void init(PSOutputFunc outputFuncA, void *outputStreamA,
+           PSFileType fileTypeA, XRef *xrefA, Catalog *catalog,
+           int firstPage, int lastPage, PSOutMode modeA);
   void setupResources(Dict *resDict);
   void setupFonts(Dict *resDict);
   void setupFont(GfxFont *font, Dict *parentResDict);
@@ -172,9 +186,11 @@ private:
                    double *x1, double *y1);
   GBool getFileSpec(Object *fileSpec, Object *fileName);
 #endif
-  void writePS(const char *fmt, ...);
-  void writePSString(GString *s);
   void writePSChar(char c);
+  void writePS(char *s);
+  void writePSFmt(const char *fmt, ...);
+  void writePSString(GString *s);
+  void writePSName(char *s);
   GString *filterPSName(GString *name);
 
   PSLevel level;               // PostScript level (1, 2, separation)
@@ -182,7 +198,8 @@ private:
   int paperWidth;              // width of paper, in pts
   int paperHeight;             // height of paper, in pts
 
-  FILE *f;                     // PostScript file
+  PSOutputFunc outputFunc;
+  void *outputStream;
   PSFileType fileType;         // file / pipe / stdout
   int seqPage;                 // current sequential page number
 
@@ -223,6 +240,7 @@ private:
 #endif
 
   GBool ok;                    // set up ok?
+
 };
 
 #endif
index c601857730d131a9e384083ed2f7a0fbb15c26ea..72b4a8e623aa7f8be3f908f211312b5424e22746 100644 (file)
@@ -6,12 +6,14 @@
 //
 //========================================================================
 
-#ifdef __GNUC__
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
 #pragma implementation
 #endif
 
-#include <aconf.h>
 #include <stddef.h>
+#include "GlobalParams.h"
 #include "Object.h"
 #include "Array.h"
 #include "Dict.h"
@@ -170,13 +172,10 @@ GBool PageAttrs::readBox(Dict *dict, char *key, PDFRectangle *box) {
 // Page
 //------------------------------------------------------------------------
 
-Page::Page(XRef *xrefA, int numA, Dict *pageDict, PageAttrs *attrsA,
-          GBool printCommandsA) {
-
+Page::Page(XRef *xrefA, int numA, Dict *pageDict, PageAttrs *attrsA) {
   ok = gTrue;
   xref = xrefA;
   num = numA;
-  printCommands = printCommandsA;
 
   // get attributes
   attrs = attrsA;
@@ -216,21 +215,87 @@ Page::~Page() {
 }
 
 void Page::display(OutputDev *out, double dpi, int rotate,
-                  Links *links, Catalog *catalog) {
+                  Links *links, Catalog *catalog,
+                  GBool (*abortCheckCbk)(void *data),
+                  void *abortCheckCbkData) {
+  displaySlice(out, dpi, rotate, -1, -1, -1, -1, links, catalog,
+              abortCheckCbk, abortCheckCbkData);
+}
+
+void Page::displaySlice(OutputDev *out, double dpi, int rotate,
+                       int sliceX, int sliceY, int sliceW, int sliceH,
+                       Links *links, Catalog *catalog,
+                       GBool (*abortCheckCbk)(void *data),
+                       void *abortCheckCbkData) {
 #ifndef PDF_PARSER_ONLY
-  PDFRectangle *box, *cropBox;
+  PDFRectangle *mediaBox, *cropBox;
+  PDFRectangle box;
   Gfx *gfx;
   Object obj;
   Link *link;
-  int i;
   Annots *annotList;
+  double k;
+  int i;
 
-  box = getBox();
+  rotate += getRotate();
+  if (rotate >= 360) {
+    rotate -= 360;
+  } else if (rotate < 0) {
+    rotate += 360;
+  }
+
+  mediaBox = getBox();
+  if (sliceW >= 0 && sliceH >= 0) {
+    k = 72.0 / dpi;
+    if (rotate == 90) {
+      if (out->upsideDown()) {
+       box.x1 = mediaBox->x1 + k * sliceY;
+       box.x2 = mediaBox->x1 + k * (sliceY + sliceH);
+      } else {
+       box.x1 = mediaBox->x2 - k * (sliceY + sliceH);
+       box.x2 = mediaBox->x2 - k * sliceY;
+      }
+      box.y1 = mediaBox->y1 + k * sliceX;
+      box.y2 = mediaBox->y1 + k * (sliceX + sliceW);
+    } else if (rotate == 180) {
+      box.x1 = mediaBox->x2 - k * (sliceX + sliceW);
+      box.x2 = mediaBox->x2 - k * sliceX;
+      if (out->upsideDown()) {
+       box.y1 = mediaBox->y1 + k * sliceY;
+       box.y2 = mediaBox->y1 + k * (sliceY + sliceH);
+      } else {
+       box.y1 = mediaBox->y2 - k * (sliceY + sliceH);
+       box.y2 = mediaBox->y2 - k * sliceY;
+      }
+    } else if (rotate == 270) {
+      if (out->upsideDown()) {
+       box.x1 = mediaBox->x2 - k * (sliceY + sliceH);
+       box.x2 = mediaBox->x2 - k * sliceY;
+      } else {
+       box.x1 = mediaBox->x1 + k * sliceY;
+       box.x2 = mediaBox->x1 + k * (sliceY + sliceH);
+      }
+      box.y1 = mediaBox->y2 - k * (sliceX + sliceW);
+      box.y2 = mediaBox->y2 - k * sliceX;
+    } else {
+      box.x1 = mediaBox->x1 + k * sliceX;
+      box.x2 = mediaBox->x1 + k * (sliceX + sliceW);
+      if (out->upsideDown()) {
+       box.y1 = mediaBox->y2 - k * (sliceY + sliceH);
+       box.y2 = mediaBox->y2 - k * sliceY;
+      } else {
+       box.y1 = mediaBox->y1 + k * sliceY;
+       box.y2 = mediaBox->y1 + k * (sliceY + sliceH);
+      }
+    }
+  } else {
+    box = *mediaBox;
+  }
   cropBox = getCropBox();
 
-  if (printCommands) {
+  if (globalParams->getPrintCommands()) {
     printf("***** MediaBox = ll:%g,%g ur:%g,%g\n",
-          box->x1, box->y1, box->x2, box->y2);
+          box.x1, box.y1, box.x2, box.y2);
     if (isCropped()) {
       printf("***** CropBox = ll:%g,%g ur:%g,%g\n",
             cropBox->x1, cropBox->y1, cropBox->x2, cropBox->y2);
@@ -238,14 +303,9 @@ void Page::display(OutputDev *out, double dpi, int rotate,
     printf("***** Rotate = %d\n", attrs->getRotate());
   }
 
-  rotate += getRotate();
-  if (rotate >= 360) {
-    rotate -= 360;
-  } else if (rotate < 0) {
-    rotate += 360;
-  }
   gfx = new Gfx(xref, out, num, attrs->getResourceDict(),
-               dpi, box, isCropped(), cropBox, rotate, printCommands);
+               dpi, &box, isCropped(), cropBox, rotate,
+               abortCheckCbk, abortCheckCbkData);
   contents.fetch(xref, &obj);
   if (!obj.isNull()) {
     gfx->display(&obj);
@@ -266,7 +326,7 @@ void Page::display(OutputDev *out, double dpi, int rotate,
   annotList = new Annots(xref, annots.fetch(xref, &obj));
   obj.free();
   if (annotList->getNumAnnots() > 0) {
-    if (printCommands) {
+    if (globalParams->getPrintCommands()) {
       printf("***** Annotations\n");
     }
     for (i = 0; i < annotList->getNumAnnots(); ++i) {
index 7207b20687cad1e3e874e84695f2043afbf72875..911c5b0e5eb4bdc45ada3eb11f7c053cd504ab5d 100644 (file)
@@ -9,7 +9,9 @@
 #ifndef PAGE_H
 #define PAGE_H
 
-#ifdef __GNUC__
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
 #pragma interface
 #endif
 
@@ -23,8 +25,14 @@ class Catalog;
 
 //------------------------------------------------------------------------
 
-struct PDFRectangle {
+class PDFRectangle {
+public:
   double x1, y1, x2, y2;
+
+  PDFRectangle() { x1 = y1 = x2 = y2 = 0; }
+  PDFRectangle(double x1A, double y1A, double x2A, double y2A)
+    { x1 = x1A; y1 = y1A; x2 = x2A; y2 = y2A; }
+  GBool isValid() { return x1 != 0 || y1 != 0 || x2 != 0 || y2 != 0; }
 };
 
 //------------------------------------------------------------------------
@@ -97,8 +105,7 @@ class Page {
 public:
 
   // Constructor.
-  Page(XRef *xrefA, int numA, Dict *pageDict, PageAttrs *attrsA,
-       GBool printCommandsA);
+  Page(XRef *xrefA, int numA, Dict *pageDict, PageAttrs *attrsA);
 
   // Destructor.
   ~Page();
@@ -135,7 +142,16 @@ public:
 
   // Display a page.
   void display(OutputDev *out, double dpi, int rotate,
-              Links *links, Catalog *catalog);
+              Links *links, Catalog *catalog,
+              GBool (*abortCheckCbk)(void *data) = NULL,
+              void *abortCheckCbkData = NULL);
+
+  // Display part of a page.
+  void displaySlice(OutputDev *out, double dpi, int rotate,
+                   int sliceX, int sliceY, int sliceW, int sliceH,
+                   Links *links, Catalog *catalog,
+                   GBool (*abortCheckCbk)(void *data) = NULL,
+                   void *abortCheckCbkData = NULL);
 
 private:
 
@@ -144,7 +160,6 @@ private:
   PageAttrs *attrs;            // page attributes
   Object annots;               // annotations array
   Object contents;             // page contents
-  GBool printCommands;         // print the drawing commands (for debugging)
   GBool ok;                    // true if page is valid
 };
 
index 4df53c986caae7dfc98811eed10290957259324d..4bcb0ceb87d30f846bd842ab5a3e56537f226544 100644 (file)
@@ -6,11 +6,12 @@
 //
 //========================================================================
 
-#ifdef __GNUC__
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
 #pragma implementation
 #endif
 
-#include <aconf.h>
 #include <stddef.h>
 #include "Object.h"
 #include "Array.h"
@@ -88,8 +89,10 @@ Object *Parser::getObj(Object *obj) {
       } else {
        key = copyString(buf1.getName());
        shift();
-       if (buf1.isEOF() || buf1.isError())
+       if (buf1.isEOF() || buf1.isError()) {
+         gfree(key);
          break;
+       }
 #ifndef NO_DECRYPTION
        obj->dictAdd(key, getObj(&obj2, fileKey, keyLength, objNum, objGen));
 #else
@@ -200,7 +203,13 @@ Stream *Parser::makeStream(Object *dict) {
 
 void Parser::shift() {
   if (inlineImg > 0) {
-    ++inlineImg;
+    if (inlineImg < 2) {
+      ++inlineImg;
+    } else {
+      // in a damaged content stream, if 'ID' shows up in the middle
+      // of a dictionary, we need to reset
+      inlineImg = 0;
+    }
   } else if (buf2.isCmd("ID")) {
     lexer->skipChar();         // skip char after 'ID' command
     inlineImg = 1;
index c11475bfd94a5e4f9500b50861bd7979d037b849..7b7a812ea2bc6ca1ee285bd24ce7839f6bae4efa 100644 (file)
@@ -9,7 +9,9 @@
 #ifndef PARSER_H
 #define PARSER_H
 
-#ifdef __GNUC__
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
 #pragma interface
 #endif
 
index 9777940da3b70a4dcea3d15092a4857a189cd8f4..0d19d4d2c362074b4a45e6a5e66b20af341801bb 100644 (file)
@@ -6,11 +6,12 @@
 //
 //========================================================================
 
-#ifdef __GNUC__
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
 #pragma implementation
 #endif
 
-#include <aconf.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <stddef.h>
@@ -28,6 +29,7 @@
 #include "Decrypt.h"
 #endif
 #include "Stream.h"
+#include "JBIG2Stream.h"
 #include "Stream-CCITT.h"
 
 #ifdef __DJGPP__
@@ -35,9 +37,6 @@ static GBool setDJSYSFLAGS = gFalse;
 #endif
 
 #ifdef VMS
-#if (__VMS_VER < 70000000)
-extern "C" int unlink(char *filename);
-#endif
 #ifdef __GNUC__
 #define SEEK_SET 0
 #define SEEK_CUR 1
@@ -45,10 +44,6 @@ extern "C" int unlink(char *filename);
 #endif
 #endif
 
-#ifdef MACOS
-#include "StuffItEngineLib.h"
-#endif
-
 //------------------------------------------------------------------------
 // Stream (base class)
 //------------------------------------------------------------------------
@@ -145,7 +140,7 @@ Stream *Stream::makeFilter(char *name, Stream *str, Object *params) {
   int encoding;
   GBool endOfLine, byteAlign, endOfBlock, black;
   int columns, rows;
-  Object obj;
+  Object globals, obj;
 
   if (!strcmp(name, "ASCIIHexDecode") || !strcmp(name, "AHx")) {
     str = new ASCIIHexStream(str);
@@ -255,6 +250,12 @@ Stream *Stream::makeFilter(char *name, Stream *str, Object *params) {
       obj.free();
     }
     str = new FlateStream(str, pred, columns, colors, bits);
+  } else if (!strcmp(name, "JBIG2Decode")) {
+    if (params->isDict()) {
+      params->dictLookup("JBIG2Globals", &globals);
+    }
+    str = new JBIG2Stream(str, &globals);
+    globals.free();
   } else {
     error(getPos(), "Unknown filter '%s'", name);
     str = new EOFStream(str);
@@ -338,51 +339,54 @@ void ImageStream::reset() {
 }
 
 GBool ImageStream::getPixel(Guchar *pix) {
+  int i;
+
+  if (imgIdx >= nVals) {
+    getLine();
+    imgIdx = 0;
+  }
+  for (i = 0; i < nComps; ++i) {
+    pix[i] = imgLine[imgIdx++];
+  }
+  return gTrue;
+}
+
+Guchar *ImageStream::getLine() {
   Gulong buf, bitMask;
   int bits;
   int c;
   int i;
 
-  if (imgIdx >= nVals) {
-
-    // read one line of image pixels
-    if (nBits == 1) {
-      for (i = 0; i < nVals; i += 8) {
-       c = str->getChar();
-       imgLine[i+0] = (Guchar)((c >> 7) & 1);
-       imgLine[i+1] = (Guchar)((c >> 6) & 1);
-       imgLine[i+2] = (Guchar)((c >> 5) & 1);
-       imgLine[i+3] = (Guchar)((c >> 4) & 1);
-       imgLine[i+4] = (Guchar)((c >> 3) & 1);
-       imgLine[i+5] = (Guchar)((c >> 2) & 1);
-       imgLine[i+6] = (Guchar)((c >> 1) & 1);
-       imgLine[i+7] = (Guchar)(c & 1);
-      }
-    } else if (nBits == 8) {
-      for (i = 0; i < nVals; ++i) {
-       imgLine[i] = str->getChar();
-      }
-    } else {
-      bitMask = (1 << nBits) - 1;
-      buf = 0;
-      bits = 0;
-      for (i = 0; i < nVals; ++i) {
-       if (bits < nBits) {
-         buf = (buf << 8) | (str->getChar() & 0xff);
-         bits += 8;
-       }
-       imgLine[i] = (Guchar)((buf >> (bits - nBits)) & bitMask);
-       bits -= nBits;
+  if (nBits == 1) {
+    for (i = 0; i < nVals; i += 8) {
+      c = str->getChar();
+      imgLine[i+0] = (Guchar)((c >> 7) & 1);
+      imgLine[i+1] = (Guchar)((c >> 6) & 1);
+      imgLine[i+2] = (Guchar)((c >> 5) & 1);
+      imgLine[i+3] = (Guchar)((c >> 4) & 1);
+      imgLine[i+4] = (Guchar)((c >> 3) & 1);
+      imgLine[i+5] = (Guchar)((c >> 2) & 1);
+      imgLine[i+6] = (Guchar)((c >> 1) & 1);
+      imgLine[i+7] = (Guchar)(c & 1);
+    }
+  } else if (nBits == 8) {
+    for (i = 0; i < nVals; ++i) {
+      imgLine[i] = str->getChar();
+    }
+  } else {
+    bitMask = (1 << nBits) - 1;
+    buf = 0;
+    bits = 0;
+    for (i = 0; i < nVals; ++i) {
+      if (bits < nBits) {
+       buf = (buf << 8) | (str->getChar() & 0xff);
+       bits += 8;
       }
+      imgLine[i] = (Guchar)((buf >> (bits - nBits)) & bitMask);
+      bits -= nBits;
     }
-
-    // reset to start of line
-    imgIdx = 0;
   }
-
-  for (i = 0; i < nComps; ++i)
-    pix[i] = imgLine[imgIdx++];
-  return gTrue;
+  return imgLine;
 }
 
 void ImageStream::skipLine() {
@@ -577,7 +581,10 @@ Stream *FileStream::makeSubStream(Guint startA, GBool limitedA,
 }
 
 void FileStream::reset() {
-#if HAVE_FSEEK64
+#if HAVE_FSEEKO
+  savePos = (Guint)ftello(f);
+  fseeko(f, start, SEEK_SET);
+#elif HAVE_FSEEK64
   savePos = (Guint)ftell64(f);
   fseek64(f, start, SEEK_SET);
 #else
@@ -595,7 +602,9 @@ void FileStream::reset() {
 
 void FileStream::close() {
   if (saved) {
-#if HAVE_FSEEK64
+#if HAVE_FSEEKO
+    fseeko(f, savePos, SEEK_SET);
+#elif HAVE_FSEEK64
     fseek64(f, savePos, SEEK_SET);
 #else
     fseek(f, savePos, SEEK_SET);
@@ -639,14 +648,19 @@ void FileStream::setPos(Guint pos, int dir) {
   Guint size;
 
   if (dir >= 0) {
-#if HAVE_FSEEK64
+#if HAVE_FSEEKO
+    fseeko(f, pos, SEEK_SET);
+#elif HAVE_FSEEK64
     fseek64(f, pos, SEEK_SET);
 #else
     fseek(f, pos, SEEK_SET);
 #endif
     bufPos = pos;
   } else {
-#if HAVE_FSEEK64
+#if HAVE_FSEEKO
+    fseeko(f, 0, SEEK_END);
+    size = (Guint)ftello(f);
+#elif HAVE_FSEEK64
     fseek64(f, 0, SEEK_END);
     size = (Guint)ftell64(f);
 #else
@@ -659,7 +673,10 @@ void FileStream::setPos(Guint pos, int dir) {
     //~ work around a bug in cygwin's implementation of fseek
     rewind(f);
 #endif
-#if HAVE_FSEEK64
+#if HAVE_FSEEKO
+    fseeko(f, -(int)pos, SEEK_END);
+    bufPos = (Guint)ftello(f);
+#elif HAVE_FSEEK64
     fseek64(f, -(int)pos, SEEK_END);
     bufPos = (Guint)ftell64(f);
 #else
@@ -969,21 +986,12 @@ LZWStream::LZWStream(Stream *strA, int predictor, int columns, int colors,
     pred = NULL;
   }
   early = earlyA;
-  zPipe = NULL;
-  bufPtr = bufEnd = buf;
+  eof = gFalse;
+  inputBits = 0;
+  clearTable();
 }
 
 LZWStream::~LZWStream() {
-  if (zPipe) {
-#ifdef HAVE_POPEN
-    pclose(zPipe);
-#else
-    fclose(zPipe);
-#endif
-    zPipe = NULL;
-    unlink(zName->getCString());
-    delete zName;
-  }
   if (pred) {
     delete pred;
   }
@@ -994,264 +1002,142 @@ int LZWStream::getChar() {
   if (pred) {
     return pred->getChar();
   }
-  return (bufPtr >= bufEnd && !fillBuf()) ? EOF : (*bufPtr++ & 0xff);
+  if (eof) {
+    return EOF;
+  }
+  if (seqIndex >= seqLength) {
+    if (!processNextCode()) {
+      return EOF;
+    }
+  }
+  return seqBuf[seqIndex++];
 }
 
 int LZWStream::lookChar() {
   if (pred) {
     return pred->lookChar();
   }
-  return (bufPtr >= bufEnd && !fillBuf()) ? EOF : (*bufPtr & 0xff);
+  if (eof) {
+    return EOF;
+  }
+  if (seqIndex >= seqLength) {
+    if (!processNextCode()) {
+      return EOF;
+    }
+  }
+  return seqBuf[seqIndex];
 }
 
 int LZWStream::getRawChar() {
-  return (bufPtr >= bufEnd && !fillBuf()) ? EOF : (*bufPtr++ & 0xff);
+  if (eof) {
+    return EOF;
+  }
+  if (seqIndex >= seqLength) {
+    if (!processNextCode()) {
+      return EOF;
+    }
+  }
+  return seqBuf[seqIndex++];
 }
 
 void LZWStream::reset() {
-  FILE *f;
-  GString *zCmd;
+  str->reset();
+  eof = gFalse;
+  inputBits = 0;
+  clearTable();
+}
 
-  //----- close old LZW stream
-  if (zPipe) {
-#ifdef HAVE_POPEN
-    pclose(zPipe);
-#else
-    fclose(zPipe);
-#endif
-    zPipe = NULL;
-    unlink(zName->getCString());
-    delete zName;
-  }
+GBool LZWStream::processNextCode() {
+  int code;
+  int nextLength;
+  int i, j;
 
-  //----- tell Delorie runtime to spawn a new instance of COMMAND.COM
-  //      to run gzip
-#if __DJGPP__
-  if (!setDJSYSFLAGS) {
-    setenv("DJSYSFLAGS", "0x0002", 0);
-    setDJSYSFLAGS = gTrue;
+  // check for EOF
+  if (eof) {
+    return gFalse;
   }
-#endif
 
-  //----- create the .Z file
-  if (!openTempFile(&zName, &f, "wb", ".Z")) {
-    error(getPos(), "Couldn't create temporary file for LZW stream");
-    return;
-  }
-  dumpFile(f);
-  fclose(f);
-
-  //----- execute uncompress / gzip
-  zCmd = new GString(uncompressCmd);
-  zCmd->append(' ');
-  zCmd->append(zName);
-#if defined(MACOS)
-  long magicCookie;
-  // first we open the engine up
-  OSErr err = OpenSITEngine(kUseExternalEngine, &magicCookie);
-  // if we found it - let's use it!
-  if (!err && magicCookie) {
-    // make sure we have the correct version of the Engine
-    if (GetSITEngineVersion(magicCookie) >= kFirstSupportedEngine) {
-      FSSpec myFSS;
-      Str255 pName;
-      strcpy((char *)pName, zName->getCString());
-      c2pstr((char *)pName);
-      FSMakeFSSpec(0, 0, pName, &myFSS);
-      short ftype = DetermineFileType(magicCookie, &myFSS);
-      OSErr expandErr = ExpandFSSpec(magicCookie, ftype, &myFSS,
-                                    NULL, NULL, kCreateFolderNever,
-                                    kDeleteOriginal, kTextConvertSmart);
-    }
-  }
-#elif defined(HAVE_POPEN)
-  if (!(zPipe = popen(zCmd->getCString(), POPEN_READ_MODE))) {
-    error(getPos(), "Couldn't popen '%s'", zCmd->getCString());
-    unlink(zName->getCString());
-    delete zName;
-    return;
-  }
-#else // HAVE_POPEN
-  if (!executeCommand(zCmd->getCString())) {
-    error(getPos(), "Couldn't execute '%s'", zCmd->getCString());
-    unlink(zName->getCString());
-    delete zName;
-    return;
+  // check for eod and clear-table codes
+ start:
+  code = getCode();
+  if (code == EOF || code == 257) {
+    eof = gTrue;
+    return gFalse;
   }
-  zName->del(zName->getLength() - 2, 2);
-  if (!(zPipe = fopen(zName->getCString(), "rb"))) {
-    error(getPos(), "Couldn't open uncompress file '%s'", zName->getCString());
-    unlink(zName->getCString());
-    delete zName;
-    return;
+  if (code == 256) {
+    clearTable();
+    goto start;
+  }
+  if (nextCode >= 4097) {
+    error(getPos(), "Bad LZW stream - expected clear-table code");
+    clearTable();
+  }
+
+  // process the next code
+  nextLength = seqLength + 1;
+  if (code < 256) {
+    seqBuf[0] = code;
+    seqLength = 1;
+  } else if (code < nextCode) {
+    seqLength = table[code].length;
+    for (i = seqLength - 1, j = code; i > 0; --i) {
+      seqBuf[i] = table[j].tail;
+      j = table[j].head;
+    }
+    seqBuf[0] = j;
+  } else if (code == nextCode) {
+    seqBuf[seqLength] = newChar;
+    ++seqLength;
+  } else {
+    error(getPos(), "Bad LZW stream - unexpected code");
+    eof = gTrue;
+    return gFalse;
   }
-#endif // HAVE_POPEN
-
-  //----- clean up
-  delete zCmd;
+  newChar = seqBuf[0];
+  if (first) {
+    first = gFalse;
+  } else {
+    table[nextCode].length = nextLength;
+    table[nextCode].head = prevCode;
+    table[nextCode].tail = newChar;
+    ++nextCode;
+    if (nextCode + early == 512)
+      nextBits = 10;
+    else if (nextCode + early == 1024)
+      nextBits = 11;
+    else if (nextCode + early == 2048)
+      nextBits = 12;
+  }
+  prevCode = code;
+
+  // reset buffer
+  seqIndex = 0;
 
-  //----- initialize buffer
-  bufPtr = bufEnd = buf;
+  return gTrue;
 }
 
-void LZWStream::dumpFile(FILE *f) {
-  int outCodeBits;             // size of output code
-  int outBits;                 // max output code
-  int outBuf[8];               // output buffer
-  int outData;                 // temporary output buffer
-  int inCode, outCode;         // input and output codes
-  int nextCode;                        // next code index
-  GBool eof;                   // set when EOF is reached
-  GBool clear;                 // set if table needs to be cleared
-  GBool first;                 // indicates first code word after clear
-  int i, j;
-
-  str->reset();
-
-  // magic number
-  fputc(0x1f, f);
-  fputc(0x9d, f);
-
-  // max code length, block mode flag
-  fputc(0x8c, f);
-
-  // init input side
-  inCodeBits = 9;
-  inputBuf = 0;
-  inputBits = 0;
-  eof = gFalse;
-
-  // init output side
-  outCodeBits = 9;
-
-  // clear table
-  first = gTrue;
+void LZWStream::clearTable() {
   nextCode = 258;
-
-  clear = gFalse;
-  do {
-    for (i = 0; i < 8; ++i) {
-      // check for table overflow
-      if (nextCode + early > 0x1001) {
-       inCode = 256;
-
-      // read input code
-      } else {
-       do {
-         inCode = getCode();
-         if (inCode == EOF) {
-           eof = gTrue;
-           inCode = 0;
-         }
-       } while (first && inCode == 256);
-      }
-
-      // compute output code
-      if (inCode < 256) {
-       outCode = inCode;
-      } else if (inCode == 256) {
-       outCode = 256;
-       clear = gTrue;
-      } else if (inCode == 257) {
-       outCode = 0;
-       eof = gTrue;
-      } else {
-       outCode = inCode - 1;
-      }
-      outBuf[i] = outCode;
-
-      // next code index
-      if (first)
-       first = gFalse;
-      else
-       ++nextCode;
-
-      // check input code size
-      if (nextCode + early == 0x200)
-       inCodeBits = 10;
-      else if (nextCode + early == 0x400) {
-       inCodeBits = 11;
-      } else if (nextCode + early == 0x800) {
-       inCodeBits = 12;
-      }
-
-      // check for eof/clear
-      if (eof)
-       break;
-      if (clear) {
-       i = 8;
-       break;
-      }
-    }
-
-    // write output block
-    outData = 0;
-    outBits = 0;
-    j = 0;
-    while (j < i || outBits > 0) {
-      if (outBits < 8 && j < i) {
-       outData = outData | (outBuf[j++] << outBits);
-       outBits += outCodeBits;
-      }
-      fputc(outData & 0xff, f);
-      outData >>= 8;
-      outBits -= 8;
-    }
-
-    // check output code size
-    if (nextCode - 1 == 512 ||
-       nextCode - 1 == 1024 ||
-       nextCode - 1 == 2048 ||
-       nextCode - 1 == 4096) {
-      outCodeBits = inCodeBits;
-    }
-
-    // clear table if necessary
-    if (clear) {
-      inCodeBits = 9;
-      outCodeBits = 9;
-      first = gTrue;
-      nextCode = 258;
-      clear = gFalse;
-    }
-  } while (!eof);
+  nextBits = 9;
+  seqIndex = seqLength = 0;
+  first = gTrue;
 }
 
 int LZWStream::getCode() {
   int c;
   int code;
 
-  while (inputBits < inCodeBits) {
+  while (inputBits < nextBits) {
     if ((c = str->getChar()) == EOF)
       return EOF;
     inputBuf = (inputBuf << 8) | (c & 0xff);
     inputBits += 8;
   }
-  code = (inputBuf >> (inputBits - inCodeBits)) & ((1 << inCodeBits) - 1);
-  inputBits -= inCodeBits;
+  code = (inputBuf >> (inputBits - nextBits)) & ((1 << nextBits) - 1);
+  inputBits -= nextBits;
   return code;
 }
 
-GBool LZWStream::fillBuf() {
-  int n;
-
-  if (!zPipe)
-    return gFalse;
-  if ((n = fread(buf, 1, 256, zPipe)) < 256) {
-#ifdef HAVE_POPEN
-    pclose(zPipe);
-#else
-    fclose(zPipe);
-#endif
-    zPipe = NULL;
-    unlink(zName->getCString());
-    delete zName;
-  }
-  bufPtr = buf;
-  bufEnd = buf + n;
-  return n > 0;
-}
-
 GString *LZWStream::getPSFilter(char *indent) {
   GString *s;
 
@@ -1882,7 +1768,6 @@ GBool CCITTFaxStream::isBinary(GBool last) {
 //------------------------------------------------------------------------
 
 // IDCT constants (20.12 fixed point format)
-#ifndef FP_IDCT
 #define dctCos1    4017                // cos(pi/16)
 #define dctSin1     799                // sin(pi/16)
 #define dctCos3    3406                // cos(3*pi/16)
@@ -1891,19 +1776,6 @@ GBool CCITTFaxStream::isBinary(GBool last) {
 #define dctSin6    3784                // sin(6*pi/16)
 #define dctSqrt2   5793                // sqrt(2)
 #define dctSqrt1d2 2896                // sqrt(2) / 2
-#endif
-
-// IDCT constants
-#ifdef FP_IDCT
-#define dctCos1    0.98078528  // cos(pi/16)
-#define dctSin1    0.19509032  // sin(pi/16)
-#define dctCos3    0.83146961  // cos(3*pi/16)
-#define dctSin3    0.55557023  // sin(3*pi/16)
-#define dctCos6    0.38268343  // cos(6*pi/16)
-#define dctSin6    0.92387953  // sin(6*pi/16)
-#define dctSqrt2   1.41421356  // sqrt(2)
-#define dctSqrt1d2 0.70710678  // sqrt(2) / 2
-#endif
 
 // color conversion parameters (16.16 fixed point format)
 #define dctCrToR   91881       //  1.4020
@@ -1939,14 +1811,18 @@ DCTStream::DCTStream(Stream *strA):
     FilterStream(strA) {
   int i, j;
 
+  progressive = interleaved = gFalse;
   width = height = 0;
   mcuWidth = mcuHeight = 0;
   numComps = 0;
   comp = 0;
   x = y = dy = 0;
-  for (i = 0; i < 4; ++i)
-    for (j = 0; j < 32; ++j)
+  for (i = 0; i < 4; ++i) {
+    for (j = 0; j < 32; ++j) {
       rowBuf[i][j] = NULL;
+    }
+    frameBuf[i] = NULL;
+  }
 
   if (!dctClipInit) {
     for (i = -256; i < 0; ++i)
@@ -1963,53 +1839,172 @@ DCTStream::~DCTStream() {
   int i, j;
 
   delete str;
-  for (i = 0; i < numComps; ++i)
-    for (j = 0; j < mcuHeight; ++j)
-      gfree(rowBuf[i][j]);
+  if (progressive || !interleaved) {
+    for (i = 0; i < numComps; ++i) {
+      gfree(frameBuf[i]);
+    }
+  } else {
+    for (i = 0; i < numComps; ++i) {
+      for (j = 0; j < mcuHeight; ++j) {
+       gfree(rowBuf[i][j]);
+      }
+    }
+  }
 }
 
 void DCTStream::reset() {
+  int minHSample, minVSample;
+  int i, j;
+
   str->reset();
+
+  progressive = interleaved = gFalse;
+  width = height = 0;
+  numComps = 0;
+  numQuantTables = 0;
+  numDCHuffTables = 0;
+  numACHuffTables = 0;
+  colorXform = 0;
+  gotAdobeMarker = gFalse;
+  restartInterval = 0;
+
   if (!readHeader()) {
     y = height;
     return;
   }
-  restartMarker = 0xd0;
-  restart();
+
+  // compute MCU size
+  mcuWidth = minHSample = compInfo[0].hSample;
+  mcuHeight = minVSample = compInfo[0].vSample;
+  for (i = 1; i < numComps; ++i) {
+    if (compInfo[i].hSample < minHSample)
+      minHSample = compInfo[i].hSample;
+    if (compInfo[i].vSample < minVSample)
+      minVSample = compInfo[i].vSample;
+    if (compInfo[i].hSample > mcuWidth)
+      mcuWidth = compInfo[i].hSample;
+    if (compInfo[i].vSample > mcuHeight)
+      mcuHeight = compInfo[i].vSample;
+  }
+  for (i = 0; i < numComps; ++i) {
+    compInfo[i].hSample /= minHSample;
+    compInfo[i].vSample /= minVSample;
+  }
+  mcuWidth = (mcuWidth / minHSample) * 8;
+  mcuHeight = (mcuHeight / minVSample) * 8;
+
+  // figure out color transform
+  if (!gotAdobeMarker && numComps == 3) {
+    if (compInfo[0].id == 1 && compInfo[1].id == 2 && compInfo[2].id == 3) {
+      colorXform = 1;
+    }
+  }
+
+  if (progressive || !interleaved) {
+
+    // allocate a buffer for the whole image
+    bufWidth = ((width + mcuWidth - 1) / mcuWidth) * mcuWidth;
+    bufHeight = ((height + mcuHeight - 1) / mcuHeight) * mcuHeight;
+    for (i = 0; i < numComps; ++i) {
+      frameBuf[i] = (int *)gmalloc(bufWidth * bufHeight * sizeof(int));
+      memset(frameBuf[i], 0, bufWidth * bufHeight * sizeof(int));
+    }
+
+    // read the image data
+    do {
+      restartMarker = 0xd0;
+      restart();
+      readScan();
+    } while (readHeader());
+
+    // decode
+    decodeImage();
+
+    // initialize counters
+    comp = 0;
+    x = 0;
+    y = 0;
+
+  } else {
+
+    // allocate a buffer for one row of MCUs
+    bufWidth = ((width + mcuWidth - 1) / mcuWidth) * mcuWidth;
+    for (i = 0; i < numComps; ++i) {
+      for (j = 0; j < mcuHeight; ++j) {
+       rowBuf[i][j] = (Guchar *)gmalloc(bufWidth * sizeof(Guchar));
+      }
+    }
+
+    // initialize counters
+    comp = 0;
+    x = 0;
+    y = 0;
+    dy = mcuHeight;
+
+    restartMarker = 0xd0;
+    restart();
+  }
 }
 
 int DCTStream::getChar() {
   int c;
 
-  c = lookChar();
-  if (c == EOF)
+  if (y >= height) {
     return EOF;
-  if (++comp == numComps) {
-    comp = 0;
-    if (++x == width) {
+  }
+  if (progressive || !interleaved) {
+    c = frameBuf[comp][y * bufWidth + x];
+    if (++comp == numComps) {
+      comp = 0;
+      if (++x == width) {
+       x = 0;
+       ++y;
+      }
+    }
+  } else {
+    if (dy >= mcuHeight) {
+      if (!readMCURow()) {
+       y = height;
+       return EOF;
+      }
+      comp = 0;
       x = 0;
-      ++y;
-      ++dy;
+      dy = 0;
+    }
+    c = rowBuf[comp][dy][x];
+    if (++comp == numComps) {
+      comp = 0;
+      if (++x == width) {
+       x = 0;
+       ++y;
+       ++dy;
+       if (y == height) {
+         readTrailer();
+       }
+      }
     }
   }
-  if (y == height)
-    readTrailer();
   return c;
 }
 
 int DCTStream::lookChar() {
-  if (y >= height)
+  if (y >= height) {
     return EOF;
-  if (dy >= mcuHeight) {
-    if (!readMCURow()) {
-      y = height;
-      return EOF;
+  }
+  if (progressive || !interleaved) {
+    return frameBuf[comp][y * bufWidth + x];
+  } else {
+    if (dy >= mcuHeight) {
+      if (!readMCURow()) {
+       y = height;
+       return EOF;
+      }
+      comp = 0;
+      x = 0;
+      dy = 0;
     }
-    comp = 0;
-    x = 0;
-    dy = 0;
+    return rowBuf[comp][dy][x];
   }
-  return rowBuf[comp][dy][x];
 }
 
 void DCTStream::restart() {
@@ -2017,12 +2012,16 @@ void DCTStream::restart() {
 
   inputBits = 0;
   restartCtr = restartInterval;
-  for (i = 0; i < numComps; ++i)
+  for (i = 0; i < numComps; ++i) {
     compInfo[i].prevDC = 0;
+  }
+  eobRun = 0;
 }
 
+// Read one row of MCUs from a sequential JPEG stream.
 GBool DCTStream::readMCURow() {
-  Guchar data[64];
+  int data1[64];
+  Guchar data2[64];
   Guchar *p1, *p2;
   int pY, pCb, pCr, pR, pG, pB;
   int h, v, horiz, vert, hSub, vSub;
@@ -2053,36 +2052,38 @@ GBool DCTStream::readMCURow() {
       vSub = vert / 8;
       for (y2 = 0; y2 < mcuHeight; y2 += vert) {
        for (x2 = 0; x2 < mcuWidth; x2 += horiz) {
-         if (!readDataUnit(&dcHuffTables[compInfo[cc].dcHuffTable],
-                           &acHuffTables[compInfo[cc].acHuffTable],
-                           quantTables[compInfo[cc].quantTable],
+         if (!readDataUnit(&dcHuffTables[scanInfo.dcHuffTable[cc]],
+                           &acHuffTables[scanInfo.acHuffTable[cc]],
                            &compInfo[cc].prevDC,
-                           data))
+                           data1)) {
            return gFalse;
+         }
+         transformDataUnit(quantTables[compInfo[cc].quantTable],
+                           data1, data2);
          if (hSub == 1 && vSub == 1) {
            for (y3 = 0, i = 0; y3 < 8; ++y3, i += 8) {
              p1 = &rowBuf[cc][y2+y3][x1+x2];
-             p1[0] = data[i];
-             p1[1] = data[i+1];
-             p1[2] = data[i+2];
-             p1[3] = data[i+3];
-             p1[4] = data[i+4];
-             p1[5] = data[i+5];
-             p1[6] = data[i+6];
-             p1[7] = data[i+7];
+             p1[0] = data2[i];
+             p1[1] = data2[i+1];
+             p1[2] = data2[i+2];
+             p1[3] = data2[i+3];
+             p1[4] = data2[i+4];
+             p1[5] = data2[i+5];
+             p1[6] = data2[i+6];
+             p1[7] = data2[i+7];
            }
          } else if (hSub == 2 && vSub == 2) {
            for (y3 = 0, i = 0; y3 < 16; y3 += 2, i += 8) {
              p1 = &rowBuf[cc][y2+y3][x1+x2];
              p2 = &rowBuf[cc][y2+y3+1][x1+x2];
-             p1[0] = p1[1] = p2[0] = p2[1] = data[i];
-             p1[2] = p1[3] = p2[2] = p2[3] = data[i+1];
-             p1[4] = p1[5] = p2[4] = p2[5] = data[i+2];
-             p1[6] = p1[7] = p2[6] = p2[7] = data[i+3];
-             p1[8] = p1[9] = p2[8] = p2[9] = data[i+4];
-             p1[10] = p1[11] = p2[10] = p2[11] = data[i+5];
-             p1[12] = p1[13] = p2[12] = p2[13] = data[i+6];
-             p1[14] = p1[15] = p2[14] = p2[15] = data[i+7];
+             p1[0] = p1[1] = p2[0] = p2[1] = data2[i];
+             p1[2] = p1[3] = p2[2] = p2[3] = data2[i+1];
+             p1[4] = p1[5] = p2[4] = p2[5] = data2[i+2];
+             p1[6] = p1[7] = p2[6] = p2[7] = data2[i+3];
+             p1[8] = p1[9] = p2[8] = p2[9] = data2[i+4];
+             p1[10] = p1[11] = p2[10] = p2[11] = data2[i+5];
+             p1[12] = p1[13] = p2[12] = p2[13] = data2[i+6];
+             p1[14] = p1[15] = p2[14] = p2[15] = data2[i+7];
            }
          } else {
            i = 0;
@@ -2090,7 +2091,7 @@ GBool DCTStream::readMCURow() {
              for (x3 = 0, x4 = 0; x3 < 8; ++x3, x4 += hSub) {
                for (y5 = 0; y5 < vSub; ++y5)
                  for (x5 = 0; x5 < hSub; ++x5)
-                   rowBuf[cc][y2+y4+y5][x1+x2+x4+x5] = data[i];
+                   rowBuf[cc][y2+y4+y5][x1+x2+x4+x5] = data2[i];
                ++i;
              }
            }
@@ -2138,71 +2139,466 @@ GBool DCTStream::readMCURow() {
   return gTrue;
 }
 
-// This IDCT algorithm is taken from:
-//   Christoph Loeffler, Adriaan Ligtenberg, George S. Moschytz,
-//   "Practical Fast 1-D DCT Algorithms with 11 Multiplications",
-//   IEEE Intl. Conf. on Acoustics, Speech & Signal Processing, 1989,
-//   988-991.
-// The stage numbers mentioned in the comments refer to Figure 1 in this
-// paper.
-#ifndef FP_IDCT
-GBool DCTStream::readDataUnit(DCTHuffTable *dcHuffTable,
-                             DCTHuffTable *acHuffTable,
-                             Guchar quantTable[64], int *prevDC,
-                             Guchar data[64]) {
-  int tmp1[64];
-  int v0, v1, v2, v3, v4, v5, v6, v7, t;
-  int run, size, amp;
+// Read one scan from a progressive or non-interleaved JPEG stream.
+void DCTStream::readScan() {
+  int data[64];
+  int x1, y1, dy1, x2, y2, y3, cc, i;
+  int h, v, horiz, vert, hSub, vSub;
+  int *p1;
   int c;
-  int i, j;
 
-  // Huffman decode and dequantize
-  size = readHuffSym(dcHuffTable);
-  if (size == 9999)
-    return gFalse;
-  if (size > 0) {
-    amp = readAmp(size);
-    if (amp == 9999)
-      return gFalse;
+  if (scanInfo.numComps == 1) {
+    for (cc = 0; cc < numComps; ++cc) {
+      if (scanInfo.comp[cc]) {
+       break;
+      }
+    }
+    dy1 = mcuHeight / compInfo[cc].vSample;
+  } else {
+    dy1 = mcuHeight;
+  }
+
+  for (y1 = 0; y1 < bufHeight; y1 += dy1) {
+    for (x1 = 0; x1 < bufWidth; x1 += mcuWidth) {
+
+      // deal with restart marker
+      if (restartInterval > 0 && restartCtr == 0) {
+       c = readMarker();
+       if (c != restartMarker) {
+         error(getPos(), "Bad DCT data: incorrect restart marker");
+         return;
+       }
+       if (++restartMarker == 0xd8) {
+         restartMarker = 0xd0;
+       }
+       restart();
+      }
+
+      // read one MCU
+      for (cc = 0; cc < numComps; ++cc) {
+       if (!scanInfo.comp[cc]) {
+         continue;
+       }
+
+       h = compInfo[cc].hSample;
+       v = compInfo[cc].vSample;
+       horiz = mcuWidth / h;
+       vert = mcuHeight / v;
+       hSub = horiz / 8;
+       vSub = vert / 8;
+       for (y2 = 0; y2 < dy1; y2 += vert) {
+         for (x2 = 0; x2 < mcuWidth; x2 += horiz) {
+
+           // pull out the current values
+           p1 = &frameBuf[cc][(y1+y2) * bufWidth + (x1+x2)];
+           for (y3 = 0, i = 0; y3 < 8; ++y3, i += 8) {
+             data[i] = p1[0];
+             data[i+1] = p1[1];
+             data[i+2] = p1[2];
+             data[i+3] = p1[3];
+             data[i+4] = p1[4];
+             data[i+5] = p1[5];
+             data[i+6] = p1[6];
+             data[i+7] = p1[7];
+             p1 += bufWidth * vSub;
+           }
+
+           // read one data unit
+           if (progressive) {
+             if (!readProgressiveDataUnit(
+                      &dcHuffTables[scanInfo.dcHuffTable[cc]],
+                      &acHuffTables[scanInfo.acHuffTable[cc]],
+                      &compInfo[cc].prevDC,
+                      data)) {
+               return;
+             }
+           } else {
+             if (!readDataUnit(&dcHuffTables[scanInfo.dcHuffTable[cc]],
+                               &acHuffTables[scanInfo.acHuffTable[cc]],
+                               &compInfo[cc].prevDC,
+                               data)) {
+               return;
+             }
+           }
+
+           // add the data unit into frameBuf
+           p1 = &frameBuf[cc][(y1+y2) * bufWidth + (x1+x2)];
+           for (y3 = 0, i = 0; y3 < 8; ++y3, i += 8) {
+             p1[0] = data[i];
+             p1[1] = data[i+1];
+             p1[2] = data[i+2];
+             p1[3] = data[i+3];
+             p1[4] = data[i+4];
+             p1[5] = data[i+5];
+             p1[6] = data[i+6];
+             p1[7] = data[i+7];
+             p1 += bufWidth * vSub;
+           }
+         }
+       }
+      }
+      --restartCtr;
+    }
+  }
+}
+
+// Read one data unit from a sequential JPEG stream.
+GBool DCTStream::readDataUnit(DCTHuffTable *dcHuffTable,
+                             DCTHuffTable *acHuffTable,
+                             int *prevDC, int data[64]) {
+  int run, size, amp;
+  int c;
+  int i, j;
+
+  if ((size = readHuffSym(dcHuffTable)) == 9999) {
+    return gFalse;
+  }
+  if (size > 0) {
+    if ((amp = readAmp(size)) == 9999) {
+      return gFalse;
+    }
   } else {
     amp = 0;
   }
-  tmp1[0] = (*prevDC += amp) * quantTable[0];
-  for (i = 1; i < 64; ++i)
-    tmp1[i] = 0;
+  data[0] = *prevDC += amp;
+  for (i = 1; i < 64; ++i) {
+    data[i] = 0;
+  }
   i = 1;
   while (i < 64) {
     run = 0;
-    while ((c = readHuffSym(acHuffTable)) == 0xf0 && run < 0x30)
+    while ((c = readHuffSym(acHuffTable)) == 0xf0 && run < 0x30) {
       run += 0x10;
-    if (c == 9999)
+    }
+    if (c == 9999) {
       return gFalse;
+    }
     if (c == 0x00) {
       break;
     } else {
       run += (c >> 4) & 0x0f;
       size = c & 0x0f;
       amp = readAmp(size);
-      if (amp == 9999)
+      if (amp == 9999) {
        return gFalse;
+      }
       i += run;
       j = dctZigZag[i++];
-      tmp1[j] = amp * quantTable[j];
+      data[j] = amp;
+    }
+  }
+  return gTrue;
+}
+
+// Read one data unit from a sequential JPEG stream.
+GBool DCTStream::readProgressiveDataUnit(DCTHuffTable *dcHuffTable,
+                                        DCTHuffTable *acHuffTable,
+                                        int *prevDC, int data[64]) {
+  int run, size, amp, bit, c;
+  int i, j, k;
+
+  // get the DC coefficient
+  i = scanInfo.firstCoeff;
+  if (i == 0) {
+    if (scanInfo.ah == 0) {
+      if ((size = readHuffSym(dcHuffTable)) == 9999) {
+       return gFalse;
+      }
+      if (size > 0) {
+       if ((amp = readAmp(size)) == 9999) {
+         return gFalse;
+       }
+      } else {
+       amp = 0;
+      }
+      data[0] += (*prevDC += amp) << scanInfo.al;
+    } else {
+      if ((bit = readBit()) == 9999) {
+       return gFalse;
+      }
+      data[0] += bit << scanInfo.al;
+    }
+    ++i;
+  }
+  if (scanInfo.lastCoeff == 0) {
+    return gTrue;
+  }
+
+  // check for an EOB run
+  if (eobRun > 0) {
+    while (i <= scanInfo.lastCoeff) {
+      j = dctZigZag[i++];
+      if (data[j] != 0) {
+       if ((bit = readBit()) == EOF) {
+         return gFalse;
+       }
+       if (bit) {
+         data[j] += 1 << scanInfo.al;
+       }
+      }
     }
+    --eobRun;
+    return gTrue;
+  }
+
+  // read the AC coefficients
+  while (i <= scanInfo.lastCoeff) {
+    if ((c = readHuffSym(acHuffTable)) == 9999) {
+      return gFalse;
+    }
+
+    // ZRL
+    if (c == 0xf0) {
+      k = 0;
+      while (k < 16) {
+       j = dctZigZag[i++];
+       if (data[j] == 0) {
+         ++k;
+       } else {
+         if ((bit = readBit()) == EOF) {
+           return gFalse;
+         }
+         if (bit) {
+           data[j] += 1 << scanInfo.al;
+         }
+       }
+      }
+
+    // EOB run
+    } else if ((c & 0x0f) == 0x00) {
+      j = c >> 4;
+      eobRun = 0;
+      for (k = 0; k < j; ++k) {
+       if ((bit = readBit()) == EOF) {
+         return 9999;
+       }
+       eobRun = (eobRun << 1) | bit;
+      }
+      eobRun += 1 << j;
+      while (i <= scanInfo.lastCoeff) {
+       j = dctZigZag[i++];
+       if (data[j] != 0) {
+         if ((bit = readBit()) == EOF) {
+           return gFalse;
+         }
+         if (bit) {
+           data[j] += 1 << scanInfo.al;
+         }
+       }
+      }
+      --eobRun;
+      break;
+
+    // zero run and one AC coefficient
+    } else {
+      run = (c >> 4) & 0x0f;
+      size = c & 0x0f;
+      if ((amp = readAmp(size)) == 9999) {
+       return gFalse;
+      }
+      k = 0;
+      do {
+       j = dctZigZag[i++];
+       while (data[j] != 0) {
+         if ((bit = readBit()) == EOF) {
+           return gFalse;
+         }
+         if (bit) {
+           data[j] += 1 << scanInfo.al;
+         }
+         j = dctZigZag[i++];
+       }
+       ++k;
+      } while (k <= run);
+      data[j] = amp << scanInfo.al;
+    }
+  }
+
+  return gTrue;
+}
+
+// Decode a progressive JPEG image.
+void DCTStream::decodeImage() {
+  int dataIn[64];
+  Guchar dataOut[64];
+  Guchar *quantTable;
+  int pY, pCb, pCr, pR, pG, pB;
+  int x1, y1, x2, y2, x3, y3, x4, y4, x5, y5, cc, i;
+  int h, v, horiz, vert, hSub, vSub;
+  int *p0, *p1, *p2;
+
+  for (y1 = 0; y1 < bufHeight; y1 += mcuHeight) {
+    for (x1 = 0; x1 < bufWidth; x1 += mcuWidth) {
+      for (cc = 0; cc < numComps; ++cc) {
+       quantTable = quantTables[compInfo[cc].quantTable];
+       h = compInfo[cc].hSample;
+       v = compInfo[cc].vSample;
+       horiz = mcuWidth / h;
+       vert = mcuHeight / v;
+       hSub = horiz / 8;
+       vSub = vert / 8;
+       for (y2 = 0; y2 < mcuHeight; y2 += vert) {
+         for (x2 = 0; x2 < mcuWidth; x2 += horiz) {
+
+           // pull out the coded data unit
+           p1 = &frameBuf[cc][(y1+y2) * bufWidth + (x1+x2)];
+           for (y3 = 0, i = 0; y3 < 8; ++y3, i += 8) {
+             dataIn[i]   = p1[0];
+             dataIn[i+1] = p1[1];
+             dataIn[i+2] = p1[2];
+             dataIn[i+3] = p1[3];
+             dataIn[i+4] = p1[4];
+             dataIn[i+5] = p1[5];
+             dataIn[i+6] = p1[6];
+             dataIn[i+7] = p1[7];
+             p1 += bufWidth * vSub;
+           }
+
+           // transform
+           transformDataUnit(quantTable, dataIn, dataOut);
+
+           // store back into frameBuf, doing replication for
+           // subsampled components
+           p1 = &frameBuf[cc][(y1+y2) * bufWidth + (x1+x2)];
+           if (hSub == 1 && vSub == 1) {
+             for (y3 = 0, i = 0; y3 < 8; ++y3, i += 8) {
+               p1[0] = dataOut[i] & 0xff;
+               p1[1] = dataOut[i+1] & 0xff;
+               p1[2] = dataOut[i+2] & 0xff;
+               p1[3] = dataOut[i+3] & 0xff;
+               p1[4] = dataOut[i+4] & 0xff;
+               p1[5] = dataOut[i+5] & 0xff;
+               p1[6] = dataOut[i+6] & 0xff;
+               p1[7] = dataOut[i+7] & 0xff;
+               p1 += bufWidth;
+             }
+           } else if (hSub == 2 && vSub == 2) {
+             p2 = p1 + bufWidth;
+             for (y3 = 0, i = 0; y3 < 16; y3 += 2, i += 8) {
+               p1[0] = p1[1] = p2[0] = p2[1] = dataOut[i] & 0xff;
+               p1[2] = p1[3] = p2[2] = p2[3] = dataOut[i+1] & 0xff;
+               p1[4] = p1[5] = p2[4] = p2[5] = dataOut[i+2] & 0xff;
+               p1[6] = p1[7] = p2[6] = p2[7] = dataOut[i+3] & 0xff;
+               p1[8] = p1[9] = p2[8] = p2[9] = dataOut[i+4] & 0xff;
+               p1[10] = p1[11] = p2[10] = p2[11] = dataOut[i+5] & 0xff;
+               p1[12] = p1[13] = p2[12] = p2[13] = dataOut[i+6] & 0xff;
+               p1[14] = p1[15] = p2[14] = p2[15] = dataOut[i+7] & 0xff;
+               p1 += bufWidth * 2;
+               p2 += bufWidth * 2;
+             }
+           } else {
+             i = 0;
+             for (y3 = 0, y4 = 0; y3 < 8; ++y3, y4 += vSub) {
+               for (x3 = 0, x4 = 0; x3 < 8; ++x3, x4 += hSub) {
+                 p2 = p1 + x4;
+                 for (y5 = 0; y5 < vSub; ++y5) {
+                   for (x5 = 0; x5 < hSub; ++x5) {
+                     p2[x5] = dataOut[i] & 0xff;
+                   }
+                   p2 += bufWidth;
+                 }
+                 ++i;
+               }
+               p1 += bufWidth * vSub;
+             }
+           }
+         }
+       }
+      }
+
+      // color space conversion
+      if (colorXform) {
+       // convert YCbCr to RGB
+       if (numComps == 3) {
+         for (y2 = 0; y2 < mcuHeight; ++y2) {
+           p0 = &frameBuf[0][(y1+y2) * bufWidth + x1];
+           p1 = &frameBuf[1][(y1+y2) * bufWidth + x1];
+           p2 = &frameBuf[2][(y1+y2) * bufWidth + x1];
+           for (x2 = 0; x2 < mcuWidth; ++x2) {
+             pY = *p0;
+             pCb = *p1 - 128;
+             pCr = *p2 - 128;
+             pR = ((pY << 16) + dctCrToR * pCr + 32768) >> 16;
+             *p0++ = dctClip[dctClipOffset + pR];
+             pG = ((pY << 16) + dctCbToG * pCb + dctCrToG * pCr +
+                   32768) >> 16;
+             *p1++ = dctClip[dctClipOffset + pG];
+             pB = ((pY << 16) + dctCbToB * pCb + 32768) >> 16;
+             *p2++ = dctClip[dctClipOffset + pB];
+           }
+         }
+       // convert YCbCrK to CMYK (K is passed through unchanged)
+       } else if (numComps == 4) {
+         for (y2 = 0; y2 < mcuHeight; ++y2) {
+           p0 = &frameBuf[0][(y1+y2) * bufWidth + x1];
+           p1 = &frameBuf[1][(y1+y2) * bufWidth + x1];
+           p2 = &frameBuf[2][(y1+y2) * bufWidth + x1];
+           for (x2 = 0; x2 < mcuWidth; ++x2) {
+             pY = *p0;
+             pCb = *p1 - 128;
+             pCr = *p2 - 128;
+             pR = ((pY << 16) + dctCrToR * pCr + 32768) >> 16;
+             *p0++ = 255 - dctClip[dctClipOffset + pR];
+             pG = ((pY << 16) + dctCbToG * pCb + dctCrToG * pCr +
+                   32768) >> 16;
+             *p1++ = 255 - dctClip[dctClipOffset + pG];
+             pB = ((pY << 16) + dctCbToB * pCb + 32768) >> 16;
+             *p2++ = 255 - dctClip[dctClipOffset + pB];
+           }
+         }
+       }
+      }
+    }
+  }
+}
+
+// Transform one data unit -- this performs the dequantization and
+// IDCT steps.  This IDCT algorithm is taken from:
+//   Christoph Loeffler, Adriaan Ligtenberg, George S. Moschytz,
+//   "Practical Fast 1-D DCT Algorithms with 11 Multiplications",
+//   IEEE Intl. Conf. on Acoustics, Speech & Signal Processing, 1989,
+//   988-991.
+// The stage numbers mentioned in the comments refer to Figure 1 in this
+// paper.
+void DCTStream::transformDataUnit(Guchar *quantTable,
+                                 int dataIn[64], Guchar dataOut[64]) {
+  int v0, v1, v2, v3, v4, v5, v6, v7, t;
+  int *p;
+  int i;
+
+  // dequant
+  for (i = 0; i < 64; ++i) {
+    dataIn[i] *= quantTable[i];
   }
 
   // inverse DCT on rows
   for (i = 0; i < 64; i += 8) {
+    p = dataIn + i;
+
+    // check for all-zero AC coefficients
+    if (p[1] == 0 && p[2] == 0 && p[3] == 0 &&
+       p[4] == 0 && p[5] == 0 && p[6] == 0 && p[7] == 0) {
+      t = (dctSqrt2 * p[0] + 512) >> 10;
+      p[0] = t;
+      p[1] = t;
+      p[2] = t;
+      p[3] = t;
+      p[4] = t;
+      p[5] = t;
+      p[6] = t;
+      p[7] = t;
+      continue;
+    }
 
     // stage 4
-    v0 = (dctSqrt2 * tmp1[i+0] + 128) >> 8;
-    v1 = (dctSqrt2 * tmp1[i+4] + 128) >> 8;
-    v2 = tmp1[i+2];
-    v3 = tmp1[i+6];
-    v4 = (dctSqrt1d2 * (tmp1[i+1] - tmp1[i+7]) + 128) >> 8;
-    v7 = (dctSqrt1d2 * (tmp1[i+1] + tmp1[i+7]) + 128) >> 8;
-    v5 = tmp1[i+3] << 4;
-    v6 = tmp1[i+5] << 4;
+    v0 = (dctSqrt2 * p[0] + 128) >> 8;
+    v1 = (dctSqrt2 * p[4] + 128) >> 8;
+    v2 = p[2];
+    v3 = p[6];
+    v4 = (dctSqrt1d2 * (p[1] - p[7]) + 128) >> 8;
+    v7 = (dctSqrt1d2 * (p[1] + p[7]) + 128) >> 8;
+    v5 = p[3] << 4;
+    v6 = p[5] << 4;
 
     // stage 3
     t = (v0 - v1+ 1) >> 1;
@@ -2233,28 +2629,44 @@ GBool DCTStream::readDataUnit(DCTHuffTable *dcHuffTable,
     v6 = t;
 
     // stage 1
-    tmp1[i+0] = v0 + v7;
-    tmp1[i+7] = v0 - v7;
-    tmp1[i+1] = v1 + v6;
-    tmp1[i+6] = v1 - v6;
-    tmp1[i+2] = v2 + v5;
-    tmp1[i+5] = v2 - v5;
-    tmp1[i+3] = v3 + v4;
-    tmp1[i+4] = v3 - v4;
+    p[0] = v0 + v7;
+    p[7] = v0 - v7;
+    p[1] = v1 + v6;
+    p[6] = v1 - v6;
+    p[2] = v2 + v5;
+    p[5] = v2 - v5;
+    p[3] = v3 + v4;
+    p[4] = v3 - v4;
   }
 
   // inverse DCT on columns
   for (i = 0; i < 8; ++i) {
+    p = dataIn + i;
+
+    // check for all-zero AC coefficients
+    if (p[1*8] == 0 && p[2*8] == 0 && p[3*8] == 0 &&
+       p[4*8] == 0 && p[5*8] == 0 && p[6*8] == 0 && p[7*8] == 0) {
+      t = (dctSqrt2 * dataIn[i+0] + 8192) >> 14;
+      p[0*8] = t;
+      p[1*8] = t;
+      p[2*8] = t;
+      p[3*8] = t;
+      p[4*8] = t;
+      p[5*8] = t;
+      p[6*8] = t;
+      p[7*8] = t;
+      continue;
+    }
 
     // stage 4
-    v0 = (dctSqrt2 * tmp1[0*8+i] + 2048) >> 12;
-    v1 = (dctSqrt2 * tmp1[4*8+i] + 2048) >> 12;
-    v2 = tmp1[2*8+i];
-    v3 = tmp1[6*8+i];
-    v4 = (dctSqrt1d2 * (tmp1[1*8+i] - tmp1[7*8+i]) + 2048) >> 12;
-    v7 = (dctSqrt1d2 * (tmp1[1*8+i] + tmp1[7*8+i]) + 2048) >> 12;
-    v5 = tmp1[3*8+i];
-    v6 = tmp1[5*8+i];
+    v0 = (dctSqrt2 * p[0*8] + 2048) >> 12;
+    v1 = (dctSqrt2 * p[4*8] + 2048) >> 12;
+    v2 = p[2*8];
+    v3 = p[6*8];
+    v4 = (dctSqrt1d2 * (p[1*8] - p[7*8]) + 2048) >> 12;
+    v7 = (dctSqrt1d2 * (p[1*8] + p[7*8]) + 2048) >> 12;
+    v5 = p[3*8];
+    v6 = p[5*8];
 
     // stage 3
     t = (v0 - v1 + 1) >> 1;
@@ -2285,181 +2697,21 @@ GBool DCTStream::readDataUnit(DCTHuffTable *dcHuffTable,
     v6 = t;
 
     // stage 1
-    tmp1[0*8+i] = v0 + v7;
-    tmp1[7*8+i] = v0 - v7;
-    tmp1[1*8+i] = v1 + v6;
-    tmp1[6*8+i] = v1 - v6;
-    tmp1[2*8+i] = v2 + v5;
-    tmp1[5*8+i] = v2 - v5;
-    tmp1[3*8+i] = v3 + v4;
-    tmp1[4*8+i] = v3 - v4;
+    p[0*8] = v0 + v7;
+    p[7*8] = v0 - v7;
+    p[1*8] = v1 + v6;
+    p[6*8] = v1 - v6;
+    p[2*8] = v2 + v5;
+    p[5*8] = v2 - v5;
+    p[3*8] = v3 + v4;
+    p[4*8] = v3 - v4;
   }
 
   // convert to 8-bit integers
-  for (i = 0; i < 64; ++i)
-    data[i] = dctClip[dctClipOffset + 128 + ((tmp1[i] + 8) >> 4)];
-
-  return gTrue;
-}
-#endif
-
-#ifdef FP_IDCT
-GBool DCTStream::readDataUnit(DCTHuffTable *dcHuffTable,
-                             DCTHuffTable *acHuffTable,
-                             Guchar quantTable[64], int *prevDC,
-                             Guchar data[64]) {
-  double tmp1[64];
-  double v0, v1, v2, v3, v4, v5, v6, v7, t;
-  int run, size, amp;
-  int c;
-  int i, j;
-
-  // Huffman decode and dequantize
-  size = readHuffSym(dcHuffTable);
-  if (size == 9999)
-    return gFalse;
-  if (size > 0) {
-    amp = readAmp(size);
-    if (amp == 9999)
-      return gFalse;
-  } else {
-    amp = 0;
-  }
-  tmp1[0] = (*prevDC += amp) * quantTable[0];
-  for (i = 1; i < 64; ++i)
-    tmp1[i] = 0;
-  i = 1;
-  while (i < 64) {
-    run = 0;
-    while ((c = readHuffSym(acHuffTable)) == 0xf0 && run < 0x30)
-      run += 0x10;
-    if (c == 9999)
-      return gFalse;
-    if (c == 0x00) {
-      break;
-    } else {
-      run += (c >> 4) & 0x0f;
-      size = c & 0x0f;
-      amp = readAmp(size);
-      if (amp == 9999)
-       return gFalse;
-      i += run;
-      j = dctZigZag[i++];
-      tmp1[j] = amp * quantTable[j];
-    }
-  }
-
-  // inverse DCT on rows
-  for (i = 0; i < 64; i += 8) {
-
-    // stage 4
-    v0 = dctSqrt2 * tmp1[i+0];
-    v1 = dctSqrt2 * tmp1[i+4];
-    v2 = tmp1[i+2];
-    v3 = tmp1[i+6];
-    v4 = dctSqrt1d2 * (tmp1[i+1] - tmp1[i+7]);
-    v7 = dctSqrt1d2 * (tmp1[i+1] + tmp1[i+7]);
-    v5 = tmp1[i+3];
-    v6 = tmp1[i+5];
-
-    // stage 3
-    t = 0.5 * (v0 - v1);
-    v0 = 0.5 * (v0 + v1);
-    v1 = t;
-    t = v2 * dctSin6 + v3 * dctCos6;
-    v2 = v2 * dctCos6 - v3 * dctSin6;
-    v3 = t;
-    t = 0.5 * (v4 - v6);
-    v4 = 0.5 * (v4 + v6);
-    v6 = t;
-    t = 0.5 * (v7 + v5);
-    v5 = 0.5 * (v7 - v5);
-    v7 = t;
-
-    // stage 2
-    t = 0.5 * (v0 - v3);
-    v0 = 0.5 * (v0 + v3);
-    v3 = t;
-    t = 0.5 * (v1 - v2);
-    v1 = 0.5 * (v1 + v2);
-    v2 = t;
-    t = v4 * dctSin3 + v7 * dctCos3;
-    v4 = v4 * dctCos3 - v7 * dctSin3;
-    v7 = t;
-    t = v5 * dctSin1 + v6 * dctCos1;
-    v5 = v5 * dctCos1 - v6 * dctSin1;
-    v6 = t;
-
-    // stage 1
-    tmp1[i+0] = v0 + v7;
-    tmp1[i+7] = v0 - v7;
-    tmp1[i+1] = v1 + v6;
-    tmp1[i+6] = v1 - v6;
-    tmp1[i+2] = v2 + v5;
-    tmp1[i+5] = v2 - v5;
-    tmp1[i+3] = v3 + v4;
-    tmp1[i+4] = v3 - v4;
+  for (i = 0; i < 64; ++i) {
+    dataOut[i] = dctClip[dctClipOffset + 128 + ((dataIn[i] + 8) >> 4)];
   }
-
-  // inverse DCT on columns
-  for (i = 0; i < 8; ++i) {
-
-    // stage 4
-    v0 = dctSqrt2 * tmp1[0*8+i];
-    v1 = dctSqrt2 * tmp1[4*8+i];
-    v2 = tmp1[2*8+i];
-    v3 = tmp1[6*8+i];
-    v4 = dctSqrt1d2 * (tmp1[1*8+i] - tmp1[7*8+i]);
-    v7 = dctSqrt1d2 * (tmp1[1*8+i] + tmp1[7*8+i]);
-    v5 = tmp1[3*8+i];
-    v6 = tmp1[5*8+i];
-
-    // stage 3
-    t = 0.5 * (v0 - v1);
-    v0 = 0.5 * (v0 + v1);
-    v1 = t;
-    t = v2 * dctSin6 + v3 * dctCos6;
-    v2 = v2 * dctCos6 - v3 * dctSin6;
-    v3 = t;
-    t = 0.5 * (v4 - v6);
-    v4 = 0.5 * (v4 + v6);
-    v6 = t;
-    t = 0.5 * (v7 + v5);
-    v5 = 0.5 * (v7 - v5);
-    v7 = t;
-
-    // stage 2
-    t = 0.5 * (v0 - v3);
-    v0 = 0.5 * (v0 + v3);
-    v3 = t;
-    t = 0.5 * (v1 - v2);
-    v1 = 0.5 * (v1 + v2);
-    v2 = t;
-    t = v4 * dctSin3 + v7 * dctCos3;
-    v4 = v4 * dctCos3 - v7 * dctSin3;
-    v7 = t;
-    t = v5 * dctSin1 + v6 * dctCos1;
-    v5 = v5 * dctCos1 - v6 * dctSin1;
-    v6 = t;
-
-    // stage 1
-    tmp1[0*8+i] = v0 + v7;
-    tmp1[7*8+i] = v0 - v7;
-    tmp1[1*8+i] = v1 + v6;
-    tmp1[6*8+i] = v1 - v6;
-    tmp1[2*8+i] = v2 + v5;
-    tmp1[5*8+i] = v2 - v5;
-    tmp1[3*8+i] = v3 + v4;
-    tmp1[4*8+i] = v3 - v4;
-  }
-
-  // convert to 8-bit integers
-  for (i = 0; i < 64; ++i)
-    data[i] = dctClip[dctClipOffset + (int)(tmp1[i] + 128.5)];
-
-  return gTrue;
 }
-#endif
 
 int DCTStream::readHuffSym(DCTHuffTable *table) {
   Gushort code;
@@ -2527,20 +2779,9 @@ int DCTStream::readBit() {
 
 GBool DCTStream::readHeader() {
   GBool doScan;
-  int minHSample, minVSample;
-  int bufWidth;
   int n;
   int c = 0;
-  int i, j;
-
-  width = height = 0;
-  numComps = 0;
-  numQuantTables = 0;
-  numDCHuffTables = 0;
-  numACHuffTables = 0;
-  colorXform = 0;
-  gotAdobeMarker = gFalse;
-  restartInterval = 0;
+  int i;
 
   // read headers
   doScan = gFalse;
@@ -2548,31 +2789,45 @@ GBool DCTStream::readHeader() {
     c = readMarker();
     switch (c) {
     case 0xc0:                 // SOF0
-      if (!readFrameInfo())
+      if (!readBaselineSOF()) {
+       return gFalse;
+      }
+      break;
+    case 0xc2:                 // SOF2
+      if (!readProgressiveSOF()) {
        return gFalse;
+      }
       break;
     case 0xc4:                 // DHT
-      if (!readHuffmanTables())
+      if (!readHuffmanTables()) {
        return gFalse;
+      }
       break;
     case 0xd8:                 // SOI
       break;
+    case 0xd9:                 // EOI
+      return gFalse;
+      break;
     case 0xda:                 // SOS
-      if (!readScanInfo())
+      if (!readScanInfo()) {
        return gFalse;
+      }
       doScan = gTrue;
       break;
     case 0xdb:                 // DQT
-      if (!readQuantTables())
+      if (!readQuantTables()) {
        return gFalse;
+      }
       break;
     case 0xdd:                 // DRI
-      if (!readRestartInterval())
+      if (!readRestartInterval()) {
        return gFalse;
+      }
       break;
     case 0xee:                 // APP14
-      if (!readAdobeMarker())
+      if (!readAdobeMarker()) {
        return gFalse;
+      }
       break;
     case EOF:
       error(getPos(), "Bad DCT header");
@@ -2581,8 +2836,9 @@ GBool DCTStream::readHeader() {
       // skip APPn / COM / etc.
       if (c >= 0xe0) {
        n = read16() - 2;
-       for (i = 0; i < n; ++i)
+       for (i = 0; i < n; ++i) {
          str->getChar();
+       }
       } else {
        error(getPos(), "Unknown DCT marker <%02x>", c);
        return gFalse;
@@ -2591,107 +2847,98 @@ GBool DCTStream::readHeader() {
     }
   }
 
-  // compute MCU size
-  mcuWidth = minHSample = compInfo[0].hSample;
-  mcuHeight = minVSample = compInfo[0].vSample;
-  for (i = 1; i < numComps; ++i) {
-    if (compInfo[i].hSample < minHSample)
-      minHSample = compInfo[i].hSample;
-    if (compInfo[i].vSample < minVSample)
-      minVSample = compInfo[i].vSample;
-    if (compInfo[i].hSample > mcuWidth)
-      mcuWidth = compInfo[i].hSample;
-    if (compInfo[i].vSample > mcuHeight)
-      mcuHeight = compInfo[i].vSample;
-  }
-  for (i = 0; i < numComps; ++i) {
-    compInfo[i].hSample /= minHSample;
-    compInfo[i].vSample /= minVSample;
-  }
-  mcuWidth = (mcuWidth / minHSample) * 8;
-  mcuHeight = (mcuHeight / minVSample) * 8;
+  return gTrue;
+}
 
-  // allocate buffers
-  bufWidth = ((width + mcuWidth - 1) / mcuWidth) * mcuWidth;
-  for (i = 0; i < numComps; ++i)
-    for (j = 0; j < mcuHeight; ++j)
-      rowBuf[i][j] = (Guchar *)gmalloc(bufWidth * sizeof(Guchar));
+GBool DCTStream::readBaselineSOF() {
+  int length;
+  int prec;
+  int i;
+  int c;
 
-  // figure out color transform
-  if (!gotAdobeMarker && numComps == 3) {
-    if (compInfo[0].id == 1 && compInfo[1].id == 2 && compInfo[2].id == 3) {
-      colorXform = 1;
-    }
+  length = read16();
+  prec = str->getChar();
+  height = read16();
+  width = read16();
+  numComps = str->getChar();
+  if (prec != 8) {
+    error(getPos(), "Bad DCT precision %d", prec);
+    return gFalse;
   }
-
-  // initialize counters
-  comp = 0;
-  x = 0;
-  y = 0;
-  dy = mcuHeight;
-
+  for (i = 0; i < numComps; ++i) {
+    compInfo[i].id = str->getChar();
+    c = str->getChar();
+    compInfo[i].hSample = (c >> 4) & 0x0f;
+    compInfo[i].vSample = c & 0x0f;
+    compInfo[i].quantTable = str->getChar();
+  }
+  progressive = gFalse;
   return gTrue;
 }
 
-GBool DCTStream::readFrameInfo() {
+GBool DCTStream::readProgressiveSOF() {
   int length;
   int prec;
   int i;
   int c;
 
-  length = read16() - 2;
+  length = read16();
   prec = str->getChar();
   height = read16();
   width = read16();
   numComps = str->getChar();
-  length -= 6;
   if (prec != 8) {
     error(getPos(), "Bad DCT precision %d", prec);
     return gFalse;
   }
   for (i = 0; i < numComps; ++i) {
     compInfo[i].id = str->getChar();
-    compInfo[i].inScan = gFalse;
     c = str->getChar();
     compInfo[i].hSample = (c >> 4) & 0x0f;
     compInfo[i].vSample = c & 0x0f;
     compInfo[i].quantTable = str->getChar();
-    compInfo[i].dcHuffTable = 0;
-    compInfo[i].acHuffTable = 0;
   }
+  progressive = gTrue;
   return gTrue;
 }
 
 GBool DCTStream::readScanInfo() {
   int length;
-  int scanComps, id, c;
+  int id, c;
   int i, j;
 
   length = read16() - 2;
-  scanComps = str->getChar();
+  scanInfo.numComps = str->getChar();
   --length;
-  if (length != 2 * scanComps + 3) {
+  if (length != 2 * scanInfo.numComps + 3) {
     error(getPos(), "Bad DCT scan info block");
     return gFalse;
   }
-  for (i = 0; i < scanComps; ++i) {
+  interleaved = scanInfo.numComps == numComps;
+  for (j = 0; j < numComps; ++j) {
+    scanInfo.comp[j] = gFalse;
+  }
+  for (i = 0; i < scanInfo.numComps; ++i) {
     id = str->getChar();
     for (j = 0; j < numComps; ++j) {
-      if (id == compInfo[j].id)
+      if (id == compInfo[j].id) {
        break;
+      }
     }
     if (j == numComps) {
       error(getPos(), "Bad DCT component ID in scan info block");
       return gFalse;
     }
-    compInfo[j].inScan = gTrue;
+    scanInfo.comp[j] = gTrue;
     c = str->getChar();
-    compInfo[j].dcHuffTable = (c >> 4) & 0x0f;
-    compInfo[j].acHuffTable = c & 0x0f;
+    scanInfo.dcHuffTable[j] = (c >> 4) & 0x0f;
+    scanInfo.acHuffTable[j] = c & 0x0f;
   }
-  str->getChar();
-  str->getChar();
-  str->getChar();
+  scanInfo.firstCoeff = str->getChar();
+  scanInfo.lastCoeff = str->getChar();
+  c = str->getChar();
+  scanInfo.ah = (c >> 4) & 0x0f;
+  scanInfo.al = c & 0x0f;
   return gTrue;
 }
 
@@ -2779,17 +3026,25 @@ GBool DCTStream::readAdobeMarker() {
   int c;
 
   length = read16();
-  if (length != 14)
+  if (length < 14) {
     goto err;
+  }
   for (i = 0; i < 12; ++i) {
-    if ((c = str->getChar()) == EOF)
+    if ((c = str->getChar()) == EOF) {
       goto err;
+    }
     buf[i] = c;
   }
-  if (strncmp(buf, "Adobe", 5))
+  if (strncmp(buf, "Adobe", 5)) {
     goto err;
+  }
   colorXform = buf[11];
   gotAdobeMarker = gTrue;
+  for (i = 14; i < length; ++i) {
+    if (str->getChar() == EOF) {
+      goto err;
+    }
+  }
   return gTrue;
 
  err:
@@ -2927,9 +3182,13 @@ FlateStream::FlateStream(Stream *strA, int predictor, int columns,
   } else {
     pred = NULL;
   }
+  litCodeTab.codes = NULL;
+  distCodeTab.codes = NULL;
 }
 
 FlateStream::~FlateStream() {
+  gfree(litCodeTab.codes);
+  gfree(distCodeTab.codes);
   if (pred) {
     delete pred;
   }
@@ -3096,6 +3355,12 @@ GBool FlateStream::startBlock() {
   int c;
   int check;
 
+  // free the code tables from the previous block
+  gfree(litCodeTab.codes);
+  litCodeTab.codes = NULL;
+  gfree(distCodeTab.codes);
+  distCodeTab.codes = NULL;
+
   // read block header
   blockHdr = getCodeWord(3);
   if (blockHdr & 1)
@@ -3130,8 +3395,9 @@ GBool FlateStream::startBlock() {
   // compressed block with dynamic codes
   } else if (blockHdr == 2) {
     compressedBlock = gTrue;
-    if (!readDynamicCodes())
+    if (!readDynamicCodes()) {
       goto err;
+    }
 
   // unknown block type
   } else {
@@ -3150,186 +3416,182 @@ err:
 void FlateStream::loadFixedCodes() {
   int i;
 
-  // set up code arrays
-  litCodeTab.codes = allCodes;
-  distCodeTab.codes = allCodes + flateMaxLitCodes;
-
-  // initialize literal code table
-  for (i = 0; i <= 143; ++i)
-    litCodeTab.codes[i].len = 8;
-  for (i = 144; i <= 255; ++i)
-    litCodeTab.codes[i].len = 9;
-  for (i = 256; i <= 279; ++i)
-    litCodeTab.codes[i].len = 7;
-  for (i = 280; i <= 287; ++i)
-    litCodeTab.codes[i].len = 8;
-  compHuffmanCodes(&litCodeTab, flateMaxLitCodes);
-
-  // initialize distance code table
-  for (i = 0; i <= 5; ++i) {
-    distCodeTab.start[i] = 0;
+  // build the literal code table
+  for (i = 0; i <= 143; ++i) {
+    codeLengths[i] = 8;
+  }
+  for (i = 144; i <= 255; ++i) {
+    codeLengths[i] = 9;
   }
-  for (i = 6; i <= flateMaxHuffman+1; ++i) {
-    distCodeTab.start[i] = flateMaxDistCodes;
+  for (i = 256; i <= 279; ++i) {
+    codeLengths[i] = 7;
   }
+  for (i = 280; i <= 287; ++i) {
+    codeLengths[i] = 8;
+  }
+  compHuffmanCodes(codeLengths, flateMaxLitCodes, &litCodeTab);
+
+  // build the distance code table
   for (i = 0; i < flateMaxDistCodes; ++i) {
-    distCodeTab.codes[i].len = 5;
-    distCodeTab.codes[i].code = i;
-    distCodeTab.codes[i].val = i;
+    codeLengths[i] = 5;
   }
+  compHuffmanCodes(codeLengths, flateMaxDistCodes, &distCodeTab);
 }
 
 GBool FlateStream::readDynamicCodes() {
   int numCodeLenCodes;
   int numLitCodes;
   int numDistCodes;
-  FlateCode codeLenCodes[flateMaxCodeLenCodes];
+  int codeLenCodeLengths[flateMaxCodeLenCodes];
   FlateHuffmanTab codeLenCodeTab;
   int len, repeat, code;
   int i;
 
   // read lengths
-  if ((numLitCodes = getCodeWord(5)) == EOF)
+  if ((numLitCodes = getCodeWord(5)) == EOF) {
     goto err;
+  }
   numLitCodes += 257;
-  if ((numDistCodes = getCodeWord(5)) == EOF)
+  if ((numDistCodes = getCodeWord(5)) == EOF) {
     goto err;
+  }
   numDistCodes += 1;
-  if ((numCodeLenCodes = getCodeWord(4)) == EOF)
+  if ((numCodeLenCodes = getCodeWord(4)) == EOF) {
     goto err;
+  }
   numCodeLenCodes += 4;
   if (numLitCodes > flateMaxLitCodes ||
       numDistCodes > flateMaxDistCodes ||
-      numCodeLenCodes > flateMaxCodeLenCodes)
+      numCodeLenCodes > flateMaxCodeLenCodes) {
     goto err;
+  }
 
-  // read code length code table
-  codeLenCodeTab.codes = codeLenCodes;
-  for (i = 0; i < flateMaxCodeLenCodes; ++i)
-    codeLenCodes[i].len = 0;
+  // build the code length code table
+  for (i = 0; i < flateMaxCodeLenCodes; ++i) {
+    codeLenCodeLengths[i] = 0;
+  }
   for (i = 0; i < numCodeLenCodes; ++i) {
-    if ((codeLenCodes[codeLenCodeMap[i]].len = getCodeWord(3)) == -1)
+    if ((codeLenCodeLengths[codeLenCodeMap[i]] = getCodeWord(3)) == -1) {
       goto err;
+    }
   }
-  compHuffmanCodes(&codeLenCodeTab, flateMaxCodeLenCodes);
+  compHuffmanCodes(codeLenCodeLengths, flateMaxCodeLenCodes, &codeLenCodeTab);
 
-  // set up code arrays
-  litCodeTab.codes = allCodes;
-  distCodeTab.codes = allCodes + numLitCodes;
-
-  // read literal and distance code tables
+  // build the literal and distance code tables
   len = 0;
   repeat = 0;
   i = 0;
   while (i < numLitCodes + numDistCodes) {
-    if ((code = getHuffmanCodeWord(&codeLenCodeTab)) == EOF)
+    if ((code = getHuffmanCodeWord(&codeLenCodeTab)) == EOF) {
       goto err;
+    }
     if (code == 16) {
-      if ((repeat = getCodeWord(2)) == EOF)
+      if ((repeat = getCodeWord(2)) == EOF) {
        goto err;
-      for (repeat += 3; repeat > 0; --repeat)
-       allCodes[i++].len = len;
+      }
+      for (repeat += 3; repeat > 0; --repeat) {
+       codeLengths[i++] = len;
+      }
     } else if (code == 17) {
-      if ((repeat = getCodeWord(3)) == EOF)
+      if ((repeat = getCodeWord(3)) == EOF) {
        goto err;
+      }
       len = 0;
-      for (repeat += 3; repeat > 0; --repeat)
-       allCodes[i++].len = 0;
+      for (repeat += 3; repeat > 0; --repeat) {
+       codeLengths[i++] = 0;
+      }
     } else if (code == 18) {
-      if ((repeat = getCodeWord(7)) == EOF)
+      if ((repeat = getCodeWord(7)) == EOF) {
        goto err;
+      }
       len = 0;
-      for (repeat += 11; repeat > 0; --repeat)
-       allCodes[i++].len = 0;
+      for (repeat += 11; repeat > 0; --repeat) {
+       codeLengths[i++] = 0;
+      }
     } else {
-      allCodes[i++].len = len = code;
+      codeLengths[i++] = len = code;
     }
   }
-  compHuffmanCodes(&litCodeTab, numLitCodes);
-  compHuffmanCodes(&distCodeTab, numDistCodes);
+  compHuffmanCodes(codeLengths, numLitCodes, &litCodeTab);
+  compHuffmanCodes(codeLengths + numLitCodes, numDistCodes, &distCodeTab);
 
+  gfree(codeLenCodeTab.codes);
   return gTrue;
 
 err:
   error(getPos(), "Bad dynamic code table in flate stream");
+  gfree(codeLenCodeTab.codes);
   return gFalse;
 }
 
-// On entry, the <tab->codes> array contains the lengths of each code,
-// stored in code value order.  This function computes the code words.
-// The result is sorted in order of (1) code length and (2) code word.
-// The length values are no longer valid.  The <tab->start> array is
-// filled with the indexes of the first code of each length.
-void FlateStream::compHuffmanCodes(FlateHuffmanTab *tab, int n) {
-  int numLengths[flateMaxHuffman+1];
-  int nextCode[flateMaxHuffman+1];
-  int nextIndex[flateMaxHuffman+2];
-  int code;
-  int i, j;
+// Convert an array <lengths> of <n> lengths, in value order, into a
+// Huffman code lookup table.
+void FlateStream::compHuffmanCodes(int *lengths, int n, FlateHuffmanTab *tab) {
+  int tabSize, len, code, code2, skip, val, i, t;
 
-  // count number of codes for each code length
-  for (i = 0; i <= flateMaxHuffman; ++i)
-    numLengths[i] = 0;
-  for (i = 0; i < n; ++i)
-    ++numLengths[tab->codes[i].len];
+  // find max code length
+  tab->maxLen = 0;
+  for (val = 0; val < n; ++val) {
+    if (lengths[val] > tab->maxLen) {
+      tab->maxLen = lengths[val];
+    }
+  }
 
-  // compute first index for each length
-  tab->start[0] = nextIndex[0] = 0;
-  for (i = 1; i <= flateMaxHuffman + 1; ++i)
-    tab->start[i] = nextIndex[i] = tab->start[i-1] + numLengths[i-1];
+  // allocate the table
+  tabSize = 1 << tab->maxLen;
+  tab->codes = (FlateCode *)gmalloc(tabSize * sizeof(FlateCode));
 
-  // compute first code for each length
-  code = 0;
-  numLengths[0] = 0;
-  for (i = 1; i <= flateMaxHuffman; ++i) {
-    code = (code + numLengths[i-1]) << 1;
-    nextCode[i] = code;
+  // clear the table
+  for (i = 0; i < tabSize; ++i) {
+    tab->codes[i].len = 0;
+    tab->codes[i].val = 0;
   }
 
-  // compute the codes -- this permutes the codes array from value
-  // order to length/code order
-  for (i = 0; i < n; ++i) {
-    j = nextIndex[tab->codes[i].len]++;
-    if (tab->codes[i].len == 0)
-      tab->codes[j].code = 0;
-    else
-      tab->codes[j].code = nextCode[tab->codes[i].len]++;
-    tab->codes[j].val = i;
+  // build the table
+  for (len = 1, code = 0, skip = 2;
+       len <= tab->maxLen;
+       ++len, code <<= 1, skip <<= 1) {
+    for (val = 0; val < n; ++val) {
+      if (lengths[val] == len) {
+
+       // bit-reverse the code
+       code2 = 0;
+       t = code;
+       for (i = 0; i < len; ++i) {
+         code2 = (code2 << 1) | (t & 1);
+         t >>= 1;
+       }
+
+       // fill in the table entries
+       for (i = code2; i < tabSize; i += skip) {
+         tab->codes[i].len = (Gushort)len;
+         tab->codes[i].val = (Gushort)val;
+       }
+
+       ++code;
+      }
+    }
   }
 }
 
 int FlateStream::getHuffmanCodeWord(FlateHuffmanTab *tab) {
-  int len;
-  int code;
+  FlateCode *code;
   int c;
-  int i, j;
-
-  code = 0;
-  for (len = 1; len <= flateMaxHuffman; ++len) {
-
-    // add a bit to the code
-    if (codeSize == 0) {
-      if ((c = str->getChar()) == EOF)
-       return EOF;
-      codeBuf = c & 0xff;
-      codeSize = 8;
-    }
-    code = (code << 1) | (codeBuf & 1);
-    codeBuf >>= 1;
-    --codeSize;
 
-    // look for code
-    i = tab->start[len];
-    j = tab->start[len + 1];
-    if (i < j && code >= tab->codes[i].code && code <= tab->codes[j-1].code) {
-      i += code - tab->codes[i].code;
-      return tab->codes[i].val;
+  while (codeSize < tab->maxLen) {
+    if ((c = str->getChar()) == EOF) {
+      break;
     }
+    codeBuf |= (c & 0xff) << codeSize;
+    codeSize += 8;
   }
-
-  // not found
-  error(getPos(), "Bad code (%04x) in flate stream", code);
-  return EOF;
+  code = &tab->codes[codeBuf & ((1 << tab->maxLen) - 1)];
+  if (codeSize == 0 || codeSize < code->len || code->len == 0) {
+    return EOF;
+  }
+  codeBuf >>= code->len;
+  codeSize -= code->len;
+  return (int)code->val;
 }
 
 int FlateStream::getCodeWord(int bits) {
index 3319dccd109130118b85867757ab41f97ca03bc2..b0a0c26c3f44adca6c5dea7972e7434dc365f6fc 100644 (file)
@@ -9,7 +9,9 @@
 #ifndef STREAM_H
 #define STREAM_H
 
-#ifdef __GNUC__
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
 #pragma interface
 #endif
 
@@ -33,6 +35,7 @@ enum StreamKind {
   strCCITTFax,
   strDCT,
   strFlate,
+  strJBIG2,
   strWeird                     // internal-use stream types
 };
 
@@ -190,6 +193,10 @@ public:
   // at least nComps elements.  Returns false at end of file.
   GBool getPixel(Guchar *pix);
 
+  // Returns a pointer to the next line of pixels.  Returns NULL at
+  // end of file.
+  Guchar *getLine();
+
   // Skip an entire line from the image.
   void skipLine();
 
@@ -419,18 +426,26 @@ private:
 
   StreamPredictor *pred;       // predictor
   int early;                   // early parameter
-  FILE *zPipe;                 // uncompress pipe
-  GString *zName;              // .Z file name
+  GBool eof;                   // true if at eof
   int inputBuf;                        // input buffer
   int inputBits;               // number of bits in input buffer
-  int inCodeBits;              // size of input code
-  char buf[256];               // buffer
-  char *bufPtr;                        // next char to read
-  char *bufEnd;                        // end of buffer
-
-  void dumpFile(FILE *f);
+  struct {                     // decoding table
+    int length;
+    int head;
+    Guchar tail;
+  } table[4097];
+  int nextCode;                        // next code to be used
+  int nextBits;                        // number of bits in next code word
+  int prevCode;                        // previous code used in stream
+  int newChar;                 // next char to be added to table
+  Guchar seqBuf[4097];         // buffer for current sequence
+  int seqLength;               // length of current sequence
+  int seqIndex;                        // index into current sequence
+  GBool first;                 // first code after a table clear
+
+  GBool processNextCode();
+  void clearTable();
   int getCode();
-  GBool fillBuf();
 };
 
 //------------------------------------------------------------------------
@@ -517,13 +532,21 @@ private:
 // DCT component info
 struct DCTCompInfo {
   int id;                      // component ID
-  GBool inScan;                        // is this component in the current scan?
   int hSample, vSample;                // horiz/vert sampling resolutions
   int quantTable;              // quantization table number
-  int dcHuffTable, acHuffTable;        // Huffman table numbers
   int prevDC;                  // DC coefficient accumulator
 };
 
+struct DCTScanInfo {
+  GBool comp[4];               // comp[i] is set if component i is
+                               //   included in this scan
+  int numComps;                        // number of components in the scan
+  int dcHuffTable[4];          // DC Huffman table numbers
+  int acHuffTable[4];          // AC Huffman table numbers
+  int firstCoeff, lastCoeff;   // first and last DCT coefficient
+  int ah, al;                  // successive approximation parameters
+};
+
 // DCT Huffman decoding table
 struct DCTHuffTable {
   Guchar firstSym[17];         // first symbol for this bit length
@@ -547,9 +570,13 @@ public:
 
 private:
 
+  GBool progressive;           // set if in progressive mode
+  GBool interleaved;           // set if in interleaved mode
   int width, height;           // image size
   int mcuWidth, mcuHeight;     // size of min coding unit, in data units
+  int bufWidth, bufHeight;     // frameBuf size
   DCTCompInfo compInfo[4];     // info for each component
+  DCTScanInfo scanInfo;                // info for the current scan
   int numComps;                        // number of components in image
   int colorXform;              // need YCbCr-to-RGB transform?
   GBool gotAdobeMarker;                // set if APP14 Adobe marker was present
@@ -560,22 +587,33 @@ private:
   DCTHuffTable acHuffTables[4];        // AC Huffman tables
   int numDCHuffTables;         // number of DC Huffman tables
   int numACHuffTables;         // number of AC Huffman tables
-  Guchar *rowBuf[4][32];       // buffer for one MCU
+  Guchar *rowBuf[4][32];       // buffer for one MCU (non-progressive mode)
+  int *frameBuf[4];            // buffer for frame (progressive mode)
   int comp, x, y, dy;          // current position within image/MCU
   int restartCtr;              // MCUs left until restart
   int restartMarker;           // next restart marker
+  int eobRun;                  // number of EOBs left in the current run
   int inputBuf;                        // input buffer for variable length codes
   int inputBits;               // number of valid bits in input buffer
 
   void restart();
   GBool readMCURow();
-  GBool readDataUnit(DCTHuffTable *dcHuffTable, DCTHuffTable *acHuffTable,
-                    Guchar quantTable[64], int *prevDC, Guchar data[64]);
+  void readScan();
+  GBool readDataUnit(DCTHuffTable *dcHuffTable,
+                    DCTHuffTable *acHuffTable,
+                    int *prevDC, int data[64]);
+  GBool readProgressiveDataUnit(DCTHuffTable *dcHuffTable,
+                               DCTHuffTable *acHuffTable,
+                               int *prevDC, int data[64]);
+  void decodeImage();
+  void transformDataUnit(Guchar *quantTable,
+                        int dataIn[64], Guchar dataOut[64]);
   int readHuffSym(DCTHuffTable *table);
   int readAmp(int size);
   int readBit();
   GBool readHeader();
-  GBool readFrameInfo();
+  GBool readBaselineSOF();
+  GBool readProgressiveSOF();
   GBool readScanInfo();
   GBool readQuantTables();
   GBool readHuffmanTables();
@@ -599,15 +637,13 @@ private:
 
 // Huffman code table entry
 struct FlateCode {
-  int len;                     // code length in bits
-  int code;                    // code word
-  int val;                     // value represented by this code
+  Gushort len;                 // code length, in bits
+  Gushort val;                 // value represented by this code
 };
 
-// Huffman code table
 struct FlateHuffmanTab {
-  int start[flateMaxHuffman+2];        // indexes of first code of each length
-  FlateCode *codes;            // codes, sorted by length and code word
+  FlateCode *codes;
+  int maxLen;
 };
 
 // Decoding info for length and distance code words
@@ -638,8 +674,8 @@ private:
   int remain;                  // number valid bytes in output buffer
   int codeBuf;                 // input buffer
   int codeSize;                        // number of bits in input buffer
-  FlateCode                    // literal and distance codes
-    allCodes[flateMaxLitCodes + flateMaxDistCodes];
+  int                          // literal and distance code lengths
+    codeLengths[flateMaxLitCodes + flateMaxDistCodes];
   FlateHuffmanTab litCodeTab;  // literal code table
   FlateHuffmanTab distCodeTab; // distance code table
   GBool compressedBlock;       // set if reading a compressed block
@@ -658,7 +694,7 @@ private:
   GBool startBlock();
   void loadFixedCodes();
   GBool readDynamicCodes();
-  void compHuffmanCodes(FlateHuffmanTab *tab, int n);
+  void compHuffmanCodes(int *lengths, int n, FlateHuffmanTab *tab);
   int getHuffmanCodeWord(FlateHuffmanTab *tab);
   int getCodeWord(int bits);
 };
index 5e5761fe04c4076e571ae2e2e32cbeb1662603f4..891752cc7df1a84982b38a2cf22fb972c4e667fe 100644 (file)
@@ -6,11 +6,12 @@
 //
 //========================================================================
 
-#ifdef __GNUC__
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
 #pragma implementation
 #endif
 
-#include <aconf.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <stddef.h>
 #include "ICSupport.h"
 #endif
 
+//------------------------------------------------------------------------
+
+#define textOutSpace    0.2
+#define textOutColSpace 0.2
+
+//------------------------------------------------------------------------
+
+struct TextOutColumnEdge {
+  double x, y0, y1;
+};
+
+//------------------------------------------------------------------------
+// TextBlock
+//------------------------------------------------------------------------
+
+class TextBlock {
+public:
+
+  TextBlock();
+  ~TextBlock();
+
+  double xMin, xMax;
+  double yMin, yMax;
+  TextString *strings;         // list of strings in the block
+  TextBlock *next;             // next block in line
+  TextBlock *xyNext;           // next block on xyBlocks list
+  Unicode *text;               // Unicode text of the block, including
+                               //   spaces between strings
+  double *xRight;              // right-hand x coord of each char
+  int len;                     // total number of Unicode characters
+  int convertedLen;            // total number of converted characters
+  int *col;                    // starting column number for each
+                               //   Unicode character
+};
+
+TextBlock::TextBlock() {
+  strings = NULL;
+  next = NULL;
+  xyNext = NULL;
+  text = NULL;
+  xRight = NULL;
+  col = NULL;
+}
+
+TextBlock::~TextBlock() {
+  TextString *p1, *p2;
+
+  for (p1 = strings; p1; p1 = p2) {
+    p2 = p1->next;
+    delete p1;
+  }
+  gfree(text);
+  gfree(xRight);
+  gfree(col);
+}
+
+//------------------------------------------------------------------------
+// TextLine
+//------------------------------------------------------------------------
+
+class TextLine {
+public:
+
+  TextLine();
+  ~TextLine();
+
+  TextBlock *blocks;
+  TextLine *next;
+  double yMin, yMax;
+};
+
+TextLine::TextLine() {
+  blocks = NULL;
+  next = NULL;
+}
+
+TextLine::~TextLine() {
+  TextBlock *p1, *p2;
+
+  for (p1 = blocks; p1; p1 = p2) {
+    p2 = p1->next;
+    delete p1;
+  }
+}
+
 //------------------------------------------------------------------------
 // TextString
 //------------------------------------------------------------------------
 
-TextString::TextString(GfxState *state, double fontSize) {
+TextString::TextString(GfxState *state, double x0, double y0,
+                      double fontSize) {
   GfxFont *font;
   double x, y;
 
-  state->transform(state->getCurX(), state->getCurY(), &x, &y);
+  state->transform(x0, y0, &x, &y);
   if ((font = state->getFont())) {
     yMin = y - font->getAscent() * fontSize;
     yMax = y - font->getDescent() * fontSize;
@@ -54,14 +141,14 @@ TextString::TextString(GfxState *state, double fontSize) {
     yMin = y;
     yMax = y + 1;
   }
-  col = 0;
+  marked = gFalse;
   text = NULL;
   xRight = NULL;
   len = size = 0;
-  yxNext = NULL;
-  xyNext = NULL;
+  next = NULL;
 }
 
+
 TextString::~TextString() {
   gfree(text);
   gfree(xRight);
@@ -90,10 +177,11 @@ TextPage::TextPage(GBool rawOrderA) {
   rawOrder = rawOrderA;
   curStr = NULL;
   fontSize = 0;
-  yxStrings = NULL;
   xyStrings = NULL;
-  yxCur1 = yxCur2 = NULL;
+  xyCur1 = xyCur2 = NULL;
+  lines = NULL;
   nest = 0;
+  nTinyChars = 0;
 }
 
 TextPage::~TextPage() {
@@ -104,7 +192,7 @@ void TextPage::updateFont(GfxState *state) {
   GfxFont *font;
   double *fm;
   char *name;
-  int code;
+  int code, mCode, letterCode, anyCode;
   double w;
 
   // adjust the font size
@@ -116,18 +204,33 @@ void TextPage::updateFont(GfxState *state) {
     // rendering the font.  This code tries to guess by looking at the
     // width of the character 'm' (which breaks if the font is a
     // subset that doesn't contain 'm').
+    mCode = letterCode = anyCode = -1;
     for (code = 0; code < 256; ++code) {
-      if ((name = ((Gfx8BitFont *)font)->getCharName(code)) &&
-         name[0] == 'm' && name[1] == '\0') {
-       break;
+      name = ((Gfx8BitFont *)font)->getCharName(code);
+      if (name && name[0] == 'm' && name[1] == '\0') {
+       mCode = code;
       }
-    }
-    if (code < 256) {
-      w = ((Gfx8BitFont *)font)->getWidth(code);
-      if (w != 0) {
-       // 600 is a generic average 'm' width -- yes, this is a hack
-       fontSize *= w / 0.6;
+      if (letterCode < 0 && name && name[1] == '\0' &&
+         ((name[0] >= 'A' && name[0] <= 'Z') ||
+          (name[0] >= 'a' && name[0] <= 'z'))) {
+       letterCode = code;
       }
+      if (anyCode < 0 && name && ((Gfx8BitFont *)font)->getWidth(code) > 0) {
+       anyCode = code;
+      }
+    }
+    if (mCode >= 0 &&
+       (w = ((Gfx8BitFont *)font)->getWidth(mCode)) > 0) {
+      // 0.6 is a generic average 'm' width -- yes, this is a hack
+      fontSize *= w / 0.6;
+    } else if (letterCode >= 0 &&
+              (w = ((Gfx8BitFont *)font)->getWidth(letterCode)) > 0) {
+      // even more of a hack: 0.5 is a generic letter width
+      fontSize *= w / 0.5;
+    } else if (anyCode >= 0 &&
+              (w = ((Gfx8BitFont *)font)->getWidth(anyCode)) > 0) {
+      // better than nothing: 0.5 is a generic character width
+      fontSize *= w / 0.5;
     }
     fm = font->getFontMatrix();
     if (fm[0] != 0) {
@@ -136,7 +239,7 @@ void TextPage::updateFont(GfxState *state) {
   }
 }
 
-void TextPage::beginString(GfxState *state) {
+void TextPage::beginString(GfxState *state, double x0, double y0) {
   // This check is needed because Type 3 characters can contain
   // text-drawing operations.
   if (curStr) {
@@ -144,7 +247,7 @@ void TextPage::beginString(GfxState *state) {
     return;
   }
 
-  curStr = new TextString(state, fontSize);
+  curStr = new TextString(state, x0, y0, fontSize);
 }
 
 void TextPage::addChar(GfxState *state, double x, double y,
@@ -153,17 +256,33 @@ void TextPage::addChar(GfxState *state, double x, double y,
   int n, i;
 
   state->transform(x, y, &x1, &y1);
-  n = curStr->len;
-  if (n > 0 &&
-      x1 - curStr->xRight[n-1] > 0.1 * (curStr->yMax - curStr->yMin)) {
-    endString();
-    beginString(state);
+  if (x1 < 0 || x1 > state->getPageWidth() ||
+      y1 < 0 || y1 > state->getPageHeight()) {
+    return;
   }
   state->textTransformDelta(state->getCharSpace() * state->getHorizScaling(),
                            0, &dx2, &dy2);
   dx -= dx2;
   dy -= dy2;
   state->transformDelta(dx, dy, &w1, &h1);
+  if (!globalParams->getTextKeepTinyChars() &&
+      fabs(w1) < 3 && fabs(h1) < 3) {
+    if (++nTinyChars > 20000) {
+      return;
+    }
+  }
+  n = curStr->len;
+  if (n > 0 && x1 - curStr->xRight[n-1] >
+               0.1 * (curStr->yMax - curStr->yMin)) {
+    // large char spacing is sometimes used to move text around
+    endString();
+    beginString(state, x, y);
+  }
+  if (uLen == 1 && u[0] == (Unicode)0x20 &&
+      w1 > 0.5 * (curStr->yMax - curStr->yMin)) {
+    // large word spacing is sometimes used to move text around
+    return;
+  }
   if (uLen != 0) {
     w1 /= uLen;
     h1 /= uLen;
@@ -174,9 +293,6 @@ void TextPage::addChar(GfxState *state, double x, double y,
 }
 
 void TextPage::endString() {
-  TextString *p1, *p2;
-  double h, y1, y2;
-
   // This check is needed because Type 3 characters can contain
   // text-drawing operations.
   if (nest > 0) {
@@ -184,58 +300,72 @@ void TextPage::endString() {
     return;
   }
 
+  addString(curStr);
+  curStr = NULL;
+}
+
+void TextPage::addString(TextString *str) {
+  TextString *p1, *p2;
+
   // throw away zero-length strings -- they don't have valid xMin/xMax
   // values, and they're useless anyway
-  if (curStr->len == 0) {
-    delete curStr;
-    curStr = NULL;
+  if (str->len == 0) {
+    delete str;
     return;
   }
 
-  // insert string in y-major list
-  h = curStr->yMax - curStr->yMin;
-  y1 = curStr->yMin + 0.5 * h;
-  y2 = curStr->yMin + 0.8 * h;
+  // insert string in xy list
   if (rawOrder) {
-    p1 = yxCur1;
+    p1 = xyCur1;
     p2 = NULL;
-  } else if ((!yxCur1 ||
-             (y1 >= yxCur1->yMin &&
-              (y2 >= yxCur1->yMax || curStr->xMax >= yxCur1->xMin))) &&
-            (!yxCur2 ||
-             (y1 < yxCur2->yMin ||
-              (y2 < yxCur2->yMax && curStr->xMax < yxCur2->xMin)))) {
-    p1 = yxCur1;
-    p2 = yxCur2;
+  } else if ((!xyCur1 || xyBefore(xyCur1, str)) &&
+            (!xyCur2 || xyBefore(str, xyCur2))) {
+    p1 = xyCur1;
+    p2 = xyCur2;
+  } else if (xyCur1 && xyBefore(xyCur1, str)) {
+    for (p1 = xyCur1, p2 = xyCur2; p2; p1 = p2, p2 = p2->next) {
+      if (xyBefore(str, p2)) {
+       break;
+      }
+    }
+    xyCur2 = p2;
   } else {
-    for (p1 = NULL, p2 = yxStrings; p2; p1 = p2, p2 = p2->yxNext) {
-      if (y1 < p2->yMin || (y2 < p2->yMax && curStr->xMax < p2->xMin)) {
+    for (p1 = NULL, p2 = xyStrings; p2; p1 = p2, p2 = p2->next) {
+      if (xyBefore(str, p2)) {
        break;
       }
     }
-    yxCur2 = p2;
+    xyCur2 = p2;
   }
-  yxCur1 = curStr;
+  xyCur1 = str;
   if (p1) {
-    p1->yxNext = curStr;
+    p1->next = str;
   } else {
-    yxStrings = curStr;
+    xyStrings = str;
   }
-  curStr->yxNext = p2;
-  curStr = NULL;
+  str->next = p2;
 }
 
 void TextPage::coalesce() {
-  TextString *str1, *str2;
-  double space, d;
-  GBool addSpace;
-  int n, i;
+  TextLine *line, *line0;
+  TextBlock *yxBlocks, *xyBlocks, *blk, *blk0, *blk1, *blk2;
+  TextString *str0, *str1, *str2, *str3, *str4;
+  TextString *str1prev, *str2prev, *str3prev;
+  TextOutColumnEdge *edges;
+  UnicodeMap *uMap;
+  GBool isUnicode;
+  char buf[8];
+  int edgesLength, edgesSize;
+  double x, yMin, yMax;
+  double space, fit1, fit2, h;
+  int col1, col2, d;
+  int i, j;
 
 #if 0 //~ for debugging
-  for (str1 = yxStrings; str1; str1 = str1->yxNext) {
-    printf("x=%3d..%3d  y=%3d..%3d  size=%2d '",
-          (int)str1->xMin, (int)str1->xMax, (int)str1->yMin, (int)str1->yMax,
-          (int)(str1->yMax - str1->yMin));
+  for (str1 = xyStrings; str1; str1 = str1->next) {
+    printf("x=%.2f..%.2f  y=%.2f..%.2f  size=%.2f '",
+          str1->xMin, str1->xMax, str1->yMin, str1->yMax,
+          (str1->yMax - str1->yMin));
     for (i = 0; i < str1->len; ++i) {
       fputc(str1->text[i] & 0xff, stdout);
     }
@@ -243,123 +373,493 @@ void TextPage::coalesce() {
   }
   printf("\n------------------------------------------------------------\n\n");
 #endif
-  str1 = yxStrings;
-  while (str1 && (str2 = str1->yxNext)) {
-    space = str1->yMax - str1->yMin;
-    d = str2->xMin - str1->xMax;
-    if (((rawOrder &&
-         ((str2->yMin >= str1->yMin && str2->yMin <= str1->yMax) ||
-          (str2->yMax >= str1->yMin && str2->yMax <= str1->yMax))) ||
-        (!rawOrder && str2->yMin < str1->yMax)) &&
-       d > -0.5 * space && d < space) {
-      n = str1->len + str2->len;
-      if ((addSpace = d > 0.1 * space)) {
-       ++n;
-      }
-      str1->size = (n + 15) & ~15;
-      str1->text = (Unicode *)grealloc(str1->text,
-                                      str1->size * sizeof(Unicode));
-      str1->xRight = (double *)grealloc(str1->xRight,
-                                       str1->size * sizeof(double));
-      if (addSpace) {
-       str1->text[str1->len] = 0x20;
-       str1->xRight[str1->len] = str2->xMin;
-       ++str1->len;
-      }
-      for (i = 0; i < str2->len; ++i) {
-       str1->text[str1->len] = str2->text[i];
-       str1->xRight[str1->len] = str2->xRight[i];
-       ++str1->len;
-      }
-      if (str2->xMax > str1->xMax) {
-       str1->xMax = str2->xMax;
-      }
-      if (str2->yMax > str1->yMax) {
-       str1->yMax = str2->yMax;
-      }
-      str1->yxNext = str2->yxNext;
-      delete str2;
+
+  // build the list of column edges
+  edges = NULL;
+  edgesLength = edgesSize = 0;
+  if (!rawOrder) {
+    for (str1prev = NULL, str1 = xyStrings;
+        str1;
+        str1prev = str1, str1 = str1->next) {
+      if (str1->marked) {
+       continue;
+      }
+      h = str1->yMax - str1->yMin;
+      if (str1prev && (str1->xMin - str1prev->xMax) / h < textOutColSpace) {
+       continue;
+      }
+      x = str1->xMin;
+      yMin = str1->yMin;
+      yMax = str1->yMax;
+      for (str2prev = str1, str2 = str1->next;
+          str2;
+          str2prev = str2, str2 = str2->next) {
+       h = str2->yMax - str2->yMin;
+       if (!str2->marked &&
+           (str2->xMin - str2prev->xMax) / h > textOutColSpace &&
+           fabs(str2->xMin - x) < 0.5 &&
+           str2->yMin - yMax < 0.3 * h &&
+           yMin - str2->yMax < 0.3 * h) {
+         break;
+       }
+      }
+      if (str2) {
+       if (str2->yMin < yMin) {
+         yMin = str2->yMin;
+       }
+       if (str2->yMax > yMax) {
+         yMax = str2->yMax;
+       }
+       str2->marked = gTrue;
+       for (str3prev = str1, str3 = str1->next;
+            str3;
+            str3prev = str3, str3 = str3->next) {
+         h = str3->yMax - str3->yMin;
+         if (!str3->marked &&
+             (str3->xMin - str3prev->xMax) / h > textOutColSpace &&
+             fabs(str3->xMin - x) < 0.5 &&
+             str3->yMin - yMax < 0.3 * h &&
+             yMin - str3->yMax < 0.3 * h) {
+           break;
+         }
+       }
+       if (str3) {
+         if (str3->yMin < yMin) {
+           yMin = str3->yMin;
+         }
+         if (str3->yMax > yMax) {
+           yMax = str3->yMax;
+         }
+         str3->marked = gTrue;
+         do {
+           for (str2prev = str1, str2 = str1->next;
+                str2;
+                str2prev = str2, str2 = str2->next) {
+             h = str2->yMax - str2->yMin;
+             if (!str2->marked &&
+                 (str2->xMin - str2prev->xMax) / h > textOutColSpace &&
+                 fabs(str2->xMin - x) < 0.5 &&
+                 str2->yMin - yMax < 0.3 * h &&
+                 yMin - str2->yMax < 0.3 * h) {
+               if (str2->yMin < yMin) {
+                 yMin = str2->yMin;
+               }
+               if (str2->yMax > yMax) {
+                 yMax = str2->yMax;
+               }
+               str2->marked = gTrue;
+               break;
+             }
+           }
+         } while (str2);
+         if (edgesLength == edgesSize) {
+           edgesSize = edgesSize ? 2 * edgesSize : 16;
+           edges = (TextOutColumnEdge *)
+             grealloc(edges, edgesSize * sizeof(TextOutColumnEdge));
+         }
+         edges[edgesLength].x = x;
+         edges[edgesLength].y0 = yMin;
+         edges[edgesLength].y1 = yMax;
+         ++edgesLength;
+       } else {
+         str2->marked = gFalse;
+       }
+      }
+      str1->marked = gTrue;
+    }
+  }
+
+#if 0 //~ for debugging
+  printf("column edges:\n");
+  for (i = 0; i < edgesLength; ++i) {
+    printf("%d: x=%.2f y0=%.2f y1=%.2f\n",
+          i, edges[i].x, edges[i].y0, edges[i].y1);
+  }
+  printf("\n------------------------------------------------------------\n\n");
+#endif
+
+  // build the blocks
+  yxBlocks = NULL;
+  blk1 = blk2 = NULL;
+  while (xyStrings) {
+
+    // build the block
+    str0 = xyStrings;
+    xyStrings = xyStrings->next;
+    str0->next = NULL;
+    blk = new TextBlock();
+    blk->strings = str0;
+    blk->xMin = str0->xMin;
+    blk->xMax = str0->xMax;
+    blk->yMin = str0->yMin;
+    blk->yMax = str0->yMax;
+    while (xyStrings) {
+      str1 = NULL;
+      str2 = xyStrings;
+      fit1 = coalesceFit(str0, str2);
+      if (!rawOrder) {
+       // look for best-fitting string
+       space = str0->yMax - str0->yMin;
+       for (str3 = xyStrings, str4 = xyStrings->next;
+            str4 && str4->xMin - str0->xMax <= space;
+            str3 = str4, str4 = str4->next) {
+         fit2 = coalesceFit(str0, str4);
+         if (fit2 < fit1) {
+           str1 = str3;
+           str2 = str4;
+           fit1 = fit2;
+         }
+       }
+      }
+      if (fit1 > 1) {
+       // no fit - we're done with this block
+       break;
+      }
+
+      // if we've hit a column edge we're done with this block
+      if (fit1 > 0.2) {
+       for (i = 0; i < edgesLength; ++i) {
+         if (str0->xMax < edges[i].x + 0.5 && edges[i].x - 0.5 < str2->xMin &&
+             str0->yMin < edges[i].y1 && str0->yMax > edges[i].y0 &&
+             str2->yMin < edges[i].y1 && str2->yMax > edges[i].y0) {
+           break;
+         }
+       }
+       if (i < edgesLength) {
+         break;
+       }
+      }
+
+      if (str1) {
+       str1->next = str2->next;
+      } else {
+       xyStrings = str2->next;
+      }
+      str0->next = str2;
+      str2->next = NULL;
+      if (str2->xMax > blk->xMax) {
+       blk->xMax = str2->xMax;
+      }
+      if (str2->yMin < blk->yMin) {
+       blk->yMin = str2->yMin;
+      }
+      if (str2->yMax > blk->yMax) {
+       blk->yMax = str2->yMax;
+      }
+      str0 = str2;
+    }
+
+    // insert block on list
+    if (!rawOrder) {
+      // insert block on list in yx order
+      for (blk1 = NULL, blk2 = yxBlocks;
+          blk2 && !yxBefore(blk, blk2);
+          blk1 = blk2, blk2 = blk2->next) ;
+    }
+    blk->next = blk2;
+    if (blk1) {
+      blk1->next = blk;
+    } else {
+      yxBlocks = blk;
+    }
+    blk1 = blk;
+  }
+
+  gfree(edges);
+
+  // the strings are now owned by the lines/blocks tree
+  xyStrings = NULL;
+
+  // build the block text
+  uMap = globalParams->getTextEncoding();
+  isUnicode = uMap ? uMap->isUnicode() : gFalse;
+  for (blk = yxBlocks; blk; blk = blk->next) {
+    blk->len = 0;
+    for (str1 = blk->strings; str1; str1 = str1->next) {
+      blk->len += str1->len;
+      if (str1->next && str1->next->xMin - str1->xMax >
+                       textOutSpace * (str1->yMax - str1->yMin)) {
+       str1->spaceAfter = gTrue;
+       ++blk->len;
+      } else {
+       str1->spaceAfter = gFalse;
+      }
+    }
+    blk->text = (Unicode *)gmalloc(blk->len * sizeof(Unicode));
+    blk->xRight = (double *)gmalloc(blk->len * sizeof(double));
+    blk->col = (int *)gmalloc(blk->len * sizeof(int));
+    i = 0;
+    for (str1 = blk->strings; str1; str1 = str1->next) {
+      for (j = 0; j < str1->len; ++j) {
+       blk->text[i] = str1->text[j];
+       blk->xRight[i] = str1->xRight[j];
+       ++i;
+      }
+      if (str1->spaceAfter) {
+       blk->text[i] = (Unicode)0x0020;
+       blk->xRight[i] = str1->next->xMin;
+       ++i;
+      }
+    }
+    blk->convertedLen = 0;
+    for (j = 0; j < blk->len; ++j) {
+      blk->col[j] = blk->convertedLen;
+      if (isUnicode) {
+       ++blk->convertedLen;
+      } else if (uMap) {
+       blk->convertedLen += uMap->mapUnicode(blk->text[j], buf, sizeof(buf));
+      }
+    }
+  }
+  if (uMap) {
+    uMap->decRefCnt();
+  }
+
+#if 0 //~ for debugging
+  for (blk = yxBlocks; blk; blk = blk->next) {
+    printf("[block: x=%.2f..%.2f y=%.2f..%.2f len=%d]\n",
+          blk->xMin, blk->xMax, blk->yMin, blk->yMax, blk->len);
+    TextString *str;
+    for (str = blk->strings; str; str = str->next) {
+      printf("    x=%.2f..%.2f  y=%.2f..%.2f  size=%.2f'",
+            str->xMin, str->xMax, str->yMin, str->yMax,
+            (str->yMax - str->yMin));
+      for (i = 0; i < str->len; ++i) {
+       fputc(str->text[i] & 0xff, stdout);
+      }
+      if (str->spaceAfter) {
+       fputc(' ', stdout);
+      }
+      printf("'\n");
+    }
+  }
+  printf("\n------------------------------------------------------------\n\n");
+#endif
+
+  // build the lines
+  lines = NULL;
+  line0 = NULL;
+  while (yxBlocks) {
+    blk0 = yxBlocks;
+    yxBlocks = yxBlocks->next;
+    blk0->next = NULL;
+    line = new TextLine();
+    line->blocks = blk0;
+    line->yMin = blk0->yMin;
+    line->yMax = blk0->yMax;
+    while (yxBlocks) {
+
+      // remove duplicated text (fake boldface, shadowed text)
+      h = blk0->yMax - blk0->yMin;
+      if (yxBlocks->len == blk0->len &&
+         !memcmp(yxBlocks->text, blk0->text,
+                 yxBlocks->len * sizeof(Unicode)) &&
+         fabs(yxBlocks->yMin - blk0->yMin) / h < 0.2 &&
+         fabs(yxBlocks->yMax - blk0->yMax) / h < 0.2 &&
+         fabs(yxBlocks->xMin - blk0->xMin) / h < 0.2 &&
+         fabs(yxBlocks->xMax - blk0->xMax) / h < 0.2) {
+       blk1 = yxBlocks;
+       yxBlocks = yxBlocks->next;
+       delete blk1;
+       continue;
+      }
+
+      if (rawOrder && yxBlocks->yMax < blk0->yMin) {
+       break;
+      }
+      if (yxBlocks->yMin > 0.2*blk0->yMin + 0.8*blk0->yMax ||
+         yxBlocks->xMin < blk0->xMax) {
+       break;
+      }
+      blk1 = yxBlocks;
+      yxBlocks = yxBlocks->next;
+      blk0->next = blk1;
+      blk1->next = NULL;
+      if (blk1->yMin < line->yMin) {
+       line->yMin = blk1->yMin;
+      }
+      if (blk1->yMax > line->yMax) {
+       line->yMax = blk1->yMax;
+      }
+      blk0 = blk1;
+    }
+    if (line0) {
+      line0->next = line;
     } else {
-      str1 = str2;
+      lines = line;
+    }
+    line->next = NULL;
+    line0 = line;
+  }
+
+
+  // sort the blocks into xy order
+  xyBlocks = NULL;
+  for (line = lines; line; line = line->next) {
+    for (blk = line->blocks; blk; blk = blk->next) {
+      for (blk1 = NULL, blk2 = xyBlocks;
+          blk2 && !xyBefore(blk, blk2);
+          blk1 = blk2, blk2 = blk2->xyNext) ;
+      blk->xyNext = blk2;
+      if (blk1) {
+       blk1->xyNext = blk;
+      } else {
+       xyBlocks = blk;
+      }
+    }
+  }
+
+#if 0 //~ for debugging
+  for (blk = xyBlocks; blk; blk = blk->xyNext) {
+    printf("[block: x=%.2f..%.2f y=%.2f..%.2f len=%d]\n",
+          blk->xMin, blk->xMax, blk->yMin, blk->yMax, blk->len);
+    TextString *str;
+    for (str = blk->strings; str; str = str->next) {
+      printf("    x=%.2f..%.2f  y=%.2f..%.2f  size=%.2f '",
+            str->xMin, str->xMax, str->yMin, str->yMax,
+            (str->yMax - str->yMin));
+      for (i = 0; i < str->len; ++i) {
+       fputc(str->text[i] & 0xff, stdout);
+      }
+      printf("'\n");
+    }
+  }
+  printf("\n------------------------------------------------------------\n\n");
+#endif
+
+  // do column assignment
+  for (blk1 = xyBlocks; blk1; blk1 = blk1->xyNext) {
+    col1 = 0;
+    for (blk2 = xyBlocks; blk2 != blk1; blk2 = blk2->xyNext) {
+      if (blk1->xMin >= blk2->xMax) {
+       d = (int)((blk1->xMin - blk2->xMax) /
+                 (0.4 * (blk1->yMax - blk1->yMin)));
+       if (d > 4) {
+         d = 4;
+       }
+       col2 = blk2->col[0] + blk2->convertedLen + d;
+       if (col2 > col1) {
+         col1 = col2;
+       }
+      } else if (blk1->xMin > blk2->xMin) {
+       for (i = 0; i < blk2->len && blk1->xMin >= blk2->xRight[i]; ++i) ;
+       col2 = blk2->col[i];
+       if (col2 > col1) {
+         col1 = col2;
+       }
+      }
+    }
+    for (j = 0; j < blk1->len; ++j) {
+      blk1->col[j] += col1;
     }
   }
+
+#if 0 //~ for debugging
+  for (line = lines; line; line = line->next) {
+    printf("[line]\n");
+    for (blk = line->blocks; blk; blk = blk->next) {
+      printf("[block: col=%d, len=%d]\n", blk->col[0], blk->len);
+      TextString *str;
+      for (str = blk->strings; str; str = str->next) {
+       printf("    x=%.2f..%.2f  y=%.2f..%.2f  size=%.2f '",
+              str->xMin, str->xMax, str->yMin, str->yMax,
+              (str->yMax - str->yMin));
+       for (i = 0; i < str->len; ++i) {
+         fputc(str->text[i] & 0xff, stdout);
+       }
+       if (str->spaceAfter) {
+         printf(" [space]\n");
+       }
+       printf("'\n");
+      }
+    }
+  }
+  printf("\n------------------------------------------------------------\n\n");
+#endif
 }
 
+
 GBool TextPage::findText(Unicode *s, int len,
                         GBool top, GBool bottom,
                         double *xMin, double *yMin,
                         double *xMax, double *yMax) {
-  TextString *str;
+  TextLine *line;
+  TextBlock *blk;
   Unicode *p;
   Unicode u1, u2;
   int m, i, j;
-  double x;
+  double x0, x1, x;
 
-  // scan all strings on page
-  for (str = yxStrings; str; str = str->yxNext) {
-
-    // check: above top limit?
-    if (!top && (str->yMax < *yMin ||
-                (str->yMin < *yMin && str->xMax <= *xMin))) {
-      continue;
-    }
-
-    // check: below bottom limit?
-    if (!bottom && (str->yMin > *yMax ||
-                   (str->yMax > *yMax && str->xMin >= *xMax))) {
-      return gFalse;
-    }
-
-    // search each position in this string
-    m = str->len;
-    for (i = 0, p = str->text; i <= m - len; ++i, ++p) {
+  // scan all blocks on page
+  for (line = lines; line; line = line->next) {
+    for (blk = line->blocks; blk; blk = blk->next) {
 
       // check: above top limit?
-      if (!top && str->yMin < *yMin) {
-       x = (((i == 0) ? str->xMin : str->xRight[i-1]) + str->xRight[i]) / 2;
-       if (x < *xMin) {
-         continue;
-       }
+      if (!top && (blk->yMax < *yMin ||
+                  (blk->yMin < *yMin && blk->xMax <= *xMin))) {
+       continue;
       }
 
       // check: below bottom limit?
-      if (!bottom && str->yMax > *yMax) {
-       x = (((i == 0) ? str->xMin : str->xRight[i-1]) + str->xRight[i]) / 2;
-       if (x > *xMax) {
-         return gFalse;
-       }
+      if (!bottom && (blk->yMin > *yMax ||
+                     (blk->yMax > *yMax && blk->xMin >= *xMax))) {
+       return gFalse;
       }
 
-      // compare the strings
-      for (j = 0; j < len; ++j) {
-#if 1 //~ this lowercases Latin A-Z only -- this will eventually be
-      //~ extended to handle other character sets
-       if (p[j] >= 0x41 && p[j] <= 0x5a) {
-         u1 = p[j] + 0x20;
-       } else {
-         u1 = p[j];
+      // search each position in this block
+      m = blk->len;
+      for (i = 0, p = blk->text; i <= m - len; ++i, ++p) {
+
+       x0 = (i == 0) ? blk->xMin : blk->xRight[i-1];
+       x1 = blk->xRight[i];
+       x = 0.5 * (x0 + x1);
+
+       // check: above top limit?
+       if (!top && blk->yMin < *yMin) {
+         if (x < *xMin) {
+           continue;
+         }
        }
-       if (s[j] >= 0x41 && s[j] <= 0x5a) {
-         u2 = s[j] + 0x20;
-       } else {
-         u2 = s[j];
+
+       // check: below bottom limit?
+       if (!bottom && blk->yMax > *yMax) {
+         if (x > *xMax) {
+           return gFalse;
+         }
        }
+
+       // compare the strings
+       for (j = 0; j < len; ++j) {
+#if 1 //~ this lowercases Latin A-Z only -- this will eventually be
+         //~ extended to handle other character sets
+         if (p[j] >= 0x41 && p[j] <= 0x5a) {
+           u1 = p[j] + 0x20;
+         } else {
+           u1 = p[j];
+         }
+         if (s[j] >= 0x41 && s[j] <= 0x5a) {
+           u2 = s[j] + 0x20;
+         } else {
+           u2 = s[j];
+         }
 #endif
-       if (u1 != u2) {
-         break;
+         if (u1 != u2) {
+           break;
+         }
        }
-      }
 
-      // found it
-      if (j == len) {
-       *xMin = (i == 0) ? str->xMin : str->xRight[i-1];
-       *xMax = str->xRight[i + len - 1];
-       *yMin = str->yMin;
-       *yMax = str->yMax;
-       return gTrue;
+       // found it
+       if (j == len) {
+         *xMin = x0;
+         *xMax = blk->xRight[i + len - 1];
+         *yMin = blk->yMin;
+         *yMax = blk->yMax;
+         return gTrue;
+       }
       }
     }
   }
+
   return gFalse;
 }
 
@@ -367,18 +867,22 @@ GString *TextPage::getText(double xMin, double yMin,
                           double xMax, double yMax) {
   GString *s;
   UnicodeMap *uMap;
+  GBool isUnicode;
   char space[8], eol[16], buf[8];
-  int spaceLen, eolLen, n;
-  TextString *str1;
-  double x0, x1, x2, y;
-  double xPrev, yPrev;
-  int i1, i2, i;
+  int spaceLen, eolLen, len;
+  TextLine *line;
+  TextBlock *blk;
+  double x0, x1, y;
+  int firstCol, col, i;
   GBool multiLine;
 
   s = new GString();
+
+  // get the output encoding
   if (!(uMap = globalParams->getTextEncoding())) {
     return s;
   }
+  isUnicode = uMap->isUnicode();
   spaceLen = uMap->mapUnicode(0x20, space, sizeof(space));
   eolLen = 0; // make gcc happy
   switch (globalParams->getTextEOL()) {
@@ -393,61 +897,126 @@ GString *TextPage::getText(double xMin, double yMin,
     eolLen = uMap->mapUnicode(0x0d, eol, sizeof(eol));
     break;
   }
-  xPrev = yPrev = 0;
+
+  // find the leftmost column
   multiLine = gFalse;
-  for (str1 = yxStrings; str1; str1 = str1->yxNext) {
-    y = 0.5 * (str1->yMin + str1->yMax);
-    if (y > yMax) {
+  firstCol = -1;
+  for (line = lines; line; line = line->next) {
+    if (line->yMin > yMax) {
       break;
     }
-    if (y > yMin && str1->xMin < xMax && str1->xMax > xMin) {
-      x0 = x1 = x2 = str1->xMin;
-      for (i1 = 0; i1 < str1->len; ++i1) {
-       x0 = (i1==0) ? str1->xMin : str1->xRight[i1-1];
-       x1 = str1->xRight[i1];
-       if (0.5 * (x0 + x1) >= xMin) {
-         break;
-       }
+    if (line->yMax < yMin) {
+      continue;
+    }
+
+    for (blk = line->blocks; blk && blk->xMax < xMin; blk = blk->next) ;
+    if (!blk || blk->xMin > xMax) {
+      continue;
+    }
+
+    y = 0.5 * (blk->yMin + blk->yMax);
+    if (y < yMin || y > yMax) {
+      continue;
+    }
+
+    if (firstCol >= 0) {
+      multiLine = gTrue;
+    }
+
+    i = 0;
+    while (1) {
+      x0 = (i==0) ? blk->xMin : blk->xRight[i-1];
+      x1 = blk->xRight[i];
+      if (0.5 * (x0 + x1) > xMin) {
+       break;
       }
-      for (i2 = str1->len - 1; i2 > i1; --i2) {
-       x1 = (i2==0) ? str1->xMin : str1->xRight[i2-1];
-       x2 = str1->xRight[i2];
-       if (0.5 * (x1 + x2) <= xMax) {
-         break;
-       }
+      ++i;
+    }
+    col = blk->col[i];
+
+    if (firstCol < 0 || col < firstCol) {
+      firstCol = col;
+    }
+  }
+
+  // extract the text
+  for (line = lines; line; line = line->next) {
+    if (line->yMin > yMax) {
+      break;
+    }
+    if (line->yMax < yMin) {
+      continue;
+    }
+
+    for (blk = line->blocks; blk && blk->xMax < xMin; blk = blk->next) ;
+    if (!blk || blk->xMin > xMax) {
+      continue;
+    }
+
+    y = 0.5 * (blk->yMin + blk->yMax);
+    if (y < yMin || y > yMax) {
+      continue;
+    }
+
+    i = 0;
+    while (1) {
+      x0 = (i==0) ? blk->xMin : blk->xRight[i-1];
+      x1 = blk->xRight[i];
+      if (0.5 * (x0 + x1) > xMin) {
+       break;
       }
-      if (s->getLength() > 0) {
-       if (x0 < xPrev || str1->yMin > yPrev) {
-         s->append(eol, eolLen);
-         multiLine = gTrue;
-       } else {
-         for (i = 0; i < 4; ++i) {
-           s->append(space, spaceLen);
-         }
+      ++i;
+    }
+
+    col = firstCol;
+
+    do {
+
+      // line this block up with the correct column
+      for (; col < blk->col[i]; ++col) {
+       s->append(space, spaceLen);
+      }
+
+      // print the block
+      for (; i < blk->len; ++i) {
+
+       x0 = (i==0) ? blk->xMin : blk->xRight[i-1];
+       x1 = blk->xRight[i];
+       if (0.5 * (x0 + x1) > xMax) {
+         break;
        }
+
+       len = uMap->mapUnicode(blk->text[i], buf, sizeof(buf));
+       s->append(buf, len);
+       col += isUnicode ? 1 : len;
       }
-      for (i = i1; i <= i2; ++i) {
-       n = uMap->mapUnicode(str1->text[i], buf, sizeof(buf));
-       s->append(buf, n);
+      if (i < blk->len) {
+       break;
       }
-      xPrev = x2;
-      yPrev = str1->yMax;
+
+      // next block
+      blk = blk->next;
+      i = 0;
+
+    } while (blk && blk->xMin < xMax);
+
+    if (multiLine) {
+      s->append(eol, eolLen);
     }
   }
-  if (multiLine) {
-    s->append(eol, eolLen);
-  }
+
   uMap->decRefCnt();
+
   return s;
 }
 
 void TextPage::dump(void *outputStream, TextOutputFunc outputFunc) {
   UnicodeMap *uMap;
   char space[8], eol[16], eop[8], buf[8];
-  int spaceLen, eolLen, eopLen, n;
-  TextString *str1, *str2, *str3;
-  double yMin, yMax;
-  int col1, col2, d, i;
+  int spaceLen, eolLen, eopLen, len;
+  TextLine *line;
+  TextBlock *blk;
+  int col, d, i;
 
   // get the output encoding
   if (!(uMap = globalParams->getTextEncoding())) {
@@ -469,129 +1038,46 @@ void TextPage::dump(void *outputStream, TextOutputFunc outputFunc) {
   }
   eopLen = uMap->mapUnicode(0x0c, eop, sizeof(eop));
 
-  // build x-major list
-  xyStrings = NULL;
-  for (str1 = yxStrings; str1; str1 = str1->yxNext) {
-    for (str2 = NULL, str3 = xyStrings;
-        str3;
-        str2 = str3, str3 = str3->xyNext) {
-      if (str1->xMin < str3->xMin ||
-         (str1->xMin == str3->xMin && str1->yMin < str3->yMin)) {
-       break;
-      }
-    }
-    if (str2) {
-      str2->xyNext = str1;
-    } else {
-      xyStrings = str1;
-    }
-    str1->xyNext = str3;
-  }
-
-  // do column assignment
-  for (str1 = xyStrings; str1; str1 = str1->xyNext) {
-    col1 = 0;
-    for (str2 = xyStrings; str2 != str1; str2 = str2->xyNext) {
-      if (str1->xMin >= str2->xMax) {
-       col2 = str2->col + str2->len + 4;
-       if (col2 > col1) {
-         col1 = col2;
-       }
-      } else if (str1->xMin > str2->xMin) {
-       col2 = str2->col +
-              (int)(((str1->xMin - str2->xMin) / (str2->xMax - str2->xMin)) *
-                    str2->len);
-       if (col2 > col1) {
-         col1 = col2;
+  // output
+  for (line = lines; line; line = line->next) {
+    col = 0;
+    for (blk = line->blocks; blk; blk = blk->next) {
+
+      // line this block up with the correct column
+      if (rawOrder && col == 0) {
+       col = blk->col[0];
+      } else {
+       for (; col < blk->col[0]; ++col) {
+         (*outputFunc)(outputStream, space, spaceLen);
        }
       }
-    }
-    str1->col = col1;
-  }
-
-#if 0 //~ for debugging
-  fprintf((FILE *)outputStream, "~~~~~~~~~~\n");
-  for (str1 = yxStrings; str1; str1 = str1->yxNext) {
-    fprintf((FILE *)outputStream, "(%4d,%4d) - (%4d,%4d) [%3d] '",
-           (int)str1->xMin, (int)str1->yMin,
-           (int)str1->xMax, (int)str1->yMax, str1->col);
-    for (i = 0; i < str1->len; ++i) {
-      fputc(str1->text[i] & 0xff, stdout);
-    }
-    printf("'\n");
-  }
-  fprintf((FILE *)outputStream, "~~~~~~~~~~\n");
-#endif
-
-  // output
-  col1 = 0;
-  yMax = yxStrings ? yxStrings->yMax : 0;
-  for (str1 = yxStrings; str1; str1 = str1->yxNext) {
 
-    // line this string up with the correct column
-    if (rawOrder && col1 == 0) {
-      col1 = str1->col;
-    } else {
-      for (; col1 < str1->col; ++col1) {
-       (*outputFunc)(outputStream, space, spaceLen);
+      // print the block
+      for (i = 0; i < blk->len; ++i) {
+       len = uMap->mapUnicode(blk->text[i], buf, sizeof(buf));
+       (*outputFunc)(outputStream, buf, len);
       }
+      col += blk->convertedLen;
     }
 
-    // print the string
-    for (i = 0; i < str1->len; ++i) {
-      if ((n = uMap->mapUnicode(str1->text[i], buf, sizeof(buf))) > 0) {
-       (*outputFunc)(outputStream, buf, n);
+    // print a return
+    (*outputFunc)(outputStream, eol, eolLen);
+
+    // print extra vertical space if necessary
+    if (line->next) {
+      d = (int)((line->next->yMin - line->yMax) /
+               (line->blocks->strings->yMax - lines->blocks->strings->yMin)
+               + 0.5);
+      // various things (weird font matrices) can result in bogus
+      // values here, so do a sanity check
+      if (rawOrder && d > 2) {
+       d = 2;
+      } else if (!rawOrder && d > 5) {
+       d = 5;
       }
-    }
-
-    // increment column
-    col1 += str1->len;
-
-    // update yMax for this line
-    if (str1->yMax > yMax) {
-      yMax = str1->yMax;
-    }
-
-    // if we've hit the end of the line...
-    if (!(str1->yxNext &&
-         !(rawOrder && str1->yxNext->yMax < str1->yMin) &&
-         str1->yxNext->yMin < 0.2*str1->yMin + 0.8*str1->yMax &&
-         str1->yxNext->xMin >= str1->xMax)) {
-
-      // print a return
-      (*outputFunc)(outputStream, eol, eolLen);
-
-      // print extra vertical space if necessary
-      if (str1->yxNext) {
-
-       // find yMin for next line
-       yMin = str1->yxNext->yMin;
-       for (str2 = str1->yxNext; str2; str2 = str2->yxNext) {
-         if (str2->yMin < yMin) {
-           yMin = str2->yMin;
-         }
-         if (!(str2->yxNext && str2->yxNext->yMin < str2->yMax &&
-               str2->yxNext->xMin >= str2->xMax))
-           break;
-       }
-         
-       // print the space
-       d = (int)((yMin - yMax) / (str1->yMax - str1->yMin) + 0.5);
-       // various things (weird font matrices) can result in bogus
-       // values here, so do a sanity check
-       if (rawOrder && d > 2) {
-         d = 2;
-       } else if (!rawOrder && d > 5) {
-         d = 5;
-       }
-       for (; d > 0; --d) {
-         (*outputFunc)(outputStream, eol, eolLen);
-       }
+      for (; d > 0; --d) {
+       (*outputFunc)(outputStream, eol, eolLen);
       }
-
-      // set up for next line
-      col1 = 0;
-      yMax = str1->yxNext ? str1->yxNext->yMax : 0;
     }
   }
 
@@ -603,20 +1089,89 @@ void TextPage::dump(void *outputStream, TextOutputFunc outputFunc) {
   uMap->decRefCnt();
 }
 
+// Returns true if <str1> should be inserted before <str2> in xy
+// order.
+GBool TextPage::xyBefore(TextString *str1, TextString *str2) {
+  return str1->xMin < str2->xMin ||
+        (str1->xMin == str2->xMin && str1->yMin < str2->yMin);
+}
+
+// Returns true if <blk1> should be inserted before <blk2> in xy
+// order.
+GBool TextPage::xyBefore(TextBlock *blk1, TextBlock *blk2) {
+  return blk1->xMin < blk2->xMin ||
+        (blk1->xMin == blk2->xMin && blk1->yMin < blk2->yMin);
+}
+
+// Returns true if <blk1> should be inserted before <blk2> in yx
+// order, allowing a little slack for vertically overlapping text.
+GBool TextPage::yxBefore(TextBlock *blk1, TextBlock *blk2) {
+  double h1, h2, overlap;
+
+  h1 = blk1->yMax - blk1->yMin;
+  h2 = blk2->yMax - blk2->yMin;
+  overlap = ((blk1->yMax < blk2->yMax ? blk1->yMax : blk2->yMax) -
+            (blk1->yMin > blk2->yMin ? blk1->yMin : blk2->yMin)) /
+            (h1 < h2 ? h1 : h2);
+  if (overlap > 0.6) {
+    return blk1->xMin < blk2->xMin;
+  }
+  return blk1->yMin < blk2->yMin;
+}
+
+double TextPage::coalesceFit(TextString *str1, TextString *str2) {
+  double h1, h2, w1, w2, r, overlap, spacing;
+
+  h1 = str1->yMax - str1->yMin;
+  h2 = str2->yMax - str2->yMin;
+  w1 = str1->xMax - str1->xMin;
+  w2 = str2->xMax - str2->xMin;
+  r = h1 / h2;
+  if (r < (1.0 / 3.0) || r > 3) {
+    return 10;
+  }
+  overlap = ((str1->yMax < str2->yMax ? str1->yMax : str2->yMax) -
+            (str1->yMin > str2->yMin ? str1->yMin : str2->yMin)) /
+            (h1 < h2 ? h1 : h2);
+  if (overlap < 0.5) {
+    return 10;
+  }
+  spacing = (str2->xMin - str1->xMax) / (h1 > h2 ? h1 : h2);
+  if (spacing < -0.5) {
+    return 10;
+  }
+  // separate text that overlaps - duplicated text (so that fake
+  // boldface and shadowed text can be cleanly removed)
+  if ((str2->xMin - str1->xMax) / (w1 < w2 ? w1 : w2) < -0.7) {
+    return 10;
+  }
+  return spacing;
+}
+
 void TextPage::clear() {
-  TextString *p1, *p2;
+  TextLine *p1, *p2;
+  TextString *s1, *s2;
 
   if (curStr) {
     delete curStr;
     curStr = NULL;
   }
-  for (p1 = yxStrings; p1; p1 = p2) {
-    p2 = p1->yxNext;
-    delete p1;
+  if (lines) {
+    for (p1 = lines; p1; p1 = p2) {
+      p2 = p1->next;
+      delete p1;
+    }
+  } else if (xyStrings) {
+    for (s1 = xyStrings; s1; s1 = s2) {
+      s2 = s1->next;
+      delete s1;
+    }
   }
-  yxStrings = NULL;
   xyStrings = NULL;
-  yxCur1 = yxCur2 = NULL;
+  xyCur1 = xyCur2 = NULL;
+  lines = NULL;
+  nest = 0;
+  nTinyChars = 0;
 }
 
 //------------------------------------------------------------------------
@@ -691,7 +1246,7 @@ void TextOutputDev::updateFont(GfxState *state) {
 }
 
 void TextOutputDev::beginString(GfxState *state, GString *s) {
-  text->beginString(state);
+  text->beginString(state, state->getCurX(), state->getCurY());
 }
 
 void TextOutputDev::endString(GfxState *state) {
@@ -711,3 +1266,9 @@ GBool TextOutputDev::findText(Unicode *s, int len,
                              double *xMax, double *yMax) {
   return text->findText(s, len, top, bottom, xMin, yMin, xMax, yMax);
 }
+
+GString *TextOutputDev::getText(double xMin, double yMin,
+                               double xMax, double yMax) {
+  return text->getText(xMin, yMin, xMax, yMax);
+}
+
index daab3c4ebb93a05f669694d9f17b7b1f02a5d40a..f681ecfa8d314135b32c7a27b70e5cc38a9440a0 100644 (file)
@@ -9,7 +9,9 @@
 #ifndef TEXTOUTPUTDEV_H
 #define TEXTOUTPUTDEV_H
 
-#ifdef __GNUC__
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
 #pragma interface
 #endif
 
 
 class GfxState;
 class GString;
+class TextBlock;
+class TextLine;
+
+#undef TEXTOUT_DO_SYMBOLS
 
 //------------------------------------------------------------------------
 
 typedef void (*TextOutputFunc)(void *stream, char *text, int len);
 
+
 //------------------------------------------------------------------------
 // TextString
 //------------------------------------------------------------------------
@@ -33,7 +40,9 @@ class TextString {
 public:
 
   // Constructor.
-  TextString(GfxState *state, double fontSize);
+  TextString(GfxState *state, double x0, double y0,
+            double fontSize);
+
 
   // Destructor.
   ~TextString();
@@ -46,15 +55,18 @@ private:
 
   double xMin, xMax;           // bounding box x coordinates
   double yMin, yMax;           // bounding box y coordinates
-  int col;                     // starting column
+  union {
+    GBool marked;              // temporary flag used by coalesce()
+    GBool spaceAfter;          // insert a space after this string?
+  };
   Unicode *text;               // the text
   double *xRight;              // right-hand x coord of each char
   int len;                     // length of text and xRight
   int size;                    // size of text and xRight arrays
-  TextString *yxNext;          // next string in y-major order
-  TextString *xyNext;          // next string in x-major order
+  TextString *next;
 
   friend class TextPage;
+  friend class TextBlock;
 };
 
 //------------------------------------------------------------------------
@@ -73,8 +85,9 @@ public:
   // Update the current font.
   void updateFont(GfxState *state);
 
+
   // Begin a new string.
-  void beginString(GfxState *state);
+  void beginString(GfxState *state, double x0, double y0);
 
   // Add a character to the current string.
   void addChar(GfxState *state, double x, double y,
@@ -83,6 +96,10 @@ public:
   // End the current string, sorting it into the list of strings.
   void endString();
 
+  // Add a string, sorting it into the list of strings.
+  void addString(TextString *str);
+
+
   // Coalesce strings that look like parts of the same line.
   void coalesce();
 
@@ -108,16 +125,25 @@ public:
 
 private:
 
+  GBool xyBefore(TextString *str1, TextString *str2);
+  GBool xyBefore(TextBlock *blk1, TextBlock *blk2);
+  GBool yxBefore(TextBlock *blk1, TextBlock *blk2);
+  double coalesceFit(TextString *str1, TextString *str2);
+
   GBool rawOrder;              // keep strings in content stream order
 
   TextString *curStr;          // currently active string
   double fontSize;             // current font size
 
-  TextString *yxStrings;       // strings in y-major order
-  TextString *xyStrings;       // strings in x-major order
-  TextString *yxCur1, *yxCur2; // cursors for yxStrings list
+  TextString *xyStrings;       // strings in x-major order (before
+                               //   they're sorted into lines)
+  TextString *xyCur1, *xyCur2; // cursors for xyStrings list
+  TextLine *lines;             // list of lines
 
   int nest;                    // current nesting level (for Type 3 fonts)
+
+  int nTinyChars;              // number of "tiny" chars seen so far
+
 };
 
 //------------------------------------------------------------------------
@@ -177,6 +203,8 @@ public:
                        double originX, double originY,
                        CharCode c, Unicode *u, int uLen);
 
+  //----- path painting
+
   //----- special access
 
   // Find a string.  If <top> is true, starts looking at top of page;
@@ -189,6 +217,10 @@ public:
                 double *xMin, double *yMin,
                 double *xMax, double *yMax);
 
+  // Get the text which is inside the specified rectangle.
+  GString *getText(double xMin, double yMin,
+                  double xMax, double yMax);
+
 private:
 
   TextOutputFunc outputFunc;   // output function
@@ -198,6 +230,7 @@ private:
   TextPage *text;              // text for the current page
   GBool rawOrder;              // keep text in content stream order
   GBool ok;                    // set up ok?
+
 };
 
 #endif
index 6f207d8e45e0fe976c16bbb384bf1fef68fe709d..3c58f5650cb44e9fc884a0a52225cd8f1be5cbdb 100644 (file)
@@ -6,11 +6,12 @@
 //
 //========================================================================
 
-#ifdef __GNUC__
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
 #pragma implementation
 #endif
 
-#include <aconf.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <stddef.h>
@@ -95,6 +96,12 @@ static XOutFontSubst xOutSubstFonts[16] = {
   {"Symbol",                0.576}
 };
 
+//------------------------------------------------------------------------
+
+static void outputToFile(void *stream, char *data, int len) {
+  fwrite(data, 1, len, (FILE *)stream);
+}
+
 //------------------------------------------------------------------------
 // XOutputFont
 //------------------------------------------------------------------------
@@ -581,18 +588,30 @@ void XOutputServer16BitFont::drawChar(GfxState *state, Pixmap pixmap,
 #if HAVE_T1LIB_H
 XOutputT1FontFile::~XOutputT1FontFile() {
   delete fontFile;
+  if (tmpFileName) {
+    unlink(tmpFileName->getCString());
+    delete tmpFileName;
+  }
 }
 #endif
 
 #if FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H)
 XOutputFTFontFile::~XOutputFTFontFile() {
   delete fontFile;
+  if (tmpFileName) {
+    unlink(tmpFileName->getCString());
+    delete tmpFileName;
+  }
 }
 #endif
 
 #if !FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H)
 XOutputTTFontFile::~XOutputTTFontFile() {
   delete fontFile;
+  if (tmpFileName) {
+    unlink(tmpFileName->getCString());
+    delete tmpFileName;
+  }
 }
 #endif
 
@@ -626,8 +645,8 @@ XOutputFontCache::~XOutputFontCache() {
   delFonts();
 }
 
-void XOutputFontCache::startDoc(int screenNum, Colormap colormap,
-                               GBool trueColor,
+void XOutputFontCache::startDoc(int screenNum, Visual *visual,
+                               Colormap colormap, GBool trueColor,
                                int rMul, int gMul, int bMul,
                                int rShift, int gShift, int bShift,
                                Gulong *colors, int numColors) {
@@ -636,8 +655,7 @@ void XOutputFontCache::startDoc(int screenNum, Colormap colormap,
 
 #if HAVE_T1LIB_H
   if (t1libControl != fontRastNone) {
-    t1Engine = new T1FontEngine(display, DefaultVisual(display, screenNum),
-                               depth, colormap,
+    t1Engine = new T1FontEngine(display, visual, depth, colormap,
                                t1libControl == fontRastAALow ||
                                  t1libControl == fontRastAAHigh,
                                t1libControl == fontRastAAHigh);
@@ -656,8 +674,7 @@ void XOutputFontCache::startDoc(int screenNum, Colormap colormap,
 
 #if FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H)
   if (freetypeControl != fontRastNone) {
-    ftEngine = new FTFontEngine(display, DefaultVisual(display, screenNum),
-                               depth, colormap,
+    ftEngine = new FTFontEngine(display, visual, depth, colormap,
                                freetypeControl == fontRastAALow ||
                                  freetypeControl == fontRastAAHigh);
     if (ftEngine->isOk()) {
@@ -675,8 +692,7 @@ void XOutputFontCache::startDoc(int screenNum, Colormap colormap,
 
 #if !FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H)
   if (freetypeControl != fontRastNone) {
-    ttEngine = new TTFontEngine(display, DefaultVisual(display, screenNum),
-                               depth, colormap,
+    ttEngine = new TTFontEngine(display, visual, depth, colormap,
                                freetypeControl == fontRastAALow ||
                                  freetypeControl == fontRastAAHigh);
     if (ttEngine->isOk()) {
@@ -978,7 +994,7 @@ XOutputFont *XOutputFontCache::tryGetFont(XRef *xref, DisplayFontParam *dfp,
   case displayFontT1:
 #if HAVE_T1LIB_H
     if (t1libControl != fontRastNone) {
-      font = tryGetT1FontFromFile(xref, dfp->t1.fileName, gfxFont,
+      font = tryGetT1FontFromFile(xref, dfp->t1.fileName, gFalse, gfxFont,
                                  m11Orig, m12Orig, m21Orig, m22Orig,
                                  m11, m12, m21, m22, subst);
     }
@@ -986,7 +1002,7 @@ XOutputFont *XOutputFontCache::tryGetFont(XRef *xref, DisplayFontParam *dfp,
 #if FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H)
     if (!font) {
       if (freetypeControl != fontRastNone) {
-       font = tryGetFTFontFromFile(xref, dfp->t1.fileName, gfxFont,
+       font = tryGetFTFontFromFile(xref, dfp->t1.fileName, gFalse, gfxFont,
                                    m11Orig, m12Orig, m21Orig, m22Orig,
                                    m11, m12, m21, m22, subst);
       }
@@ -1001,14 +1017,14 @@ XOutputFont *XOutputFontCache::tryGetFont(XRef *xref, DisplayFontParam *dfp,
   case displayFontTT:
 #if FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H)
     if (freetypeControl != fontRastNone) {
-      font = tryGetFTFontFromFile(xref, dfp->tt.fileName, gfxFont,
+      font = tryGetFTFontFromFile(xref, dfp->tt.fileName, gFalse, gfxFont,
                                  m11Orig, m12Orig, m21Orig, m22Orig,
                                  m11, m12, m21, m22, subst);
     }
 #endif
 #if !FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H)
     if (freetypeControl != fontRastNone) {
-      font = tryGetTTFontFromFile(xref, dfp->tt.fileName, gfxFont,
+      font = tryGetTTFontFromFile(xref, dfp->tt.fileName, gFalse, gfxFont,
                                  m11Orig, m12Orig, m21Orig, m22Orig,
                                  m11, m12, m21, m22, subst);
     }
@@ -1074,7 +1090,7 @@ XOutputFont *XOutputFontCache::tryGetT1Font(XRef *xref,
        return NULL;
       }
       ff = new Type1CFontFile(fontBuf, fontLen);
-      ff->convertToType1(f);
+      ff->convertToType1(outputToFile, f);
       delete ff;
       gfree(fontBuf);
     } else { // fontType1
@@ -1091,17 +1107,19 @@ XOutputFont *XOutputFontCache::tryGetT1Font(XRef *xref,
     fclose(f);
 
     // create the Font
-    font = tryGetT1FontFromFile(xref, fileName, gfxFont,
+    font = tryGetT1FontFromFile(xref, fileName, gTrue, gfxFont,
                                m11, m12, m21, m22,
                                m11, m12, m21, m22, gFalse);
 
-    // remove the temporary file
+    // on systems with Unix hard link semantics, this will remove the
+    // last link to the temp file
     unlink(fileName->getCString());
+
     delete fileName;
 
   // check for an external font file
   } else if ((fileName = gfxFont->getExtFontFile())) {
-    font = tryGetT1FontFromFile(xref, fileName, gfxFont,
+    font = tryGetT1FontFromFile(xref, fileName, gFalse, gfxFont,
                                m11, m12, m21, m22,
                                m11, m12, m21, m22, gFalse);
 
@@ -1114,6 +1132,7 @@ XOutputFont *XOutputFontCache::tryGetT1Font(XRef *xref,
 
 XOutputFont *XOutputFontCache::tryGetT1FontFromFile(XRef *xref,
                                                    GString *fileName,
+                                                   GBool deleteFile,
                                                    GfxFont *gfxFont,
                                                    double m11Orig,
                                                    double m12Orig,
@@ -1134,13 +1153,18 @@ XOutputFont *XOutputFontCache::tryGetT1FontFromFile(XRef *xref,
     error(-1, "Couldn't create t1lib font from '%s'",
          fileName->getCString());
     delete fontFile;
+    if (deleteFile) {
+      unlink(fileName->getCString());
+    }
     return NULL;
   }
 
   // add to list
   id = gfxFont->getID();
   t1FontFiles->append(new XOutputT1FontFile(id->num, id->gen,
-                                           subst, fontFile));
+                                           subst, fontFile,
+                                           deleteFile ? fileName->copy()
+                                                      : (GString *)NULL));
 
   // create the Font
   font = new XOutputT1Font(gfxFont->getID(), fontFile,
@@ -1236,17 +1260,19 @@ XOutputFont *XOutputFontCache::tryGetFTFont(XRef *xref,
     fclose(f);
 
     // create the Font
-    font = tryGetFTFontFromFile(xref, fileName, gfxFont,
+    font = tryGetFTFontFromFile(xref, fileName, gTrue, gfxFont,
                                m11, m12, m21, m22,
                                m11, m12, m21, m22, gFalse);
 
-    // remove the temporary file
+    // on systems with Unix hard link semantics, this will remove the
+    // last link to the temp file
     unlink(fileName->getCString());
+
     delete fileName;
 
   // check for an external font file
   } else if ((fileName = gfxFont->getExtFontFile())) {
-    font = tryGetFTFontFromFile(xref, fileName, gfxFont,
+    font = tryGetFTFontFromFile(xref, fileName, gFalse, gfxFont,
                                m11, m12, m21, m22,
                                m11, m12, m21, m22, gFalse);
 
@@ -1259,6 +1285,7 @@ XOutputFont *XOutputFontCache::tryGetFTFont(XRef *xref,
 
 XOutputFont *XOutputFontCache::tryGetFTFontFromFile(XRef *xref,
                                                    GString *fileName,
+                                                   GBool deleteFile,
                                                    GfxFont *gfxFont,
                                                    double m11Orig,
                                                    double m12Orig,
@@ -1289,13 +1316,18 @@ XOutputFont *XOutputFontCache::tryGetFTFontFromFile(XRef *xref,
     error(-1, "Couldn't create FreeType font from '%s'",
          fileName->getCString());
     delete fontFile;
+    if (deleteFile) {
+      unlink(fileName->getCString());
+    }
     return NULL;
   }
 
   // add to list
   id = gfxFont->getID();
   ftFontFiles->append(new XOutputFTFontFile(id->num, id->gen,
-                                           subst, fontFile));
+                                           subst, fontFile,
+                                           deleteFile ? fileName->copy()
+                                                      : (GString *)NULL));
 
   // create the Font
   font = new XOutputFTFont(gfxFont->getID(), fontFile,
@@ -1363,16 +1395,18 @@ XOutputFont *XOutputFontCache::tryGetTTFont(XRef *xref,
     fclose(f);
 
     // create the Font
-    font = tryGetTTFontFromFile(xref, fileName, gfxFont,
+    font = tryGetTTFontFromFile(xref, fileName, gTrue, gfxFont,
                                m11, m12, m21, m22,
                                m11, m12, m21, m22, gFalse);
 
-    // remove the temporary file
+    // on systems with Unix hard link semantics, this will remove the
+    // last link to the temp file
     unlink(fileName->getCString());
+
     delete fileName;
 
   } else if ((fileName = gfxFont->getExtFontFile())) {
-    font = tryGetTTFontFromFile(xref, fileName, gfxFont,
+    font = tryGetTTFontFromFile(xref, fileName, gFalse, gfxFont,
                                m11, m12, m21, m22,
                                m11, m12, m21, m22, gFalse);
 
@@ -1385,6 +1419,7 @@ XOutputFont *XOutputFontCache::tryGetTTFont(XRef *xref,
 
 XOutputFont *XOutputFontCache::tryGetTTFontFromFile(XRef *xref,
                                                    GString *fileName,
+                                                   GBool deleteFile,
                                                    GfxFont *gfxFont,
                                                    double m11Orig,
                                                    double m12Orig,
@@ -1412,13 +1447,18 @@ XOutputFont *XOutputFontCache::tryGetTTFontFromFile(XRef *xref,
     error(-1, "Couldn't create FreeType font from '%s'",
          fileName->getCString());
     delete fontFile;
+    if (deleteFile) {
+      unlink(fileName->getCString());
+    }
     return NULL;
   }
 
   // add to list
   id = gfxFont->getID();
   ttFontFiles->append(new XOutputTTFontFile(id->num, id->gen,
-                                           subst, fontFile));
+                                           subst, fontFile,
+                                           deleteFile ? fileName->copy()
+                                                      : (GString *)NULL));
 
   // create the Font
   font = new XOutputTTFont(gfxFont->getID(), fontFile,
@@ -1570,6 +1610,7 @@ struct T3GlyphStack {
   GC origStrokeGC;
   GC origFillGC;
   Region origClipRegion;
+  double origCTM4, origCTM5;
   double wx, wy;               // untransformed glyph metrics
   T3GlyphStack *next;
 };
@@ -1578,53 +1619,63 @@ struct T3GlyphStack {
 // XOutputDev
 //------------------------------------------------------------------------
 
-XOutputDev::XOutputDev(Display *displayA, Pixmap pixmapA, Guint depthA,
-                      Colormap colormapA, GBool reverseVideoA,
-                      unsigned long paperColor, GBool installCmap,
-                      int rgbCubeSize) {
+XOutputDev::XOutputDev(Display *displayA, int screenNumA,
+                      Visual *visualA, Colormap colormapA,
+                      GBool reverseVideoA, unsigned long paperColorA,
+                      GBool installCmap, int rgbCubeSize,
+                      int forceDepth) {
   XVisualInfo visualTempl;
   XVisualInfo *visualList;
   int nVisuals;
   Gulong mask;
-  XGCValues gcValues;
   XColor xcolor;
   XColor *xcolors;
-  int r, g, b, n, m, i;
+  int r, g, b, n, m;
   GBool ok;
 
+  // no document yet
   xref = NULL;
 
-  // get display/pixmap info
+  // display / screen / visual / colormap
   display = displayA;
-  screenNum = DefaultScreen(display);
-  pixmap = pixmapA;
-  depth = depthA;
+  screenNum = screenNumA;
+  visual = visualA;
   colormap = colormapA;
 
+  // no pixmap yet
+  pixmapW = pixmapH = 0;
+
   // check for TrueColor visual
-  trueColor = gFalse;
-  if (depth == 0) {
-    depth = DefaultDepth(display, screenNum);
-    visualList = XGetVisualInfo(display, 0, &visualTempl, &nVisuals);
-    for (i = 0; i < nVisuals; ++i) {
-      if (visualList[i].visual == DefaultVisual(display, screenNum)) {
-       if (visualList[i].c_class == TrueColor) {
-         trueColor = gTrue;
-         for (mask = visualList[i].red_mask, rShift = 0;
-              mask && !(mask & 1);
-              mask >>= 1, ++rShift) ;
-         rMul = (int)mask;
-         for (mask = visualList[i].green_mask, gShift = 0;
-              mask && !(mask & 1);
-              mask >>= 1, ++gShift) ;
-         gMul = (int)mask;
-         for (mask = visualList[i].blue_mask, bShift = 0;
-              mask && !(mask & 1);
-              mask >>= 1, ++bShift) ;
-         bMul = (int)mask;
-       }
-       break;
-      }
+  if (forceDepth != 0) {
+    depth = forceDepth;
+    trueColor = depth >= 16;
+  } else {
+    visualTempl.visualid = XVisualIDFromVisual(visual);
+    visualList = XGetVisualInfo(display, VisualIDMask,
+                               &visualTempl, &nVisuals);
+    if (nVisuals < 1) {
+      // this shouldn't happen
+      XFree((XPointer)visualList);
+      visualList = XGetVisualInfo(display, VisualNoMask, &visualTempl,
+                                 &nVisuals);
+    }
+    depth = visualList->depth;
+    if (visualList->c_class == TrueColor) {
+      trueColor = gTrue;
+      for (mask = visualList->red_mask, rShift = 0;
+          mask && !(mask & 1);
+          mask >>= 1, ++rShift) ;
+      rMul = (int)mask;
+      for (mask = visualList->green_mask, gShift = 0;
+          mask && !(mask & 1);
+          mask >>= 1, ++gShift) ;
+      gMul = (int)mask;
+      for (mask = visualList->blue_mask, bShift = 0;
+          mask && !(mask & 1);
+          mask >>= 1, ++bShift) ;
+      bMul = (int)mask;
+    } else {
+      trueColor = gFalse;
     }
     XFree((XPointer)visualList);
   }
@@ -1710,27 +1761,9 @@ XOutputDev::XOutputDev(Display *displayA, Pixmap pixmapA, Guint depthA,
     }
   }
 
-  // reverse video mode
+  // misc parameters
   reverseVideo = reverseVideoA;
-
-  // allocate GCs
-  gcValues.foreground = BlackPixel(display, screenNum);
-  gcValues.background = WhitePixel(display, screenNum);
-  gcValues.line_width = 0;
-  gcValues.line_style = LineSolid;
-  strokeGC = XCreateGC(display, pixmap,
-                      GCForeground | GCBackground | GCLineWidth | GCLineStyle,
-                       &gcValues);
-  fillGC = XCreateGC(display, pixmap,
-                    GCForeground | GCBackground | GCLineWidth | GCLineStyle,
-                    &gcValues);
-  gcValues.foreground = paperColor;
-  paperGC = XCreateGC(display, pixmap,
-                     GCForeground | GCBackground | GCLineWidth | GCLineStyle,
-                     &gcValues);
-
-  // no clip region yet
-  clipRegion = NULL;
+  paperColor = paperColorA;
 
   // set up the font cache and fonts
   gfxFont = NULL;
@@ -1755,12 +1788,6 @@ XOutputDev::~XOutputDev() {
   for (i = 0; i < nT3Fonts; ++i) {
     delete t3FontCache[i];
   }
-  XFreeGC(display, strokeGC);
-  XFreeGC(display, fillGC);
-  XFreeGC(display, paperGC);
-  if (clipRegion) {
-    XDestroyRegion(clipRegion);
-  }
   delete text;
 }
 
@@ -1768,7 +1795,7 @@ void XOutputDev::startDoc(XRef *xrefA) {
   int i;
 
   xref = xrefA;
-  fontCache->startDoc(screenNum, colormap, trueColor, rMul, gMul, bMul,
+  fontCache->startDoc(screenNum, visual, colormap, trueColor, rMul, gMul, bMul,
                      rShift, gShift, bShift, colors, numColors);
   for (i = 0; i < nT3Fonts; ++i) {
     delete t3FontCache[i];
@@ -1777,39 +1804,29 @@ void XOutputDev::startDoc(XRef *xrefA) {
 }
 
 void XOutputDev::startPage(int pageNum, GfxState *state) {
-  XOutputState *s;
   XGCValues gcValues;
   XRectangle rect;
 
-  // clear state stack
-  while (save) {
-    s = save;
-    save = save->next;
-    XFreeGC(display, s->strokeGC);
-    XFreeGC(display, s->fillGC);
-    XDestroyRegion(s->clipRegion);
-    delete s;
-  }
-  save = NULL;
-
   // default line flatness
   flatness = 0;
 
-  // reset GCs
+  // allocate GCs
   gcValues.foreground = BlackPixel(display, screenNum);
   gcValues.background = WhitePixel(display, screenNum);
   gcValues.line_width = 0;
   gcValues.line_style = LineSolid;
-  XChangeGC(display, strokeGC,
-           GCForeground | GCBackground | GCLineWidth | GCLineStyle,
-           &gcValues);
-  XChangeGC(display, fillGC,
-           GCForeground | GCBackground | GCLineWidth | GCLineStyle,
-           &gcValues);
-
-  // clear clipping region
-  if (clipRegion)
-    XDestroyRegion(clipRegion);
+  strokeGC = XCreateGC(display, pixmap,
+                      GCForeground | GCBackground | GCLineWidth | GCLineStyle,
+                       &gcValues);
+  fillGC = XCreateGC(display, pixmap,
+                    GCForeground | GCBackground | GCLineWidth | GCLineStyle,
+                    &gcValues);
+  gcValues.foreground = paperColor;
+  paperGC = XCreateGC(display, pixmap,
+                     GCForeground | GCBackground | GCLineWidth | GCLineStyle,
+                     &gcValues);
+
+  // initialize clip region
   clipRegion = XCreateRegion();
   rect.x = rect.y = 0;
   rect.width = pixmapW;
@@ -1830,7 +1847,23 @@ void XOutputDev::startPage(int pageNum, GfxState *state) {
 }
 
 void XOutputDev::endPage() {
+  XOutputState *s;
+
   text->coalesce();
+
+  // clear state stack, free all GCs, free the clip region
+  while (save) {
+    s = save;
+    save = save->next;
+    XFreeGC(display, s->strokeGC);
+    XFreeGC(display, s->fillGC);
+    XDestroyRegion(s->clipRegion);
+    delete s;
+  }
+  XFreeGC(display, strokeGC);
+  XFreeGC(display, fillGC);
+  XFreeGC(display, paperGC);
+  XDestroyRegion(clipRegion);
 }
 
 void XOutputDev::drawLink(Link *link, Catalog *catalog) {
@@ -2026,6 +2059,8 @@ void XOutputDev::updateStrokeColor(GfxState *state) {
 void XOutputDev::updateFont(GfxState *state) {
   double m11, m12, m21, m22;
 
+  text->updateFont(state);
+
   if (!(gfxFont = state->getFont())) {
     font = NULL;
     return;
@@ -2042,8 +2077,6 @@ void XOutputDev::updateFont(GfxState *state) {
     font->updateGC(fillGC);
     font->updateGC(strokeGC);
   }
-
-  text->updateFont(state);
 }
 
 void XOutputDev::stroke(GfxState *state) {
@@ -2450,7 +2483,7 @@ void XOutputDev::addPoint(XPoint **points, int *size, int *k, int x, int y) {
 }
 
 void XOutputDev::beginString(GfxState *state, GString *s) {
-  text->beginString(state);
+  text->beginString(state, state->getCurX(), state->getCurY());
 }
 
 void XOutputDev::endString(GfxState *state) {
@@ -2625,9 +2658,7 @@ GBool XOutputDev::beginType3Char(GfxState *state,
                                       (int)floor(yMin - yt),
                                       (int)ceil(xMax) - (int)floor(xMin) + 3,
                                       (int)ceil(yMax) - (int)floor(yMin) + 3,
-                                      display,
-                                      DefaultVisual(display, screenNum),
-                                      depth, pixmap);
+                                      display, visual, depth, pixmap);
     }
   }
   t3Font = t3FontCache[0];
@@ -2676,6 +2707,7 @@ void XOutputDev::endType3Char(GfxState *state) {
   Gulong pixel;
   double alpha;
   T3GlyphStack *t3gs;
+  double *ctm;
 
   if (t3GlyphStack->cacheable) {
     image = t3GlyphStack->cache->image;
@@ -2716,6 +2748,11 @@ void XOutputDev::endType3Char(GfxState *state) {
     drawType3Glyph(t3GlyphStack->cache,
                   t3GlyphStack->cacheTag, t3GlyphStack->cacheData,
                   t3GlyphStack->x, t3GlyphStack->y, &t3GlyphStack->color);
+    // the CTM must be restored here in order for TextPage::addChar to
+    // work correctly
+    ctm = state->getCTM();
+    state->setCTM(ctm[0], ctm[1], ctm[2], ctm[3],
+                 t3GlyphStack->origCTM4, t3GlyphStack->origCTM5);
   }
   text->addChar(state, 0, 0, t3GlyphStack->wx, t3GlyphStack->wy,
                t3GlyphStack->u, t3GlyphStack->uLen);
@@ -2878,6 +2915,8 @@ void XOutputDev::type3D1(GfxState *state, double wx, double wy,
   XSetRegion(display, strokeGC, clipRegion);
   XSetRegion(display, fillGC, clipRegion);
   ctm = state->getCTM();
+  t3GlyphStack->origCTM4 = ctm[4];
+  t3GlyphStack->origCTM5 = ctm[5];
   state->setCTM(ctm[0], ctm[1], ctm[2], ctm[3], 
                -t3GlyphStack->cache->glyphX, -t3GlyphStack->cache->glyphY);
 }
@@ -3118,8 +3157,7 @@ void XOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
   pixBuf = (Guchar *)gmalloc((yp + 1) * width * sizeof(Guchar));
 
   // allocate XImage and read from page pixmap
-  image = XCreateImage(display, DefaultVisual(display, screenNum),
-                      depth, ZPixmap, 0, NULL, bw, bh, 8, 0);
+  image = XCreateImage(display, visual, depth, ZPixmap, 0, NULL, bw, bh, 8, 0);
   image->data = (char *)gmalloc(bh * image->bytes_per_line);
   XGetSubImage(display, pixmap, cx0, cy0, cw, ch, (1 << depth) - 1, ZPixmap,
               image, cx1, cy1);
@@ -3167,13 +3205,13 @@ void XOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
     if (n > 0) {
       p = pixBuf;
       for (i = 0; i < n; ++i) {
-       for (j = 0; j < width; ++j) {
-         imgStr->getPixel(p);
-         if (invert) {
-           *p ^= 1;
+       memcpy(p, imgStr->getLine(), width);
+       if (invert) {
+         for (j = 0; j < width; ++j) {
+           p[j] ^= 1;
          }
-         ++p;
        }
+       p += width;
       }
     }
     lastYStep = yStep;
@@ -3279,7 +3317,7 @@ void XOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
   double r0, g0, b0, alpha, mul;
   Gulong pix;
   GfxRGB *p;
-  Guchar *q;
+  Guchar *q, *p2;
   GBool oneBitMode;
   GfxRGB oneBitRGB[2];
   int x, y, x1, y1, x2, y2;
@@ -3321,6 +3359,14 @@ void XOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
   }
   tx = xoutRound(ctm[2] + ctm[4]);
   ty = xoutRound(ctm[3] + ctm[5]);
+  if (xScale < 0) {
+    // this is the right edge which needs to be (left + width - 1)
+    --tx;
+  }
+  if (yScale < 0) {
+    // this is the bottom edge which needs to be (top + height - 1)
+    --ty;
+  }
   // use ceil() to avoid gaps between "striped" images
   scaledWidth = (int)ceil(fabs(xScale));
   xSign = (xScale < 0) ? -1 : 1;
@@ -3425,8 +3471,7 @@ void XOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
   }
 
   // allocate XImage
-  image = XCreateImage(display, DefaultVisual(display, screenNum),
-                      depth, ZPixmap, 0, NULL, bw, bh, 8, 0);
+  image = XCreateImage(display, visual, depth, ZPixmap, 0, NULL, bw, bh, 8, 0);
   image->data = (char *)gmalloc(bh * image->bytes_per_line);
 
   // if the transform is anything other than a 0/90/180/270 degree
@@ -3484,26 +3529,27 @@ void XOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
       p = pixBuf;
       q = alphaBuf;
       for (i = 0; i < n; ++i) {
-       for (j = 0; j < width; ++j) {
-         imgStr->getPixel(pixBuf2);
-         if (oneBitMode) {
-           *p++ = oneBitRGB[pixBuf2[0]];
-         } else {
-           colorMap->getRGB(pixBuf2, p);
-           ++p;
-         }
-         if (q) {
-           *q = 1;
-           for (k = 0; k < nComps; ++k) {
-             if (pixBuf2[k] < maskColors[2*k] ||
-                 pixBuf2[k] > maskColors[2*k]) {
-               *q = 0;
-               break;
-             }
-           }
-           ++q;
-         }
-       }
+        p2 = imgStr->getLine();
+        for (j = 0; j < width; ++j) {
+          if (oneBitMode) {
+            *p = oneBitRGB[*p2];
+          } else {
+            colorMap->getRGB(p2, p);
+          }
+          ++p;
+          if (q) {
+            *q = 1;
+            for (k = 0; k < nComps; ++k) {
+              if (p2[k] < maskColors[2*k] ||
+                  p2[k] > maskColors[2*k]) {
+                *q = 0;
+                break;
+              }
+            }
+            ++q;
+          }
+          p2 += nComps;
+        }
       }
     }
     lastYStep = yStep;
index dc6af01e88cd0ea9db9231174a5e41501474d005..149fd6e485a6a8cc97c66ed76cfc9c501163c12d 100644 (file)
@@ -9,7 +9,9 @@
 #ifndef XOUTPUTDEV_H
 #define XOUTPUTDEV_H
 
-#ifdef __GNUC__
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
 #pragma interface
 #endif
 
@@ -328,36 +330,45 @@ private:
 #if HAVE_T1LIB_H
 class XOutputT1FontFile {
 public:
-  XOutputT1FontFile(int numA, int genA, GBool substA, T1FontFile *fontFileA)
-    { num = numA; gen = genA; subst = substA; fontFile = fontFileA; }
+  XOutputT1FontFile(int numA, int genA, GBool substA,
+                   T1FontFile *fontFileA, GString *tmpFileNameA)
+    { num = numA; gen = genA; subst = substA;
+      fontFile = fontFileA; tmpFileName = tmpFileNameA; }
   ~XOutputT1FontFile();
   int num, gen;
   GBool subst;
   T1FontFile *fontFile;
+  GString *tmpFileName;
 };
 #endif
 
 #if FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H)
 class XOutputFTFontFile {
 public:
-  XOutputFTFontFile(int numA, int genA, GBool substA, FTFontFile *fontFileA)
-    { num = numA; gen = genA; subst = substA; fontFile = fontFileA; }
+  XOutputFTFontFile(int numA, int genA, GBool substA,
+                   FTFontFile *fontFileA, GString *tmpFileNameA)
+    { num = numA; gen = genA; subst = substA;
+      fontFile = fontFileA; tmpFileName = tmpFileNameA; }
   ~XOutputFTFontFile();
   int num, gen;
   GBool subst;
   FTFontFile *fontFile;
+  GString *tmpFileName;
 };
 #endif
 
 #if !FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H)
 class XOutputTTFontFile {
 public:
-  XOutputTTFontFile(int numA, int genA, GBool substA, TTFontFile *fontFileA)
-    { num = numA; gen = genA; subst = substA; fontFile = fontFileA; }
+  XOutputTTFontFile(int numA, int genA, GBool substA,
+                   TTFontFile *fontFileA, GString *tmpFileNameA)
+    { num = numA; gen = genA; subst = substA;
+      fontFile = fontFileA; tmpFileName = tmpFileNameA; }
   ~XOutputTTFontFile();
   int num, gen;
   GBool subst;
   TTFontFile *fontFile;
+  GString *tmpFileName;
 };
 #endif
 
@@ -374,8 +385,8 @@ public:
   ~XOutputFontCache();
 
   // Initialize (or re-initialize) the font cache for a new document.
-  void startDoc(int screenNum, Colormap colormap,
-               GBool trueColor,
+  void startDoc(int screenNum, Visual *visual,
+               Colormap colormap, GBool trueColor,
                int rMul, int gMul, int bMul,
                int rShift, int gShift, int bShift,
                Gulong *colors, int numColors);
@@ -397,7 +408,7 @@ private:
   XOutputFont *tryGetT1Font(XRef *xref, GfxFont *gfxFont,
                            double m11, double m12, double m21, double m22);
   XOutputFont *tryGetT1FontFromFile(XRef *xref, GString *fileName,
-                                   GfxFont *gfxFont,
+                                   GBool deleteFile, GfxFont *gfxFont,
                                    double m11Orig, double m12Orig,
                                    double m21Orig, double m22Orig,
                                    double m11, double m12,
@@ -407,7 +418,7 @@ private:
   XOutputFont *tryGetFTFont(XRef *xref, GfxFont *gfxFont,
                            double m11, double m12, double m21, double m22);
   XOutputFont *tryGetFTFontFromFile(XRef *xref, GString *fileName,
-                                   GfxFont *gfxFont,
+                                   GBool deleteFile, GfxFont *gfxFont,
                                    double m11Orig, double m12Orig,
                                    double m21Orig, double m22Orig,
                                    double m11, double m12,
@@ -417,7 +428,7 @@ private:
   XOutputFont *tryGetTTFont(XRef *xref, GfxFont *gfxFont,
                            double m11, double m12, double m21, double m22);
   XOutputFont *tryGetTTFontFromFile(XRef *xref, GString *fileName,
-                                   GfxFont *gfxFont,
+                                   GBool deleteFile, GfxFont *gfxFont,
                                    double m11Orig, double m12Orig,
                                    double m21Orig, double m22Orig,
                                    double m11, double m12,
@@ -480,10 +491,11 @@ class XOutputDev: public OutputDev {
 public:
 
   // Constructor.
-  XOutputDev(Display *displayA, Pixmap pixmapA, Guint depthA,
-            Colormap colormapA, GBool reverseVideoA,
-            unsigned long paperColor, GBool installCmap,
-            int rgbCubeSize);
+  XOutputDev(Display *displayA, int screenNumA,
+            Visual *visualA, Colormap colormapA,
+            GBool reverseVideoA, unsigned long paperColorA,
+            GBool installCmap, int rgbCubeSize,
+            int forceDepth = 0);
 
   // Destructor.
   virtual ~XOutputDev();
@@ -583,11 +595,18 @@ public:
 
   GBool isReverseVideo() { return reverseVideo; }
 
-protected:
-
   // Update pixmap ID after a page change.
-  void setPixmap(Pixmap pixmap1, int pixmapW1, int pixmapH1)
-    { pixmap = pixmap1; pixmapW = pixmapW1; pixmapH = pixmapH1; }
+  void setPixmap(Pixmap pixmapA, int pixmapWA, int pixmapHA)
+    { pixmap = pixmapA; pixmapW = pixmapWA; pixmapH = pixmapHA; }
+
+  // Get the off-screen pixmap, its size, various display info.
+  Pixmap getPixmap() { return pixmap; }
+  int getPixmapWidth() { return pixmapW; }
+  int getPixmapHeight() { return pixmapH; }
+  Display *getDisplay() { return display; }
+  Guint getDepth() { return depth; }
+
+  Gulong findColor(GfxRGB *rgb);
 
 private:
 
@@ -597,6 +616,7 @@ private:
   Pixmap pixmap;               // pixmap to draw into
   int pixmapW, pixmapH;                // size of pixmap
   Guint depth;                 // pixmap depth
+  Visual *visual;              // X visual
   Colormap colormap;           // X colormap
   int flatness;                        // line flatness
   GC paperGC;                  // GC for background
@@ -608,6 +628,7 @@ private:
   int rShift, gShift, bShift;  // RGB shifts (for TrueColor)
   Gulong                       // color cube
     colors[maxRGBCube * maxRGBCube * maxRGBCube];
+  unsigned long paperColor;    // paper color (pixel value)
   int numColors;               // size of color cube
   double redMap[256];          // map pixel (from color cube) to red value
   GBool reverseVideo;          // reverse video mode
@@ -642,7 +663,6 @@ private:
   void drawType3Glyph(T3FontCache *t3Font,
                      T3FontCacheTag *tag, Guchar *data,
                      double x, double y, GfxRGB *color);
-  Gulong findColor(GfxRGB *rgb);
   Gulong findColor(GfxRGB *x, GfxRGB *err);
 };
 
index 0e1bbc9c6c620a4162cc2e7609fcad89bc1122e1..901caa596b3acd7cb9f56a28e8e4a1406664046a 100644 (file)
@@ -6,11 +6,12 @@
 //
 //========================================================================
 
-#ifdef __GNUC__
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
 #pragma implementation
 #endif
 
-#include <aconf.h>
 #include <stdlib.h>
 #include <stddef.h>
 #include <string.h>
@@ -454,6 +455,7 @@ GBool XRef::checkEncrypted(GString *ownerPassword, GString *userPassword) {
   ret = gFalse;
 
   permFlags = defPermFlags;
+  ownerPasswordOk = gFalse;
   trailerDict.dictLookup("Encrypt", &encrypt);
   if ((encrypted1 = encrypt.isDict())) {
     ret = gTrue;
index 7876fa6c5807473a69ad69e9c181fcd14d499abd..ebe5b5434b731e07227b32cea58ee03b714aeab5 100644 (file)
@@ -9,7 +9,9 @@
 #ifndef XREF_H
 #define XREF_H
 
-#ifdef __GNUC__
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
 #pragma interface
 #endif
 
@@ -81,6 +83,11 @@ public:
   // Returns false if unknown or file is not damaged.
   GBool getStreamEnd(Guint streamStart, Guint *streamEnd);
 
+  // Direct access.
+  int getSize() { return size; }
+  XRefEntry *getEntry(int i) { return &entries[i]; }
+  Object *getTrailerDict() { return &trailerDict; }
+
 private:
 
   BaseStream *str;             // input stream
index 898ad00ed5f5ea951f51e17e0dea99b495543c8a..5e35bfb48fad33678e6877525b1d891b53f70388 100644 (file)
@@ -72,6 +72,9 @@ int main(int argc, char *argv[]) {
   GString *ownerPW, *userPW;
   ImageOutputDev *imgOut;
   GBool ok;
+  int exitCode;
+
+  exitCode = 99;
 
   // parse args
   ok = parseArgs(argDesc, &argc, argv);
@@ -81,7 +84,7 @@ int main(int argc, char *argv[]) {
     if (!printVersion) {
       printUsage("pdfimages", "<PDF-file> <image-root>", argDesc);
     }
-    exit(1);
+    goto err0;
   }
   fileName = new GString(argv[1]);
   imgRoot = argv[2];
@@ -111,13 +114,15 @@ int main(int argc, char *argv[]) {
     delete ownerPW;
   }
   if (!doc->isOk()) {
-    goto err;
+    exitCode = 1;
+    goto err1;
   }
 
   // check for copy permission
   if (!doc->okToCopy()) {
     error(-1, "Copying of images from this document is not allowed.");
-    goto err;
+    exitCode = 3;
+    goto err1;
   }
 
   // get page range
@@ -128,18 +133,22 @@ int main(int argc, char *argv[]) {
 
   // write image files
   imgOut = new ImageOutputDev(imgRoot, dumpJPEG);
-  if (imgOut->isOk())
+  if (imgOut->isOk()) {
     doc->displayPages(imgOut, firstPage, lastPage, 72, 0, gFalse);
+  }
   delete imgOut;
 
+  exitCode = 0;
+
   // clean up
- err:
+ err1:
   delete doc;
   delete globalParams;
+ err0:
 
   // check for memory leaks
   Object::memCheck(stderr);
   gMemReport(stderr);
 
-  return 0;
+  return exitCode;
 }
index ea70afe93d8803a3702de673f08e07b0203b6530..f1df0953dd0869eff9a8372661fb78d0bf88ca91 100644 (file)
@@ -76,6 +76,9 @@ int main(int argc, char *argv[]) {
   FILE *f;
   GString *metadata;
   GBool ok;
+  int exitCode;
+
+  exitCode = 99;
 
   // parse args
   ok = parseArgs(argDesc, &argc, argv);
@@ -85,7 +88,7 @@ int main(int argc, char *argv[]) {
     if (!printVersion) {
       printUsage("pdfinfo", "<PDF-file>", argDesc);
     }
-    exit(1);
+    goto err0;
   }
   fileName = new GString(argv[1]);
 
@@ -121,6 +124,7 @@ int main(int argc, char *argv[]) {
     delete ownerPW;
   }
   if (!doc->isOk()) {
+    exitCode = 1;
     goto err2;
   }
 
@@ -179,7 +183,10 @@ int main(int argc, char *argv[]) {
   f = fopen(fileName->getCString(), "rb");
 #endif
   if (f) {
-#if HAVE_FSEEK64
+#if HAVE_FSEEKO
+    fseeko(f, 0, SEEK_END);
+    printf("File size:    %u bytes\n", (Guint)ftello(f));
+#elif HAVE_FSEEK64
     fseek64(f, 0, SEEK_END);
     printf("File size:    %u bytes\n", (Guint)ftell64(f));
 #else
@@ -203,18 +210,21 @@ int main(int argc, char *argv[]) {
     delete metadata;
   }
 
+  exitCode = 0;
+
   // clean up
  err2:
   uMap->decRefCnt();
   delete doc;
  err1:
   delete globalParams;
+ err0:
 
   // check for memory leaks
   Object::memCheck(stderr);
   gMemReport(stderr);
 
-  return 0;
+  return exitCode;
 }
 
 static void printInfoString(Dict *infoDict, char *key, char *text,
@@ -278,8 +288,9 @@ static void printInfoDate(Dict *infoDict, char *key, char *text) {
       tmStruct.tm_wday = -1;
       tmStruct.tm_yday = -1;
       tmStruct.tm_isdst = -1;
-      mktime(&tmStruct); // compute the tm_wday and tm_yday fields
-      if (strftime(buf, sizeof(buf), "%c", &tmStruct)) {
+      // compute the tm_wday and tm_yday fields
+      if (mktime(&tmStruct) != (time_t)-1 &&
+         strftime(buf, sizeof(buf), "%c", &tmStruct)) {
        fputs(buf, stdout);
       } else {
        fputs(s, stdout);
index 460377ffac6bc040637cf80ae1271b2ee78660f5..76f60a6af85a747e998f29943c0f6121b6a44160 100644 (file)
@@ -72,6 +72,9 @@ int main(int argc, char *argv[]) {
   GString *ownerPW, *userPW;
   PBMOutputDev *pbmOut;
   GBool ok;
+  int exitCode;
+
+  exitCode = 99;
 
   // parse args
   ok = parseArgs(argDesc, &argc, argv);
@@ -81,7 +84,7 @@ int main(int argc, char *argv[]) {
     if (!printVersion) {
       printUsage("pdftopbm", "<PDF-file> <PBM-root>", argDesc);
     }
-    exit(1);
+    goto err0;
   }
   fileName = new GString(argv[1]);
   pbmRoot = argv[2];
@@ -111,7 +114,8 @@ int main(int argc, char *argv[]) {
     delete ownerPW;
   }
   if (!doc->isOk()) {
-    goto err;
+    exitCode = 1;
+    goto err1;
   }
 
   // get page range
@@ -126,14 +130,17 @@ int main(int argc, char *argv[]) {
   doc->displayPages(pbmOut, firstPage, lastPage, resolution, 0, gFalse);
   PBMOutputDev::killPBMOutputDev(pbmOut);
 
+  exitCode = 0;
+
   // clean up
- err:
+ err1:
   delete doc;
   delete globalParams;
+ err0:
 
   // check for memory leaks
   Object::memCheck(stderr);
   gMemReport(stderr);
 
-  return 0;
+  return exitCode;
 }
index 7f60be024f9063f20ffd1a3b50b8eb8be227c2c8..d5d2de85d38fcf50255062eab3e9f2079eb42454 100644 (file)
@@ -127,6 +127,9 @@ int main(int argc, char *argv[]) {
   PSOutputDev *psOut;
   GBool ok;
   char *p;
+  int exitCode;
+
+  exitCode = 99;
 
   // parse args
   ok = parseArgs(argDesc, &argc, argv);
@@ -178,7 +181,7 @@ int main(int argc, char *argv[]) {
   if (paperSize[0]) {
     if (!globalParams->setPSPaperSize(paperSize)) {
       fprintf(stderr, "Invalid paper size\n");
-      exit(1);
+      goto err0;
     }
   } else {
     if (paperWidth) {
@@ -234,12 +237,14 @@ int main(int argc, char *argv[]) {
     delete ownerPW;
   }
   if (!doc->isOk()) {
+    exitCode = 1;
     goto err1;
   }
 
   // check for print permission
   if (!doc->okToPrint()) {
     error(-1, "Printing this document is not allowed.");
+    exitCode = 3;
     goto err1;
   }
 
@@ -276,19 +281,26 @@ int main(int argc, char *argv[]) {
                          doc->getCatalog(), firstPage, lastPage, mode);
   if (psOut->isOk()) {
     doc->displayPages(psOut, firstPage, lastPage, 72, 0, gFalse);
+  } else {
+    delete psOut;
+    exitCode = 2;
+    goto err2;
   }
   delete psOut;
 
+  exitCode = 0;
+
   // clean up
  err2:
   delete psFileName;
  err1:
   delete doc;
   delete globalParams;
+ err0:
 
   // check for memory leaks
   Object::memCheck(stderr);
   gMemReport(stderr);
 
-  return 0;
+  return exitCode;
 }
index f4b39ce9452ffc2e150bdd5b283cccf66b4a6650..150954fc983210c2a7a1aed5c778eaeab2736819 100644 (file)
@@ -91,6 +91,9 @@ int main(int argc, char *argv[]) {
   Object info;
   GBool ok;
   char *p;
+  int exitCode;
+
+  exitCode = 99;
 
   // parse args
   ok = parseArgs(argDesc, &argc, argv);
@@ -100,7 +103,7 @@ int main(int argc, char *argv[]) {
     if (!printVersion) {
       printUsage("pdftotext", "<PDF-file> [<text-file>]", argDesc);
     }
-    exit(1);
+    goto err0;
   }
   fileName = new GString(argv[1]);
 
@@ -144,12 +147,14 @@ int main(int argc, char *argv[]) {
     delete ownerPW;
   }
   if (!doc->isOk()) {
+    exitCode = 1;
     goto err2;
   }
 
   // check for copy permission
   if (!doc->okToCopy()) {
     error(-1, "Copying of text from this document is not allowed.");
+    exitCode = 3;
     goto err2;
   }
 
@@ -182,6 +187,7 @@ int main(int argc, char *argv[]) {
     } else {
       if (!(f = fopen(textFileName->getCString(), "wb"))) {
        error(-1, "Couldn't open text file '%s'", textFileName->getCString());
+       exitCode = 2;
        goto err3;
       }
     }
@@ -219,6 +225,10 @@ int main(int argc, char *argv[]) {
   textOut = new TextOutputDev(textFileName->getCString(), rawOrder, htmlMeta);
   if (textOut->isOk()) {
     doc->displayPages(textOut, firstPage, lastPage, 72, 0, gFalse);
+  } else {
+    delete textOut;
+    exitCode = 2;
+    goto err3;
   }
   delete textOut;
 
@@ -229,6 +239,7 @@ int main(int argc, char *argv[]) {
     } else {
       if (!(f = fopen(textFileName->getCString(), "ab"))) {
        error(-1, "Couldn't open text file '%s'", textFileName->getCString());
+       exitCode = 2;
        goto err3;
       }
     }
@@ -240,6 +251,8 @@ int main(int argc, char *argv[]) {
     }
   }
 
+  exitCode = 0;
+
   // clean up
  err3:
   delete textFileName;
@@ -248,12 +261,13 @@ int main(int argc, char *argv[]) {
   uMap->decRefCnt();
  err1:
   delete globalParams;
+ err0:
 
   // check for memory leaks
   Object::memCheck(stderr);
   gMemReport(stderr);
 
-  return 0;
+  return exitCode;
 }
 
 static void printInfoString(FILE *f, Dict *infoDict, char *key,
diff --git a/pdf/xpdf/postscript.xbm b/pdf/xpdf/postscript.xbm
deleted file mode 100644 (file)
index 016e126..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-#define postscript_width 15
-#define postscript_height 15
-static unsigned char postscript_bits[] = {
-   0xf0, 0x7f, 0x10, 0x40, 0x10, 0x40, 0xc8, 0x23, 0x08, 0x20, 0x68, 0x23,
-   0x04, 0x10, 0x34, 0x10, 0x04, 0x10, 0xff, 0x7f, 0x55, 0x55, 0xab, 0x6a,
-   0x55, 0x55, 0xab, 0x6a, 0xfe, 0x3f};
index c46f50f1d8efe958e1b2b6335981ef62c09483f5..35cc7c1cf63a38f7ad586e3c82df7299171dc0de 100644 (file)
@@ -15,19 +15,21 @@ $ if f$search("COMMON.OLB").eqs."" then lib/create common.olb
 $!
 $ COMMON_OBJS = "Annot.obj,Array.obj,BuiltinFont.obj," + - 
                 "BuiltinFontTables.obj,Catalog.obj,CharCodeToUnicode.obj," + - 
-               "CMap.obj,Decrypt.obj,Dict.obj,Error.obj," + -
+                "CMap.obj,Decrypt.obj,Dict.obj,Error.obj," + -
                 "FontEncodingTables.obj,FontFile.obj," + -
                 "Function.obj,Gfx.obj,GfxFont.obj,GfxState.obj,"+ - 
-               "GlobalParams.obj,Lexer.obj," + -
-                "Link.obj,NameToCharCode.obj,Object.obj,OutputDev.obj," + -
-               "Page.obj,Parser.obj,PDFdoc.obj,PSTokenizer.obj," + -
-                "Stream.obj,UnicodeMap.obj,XRef.obj"
+                "GlobalParams.obj,JBIG2Stream.obj,Lexer.obj," + -
+                "Link.obj,NameToCharCode.obj,Object.obj,Outline.obj,"+ -
+                "OutputDev.obj,Page.obj,Parser.obj,PDFdoc.obj," + -
+                "PDFDocEncoding.obj,PSTokenizer.obj,Stream.obj," + -
+                "UnicodeMap.obj,XRef.obj"
 $ COMMON_LIBS = "[]common.olb/lib,[-.goo]libgoo.olb/lib"
 $!
-$ XPDF_OBJS = "xpdf.obj,FTFont.obj,LTKOutputDev.obj,PSOutputDev.obj," + -
+$ XPDF_OBJS = "xpdf.obj,FTFont.obj,PSOutputDev.obj," + -
               "SFont.obj,T1Font.obj,TextOutputDev.obj,TTFont.obj," + -
-              "XOutputDev.obj"
-$ XPDF_LIBS = "[-.ltk]libltk.olb/lib"
+              "XOutputDev.obj,XPDFApp.o,XPDFCore.o,XPDFTree.o," + -
+              "XPDFViewer.o,XPixmapOutputDev.o"
+$ XPDF_LIBS = ""
 $!
 $ PDFTOPS_OBJS   = "pdftops.obj,PSOutputDev.obj" 
 $ PDFTOPS_LIBS   = ""
@@ -47,11 +49,6 @@ $ PDFIMAGES_LIBS = ""
 $!
 $ PDFFONTS_OBJS  = "pdffonts.obj"
 $ PDFFONTS_LIBS  = ""
-$! Build xpdf-ltk.h
-$ close sys$input 
-$ define/user sys$input xpdf.ltk
-$ define/user sys$output xpdf-ltk.h
-$ run [-.ltk]ltkbuild
 $!
 $COMPILE_CXX_LOOP:
 $ file = f$element(i, ",",COMMON_OBJS)
diff --git a/pdf/xpdf/xpdf-flip.ltk b/pdf/xpdf/xpdf-flip.ltk
deleted file mode 100644 (file)
index 4a249f8..0000000
+++ /dev/null
@@ -1,253 +0,0 @@
-#========================================================================
-#
-# xpdf-flip.ltk
-#
-# Copyright 1997-2002 Glyph & Cog, LLC
-#
-#========================================================================
-
-#------------------------------------------------------------------------
-# main window
-#------------------------------------------------------------------------
-
-Window(func:makeWindow title:"xpdf" icon:xpdfIcon) {
-  Box(x:2 y:1 xfill:1 yfill:1) {
-
-    # canvas, scrollbars, page number
-    Box(x:2 y:2 xfill:1 yfill:1) {
-      Box1(xfill:1 yfill:1 sunken) {
-        ScrollingCanvas(name:"canvas" w:100 h:100)
-      }
-      Box1(yfill:1) {
-        Scrollbar(name:"vScrollbar" vert min:0 max:100 move:&scrollVertCbk)
-      }
-      Box(x:4 y:1) {
-        Box1() {
-          Label(text:"Page"
-                font:"-*-courier-medium-r-*-*-14-*-*-*-*-*-*-*")
-        }
-        Box1(sunken left:4 right:4) {
-          TextIn(name:"pageNum" mw:6 done:&pageNumCbk
-                 font:"-*-courier-medium-r-*-*-14-*-*-*-*-*-*-*")
-        }
-        Box1() {
-          Label(name:"numPages" length:9
-                font:"-*-courier-medium-r-*-*-14-*-*-*-*-*-*-*")
-        }
-        Box(x:1 y:2 xfill:1) {
-          Box1(xfill:1) {
-            Scrollbar(name:"hScrollbar" horiz min:0 max:100
-                      move:&scrollHorizCbk)
-          }
-          Box1(xfill:1 yfill:1) { Empty() }
-        }
-      }
-      Box1() { Empty() }
-    }
-
-    # buttons
-    Box(x:1 y:12 yfill:1) {
-      Box1() {
-        IconButton(bitmap:leftArrow_bits w:leftArrow_width
-                   h:leftArrow_height press:&prevPageCbk)
-      }
-      Box1() {
-        IconButton(bitmap:rightArrow_bits w:rightArrow_width
-                   h:rightArrow_height press:&nextPageCbk)
-      }
-      Box1() {
-        IconButton(bitmap:dblLeftArrow_bits w:dblLeftArrow_width
-                   h:dblLeftArrow_height press:&prevTenPageCbk)
-      }
-      Box1() {
-        IconButton(bitmap:dblRightArrow_bits w:dblRightArrow_width
-                   h:dblRightArrow_height press:&nextTenPageCbk)
-      }
-      Box1(yfill:1) { Empty() }
-      Box1() {
-        IconButton(bitmap:zoomIn_bits w:zoomIn_width
-                   h:zoomIn_height press:&zoomInCbk)
-      }
-      Box1() {
-        IconButton(bitmap:zoomOut_bits w:zoomOut_width
-                   h:zoomOut_height press:&zoomOutCbk)
-      }
-      Box1(yfill:1) { Empty() }
-      Box1() {
-        IconButton(bitmap:postscript_bits w:postscript_width
-                   h:postscript_height press:&postScriptCbk)
-      }
-      Box1() {
-        IconButton(bitmap:about_bits w:about_width h:about_height
-                   press:&aboutCbk)
-      }
-      Box1(yfill:1) { Empty() }
-      Box1() { Button(label:"Quit" press:&quitCbk) }
-    }
-  }
-}
-
-#------------------------------------------------------------------------
-# menu for main window
-#------------------------------------------------------------------------
-
-Menu(func:makeMenu title:"xpdf" n:9) {
-  MenuItem(text:"Open..."      shortcut:"O"     num:menuOpen
-           select:&menuCbk)
-  MenuItem(text:"Save PDF..."                   num:menuSavePDF
-           select:&menuCbk)
-  MenuItem(text:NULL)
-  MenuItem(text:"Find"         shortcut:"F"     num:menuFind
-           select:&menuCbk)
-  MenuItem(text:NULL)
-  MenuItem(text:"Rotate counterclockwise"       num:menuRotateCCW
-           select:&menuCbk)
-  MenuItem(text:"Rotate clockwise"              num:menuRotateCW
-           select:&menuCbk)
-  MenuItem(text:NULL)
-  MenuItem(text:"Quit"         shortcut:"Q"     num:menuQuit
-           select:&menuCbk)
-}
-
-#------------------------------------------------------------------------
-# "PostScript output" dialog
-#------------------------------------------------------------------------
-
-Window(func:makePostScriptDialog dialog:gTrue defWidget:"ok"
-       title:"xpdf: PostScript output") {
-  Box(x:1 y:3) {
-    Box(x:4 y:1) {
-      Box1() { Label(text:"Pages:") }
-      Box1(sunken) {
-        TextIn(name:"firstPage" mw:6 tab:"lastPage"
-               font:"-*-courier-medium-r-*-*-14-*-*-*-*-*-*-*")
-      }
-      Box1() { Label(text:"to") }
-      Box1(sunken) {
-        TextIn(name:"lastPage" mw:6 tab:"fileName"
-               font:"-*-courier-medium-r-*-*-14-*-*-*-*-*-*-*")
-      }
-    }
-    Box(x:2 y:1) {
-      Box1() { Label(text:"File:") }
-      Box1(sunken xfill:1) {
-        TextIn(name:"fileName" mw:32
-               font:"-*-courier-medium-r-*-*-14-*-*-*-*-*-*-*")
-      }
-    }
-    Box(x:3 y:1 top:8) {
-      Box1(left:8) { Button(name:"ok" label:"Ok" press:&psButtonCbk num:1) }
-      Box1(xfill:1) { Empty() }
-      Box1(right:8) { Button(label:"Cancel" press:&psButtonCbk num:0) }
-    }
-  }
-}
-
-#------------------------------------------------------------------------
-# "open" dialog
-#------------------------------------------------------------------------
-
-Window(func:makeOpenDialog dialog:gTrue defWidget:"open"
-       title:"xpdf: Open...") {
-  Box(x:1 y:2 xfill:1 yfill:1) {
-    Box1(xfill:1 yfill:1) {
-      FileReq(name:"fileReq" select:openSelectCbk
-              font:"-*-courier-medium-r-*-*-14-*-*-*-*-*-*-*")
-    }
-    Box(x:3 y:1 top:8 xfill:1) {
-      Box1(left:8) { Button(name:"open" label:"Open" press:&openButtonCbk
-                            num:1) }
-      Box1(xfill:1) { Empty() }
-      Box1(right:8) { Button(label:"Cancel" press:&openButtonCbk num:0) }
-    }
-  }
-}
-
-#------------------------------------------------------------------------
-# "save" dialog
-#------------------------------------------------------------------------
-
-Window(func:makeSaveDialog dialog:gTrue defWidget:"save"
-       title:"xpdf: Save as...") {
-  Box(x:1 y:2 xfill:1 yfill:1) {
-    Box1(xfill:1 yfill:1) {
-      FileReq(name:"fileReq" select:saveSelectCbk
-              font:"-*-courier-medium-r-*-*-14-*-*-*-*-*-*-*")
-    }
-    Box(x:3 y:1 top:8 xfill:1) {
-      Box1(left:8) { Button(name:"save" label:"Save" press:&saveButtonCbk
-                            num:1) }
-      Box1(xfill:1) { Empty() }
-      Box1(right:8) { Button(label:"Cancel" press:&saveButtonCbk num:0) }
-    }
-  }
-}
-
-#------------------------------------------------------------------------
-# "find" window
-#------------------------------------------------------------------------
-
-Window(func:makeFindWindow defWidget:"find" title:"xpdf: Find") {
-  Box(x:1 y:3 xfill:1 yfill:1) {
-    Box(x:2 y:1 xfill:1) {
-      Box1() { Label(text:"Text:") }
-      Box1(xfill:1 sunken) {
-        TextIn(name:"text" mw:32
-               font:"-*-courier-medium-r-*-*-14-*-*-*-*-*-*-*")
-      }
-    }
-    Box1(xfill:1 yfill:1) { Empty() }
-    Box(x:3 y:1 top:8 xfill:1) {
-      Box1(left:8) { Button(name:"find" label:"Find" press:&findButtonCbk
-                            num:1) }
-      Box1(xfill:1) { Empty() }
-      Box1(right:8) { Button(label:"Close" press:&findButtonCbk num:0) }
-    }
-  }
-}
-
-#------------------------------------------------------------------------
-# "about" window
-#------------------------------------------------------------------------
-
-Window(func:makeAboutWindow defWidget:"close" title:"About xpdf") {
-  Box(x:1 y:2) {
-    Box(x:1 y:10 left:2 right:2 top:2 bottom:2 sunken) {
-      Box1(bottom:0) {
-        Label(text:"xpdf"
-              font:"-*-times-bold-i-*-*-24-*-*-*-*-*-*-*")
-      }
-      Box1(bottom:12) {
-        Label(text:["Version " xpdfVersion])
-      }
-      Box1(bottom:0) {
-        Label(text:xpdfCopyright)
-      }
-      Box1(bottom:12) {
-        Label(text:"derekn@foolabs.com")
-      }
-      Box1(bottom:0) {
-        Label(text:["Supports PDF version " pdfVersion "."])
-      }
-      Box1(bottom:0) {
-        Label(text:"The PDF data structures, operators, and specification")
-      }
-      Box1(bottom:12) {
-        Label(text:"are copyright 1995 Adobe Systems Inc.")
-      }
-      Box1(bottom:0) {
-        Label(text:"Mouse button 1: select text / follow link")
-      }
-      Box1(bottom:12) {
-        Label(text:"Mouse button 3: menu")
-      }
-      Box1(bottom:12) {
-        Label(text:"http://www.foolabs.com/xpdf/")
-      }
-    }
-    Box(x:2 y:1) {
-      Box1(xfill:1) { Empty() }
-      Box1() { Button(name:"close" label:"Close" press:&closeAboutCbk) }
-    }
-  }
-}
diff --git a/pdf/xpdf/xpdf-ltk.h b/pdf/xpdf/xpdf-ltk.h
deleted file mode 100644 (file)
index 5665ee7..0000000
+++ /dev/null
@@ -1,252 +0,0 @@
-// This file was generated by ltkbuild 1.01
-
-LTKWindow *makeWindow(LTKApp *a) {
-  return new LTKWindow(a, gFalse, "xpdf", xpdfIcon, NULL,
-    new LTKBox(NULL, 1, 2, 0, 0, 0, 0, ltkBorderNone, 1, 1,
-      new LTKBox(NULL, 2, 2, 0, 0, 0, 0, ltkBorderNone, 1, 1,
-        new LTKBox(NULL, 1, 1, 2, 2, 2, 2, ltkBorderSunken, 1, 1,
-          new LTKScrollingCanvas("canvas", 0, 100, 100, 32, 32)
-        ),
-        new LTKBox(NULL, 1, 1, 2, 2, 2, 2, ltkBorderNone, 0, 1,
-          new LTKScrollbar("vScrollbar", 0, gTrue, 0, 100, &scrollVertCbk)
-        ),
-        new LTKBox(NULL, 1, 1, 2, 2, 2, 2, ltkBorderNone, 1, 0,
-          new LTKScrollbar("hScrollbar", 0, gFalse, 0, 100, &scrollHorizCbk)
-        ),
-        new LTKBox(NULL, 1, 1, 2, 2, 2, 2, ltkBorderNone, 0, 0,
-          new LTKEmpty()
-        )
-      ),
-      new LTKBox(NULL, 15, 1, 0, 0, 0, 0, ltkBorderNone, 1, 0,
-        new LTKBox(NULL, 1, 1, 2, 2, 2, 2, ltkBorderNone, 0, 0,
-          new LTKButton(NULL, 0, backArrow_bits, backArrow_width, backArrow_height, ltkButtonClick, &backCbk)
-        ),
-        new LTKBox(NULL, 1, 1, 2, 2, 2, 2, ltkBorderNone, 0, 0,
-          new LTKButton(NULL, 0, dblLeftArrow_bits, dblLeftArrow_width, dblLeftArrow_height, ltkButtonClick, &prevTenPageCbk)
-        ),
-        new LTKBox(NULL, 1, 1, 2, 2, 2, 2, ltkBorderNone, 0, 0,
-          new LTKButton(NULL, 0, leftArrow_bits, leftArrow_width, leftArrow_height, ltkButtonClick, &prevPageCbk)
-        ),
-        new LTKBox(NULL, 1, 1, 2, 2, 2, 2, ltkBorderNone, 0, 0,
-          new LTKButton(NULL, 0, rightArrow_bits, rightArrow_width, rightArrow_height, ltkButtonClick, &nextPageCbk)
-        ),
-        new LTKBox(NULL, 1, 1, 2, 2, 2, 2, ltkBorderNone, 0, 0,
-          new LTKButton(NULL, 0, dblRightArrow_bits, dblRightArrow_width, dblRightArrow_height, ltkButtonClick, &nextTenPageCbk)
-        ),
-        new LTKBox(NULL, 1, 1, 2, 2, 2, 2, ltkBorderNone, 0, 0,
-          new LTKButton(NULL, 0, forwardArrow_bits, forwardArrow_width, forwardArrow_height, ltkButtonClick, &forwardCbk)
-        ),
-        new LTKBox(NULL, 1, 1, 2, 2, 2, 2, ltkBorderNone, 0, 0,
-          new LTKLabel(NULL, 0, ltkLabelStatic, 8, "-*-courier-medium-r-normal-*-14-*-*-*-*-*-*-*", "Page")
-        ),
-        new LTKBox(NULL, 1, 1, 4, 4, 2, 2, ltkBorderSunken, 0, 0,
-          new LTKTextIn("pageNum", 0, 6, "-*-courier-medium-r-normal-*-14-*-*-*-*-*-*-*", &pageNumCbk, NULL)
-        ),
-        new LTKBox(NULL, 1, 1, 2, 2, 2, 2, ltkBorderNone, 0, 0,
-          new LTKLabel("numPages", 0, ltkLabelMaxLength, 9, "-*-courier-medium-r-normal-*-14-*-*-*-*-*-*-*", NULL)
-        ),
-        new LTKBox(NULL, 1, 1, 2, 2, 2, 2, ltkBorderNone, 0, 0,
-          new LTKMenuButton("zoom", 0, zoomMenu)
-        ),
-        new LTKBox(NULL, 1, 1, 2, 2, 2, 2, ltkBorderNone, 0, 0,
-          new LTKButton(NULL, 0, find_bits, find_width, find_height, ltkButtonClick, &findCbk)
-        ),
-        new LTKBox(NULL, 1, 1, 2, 2, 2, 2, ltkBorderNone, 0, 0,
-          new LTKButton(NULL, 0, postscript_bits, postscript_width, postscript_height, ltkButtonClick, &postScriptCbk)
-        ),
-        new LTKBox(NULL, 1, 1, 2, 2, 2, 2, ltkBorderNone, 0, 0,
-          new LTKButton(NULL, 0, about_bits, about_width, about_height, ltkButtonClick, &aboutCbk)
-        ),
-        new LTKBox(NULL, 1, 1, 2, 2, 2, 2, ltkBorderNone, 1, 0,
-          new LTKLabel("link", 0, ltkLabelFixedWidth, 8, "-*-helvetica-medium-r-normal-*-12-*-*-*-*-*-*-*", NULL)
-        ),
-        new LTKBox(NULL, 1, 1, 2, 2, 2, 2, ltkBorderNone, 0, 0,
-          new LTKButton(NULL, 0, "Quit", ltkButtonClick, &quitCbk)
-        )
-      )
-    )
-  );
-}
-
-LTKWindow *makeFullScreenWindow(LTKApp *a) {
-  return new LTKWindow(a, gFalse, "xpdf", xpdfIcon, NULL,
-    new LTKBox(NULL, 1, 1, 0, 0, 0, 0, ltkBorderNone, 1, 1,
-      new LTKScrollingCanvas("canvas", 0, 100, 100, 32, 32)
-    )
-  );
-}
-
-LTKMenu *makeMenu() {
-  return new LTKMenu("xpdf", 8,
-    new LTKMenuItem("Open...", "O", menuOpen, &menuCbk, NULL),
-    new LTKMenuItem("Reload", "R", menuReload, &menuCbk, NULL),
-    new LTKMenuItem("Save as...", NULL, menuSavePDF, &menuCbk, NULL),
-    new LTKMenuItem(NULL, NULL, 0, NULL, NULL),
-    new LTKMenuItem("Rotate counterclockwise", NULL, menuRotateCCW, &menuCbk, NULL),
-    new LTKMenuItem("Rotate clockwise", NULL, menuRotateCW, &menuCbk, NULL),
-    new LTKMenuItem(NULL, NULL, 0, NULL, NULL),
-    new LTKMenuItem("Quit", "Q", menuQuit, &menuCbk, NULL)
-
-  );
-}
-
-LTKMenu *makeZoomMenu() {
-  return new LTKMenu("zoom", 14,
-    new LTKMenuItem("-5", NULL, -5, &zoomMenuCbk, NULL),
-    new LTKMenuItem("-4", NULL, -4, &zoomMenuCbk, NULL),
-    new LTKMenuItem("-3", NULL, -3, &zoomMenuCbk, NULL),
-    new LTKMenuItem("-2", NULL, -2, &zoomMenuCbk, NULL),
-    new LTKMenuItem("-1", NULL, -1, &zoomMenuCbk, NULL),
-    new LTKMenuItem("0", NULL, 0, &zoomMenuCbk, NULL),
-    new LTKMenuItem("+1", NULL, 1, &zoomMenuCbk, NULL),
-    new LTKMenuItem("+2", NULL, 2, &zoomMenuCbk, NULL),
-    new LTKMenuItem("+3", NULL, 3, &zoomMenuCbk, NULL),
-    new LTKMenuItem("+4", NULL, 4, &zoomMenuCbk, NULL),
-    new LTKMenuItem("+5", NULL, 5, &zoomMenuCbk, NULL),
-    new LTKMenuItem(NULL, NULL, 0, NULL, NULL),
-    new LTKMenuItem("fit page", "z", 100, &zoomMenuCbk, NULL),
-    new LTKMenuItem("fit width", "w", 101, &zoomMenuCbk, NULL)
-
-  );
-}
-
-LTKWindow *makePostScriptDialog(LTKApp *a) {
-  return new LTKWindow(a, gTrue, "xpdf: PostScript output", NULL, "ok",
-    new LTKBox(NULL, 1, 3, 0, 0, 0, 0, ltkBorderNone, 0, 0,
-      new LTKBox(NULL, 4, 1, 0, 0, 0, 0, ltkBorderNone, 0, 0,
-        new LTKBox(NULL, 1, 1, 2, 2, 2, 2, ltkBorderNone, 0, 0,
-          new LTKLabel(NULL, 0, ltkLabelStatic, 8, NULL, "Pages:")
-        ),
-        new LTKBox(NULL, 1, 1, 2, 2, 2, 2, ltkBorderSunken, 0, 0,
-          new LTKTextIn("firstPage", 0, 6, "-*-courier-medium-r-normal-*-14-*-*-*-*-*-*-*", NULL, "lastPage")
-        ),
-        new LTKBox(NULL, 1, 1, 2, 2, 2, 2, ltkBorderNone, 0, 0,
-          new LTKLabel(NULL, 0, ltkLabelStatic, 8, NULL, "to")
-        ),
-        new LTKBox(NULL, 1, 1, 2, 2, 2, 2, ltkBorderSunken, 0, 0,
-          new LTKTextIn("lastPage", 0, 6, "-*-courier-medium-r-normal-*-14-*-*-*-*-*-*-*", NULL, "fileName")
-        )
-      ),
-      new LTKBox(NULL, 2, 1, 0, 0, 0, 0, ltkBorderNone, 0, 0,
-        new LTKBox(NULL, 1, 1, 2, 2, 2, 2, ltkBorderNone, 0, 0,
-          new LTKLabel(NULL, 0, ltkLabelStatic, 8, NULL, "File:")
-        ),
-        new LTKBox(NULL, 1, 1, 2, 2, 2, 2, ltkBorderSunken, 1, 0,
-          new LTKTextIn("fileName", 0, 32, "-*-courier-medium-r-normal-*-14-*-*-*-*-*-*-*", NULL, NULL)
-        )
-      ),
-      new LTKBox(NULL, 3, 1, 0, 0, 8, 0, ltkBorderNone, 0, 0,
-        new LTKBox(NULL, 1, 1, 8, 2, 2, 2, ltkBorderNone, 0, 0,
-          new LTKButton("ok", 1, "Ok", ltkButtonClick, &psButtonCbk)
-        ),
-        new LTKBox(NULL, 1, 1, 2, 2, 2, 2, ltkBorderNone, 1, 0,
-          new LTKEmpty()
-        ),
-        new LTKBox(NULL, 1, 1, 2, 8, 2, 2, ltkBorderNone, 0, 0,
-          new LTKButton(NULL, 0, "Cancel", ltkButtonClick, &psButtonCbk)
-        )
-      )
-    )
-  );
-}
-
-LTKWindow *makeOpenDialog(LTKApp *a) {
-  return new LTKWindow(a, gTrue, "xpdf: Open...", NULL, "open",
-    new LTKBox(NULL, 1, 2, 0, 0, 0, 0, ltkBorderNone, 1, 1,
-      new LTKBox(NULL, 1, 1, 2, 2, 2, 2, ltkBorderNone, 1, 1,
-        new LTKFileReq("fileReq", 0, openSelectCbk, "-*-courier-medium-r-normal-*-14-*-*-*-*-*-*-*")
-      ),
-      new LTKBox(NULL, 3, 1, 0, 0, 8, 0, ltkBorderNone, 1, 0,
-        new LTKBox(NULL, 1, 1, 8, 2, 2, 2, ltkBorderNone, 0, 0,
-          new LTKButton("open", 1, "Open", ltkButtonClick, &openButtonCbk)
-        ),
-        new LTKBox(NULL, 1, 1, 2, 2, 2, 2, ltkBorderNone, 1, 0,
-          new LTKEmpty()
-        ),
-        new LTKBox(NULL, 1, 1, 2, 8, 2, 2, ltkBorderNone, 0, 0,
-          new LTKButton(NULL, 0, "Cancel", ltkButtonClick, &openButtonCbk)
-        )
-      )
-    )
-  );
-}
-
-LTKWindow *makeSaveDialog(LTKApp *a) {
-  return new LTKWindow(a, gTrue, "xpdf: Save as...", NULL, "save",
-    new LTKBox(NULL, 1, 2, 0, 0, 0, 0, ltkBorderNone, 1, 1,
-      new LTKBox(NULL, 1, 1, 2, 2, 2, 2, ltkBorderNone, 1, 1,
-        new LTKFileReq("fileReq", 0, saveSelectCbk, "-*-courier-medium-r-normal-*-14-*-*-*-*-*-*-*")
-      ),
-      new LTKBox(NULL, 3, 1, 0, 0, 8, 0, ltkBorderNone, 1, 0,
-        new LTKBox(NULL, 1, 1, 8, 2, 2, 2, ltkBorderNone, 0, 0,
-          new LTKButton("save", 1, "Save", ltkButtonClick, &saveButtonCbk)
-        ),
-        new LTKBox(NULL, 1, 1, 2, 2, 2, 2, ltkBorderNone, 1, 0,
-          new LTKEmpty()
-        ),
-        new LTKBox(NULL, 1, 1, 2, 8, 2, 2, ltkBorderNone, 0, 0,
-          new LTKButton(NULL, 0, "Cancel", ltkButtonClick, &saveButtonCbk)
-        )
-      )
-    )
-  );
-}
-
-LTKWindow *makeFindWindow(LTKApp *a) {
-  return new LTKWindow(a, gFalse, "xpdf: Find", NULL, "find",
-    new LTKBox(NULL, 1, 3, 0, 0, 0, 0, ltkBorderNone, 1, 1,
-      new LTKBox(NULL, 2, 1, 0, 0, 0, 0, ltkBorderNone, 1, 0,
-        new LTKBox(NULL, 1, 1, 2, 2, 2, 2, ltkBorderNone, 0, 0,
-          new LTKLabel(NULL, 0, ltkLabelStatic, 8, NULL, "Text:")
-        ),
-        new LTKBox(NULL, 1, 1, 2, 2, 2, 2, ltkBorderSunken, 1, 0,
-          new LTKTextIn("text", 0, 32, "-*-courier-medium-r-normal-*-14-*-*-*-*-*-*-*", NULL, NULL)
-        )
-      ),
-      new LTKBox(NULL, 1, 1, 2, 2, 2, 2, ltkBorderNone, 1, 1,
-        new LTKEmpty()
-      ),
-      new LTKBox(NULL, 3, 1, 0, 0, 8, 0, ltkBorderNone, 1, 0,
-        new LTKBox(NULL, 1, 1, 8, 2, 2, 2, ltkBorderNone, 0, 0,
-          new LTKButton("find", 1, "Find", ltkButtonClick, &findButtonCbk)
-        ),
-        new LTKBox(NULL, 1, 1, 2, 2, 2, 2, ltkBorderNone, 1, 0,
-          new LTKEmpty()
-        ),
-        new LTKBox(NULL, 1, 1, 2, 8, 2, 2, ltkBorderNone, 0, 0,
-          new LTKButton(NULL, 0, "Close", ltkButtonClick, &findButtonCbk)
-        )
-      )
-    )
-  );
-}
-
-LTKWindow *makeAboutWindow(LTKApp *a) {
-  return new LTKWindow(a, gFalse, "About xpdf", NULL, "close",
-    new LTKBox(NULL, 1, 2, 0, 0, 0, 0, ltkBorderNone, 1, 1,
-      new LTKBox(NULL, 2, 2, 0, 0, 0, 0, ltkBorderNone, 1, 1,
-        new LTKBox(NULL, 1, 1, 2, 2, 2, 2, ltkBorderSunken, 1, 1,
-          new LTKList("list", 0, 400, 30, gFalse, "-*-courier-medium-r-normal-*-12-*-*-*-*-*-*-*")
-        ),
-        new LTKBox(NULL, 1, 1, 2, 2, 2, 2, ltkBorderNone, 0, 1,
-          new LTKScrollbar("vScrollbar", 0, gTrue, 0, 100, &aboutScrollVertCbk)
-        ),
-        new LTKBox(NULL, 1, 1, 2, 2, 2, 2, ltkBorderNone, 1, 0,
-          new LTKScrollbar("hScrollbar", 0, gFalse, 0, 100, &aboutScrollHorizCbk)
-        ),
-        new LTKBox(NULL, 1, 1, 2, 2, 2, 2, ltkBorderNone, 0, 0,
-          new LTKEmpty()
-        )
-      ),
-      new LTKBox(NULL, 2, 1, 0, 0, 0, 0, ltkBorderNone, 0, 0,
-        new LTKBox(NULL, 1, 1, 2, 2, 2, 2, ltkBorderNone, 1, 0,
-          new LTKEmpty()
-        ),
-        new LTKBox(NULL, 1, 1, 2, 2, 2, 2, ltkBorderNone, 0, 0,
-          new LTKButton("close", 0, "Close", ltkButtonClick, &closeAboutCbk)
-        )
-      )
-    )
-  );
-}
-
diff --git a/pdf/xpdf/xpdf-top.ltk b/pdf/xpdf/xpdf-top.ltk
deleted file mode 100644 (file)
index e00a385..0000000
+++ /dev/null
@@ -1,252 +0,0 @@
-#========================================================================
-#
-# xpdf-top.ltk
-#
-# Copyright 1996-2002 Glyph & Cog, LLC
-#
-#========================================================================
-
-#------------------------------------------------------------------------
-# main window
-#------------------------------------------------------------------------
-
-Window(func:makeWindow title:"xpdf" icon:xpdfIcon) {
-  Box(x:1 y:2 xfill:1 yfill:1) {
-
-    # buttons, page number, etc.
-    Box(x:14 y:1 xfill:1 top:2) {
-      Box1() {
-        IconButton(bitmap:dblLeftArrow_bits w:dblLeftArrow_width
-                   h:dblLeftArrow_height press:&prevTenPageCbk)
-      }
-      Box1() {
-        IconButton(bitmap:leftArrow_bits w:leftArrow_width
-                   h:leftArrow_height press:&prevPageCbk)
-      }
-      Box1() {
-        IconButton(bitmap:rightArrow_bits w:rightArrow_width
-                   h:rightArrow_height press:&nextPageCbk)
-      }
-      Box1() {
-        IconButton(bitmap:dblRightArrow_bits w:dblRightArrow_width
-                   h:dblRightArrow_height press:&nextTenPageCbk)
-      }
-      Box1() {
-        Label(text:"Page"
-              font:"-*-courier-medium-r-normal-*-14-*-*-*-*-*-*-*")
-      }
-      Box1(sunken left:4 right:4) {
-        TextIn(name:"pageNum" mw:6 done:&pageNumCbk
-               font:"-*-courier-medium-r-normal-*-14-*-*-*-*-*-*-*")
-      }
-      Box1() {
-        Label(name:"numPages" maxLength length:9
-              font:"-*-courier-medium-r-normal-*-14-*-*-*-*-*-*-*")
-      }
-      Box1() {
-        IconButton(bitmap:zoomIn_bits w:zoomIn_width
-                   h:zoomIn_height press:&zoomInCbk)
-      }
-      Box1() {
-        IconButton(bitmap:zoomOut_bits w:zoomOut_width
-                   h:zoomOut_height press:&zoomOutCbk)
-      }
-      Box1() {
-        IconButton(bitmap:find_bits w:find_width
-                   h:find_height press:&findCbk)
-      }
-      Box1() {
-        IconButton(bitmap:postscript_bits w:postscript_width
-                   h:postscript_height press:&postScriptCbk)
-      }
-      Box1() {
-        IconButton(bitmap:about_bits w:about_width h:about_height
-                   press:&aboutCbk)
-      }
-      Box1(xfill:1) {
-        Label(name:"link" fixedWidth
-              font:"-*-helvetica-medium-r-normal-*-12-*-*-*-*-*-*-*")
-      }
-      Box1() { Button(label:"Quit" press:&quitCbk) }
-    }
-
-    # canvas and scrollbars
-    Box(x:2 y:2 xfill:1 yfill:1) {
-      Box1(xfill:1 yfill:1 sunken) {
-        ScrollingCanvas(name:"canvas" w:100 h:100)
-      }
-      Box1(yfill:1) {
-        Scrollbar(name:"vScrollbar" vert min:0 max:100 move:&scrollVertCbk)
-      }
-      Box1(xfill:1) {
-        Scrollbar(name:"hScrollbar" horiz min:0 max:100 move:&scrollHorizCbk)
-      }
-      Box1() { Empty() }
-    }
-  }
-}
-
-#------------------------------------------------------------------------
-# menu for main window
-#------------------------------------------------------------------------
-
-Menu(func:makeMenu title:"xpdf" n:7) {
-  MenuItem(text:"Open..."      shortcut:"O"     num:menuOpen
-           select:&menuCbk)
-  MenuItem(text:"Save as..."                    num:menuSavePDF
-           select:&menuCbk)
-  MenuItem(text:NULL)
-  MenuItem(text:"Rotate counterclockwise"       num:menuRotateCCW
-           select:&menuCbk)
-  MenuItem(text:"Rotate clockwise"              num:menuRotateCW
-           select:&menuCbk)
-  MenuItem(text:NULL)
-  MenuItem(text:"Quit"         shortcut:"Q"     num:menuQuit
-           select:&menuCbk)
-}
-
-#------------------------------------------------------------------------
-# "PostScript output" dialog
-#------------------------------------------------------------------------
-
-Window(func:makePostScriptDialog dialog:gTrue defWidget:"ok"
-       title:"xpdf: PostScript output") {
-  Box(x:1 y:3) {
-    Box(x:4 y:1) {
-      Box1() { Label(text:"Pages:") }
-      Box1(sunken) {
-        TextIn(name:"firstPage" mw:6 tab:"lastPage"
-               font:"-*-courier-medium-r-normal-*-14-*-*-*-*-*-*-*")
-      }
-      Box1() { Label(text:"to") }
-      Box1(sunken) {
-        TextIn(name:"lastPage" mw:6 tab:"fileName"
-               font:"-*-courier-medium-r-normal-*-14-*-*-*-*-*-*-*")
-      }
-    }
-    Box(x:2 y:1) {
-      Box1() { Label(text:"File:") }
-      Box1(sunken xfill:1) {
-        TextIn(name:"fileName" mw:32
-               font:"-*-courier-medium-r-normal-*-14-*-*-*-*-*-*-*")
-      }
-    }
-    Box(x:3 y:1 top:8) {
-      Box1(left:8) { Button(name:"ok" label:"Ok" press:&psButtonCbk num:1) }
-      Box1(xfill:1) { Empty() }
-      Box1(right:8) { Button(label:"Cancel" press:&psButtonCbk num:0) }
-    }
-  }
-}
-
-#------------------------------------------------------------------------
-# "open" dialog
-#------------------------------------------------------------------------
-
-Window(func:makeOpenDialog dialog:gTrue defWidget:"open"
-       title:"xpdf: Open...") {
-  Box(x:1 y:2 xfill:1 yfill:1) {
-    Box1(xfill:1 yfill:1) {
-      FileReq(name:"fileReq" select:openSelectCbk
-              font:"-*-courier-medium-r-normal-*-14-*-*-*-*-*-*-*")
-    }
-    Box(x:3 y:1 top:8 xfill:1) {
-      Box1(left:8) { Button(name:"open" label:"Open" press:&openButtonCbk
-                            num:1) }
-      Box1(xfill:1) { Empty() }
-      Box1(right:8) { Button(label:"Cancel" press:&openButtonCbk num:0) }
-    }
-  }
-}
-
-#------------------------------------------------------------------------
-# "save" dialog
-#------------------------------------------------------------------------
-
-Window(func:makeSaveDialog dialog:gTrue defWidget:"save"
-       title:"xpdf: Save as...") {
-  Box(x:1 y:2 xfill:1 yfill:1) {
-    Box1(xfill:1 yfill:1) {
-      FileReq(name:"fileReq" select:saveSelectCbk
-              font:"-*-courier-medium-r-normal-*-14-*-*-*-*-*-*-*")
-    }
-    Box(x:3 y:1 top:8 xfill:1) {
-      Box1(left:8) { Button(name:"save" label:"Save" press:&saveButtonCbk
-                            num:1) }
-      Box1(xfill:1) { Empty() }
-      Box1(right:8) { Button(label:"Cancel" press:&saveButtonCbk num:0) }
-    }
-  }
-}
-
-#------------------------------------------------------------------------
-# "find" window
-#------------------------------------------------------------------------
-
-Window(func:makeFindWindow defWidget:"find" title:"xpdf: Find") {
-  Box(x:1 y:3 xfill:1 yfill:1) {
-    Box(x:2 y:1 xfill:1) {
-      Box1() { Label(text:"Text:") }
-      Box1(xfill:1 sunken) {
-        TextIn(name:"text" mw:32
-               font:"-*-courier-medium-r-normal-*-14-*-*-*-*-*-*-*")
-      }
-    }
-    Box1(xfill:1 yfill:1) { Empty() }
-    Box(x:3 y:1 top:8 xfill:1) {
-      Box1(left:8) { Button(name:"find" label:"Find" press:&findButtonCbk
-                            num:1) }
-      Box1(xfill:1) { Empty() }
-      Box1(right:8) { Button(label:"Close" press:&findButtonCbk num:0) }
-    }
-  }
-}
-
-#------------------------------------------------------------------------
-# "about" window
-#------------------------------------------------------------------------
-
-Window(func:makeAboutWindow defWidget:"close" title:"About xpdf") {
-  Box(x:1 y:2) {
-    Box(x:1 y:11 left:2 right:2 top:2 bottom:2 sunken) {
-      Box1(bottom:0) {
-        Label(text:"xpdf"
-              font:"-*-times-bold-i-normal-*-24-*-*-*-*-*-*-*")
-      }
-      Box1(bottom:12) {
-        Label(text:["Version " xpdfVersion])
-      }
-      Box1(bottom:0) {
-        Label(text:xpdfCopyright)
-      }
-      Box1(bottom:12) {
-        Label(text:"derekn@foolabs.com")
-      }
-      Box1(bottom:2) {
-        Label(text:["Supports PDF version " pdfVersion "."])
-      }
-      Box1(bottom:0) {
-        Label(text:"The PDF data structures, operators, and specification")
-      }
-      Box1(bottom:12) {
-        Label(text:"are copyright 1995 Adobe Systems Inc.")
-      }
-      Box1(bottom:0) {
-        Label(text:"Mouse button 1: select text / follow link")
-      }
-      Box1(bottom:0) {
-        Label(text:"Mouse button 2: pan window")
-      }
-      Box1(bottom:12) {
-        Label(text:"Mouse button 3: menu")
-      }
-      Box1(bottom:12) {
-        Label(text:"http://www.foolabs.com/xpdf/")
-      }
-    }
-    Box(x:2 y:1) {
-      Box1(xfill:1) { Empty() }
-      Box1() { Button(name:"close" label:"Close" press:&closeAboutCbk) }
-    }
-  }
-}
index ff5455031535202237a758ff383dab047c40cfa6..ef47fb6e5dd067c5140d089158801b7edd56be4b 100644 (file)
 //========================================================================
 
 #include <aconf.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <stddef.h>
-#include <string.h>
-#include <time.h>
-#include <X11/X.h>
-#include <X11/cursorfont.h>
-#include <X11/keysym.h>
 #include "gtypes.h"
 #include "GString.h"
 #include "parseargs.h"
 #include "gfile.h"
 #include "gmem.h"
 #include "GlobalParams.h"
-#include "LTKAll.h"
 #include "Object.h"
-#include "Stream.h"
-#include "Array.h"
-#include "Dict.h"
-#include "XRef.h"
-#include "Catalog.h"
-#include "Page.h"
-#include "Link.h"
-#include "PDFDoc.h"
-#include "XOutputDev.h"
-#include "LTKOutputDev.h"
-#include "PSOutputDev.h"
-#include "TextOutputDev.h"
-#include "Error.h"
+#include "XPDFApp.h"
 #include "config.h"
 
-#ifdef XlibSpecificationRelease
-#if XlibSpecificationRelease < 5
-typedef char *XPointer;
-#endif
-#else
-typedef char *XPointer;
-#endif
-
-// hack around old X includes which are missing these symbols
-#ifndef XK_Page_Up
-#define XK_Page_Up              0xFF55
-#endif
-#ifndef XK_Page_Down
-#define XK_Page_Down            0xFF56
-#endif
-#ifndef XK_KP_Home
-#define XK_KP_Home              0xFF95
-#endif
-#ifndef XK_KP_Left
-#define XK_KP_Left              0xFF96
-#endif
-#ifndef XK_KP_Up
-#define XK_KP_Up                0xFF97
-#endif
-#ifndef XK_KP_Right
-#define XK_KP_Right             0xFF98
-#endif
-#ifndef XK_KP_Down
-#define XK_KP_Down              0xFF99
-#endif
-#ifndef XK_KP_Prior
-#define XK_KP_Prior             0xFF9A
-#endif
-#ifndef XK_KP_Page_Up
-#define XK_KP_Page_Up           0xFF9A
-#endif
-#ifndef XK_KP_Next
-#define XK_KP_Next              0xFF9B
-#endif
-#ifndef XK_KP_Page_Down
-#define XK_KP_Page_Down         0xFF9B
-#endif
-#ifndef XK_KP_End
-#define XK_KP_End               0xFF9C
-#endif
-#ifndef XK_KP_Begin
-#define XK_KP_Begin             0xFF9D
-#endif
-#ifndef XK_KP_Insert
-#define XK_KP_Insert            0xFF9E
-#endif
-#ifndef XK_KP_Delete
-#define XK_KP_Delete            0xFF9F
-#endif
-
-//------------------------------------------------------------------------
-// misc constants / enums
-//------------------------------------------------------------------------
-
-#define remoteCmdLength 512
-
-enum XpdfMenuItem {
-  menuOpen,
-  menuReload,
-  menuSavePDF,
-  menuRotateCCW,
-  menuRotateCW,
-  menuQuit
-};
-
-//------------------------------------------------------------------------
-// prototypes
-//------------------------------------------------------------------------
-
-// loadFile / displayPage
-static GBool loadFile(GString *fileName);
-static void displayPage(int page1, int zoomA, int rotateA, GBool addToHist);
-static void displayDest(LinkDest *dest, int zoomA, int rotateA,
-                       GBool addToHist);
-
-// key press and menu callbacks
-static void keyPressCbk(LTKWindow *win1, KeySym key, Guint modifiers,
-                       char *s, int n);
-static void menuCbk(LTKMenuItem *item);
-
-// mouse callbacks
-static void buttonPressCbk(LTKWidget *canvas1, int n,
-                          int mx, int my, int button, GBool dblClick);
-static void buttonReleaseCbk(LTKWidget *canvas1, int n,
-                            int mx, int my, int button, GBool click);
-static void doLink(int mx, int my);
-static void mouseMoveCbk(LTKWidget *widget, int widgetNum, int mx, int my);
-static void mouseDragCbk(LTKWidget *widget, int widgetNum,
-                        int mx, int my, int button);
-
-// button callbacks
-static void nextPageCbk(LTKWidget *button, int n, GBool on);
-static void nextTenPageCbk(LTKWidget *button, int n, GBool on);
-static void gotoNextPage(int inc, GBool top);
-static void prevPageCbk(LTKWidget *button, int n, GBool on);
-static void prevTenPageCbk(LTKWidget *button, int n, GBool on);
-static void gotoPrevPage(int dec, GBool top, GBool bottom);
-static void backCbk(LTKWidget *button, int n, GBool on);
-static void forwardCbk(LTKWidget *button, int n, GBool on);
-static void pageNumCbk(LTKWidget *textIn, int n, GString *text);
-static void zoomMenuCbk(LTKMenuItem *item);
-static void postScriptCbk(LTKWidget *button, int n, GBool on);
-static void aboutCbk(LTKWidget *button, int n, GBool on);
-static void quitCbk(LTKWidget *button, int n, GBool on);
-
-// scrollbar callbacks
-static void scrollVertCbk(LTKWidget *scrollbar, int n, int val);
-static void scrollHorizCbk(LTKWidget *scrollbar, int n, int val);
-
-// misc callbacks
-static void layoutCbk(LTKWindow *win1);
-static void updateScrollbars();
-static void propChangeCbk(LTKWindow *win1, Atom atom);
-
-// selection
-static void setSelection(int newXMin, int newYMin, int newXMax, int newYMax);
-
-// "Open" dialog
-static void mapOpenDialog();
-static void openButtonCbk(LTKWidget *button, int n, GBool on);
-static void openSelectCbk(LTKWidget *widget, int n, GString *name);
-
-// "Reload"
-static void reloadCbk();
-
-// "Save PDF" dialog
-static void mapSaveDialog();
-static void saveButtonCbk(LTKWidget *button, int n, GBool on);
-static void saveSelectCbk(LTKWidget *widget, int n, GString *name);
-
-// "PostScript" dialog
-static void mapPSDialog();
-static void psButtonCbk(LTKWidget *button, int n, GBool on);
-
-// "About" window
-static void mapAboutWin();
-static void closeAboutCbk(LTKWidget *button, int n, GBool on);
-static void aboutLayoutCbk(LTKWindow *winA);
-static void aboutScrollVertCbk(LTKWidget *scrollbar, int n, int val);
-static void aboutScrollHorizCbk(LTKWidget *scrollbar, int n, int val);
-
-// "Find" window
-static void findCbk(LTKWidget *button, int n, GBool on);
-static void mapFindWin();
-static void findButtonCbk(LTKWidget *button, int n, GBool on);
-static void doFind(char *s);
-
-// app kill callback
-static void killCbk(LTKWindow *win1);
-
-//------------------------------------------------------------------------
-// "About" window text
-//------------------------------------------------------------------------
-
-static char *aboutWinText[] = {
-  "X     X           d   fff",
-  " X   X            d  f   f",
-  "  X X             d  f",
-  "   X    pppp   dddd ffff",
-  "  X X   p   p d   d  f",
-  " X   X  p   p d   d  f",
-  "X     X pppp   dddd  f      Version " xpdfVersion,
-  "        p",
-  "        p",
-  "        p",
-  "",
-  xpdfCopyright,
-  "",
-  "http://www.foolabs.com/xpdf/",
-  "derekn@foolabs.com",
-  "",
-  "Licensed under the GNU General Public License (GPL).",
-  "See the 'COPYING' file for details.",
-  "",
-  "Supports PDF version " supportedPDFVersionStr ".",
-  "",
-  "The PDF data structures, operators, and specification",
-  "are copyright 1985-2001 Adobe Systems Inc.",
-  "",
-  "Mouse bindings:",
-  "  button 1: select text / follow link",
-  "  button 2: pan window",
-  "  button 3: menu",
-  "",
-  "Key bindings:",
-  "  o              = open file",
-  "  r              = reload",
-  "  f              = find text",
-  "  n = <PgDn>     = next page",
-  "  p = <PgUp>     = previous page",
-  "  <space>        = scroll down",
-  "  <backspace>    = <delete> = scroll up",
-  "  v              = forward (history path)",
-  "  b              = backward (history path)",
-  "  0 / + / -      = zoom zero / in / out",
-  "  z / w          = zoom page / page width",
-  "  ctrl-L         = redraw",
-  "  q              = quit",
-  "  <home> / <end> = top / bottom of page",
-  "  <arrows>       = scroll",
-  "",
-  "For more information, please read the xpdf(1) man page.",
-  NULL
-};
-
 //------------------------------------------------------------------------
 // command line options
 //------------------------------------------------------------------------
 
-static char initialZoomStr[32] = "";
 static char t1libControlStr[16] = "";
 static char freetypeControlStr[16] = "";
 static char psFileArg[256];
@@ -273,7 +41,6 @@ static GBool quiet = gFalse;
 static char cfgFileName[256] = "";
 static GBool printVersion = gFalse;
 static GBool printHelp = gFalse;
-static GBool viKeys = gFalse;
 
 static ArgDesc argDesc[] = {
   {"-g",          argStringDummy, NULL,           0,
@@ -290,7 +57,7 @@ static ArgDesc argDesc[] = {
    "reverse video"},
   {"-papercolor", argStringDummy, NULL,           0,
    "color of paper background"},
-  {"-z",          argString,      initialZoomStr, sizeof(initialZoomStr),
+  {"-z",          argStringDummy, NULL,           0,
    "initial zoom level (-5..5, page, width)"},
 #if HAVE_T1LIB_H
   {"-t1lib",      argString,      t1libControlStr, sizeof(t1libControlStr),
@@ -345,154 +112,19 @@ static ArgDesc argDesc[] = {
   {NULL}
 };
 
-static XrmOptionDescRec opts[] = {
-  {"-display",       ".display",         XrmoptionSepArg,  NULL},
-  {"-foreground",    ".foreground",      XrmoptionSepArg,  NULL},
-  {"-fg",            ".foreground",      XrmoptionSepArg,  NULL},
-  {"-background",    ".background",      XrmoptionSepArg,  NULL},
-  {"-bg",            ".background",      XrmoptionSepArg,  NULL},
-  {"-geometry",      ".geometry",        XrmoptionSepArg,  NULL},
-  {"-g",             ".geometry",        XrmoptionSepArg,  NULL},
-  {"-font",          ".font",            XrmoptionSepArg,  NULL},
-  {"-fn",            ".font",            XrmoptionSepArg,  NULL},
-  {"-title",         ".title",           XrmoptionSepArg,  NULL},
-  {"-cmap",          ".installCmap",     XrmoptionNoArg,   (XPointer)"on"},
-  {"-rgb",           ".rgbCubeSize",     XrmoptionSepArg,  NULL},
-  {"-rv",            ".reverseVideo",    XrmoptionNoArg,   (XPointer)"true"},
-  {"-papercolor",    ".paperColor",      XrmoptionSepArg,  NULL},
-  {NULL}
-};
-
-//------------------------------------------------------------------------
-// global variables
-//------------------------------------------------------------------------
-
-// zoom factor is 1.2 (similar to DVI magsteps)
-#define minZoom    -5
-#define maxZoom     5
-#define zoomPage  100
-#define zoomWidth 101
-#define defZoom     1
-static int zoomDPI[maxZoom - minZoom + 1] = {
-  29, 35, 42, 50, 60,
-  72,
-  86, 104, 124, 149, 179
-};
-
-static PDFDoc *doc;
-
-static LTKOutputDev *out;
-
-static int page;
-static int zoom;
-static int rotate;
-static GBool quit;
-
-static time_t modTime;         // last modification time of PDF file
-
-static LinkAction *linkAction; // mouse pointer is over this link
-static int                     // coordinates of current selection:
-  selectXMin, selectYMin,      //   (xMin==xMax || yMin==yMax) means there
-  selectXMax, selectYMax;      //   is no selection
-static GBool lastDragLeft;     // last dragged selection edge was left/right
-static GBool lastDragTop;      // last dragged selection edge was top/bottom
-static int panMX, panMY;       // last mouse position for pan
-
-struct History {
-  GString *fileName;
-  int page;
-};
-#define historySize 50
-static History                 // page history queue
-  history[historySize];
-static int historyCur;         // currently displayed page
-static int historyBLen;                // number of valid entries backward from
-                               //   current entry
-static int historyFLen;                // number of valid entries forward from
-                               //   current entry
-
-static GString *psFileName;
-static int psFirstPage, psLastPage;
-
-static GString *fileReqDir;    // current directory for file requesters
-
-static GString *urlCommand;    // command to execute for URI links
-
-static GString *windowTitle;   // window title string
-
-static LTKApp *app;
-static Display *display;
-static LTKWindow *win;
-static LTKMenu *zoomMenu;
-static LTKScrollingCanvas *canvas;
-static LTKScrollbar *hScrollbar, *vScrollbar;
-static LTKTextIn *pageNumText;
-static LTKLabel *numPagesLabel;
-static LTKLabel *linkLabel;
-static LTKMenuButton *zoomMenuBtn;
-static LTKWindow *aboutWin;
-static LTKList *aboutList;
-static LTKScrollbar *aboutHScrollbar, *aboutVScrollbar;
-static LTKWindow *psDialog;
-static LTKWindow *openDialog;
-static LTKWindow *saveDialog;
-static LTKWindow *findWin;
-static LTKTextIn *findTextIn;
-static Atom remoteAtom;
-static GC selectGC;
-
-//------------------------------------------------------------------------
-// GUI includes
-//------------------------------------------------------------------------
-
-#include "xpdfIcon.xpm"
-#include "leftArrow.xbm"
-#include "dblLeftArrow.xbm"
-#include "rightArrow.xbm"
-#include "dblRightArrow.xbm"
-#include "backArrow.xbm"
-#include "forwardArrow.xbm"
-#include "find.xbm"
-#include "postscript.xbm"
-#include "about.xbm"
-#include "xpdf-ltk.h"
-
-//------------------------------------------------------------------------
-// main program
 //------------------------------------------------------------------------
 
 int main(int argc, char *argv[]) {
-  Window xwin;
-  XGCValues gcValues;
-  char cmd[remoteCmdLength];
-  LTKMenu *menu;
-  GString *name;
-  GString *title;
-  GString *initialZoom;
-  GBool reverseVideo;
-  unsigned long paperColor;
-  GBool installCmap;
-  int rgbCubeSize;
+  XPDFApp *app;
+  GString *fileName;
   int pg;
   GString *destName;
-  LinkDest *dest;
-  int x, y;
-  Guint width, height;
-  double width1, height1;
+  GString *userPasswordStr, *ownerPasswordStr;
   GBool ok;
-  char s[20];
-  int i;
-  int ret;
+  int exitCode;
 
-  // initialize
-  app = NULL;
-  win = NULL;
-  out = NULL;
-  remoteAtom = None;
-  doc = NULL;
-  psFileName = NULL;
-  fileReqDir = getCurrentDir();
-  ret = 0;
+  exitCode = 0;
+  userPasswordStr = ownerPasswordStr = NULL;
 
   // parse args
   ok = parseArgs(argDesc, &argc, argv);
@@ -525,9 +157,6 @@ int main(int argc, char *argv[]) {
       fprintf(stderr, "Bad '-eol' value on command line\n");
     }
   }
-  if (initialZoomStr[0]) {
-    globalParams->setInitialZoom(initialZoomStr);
-  }
   if (t1libControlStr[0]) {
     if (!globalParams->setT1libControl(t1libControlStr)) {
       fprintf(stderr, "Bad '-t1lib' value on command line\n");
@@ -538,35 +167,43 @@ int main(int argc, char *argv[]) {
       fprintf(stderr, "Bad '-freetype' value on command line\n");
     }
   }
+  if (printCommands) {
+    globalParams->setPrintCommands(printCommands);
+  }
   if (quiet) {
     globalParams->setErrQuiet(quiet);
   }
 
-  // create LTKApp (and parse X-related args)
-  app = new LTKApp("xpdf", "Xpdf", opts, &argc, argv);
-  app->setKillCbk(&killCbk);
-  display = app->getDisplay();
+  // create the XPDFApp object
+  app = new XPDFApp(&argc, argv);
+
+  // the initialZoom parameter can be set in either the config file or
+  // as an X resource (or command line arg)
+  if (app->getInitialZoom()) {
+    globalParams->setInitialZoom(app->getInitialZoom()->getCString());
+  }
 
   // check command line
-  if (doRemoteRaise)
+  if (doRemoteRaise) {
     ok = ok && remoteName[5] && !doRemoteQuit && argc >= 1 && argc <= 3;
-  else if (doRemoteQuit)
+  } else if (doRemoteQuit) {
     ok = ok && remoteName[5] && argc == 1;
-  else
+  } else {
     ok = ok && argc >= 1 && argc <= 3;
+  }
   if (!ok || printVersion || printHelp) {
     fprintf(stderr, "xpdf version %s\n", xpdfVersion);
     fprintf(stderr, "%s\n", xpdfCopyright);
     if (!printVersion) {
       printUsage("xpdf", "[<PDF-file> [<page> | +<dest>]]", argDesc);
     }
-    ret = 1;
-    goto done2;
+    exitCode = 99;
+    goto done1;
   }
   if (argc >= 2) {
-    name = new GString(argv[1]);
+    fileName = new GString(argv[1]);
   } else {
-    name = NULL;
+    fileName = NULL;
   }
   pg = 1;
   destName = NULL;
@@ -578,1953 +215,75 @@ int main(int argc, char *argv[]) {
     }
   }
 
-  // look for already-running remote server
+  // handle remote server stuff
   if (remoteName[5]) {
-    remoteAtom = XInternAtom(display, remoteName, False);
-    xwin = XGetSelectionOwner(display, remoteAtom);
-    if (xwin != None) {
-      if (name) {
+    app->setRemoteName(remoteName);
+    if (app->remoteServerRunning()) {
+      if (fileName) {
        if (destName) {
-         sprintf(cmd, "%c +%.256s %.200s", doRemoteRaise ? 'D' : 'd',
-                 destName->getCString(), name->getCString());
-         delete destName;
+         app->remoteOpenAtDest(fileName, destName, doRemoteRaise);
        } else {
-         sprintf(cmd, "%c %d %.200s", doRemoteRaise ? 'D' : 'd',
-                 pg, name->getCString());
+         app->remoteOpen(fileName, pg, doRemoteRaise);
        }
-       XChangeProperty(display, xwin, remoteAtom, remoteAtom, 8,
-                       PropModeReplace, (Guchar *)cmd, strlen(cmd) + 1);
-       delete name;
       } else if (doRemoteRaise) {
-       XChangeProperty(display, xwin, remoteAtom, remoteAtom, 8,
-                       PropModeReplace, (Guchar *)"r", 2);
+       app->remoteRaise();
       } else if (doRemoteQuit) {
-       XChangeProperty(display, xwin, remoteAtom, remoteAtom, 8,
-                       PropModeReplace, (Guchar *)"q", 2);
+       app->remoteQuit();
       }
       goto done2;
     }
     if (doRemoteQuit) {
-      if (destName) {
-       delete destName;
-      }
       goto done2;
     }
   }
 
-  // no history yet
-  historyCur = historySize - 1;
-  historyBLen = historyFLen = 0;
-  for (i = 0; i < historySize; ++i)
-    history[i].fileName = NULL;
-
-  // open PDF file
-  if (name) {
-    if (!loadFile(name)) {
-      ret = 1;
-      goto done1;
-    }
-    delete fileReqDir;
-    fileReqDir = makePathAbsolute(grabPath(name->getCString()));
-  }
-
-  // check for legal page number
-  if (doc && (pg < 1 || pg > doc->getNumPages())) {
-    pg = 1;
-  }
-
-  // create window
-  menu = makeMenu();
-  if (fullScreen) {
-    zoomMenu = NULL;
-    win = makeFullScreenWindow(app);
-    win->setDecorated(gFalse);
-  } else {
-    zoomMenu = makeZoomMenu();
-    win = makeWindow(app);
-  }
-  win->setMenu(menu);
-  canvas = (LTKScrollingCanvas *)win->findWidget("canvas");
-  hScrollbar = (LTKScrollbar *)win->findWidget("hScrollbar");
-  vScrollbar = (LTKScrollbar *)win->findWidget("vScrollbar");
-  pageNumText = (LTKTextIn *)win->findWidget("pageNum");
-  numPagesLabel = (LTKLabel *)win->findWidget("numPages");
-  linkLabel = (LTKLabel *)win->findWidget("link");
-  zoomMenuBtn = (LTKMenuButton *)win->findWidget("zoom");
-  win->setKeyCbk(&keyPressCbk);
-  win->setLayoutCbk(&layoutCbk);
-  canvas->setButtonPressCbk(&buttonPressCbk);
-  canvas->setButtonReleaseCbk(&buttonReleaseCbk);
-  canvas->setMouseMoveCbk(&mouseMoveCbk);
-  canvas->setMouseDragCbk(&mouseDragCbk);
-  if (!fullScreen) {
-    hScrollbar->setRepeatPeriod(0);
-    vScrollbar->setRepeatPeriod(0);
-  }
-
-  // get parameters
-  urlCommand = globalParams->getURLCommand();
-
-  // get X resources
-  windowTitle = app->getStringResource("title", NULL);
-  installCmap = app->getBoolResource("installCmap", gFalse);
-  if (installCmap) {
-    win->setInstallCmap(gTrue);
-  }
-  rgbCubeSize = app->getIntResource("rgbCubeSize", defaultRGBCube);
-  reverseVideo = app->getBoolResource("reverseVideo", gFalse);
-  paperColor = app->getColorResource(
-                 "paperColor",
-                 reverseVideo ? (char *)"black" : (char *)"white",
-                 reverseVideo ? BlackPixel(display, app->getScreenNum())
-                              : WhitePixel(display, app->getScreenNum()),
-                 NULL);
-  if (fullScreen) {
-    zoom = zoomPage;
-  } else {
-    initialZoom = globalParams->getInitialZoom();
-    if (!initialZoom->cmp("page")) {
-      zoom = zoomPage;
-      i = maxZoom - minZoom + 2;
-    } else if (!initialZoom->cmp("width")) {
-      zoom = zoomWidth;
-      i = maxZoom - minZoom + 3;
-    } else {
-      zoom = atoi(initialZoom->getCString());
-      if (zoom < minZoom) {
-       zoom = minZoom;
-      } else if (zoom > maxZoom) {
-       zoom = maxZoom;
-      }
-      i = zoom - minZoom;
-    }
-    zoomMenuBtn->setInitialMenuItem(zoomMenu->getItem(i));
-  }
-  viKeys = app->getBoolResource("viKeys", gFalse);
-
-  // get geometry
-  if (fullScreen) {
-    x = y = 0;
-    width = app->getDisplayWidth();
-    height = app->getDisplayHeight();
-  } else {
-    x = y = -1;
-    width = height = 0;
-    app->getGeometryResource("geometry", &x, &y, &width, &height);
-    if (width == 0 || height == 0) {
-      if (!doc || doc->getNumPages() == 0) {
-       width1 = 612;
-       height1 = 792;
-      } else if (doc->getPageRotate(pg) == 90 ||
-                doc->getPageRotate(pg) == 270) {
-       width1 = doc->getPageHeight(pg);
-       height1 = doc->getPageWidth(pg);
-      } else {
-       width1 = doc->getPageWidth(pg);
-       height1 = doc->getPageHeight(pg);
-      }
-      if (zoom == zoomPage || zoom == zoomWidth) {
-       width = (int)((width1 * zoomDPI[defZoom - minZoom]) / 72 + 0.5);
-       height = (int)((height1 * zoomDPI[defZoom - minZoom]) / 72 + 0.5);
-      } else {
-       width = (int)((width1 * zoomDPI[zoom - minZoom]) / 72 + 0.5);
-       height = (int)((height1 * zoomDPI[zoom - minZoom]) / 72 + 0.5);
-      }
-      width += 28;
-      height += 56;
-      if (width > (Guint)app->getDisplayWidth() - 100) {
-       width = app->getDisplayWidth() - 100;
-      }
-      if (height > (Guint)app->getDisplayHeight() - 100) {
-       height = app->getDisplayHeight() - 100;
-      }
-    }
-  }
-
-  // finish setting up window
-  if (!fullScreen) {
-    sprintf(s, "of %d", doc ? doc->getNumPages() : 0);
-    numPagesLabel->setText(s);
-  }
-  if (windowTitle) {
-    title = windowTitle->copy();
-  } else if (name) {
-    title = new GString("xpdf: ");
-    title->append(name);
-  } else {
-    title = new GString("xpdf");
-  }
-  win->setTitle(title);
-  win->layout(x, y, width, height);
-  win->map();
-  aboutWin = NULL;
-  psDialog = NULL;
-  openDialog = NULL;
-  saveDialog = NULL;
-  findWin = NULL;
-  gcValues.foreground = BlackPixel(display, win->getScreenNum()) ^
-                        WhitePixel(display, win->getScreenNum());
-  gcValues.function = GXxor;
-  selectGC = XCreateGC(display, win->getXWindow(),
-                      GCForeground | GCFunction, &gcValues);
-
-  // set up remote server
-  if (remoteAtom != None) {
-    win->setPropChangeCbk(&propChangeCbk);
-    xwin = win->getXWindow();
-    XSetSelectionOwner(display, remoteAtom, xwin, CurrentTime);
-  }
+  // set options
+  app->setFullScreen(fullScreen);
 
-  // create output device
-  out = new LTKOutputDev(win, reverseVideo, paperColor,
-                        installCmap, rgbCubeSize, !fullScreen);
-  out->startDoc(doc ? doc->getXRef() : (XRef *)NULL);
+  // check for password string(s)
+  ownerPasswordStr = ownerPassword[0] ? new GString(ownerPassword)
+                                      : (GString *)NULL;
+  userPasswordStr = userPassword[0] ? new GString(userPassword)
+                                    : (GString *)NULL;
 
-  // display first page
+  // open the file and run the main loop
   if (destName) {
-    if (doc) {
-      if ((dest = doc->findDest(destName))) {
-       displayDest(dest, zoom, 0, gTrue);
-       delete dest;
-      } else {
-       error(-1, "Unknown named destination '%s'", destName->getCString());
-       displayPage(1, zoom, 0, gTrue);
-      }
+    if (!app->openAtDest(fileName, destName,
+                        ownerPasswordStr, userPasswordStr)) {
+      exitCode = 1;
+      goto done2;
     }
-    delete destName;
   } else {
-    displayPage(pg, zoom, 0, gTrue);
+    if (!app->open(fileName, pg, ownerPasswordStr, userPasswordStr)) {
+      exitCode = 1;
+      goto done2;
+    }
   }
+  app->run();
 
-  // event loop
-  quit = gFalse;
-  do {
-    app->doEvent(gTrue);
-  } while (!quit);
-
- done1:
-  // release remote control atom
-  if (remoteAtom != None)
-    XSetSelectionOwner(display, remoteAtom, None, CurrentTime);
+  exitCode = 0;
 
+  // clean up
  done2:
-  // free stuff
-  if (out) {
-    delete out;
-  }
-  if (win) {
-    delete win;
+  if (userPasswordStr) {
+    delete userPasswordStr;
   }
-  if (aboutWin) {
-    delete aboutWin;
+  if (ownerPasswordStr) {
+    delete ownerPasswordStr;
   }
-  if (findWin) {
-    delete findWin;
-  }
-  if (app) {
-    delete app;
-  }
-  if (doc) {
-    delete doc;
-  }
-  if (psFileName) {
-    delete psFileName;
-  }
-  if (fileReqDir) {
-    delete fileReqDir;
-  }
-  if (windowTitle) {
-    delete windowTitle;
+  if (destName) {
+    delete destName;
   }
-  for (i = 0; i < historySize; ++i) {
-    if (history[i].fileName) {
-      delete history[i].fileName;
-    }
+  if (fileName) {
+    delete fileName;
   }
+ done1:
+  delete app;
   delete globalParams;
 
   // check for memory leaks
   Object::memCheck(stderr);
   gMemReport(stderr);
 
-  return ret;
-}
-
-//------------------------------------------------------------------------
-// loadFile / displayPage / displayDest
-//------------------------------------------------------------------------
-
-static GBool loadFile(GString *fileName) {
-  GString *title;
-  PDFDoc *newDoc;
-  GString *ownerPW, *userPW;
-  char s[20];
-  char *p;
-
-  // busy cursor
-  if (win)
-    win->setBusyCursor(gTrue);
-
-  // open PDF file
-  if (ownerPassword[0]) {
-    ownerPW = new GString(ownerPassword);
-  } else {
-    ownerPW = NULL;
-  }
-  if (userPassword[0]) {
-    userPW = new GString(userPassword);
-  } else {
-    userPW = NULL;
-  }
-  newDoc = new PDFDoc(fileName, ownerPW, userPW, printCommands);
-  if (userPW) {
-    delete userPW;
-  }
-  if (ownerPW) {
-    delete ownerPW;
-  }
-  if (!newDoc->isOk()) {
-    delete newDoc;
-    if (win)
-      win->setBusyCursor(gFalse);
-    return gFalse;
-  }
-
-  // replace old document
-  if (doc)
-    delete doc;
-  doc = newDoc;
-  if (out) {
-    out->startDoc(doc->getXRef());
-  }
-
-  // nothing displayed yet
-  page = -99;
-
-  // save the modification time
-  modTime = getModTime(fileName->getCString());
-
-  // init PostScript output params
-  if (psFileName)
-    delete psFileName;
-  if (globalParams->getPSFile()) {
-    psFileName = globalParams->getPSFile()->copy();
-  } else {
-    p = fileName->getCString() + fileName->getLength() - 4;
-    if (!strcmp(p, ".pdf") || !strcmp(p, ".PDF"))
-      psFileName = new GString(fileName->getCString(),
-                              fileName->getLength() - 4);
-    else
-      psFileName = fileName->copy();
-    psFileName->append(".ps");
-  }
-  psFirstPage = 1;
-  psLastPage = doc->getNumPages();
-
-  // set up title, number-of-pages display; back to normal cursor
-  if (win) {
-    if (!windowTitle) {
-      title = new GString("xpdf: ");
-      title->append(fileName);
-      win->setTitle(title);
-    }
-    if (!fullScreen) {
-      sprintf(s, "of %d", doc->getNumPages());
-      numPagesLabel->setText(s);
-    }
-    win->setBusyCursor(gFalse);
-  }
-
-  // done
-  return gTrue;
-}
-
-static void displayPage(int pageA, int zoomA, int rotateA, GBool addToHist) {
-  time_t modTime1;
-  double hDPI, vDPI, dpi;
-  int rot;
-  char s[20];
-  History *h;
-
-  // check for document
-  if (!doc || doc->getNumPages() == 0)
-    return;
-
-  // check for changes to the file
-  modTime1 = getModTime(doc->getFileName()->getCString());
-  if (modTime1 != modTime) {
-    if (loadFile(doc->getFileName()->copy())) {
-      if (pageA > doc->getNumPages()) {
-       pageA = doc->getNumPages();
-      }
-    }
-    modTime = modTime1;
-  }
-
-  // busy cursor
-  if (win)
-    win->setBusyCursor(gTrue);
-
-  // new page/zoom/rotate values
-  page = pageA;
-  zoom = zoomA;
-  rotate = rotateA;
-
-  // initialize mouse-related stuff
-  linkAction = NULL;
-  win->setDefaultCursor();
-  if (!fullScreen) {
-    linkLabel->setText(NULL);
-  }
-  selectXMin = selectXMax = 0;
-  selectYMin = selectYMax = 0;
-  lastDragLeft = lastDragTop = gTrue;
-
-  // draw the page
-  rot = rotate + doc->getPageRotate(page);
-  if (rot >= 360)
-    rot -= 360;
-  else if (rotate < 0)
-    rot += 360;
-  if (fullScreen) {
-    if (rot == 90 || rot == 270) {
-      hDPI = (win->getWidth() / doc->getPageHeight(page)) * 72;
-      vDPI = (win->getHeight() / doc->getPageWidth(page)) * 72;
-    } else {
-      hDPI = (win->getWidth() / doc->getPageWidth(page)) * 72;
-      vDPI = (win->getHeight() / doc->getPageHeight(page)) * 72;
-    }
-    dpi = (hDPI < vDPI) ? hDPI : vDPI;
-  } else if (zoom == zoomPage) {
-    if (rot == 90 || rot == 270) {
-      hDPI = (canvas->getWidth() / doc->getPageHeight(page)) * 72;
-      vDPI = (canvas->getHeight() / doc->getPageWidth(page)) * 72;
-    } else {
-      hDPI = (canvas->getWidth() / doc->getPageWidth(page)) * 72;
-      vDPI = (canvas->getHeight() / doc->getPageHeight(page)) * 72;
-    }
-    dpi = (hDPI < vDPI) ? hDPI : vDPI;
-  } else if (zoom == zoomWidth) {
-    if (rot == 90 || rot == 270) {
-      dpi = (canvas->getWidth() / doc->getPageHeight(page)) * 72;
-    } else {
-      dpi = (canvas->getWidth() / doc->getPageWidth(page)) * 72;
-    }
-  } else {
-    dpi = zoomDPI[zoom - minZoom];
-  }
-  doc->displayPage(out, page, dpi, rotate, gTrue);
-  updateScrollbars();
-
-  // update page number display
-  if (!fullScreen) {
-    sprintf(s, "%d", page);
-    pageNumText->setText(s);
-  }
-
-  // add to history
-  if (addToHist) {
-    if (++historyCur == historySize)
-      historyCur = 0;
-    h = &history[historyCur];
-    if (h->fileName)
-      delete h->fileName;
-    h->fileName = doc->getFileName()->copy();
-    h->page = page;
-    if (historyBLen < historySize)
-      ++historyBLen;
-    historyFLen = 0;
-  }
-
-  // back to regular cursor
-  win->setBusyCursor(gFalse);
-}
-
-static void displayDest(LinkDest *dest, int zoomA, int rotateA,
-                       GBool addToHist) {
-  Ref pageRef;
-  int pg;
-  int dx, dy;
-
-  if (dest->isPageRef()) {
-    pageRef = dest->getPageRef();
-    pg = doc->findPage(pageRef.num, pageRef.gen);
-  } else {
-    pg = dest->getPageNum();
-  }
-  if (pg <= 0 || pg > doc->getNumPages()) {
-    pg = 1;
-  }
-  if (pg != page) {
-    displayPage(pg, zoomA, rotateA, addToHist);
-  }
-
-  if (fullScreen) {
-    return;
-  }
-  switch (dest->getKind()) {
-  case destXYZ:
-    out->cvtUserToDev(dest->getLeft(), dest->getTop(), &dx, &dy);
-    if (dest->getChangeLeft() || dest->getChangeTop()) {
-      if (dest->getChangeLeft()) {
-       hScrollbar->setPos(dx, canvas->getWidth());
-      }
-      if (dest->getChangeTop()) {
-       vScrollbar->setPos(dy, canvas->getHeight());
-      }
-      canvas->scroll(hScrollbar->getPos(), vScrollbar->getPos());
-    }
-    //~ what is the zoom parameter?
-    break;
-  case destFit:
-  case destFitB:
-    //~ do fit
-    hScrollbar->setPos(0, canvas->getWidth());
-    vScrollbar->setPos(0, canvas->getHeight());
-    canvas->scroll(hScrollbar->getPos(), vScrollbar->getPos());
-    break;
-  case destFitH:
-  case destFitBH:
-    //~ do fit
-    out->cvtUserToDev(0, dest->getTop(), &dx, &dy);
-    hScrollbar->setPos(0, canvas->getWidth());
-    vScrollbar->setPos(dy, canvas->getHeight());
-    canvas->scroll(hScrollbar->getPos(), vScrollbar->getPos());
-    break;
-  case destFitV:
-  case destFitBV:
-    //~ do fit
-    out->cvtUserToDev(dest->getLeft(), 0, &dx, &dy);
-    hScrollbar->setPos(dx, canvas->getWidth());
-    vScrollbar->setPos(0, canvas->getHeight());
-    canvas->scroll(hScrollbar->getPos(), vScrollbar->getPos());
-    break;
-  case destFitR:
-    //~ do fit
-    out->cvtUserToDev(dest->getLeft(), dest->getTop(), &dx, &dy);
-    hScrollbar->setPos(dx, canvas->getWidth());
-    vScrollbar->setPos(dy, canvas->getHeight());
-    canvas->scroll(hScrollbar->getPos(), vScrollbar->getPos());
-    break;
-  }
-}
-
-//------------------------------------------------------------------------
-// key press and menu callbacks
-//------------------------------------------------------------------------
-
-static void keyPressCbk(LTKWindow *win1, KeySym key, Guint modifiers,
-                       char *s, int n) {
-  if (n > 0) {
-    switch (s[0]) {
-    case 'O':
-    case 'o':
-      mapOpenDialog();
-      break;
-    case 'R':
-    case 'r':
-      reloadCbk();
-      break;
-    case 'F':
-    case 'f':
-      mapFindWin();
-      break;
-    case 'N':
-    case 'n':
-      gotoNextPage(1, !(modifiers & Mod5Mask));
-      break;
-    case 'P':
-    case 'p':
-      gotoPrevPage(1, !(modifiers & Mod5Mask), gFalse);
-      break;
-    case ' ':
-      if (fullScreen ||
-         vScrollbar->getPos() >=
-           canvas->getRealHeight() - canvas->getHeight()) {
-       gotoNextPage(1, gTrue);
-      } else {
-       vScrollbar->setPos(vScrollbar->getPos() + canvas->getHeight(),
-                          canvas->getHeight());
-       canvas->scroll(hScrollbar->getPos(), vScrollbar->getPos());
-      }
-      break;
-    case '\b':                 // bs
-    case '\177':               // del
-      if (fullScreen) {
-       gotoPrevPage(1, gTrue, gFalse);
-      } else if (vScrollbar->getPos() == 0) {
-       gotoPrevPage(1, gFalse, gTrue);
-      } else {
-       vScrollbar->setPos(vScrollbar->getPos() - canvas->getHeight(),
-                          canvas->getHeight());
-       canvas->scroll(hScrollbar->getPos(), vScrollbar->getPos());
-      }
-      break;
-    case 'v':
-      forwardCbk(NULL, 0, gTrue);
-      break;
-    case 'b':
-      backCbk(NULL, 0, gTrue);
-      break;
-    case 'g':
-      if (fullScreen) {
-       break;
-      }
-      pageNumText->selectAll();
-      pageNumText->activate(gTrue);
-      break;
-    case 'h':                  // vi-style left
-      if (fullScreen) {
-       break;
-      }
-      if (viKeys) {
-       hScrollbar->setPos(hScrollbar->getPos() - 16, canvas->getWidth());
-       canvas->scroll(hScrollbar->getPos(), vScrollbar->getPos());
-      }
-      break;
-    case 'l':                  // vi-style right
-      if (fullScreen) {
-       break;
-      }
-      if (viKeys) {
-       hScrollbar->setPos(hScrollbar->getPos() + 16, canvas->getWidth());
-       canvas->scroll(hScrollbar->getPos(), vScrollbar->getPos());
-      }
-      break;
-    case 'k':                  // vi-style up
-      if (fullScreen) {
-       break;
-      }
-      if (viKeys) {
-       vScrollbar->setPos(vScrollbar->getPos() - 16, canvas->getHeight());
-       canvas->scroll(hScrollbar->getPos(), vScrollbar->getPos());
-      }
-      break;
-    case 'j':                  // vi-style down
-      if (fullScreen) {
-       break;
-      }
-      if (viKeys) {
-       vScrollbar->setPos(vScrollbar->getPos() + 16, canvas->getHeight());
-       canvas->scroll(hScrollbar->getPos(), vScrollbar->getPos());
-      }
-      break;
-    case '0':
-      if (fullScreen) {
-       break;
-      }
-      if (zoom != defZoom) {
-       zoomMenuBtn->setMenuItem(zoomMenu->getItem(defZoom - minZoom));
-      }
-      break;
-    case '+':
-      if (fullScreen) {
-       break;
-      }
-      if (zoom >= minZoom && zoom < maxZoom) {
-       zoomMenuBtn->setMenuItem(zoomMenu->getItem(zoom + 1 - minZoom));
-      }
-      break;
-    case '-':
-      if (fullScreen) {
-       break;
-      }
-      if (zoom > minZoom && zoom <= maxZoom) {
-       zoomMenuBtn->setMenuItem(zoomMenu->getItem(zoom - 1 - minZoom));
-      }
-      break;
-    case 'z':
-      if (fullScreen) {
-       break;
-      }
-      zoomMenuBtn->setMenuItem(zoomMenu->getItem(maxZoom - minZoom + 2));
-      break;
-    case 'w':
-      if (fullScreen) {
-       break;
-      }
-      zoomMenuBtn->setMenuItem(zoomMenu->getItem(maxZoom - minZoom + 3));
-      break;
-    case '\014':               // ^L
-      win->redraw();
-      displayPage(page, zoom, rotate, gFalse);
-      break;
-    case 'Q':
-    case 'q':
-      quitCbk(NULL, 0, gTrue);
-      break;
-    }
-  } else {
-    switch (key) {
-    case XK_Home:
-    case XK_KP_Home:
-      if (fullScreen) {
-       break;
-      }
-      hScrollbar->setPos(0, canvas->getWidth());
-      vScrollbar->setPos(0, canvas->getHeight());
-      canvas->scroll(hScrollbar->getPos(), vScrollbar->getPos());
-      break;
-    case XK_End:
-    case XK_KP_End:
-      if (fullScreen) {
-       break;
-      }
-      hScrollbar->setPos(canvas->getRealWidth() - canvas->getWidth(),
-                        canvas->getWidth());
-      vScrollbar->setPos(canvas->getRealHeight() - canvas->getHeight(),
-                        canvas->getHeight());
-      canvas->scroll(hScrollbar->getPos(), vScrollbar->getPos());
-      break;
-    case XK_Page_Up:
-    case XK_KP_Page_Up:
-      if (fullScreen) {
-       gotoPrevPage(1, gTrue, gFalse);
-      } else if (vScrollbar->getPos() == 0) {
-       gotoPrevPage(1, gFalse, gTrue);
-      } else {
-       vScrollbar->setPos(vScrollbar->getPos() - canvas->getHeight(),
-                          canvas->getHeight());
-       canvas->scroll(hScrollbar->getPos(), vScrollbar->getPos());
-      }
-      break;
-    case XK_Page_Down:
-    case XK_KP_Page_Down:
-      if (fullScreen ||
-         vScrollbar->getPos() >=
-           canvas->getRealHeight() - canvas->getHeight()) {
-       gotoNextPage(1, gTrue);
-      } else {
-       vScrollbar->setPos(vScrollbar->getPos() + canvas->getHeight(),
-                          canvas->getHeight());
-       canvas->scroll(hScrollbar->getPos(), vScrollbar->getPos());
-      }
-      break;
-    case XK_Left:
-    case XK_KP_Left:
-      if (fullScreen) {
-       break;
-      }
-      hScrollbar->setPos(hScrollbar->getPos() - 16, canvas->getWidth());
-      canvas->scroll(hScrollbar->getPos(), vScrollbar->getPos());
-      break;
-    case XK_Right:
-    case XK_KP_Right:
-      if (fullScreen) {
-       break;
-      }
-      hScrollbar->setPos(hScrollbar->getPos() + 16, canvas->getWidth());
-      canvas->scroll(hScrollbar->getPos(), vScrollbar->getPos());
-      break;
-    case XK_Up:
-    case XK_KP_Up:
-      if (fullScreen) {
-       break;
-      }
-      vScrollbar->setPos(vScrollbar->getPos() - 16, canvas->getHeight());
-      canvas->scroll(hScrollbar->getPos(), vScrollbar->getPos());
-      break;
-    case XK_Down:
-    case XK_KP_Down:
-      if (fullScreen) {
-       break;
-      }
-      vScrollbar->setPos(vScrollbar->getPos() + 16, canvas->getHeight());
-      canvas->scroll(hScrollbar->getPos(), vScrollbar->getPos());
-      break;
-    }
-  }
-}
-
-static void menuCbk(LTKMenuItem *item) {
-  int r;
-
-  switch (item->getItemNum()) {
-  case menuOpen:
-    mapOpenDialog();
-    break;
-  case menuReload:
-    reloadCbk();
-    break;
-  case menuSavePDF:
-    if (doc)
-      mapSaveDialog();
-    break;
-  case menuRotateCCW:
-    if (doc) {
-      r = (rotate == 0) ? 270 : rotate - 90;
-      displayPage(page, zoom, r, gFalse);
-    }
-    break;
-  case menuRotateCW:
-    if (doc) {
-      r = (rotate == 270) ? 0 : rotate + 90;
-      displayPage(page, zoom, r, gFalse);
-    }
-    break;
-  case menuQuit:
-    quit = gTrue;
-    break;
-  }
-}
-
-//------------------------------------------------------------------------
-// mouse callbacks
-//------------------------------------------------------------------------
-
-static void buttonPressCbk(LTKWidget *canvas1, int n,
-                          int mx, int my, int button, GBool dblClick) {
-  if (!doc || doc->getNumPages() == 0) {
-    return;
-  }
-  switch (button) {
-  case 1:
-    setSelection(mx, my, mx, my);
-    break;
-  case 2:
-    if (!fullScreen) {
-      panMX = mx - hScrollbar->getPos();
-      panMY = my - vScrollbar->getPos();
-    }
-    break;
-  case 4: // mouse wheel up
-    if (fullScreen) {
-      gotoPrevPage(1, gTrue, gFalse);
-    } else if (vScrollbar->getPos() == 0) {
-      gotoPrevPage(1, gFalse, gTrue);
-    } else {
-      vScrollbar->setPos(vScrollbar->getPos() - 16, canvas->getHeight());
-      canvas->scroll(hScrollbar->getPos(), vScrollbar->getPos());
-    }
-    break;
-  case 5: // mouse wheel down
-    if (fullScreen ||
-       vScrollbar->getPos() >=
-       canvas->getRealHeight() - canvas->getHeight()) {
-      gotoNextPage(1, gTrue);
-    } else {
-      vScrollbar->setPos(vScrollbar->getPos() + 16, canvas->getHeight());
-      canvas->scroll(hScrollbar->getPos(), vScrollbar->getPos());
-    }
-    break;
-  case 6: // second mouse wheel right
-    if (fullScreen) {
-      return;
-    }
-    hScrollbar->setPos(hScrollbar->getPos() + 16, canvas->getWidth());
-    canvas->scroll(hScrollbar->getPos(), vScrollbar->getPos());
-    break;
-  case 7: // second mouse wheel left
-    if (fullScreen) {
-      return;
-    }
-    hScrollbar->setPos(hScrollbar->getPos() - 16, canvas->getWidth());
-    canvas->scroll(hScrollbar->getPos(), vScrollbar->getPos());
-    break;
-  }
-}
-
-static void buttonReleaseCbk(LTKWidget *canvas1, int n,
-                            int mx, int my, int button, GBool click) {
-  GString *s;
-
-  if (!doc || doc->getNumPages() == 0)
-    return;
-
-  if (button == 1) {
-    // selection
-    if (selectXMin < selectXMax && selectYMin < selectYMax) {
-#ifndef NO_TEXT_SELECT
-      if (doc->okToCopy()) {
-       s = out->getText(selectXMin, selectYMin, selectXMax, selectYMax);
-       win->setSelection(NULL, s);
-      } else {
-       error(-1, "Copying of text from this document is not allowed.");
-      }
-#endif
-
-    // link
-    } else {
-      setSelection(mx, my, mx, my);
-      doLink(mx, my);
-    }
-  }
-}
-
-static void doLink(int mx, int my) {
-  LinkActionKind kind;
-  LinkAction *action = NULL;
-  LinkDest *dest;
-  GString *namedDest;
-  char *s;
-  GString *fileName;
-  GString *actionName;
-  double x, y;
-  LTKButtonDialog *dialog;
-  int i;
-
-  // look for a link
-  out->cvtDevToUser(mx, my, &x, &y);
-  if ((action = doc->findLink(x, y))) {
-    switch (kind = action->getKind()) {
-
-    // GoTo / GoToR action
-    case actionGoTo:
-    case actionGoToR:
-      if (kind == actionGoTo) {
-       dest = NULL;
-       namedDest = NULL;
-       if ((dest = ((LinkGoTo *)action)->getDest()))
-         dest = dest->copy();
-       else if ((namedDest = ((LinkGoTo *)action)->getNamedDest()))
-         namedDest = namedDest->copy();
-      } else {
-       dest = NULL;
-       namedDest = NULL;
-       if ((dest = ((LinkGoToR *)action)->getDest()))
-         dest = dest->copy();
-       else if ((namedDest = ((LinkGoToR *)action)->getNamedDest()))
-         namedDest = namedDest->copy();
-       s = ((LinkGoToR *)action)->getFileName()->getCString();
-       //~ translate path name for VMS (deal with '/')
-       if (isAbsolutePath(s))
-         fileName = new GString(s);
-       else
-         fileName = appendToPath(
-                        grabPath(doc->getFileName()->getCString()), s);
-       if (!loadFile(fileName)) {
-         if (dest)
-           delete dest;
-         if (namedDest)
-           delete namedDest;
-         return;
-       }
-      }
-      if (namedDest) {
-       dest = doc->findDest(namedDest);
-       delete namedDest;
-      }
-      if (dest) {
-       displayDest(dest, zoom, rotate, gTrue);
-       delete dest;
-      } else {
-       if (kind == actionGoToR) {
-         displayPage(1, zoom, 0, gTrue);
-       }
-      }
-      break;
-
-    // Launch action
-    case actionLaunch:
-      fileName = ((LinkLaunch *)action)->getFileName();
-      s = fileName->getCString();
-      if (!strcmp(s + fileName->getLength() - 4, ".pdf") ||
-         !strcmp(s + fileName->getLength() - 4, ".PDF")) {
-       //~ translate path name for VMS (deal with '/')
-       if (isAbsolutePath(s))
-         fileName = fileName->copy();
-       else
-         fileName = appendToPath(
-                        grabPath(doc->getFileName()->getCString()), s);
-       if (!loadFile(fileName))
-         return;
-       displayPage(1, zoom, rotate, gTrue);
-      } else {
-       fileName = fileName->copy();
-       if (((LinkLaunch *)action)->getParams()) {
-         fileName->append(' ');
-         fileName->append(((LinkLaunch *)action)->getParams());
-       }
-#ifdef VMS
-       fileName->insert(0, "spawn/nowait ");
-#elif defined(__EMX__)
-       fileName->insert(0, "start /min /n ");
-#else
-       fileName->append(" &");
-#endif
-       dialog = new LTKButtonDialog(win, "xpdf: Launch",
-                                    "Execute the command:",
-                                    fileName->getCString(),
-                                    NULL, "Ok", "Cancel");
-       if (dialog->go())
-         system(fileName->getCString());
-       delete dialog;
-       delete fileName;
-      }
-      break;
-
-    // URI action
-    case actionURI:
-      if (urlCommand) {
-       for (s = urlCommand->getCString(); *s; ++s) {
-         if (s[0] == '%' && s[1] == 's') {
-           break;
-         }
-       }
-       if (s) {
-         fileName = ((LinkURI *)action)->getURI()->copy();
-         // filter out any quote marks (' or ") to avoid a potential
-         // security hole
-         i = 0;
-         while (i < fileName->getLength()) {
-           if (fileName->getChar(i) == '"') {
-             fileName->del(i);
-             fileName->insert(i, "%22");
-             i += 3;
-           } else if (fileName->getChar(i) == '\'') {
-             fileName->del(i);
-             fileName->insert(i, "%27");
-             i += 3;
-           } else {
-             ++i;
-           }
-         }
-         fileName->insert(0, urlCommand->getCString(),
-                          s - urlCommand->getCString());
-         fileName->append(s+2);
-       } else {
-         fileName = urlCommand->copy();
-       }
-#ifdef VMS
-       fileName->insert(0, "spawn/nowait ");
-#elif defined(__EMX__)
-       fileName->insert(0, "start /min /n ");
-#else
-       fileName->append(" &");
-#endif
-       system(fileName->getCString());
-       delete fileName;
-      } else {
-       printf("URI: %s\n", ((LinkURI *)action)->getURI()->getCString());
-      }
-      break;
-
-    // Named action
-    case actionNamed:
-      actionName = ((LinkNamed *)action)->getName();
-      if (!actionName->cmp("NextPage")) {
-       gotoNextPage(1, gTrue);
-      } else if (!actionName->cmp("PrevPage")) {
-       gotoPrevPage(1, gTrue, gFalse);
-      } else if (!actionName->cmp("FirstPage")) {
-       if (page != 1) {
-         displayPage(1, zoom, rotate, gTrue);
-       }
-      } else if (!actionName->cmp("LastPage")) {
-       if (page != doc->getNumPages()) {
-         displayPage(doc->getNumPages(), zoom, rotate, gTrue);
-       }
-      } else if (!actionName->cmp("GoBack")) {
-       backCbk(NULL, 0, gTrue);
-      } else if (!actionName->cmp("GoForward")) {
-       forwardCbk(NULL, 0, gTrue);
-      } else if (!actionName->cmp("Quit")) {
-       quitCbk(NULL, 0, gTrue);
-      } else {
-       error(-1, "Unknown named action: '%s'", actionName->getCString());
-      }
-      break;
-
-    // unknown action type
-    case actionUnknown:
-      error(-1, "Unknown link action type: '%s'",
-           ((LinkUnknown *)action)->getAction()->getCString());
-      break;
-    }
-  }
-}
-
-static void mouseMoveCbk(LTKWidget *widget, int widgetNum, int mx, int my) {
-  double x, y;
-  LinkAction *action;
-  char *s;
-
-  if (!doc || doc->getNumPages() == 0)
-    return;
-  out->cvtDevToUser(mx, my, &x, &y);
-  if ((action = doc->findLink(x, y))) {
-    if (action != linkAction) {
-      if (!linkAction) {
-       win->setCursor(XC_hand2);
-      }
-      linkAction = action;
-      if (!fullScreen) {
-       s = NULL;
-       switch (linkAction->getKind()) {
-       case actionGoTo:
-         s = "[internal link]";
-         break;
-       case actionGoToR:
-         s = ((LinkGoToR *)linkAction)->getFileName()->getCString();
-         break;
-       case actionLaunch:
-         s = ((LinkLaunch *)linkAction)->getFileName()->getCString();
-         break;
-       case actionURI:
-         s = ((LinkURI *)action)->getURI()->getCString();
-         break;
-       case actionNamed:
-         s = ((LinkNamed *)linkAction)->getName()->getCString();
-         break;
-       case actionUnknown:
-         s = "[unknown link]";
-         break;
-       }
-       linkLabel->setText(s);
-      }
-    }
-  } else {
-    if (linkAction) {
-      linkAction = NULL;
-      win->setDefaultCursor();
-      if (!fullScreen) {
-       linkLabel->setText(NULL);
-      }
-    }
-  }
-}
-
-static void mouseDragCbk(LTKWidget *widget, int widgetNum,
-                        int mx, int my, int button) {
-  int x, y;
-  int xMin, yMin, xMax, yMax;
-
-  // button 1: select
-  if (button == 1) {
-
-    // clip mouse coords
-    x = mx;
-    if (x < 0)
-      x = 0;
-    else if (x >= canvas->getRealWidth())
-      x = canvas->getRealWidth() - 1;
-    y = my;
-    if (y < 0)
-      y = 0;
-    else if (y >= canvas->getRealHeight())
-      y = canvas->getRealHeight() - 1;
-
-    // move appropriate edges of selection
-    if (lastDragLeft) {
-      if (x < selectXMax) {
-       xMin = x;
-       xMax = selectXMax;
-      } else {
-       xMin = selectXMax;
-       xMax = x;
-       lastDragLeft = gFalse;
-      }      
-    } else {
-      if (x > selectXMin) {
-       xMin = selectXMin;
-       xMax = x;
-      } else {
-       xMin = x;
-       xMax = selectXMin;
-       lastDragLeft = gTrue;
-      }
-    }
-    if (lastDragTop) {
-      if (y < selectYMax) {
-       yMin = y;
-       yMax = selectYMax;
-      } else {
-       yMin = selectYMax;
-       yMax = y;
-       lastDragTop = gFalse;
-      }
-    } else {
-      if (y > selectYMin) {
-       yMin = selectYMin;
-       yMax = y;
-      } else {
-       yMin = y;
-       yMax = selectYMin;
-       lastDragTop = gTrue;
-      }
-    }
-
-    // redraw the selection
-    setSelection(xMin, yMin, xMax, yMax);
-
-  // button 2: pan
-  } else if (!fullScreen && button == 2) {
-    mx -= hScrollbar->getPos();
-    my -= vScrollbar->getPos();
-    hScrollbar->setPos(hScrollbar->getPos() - (mx - panMX),
-                      canvas->getWidth());
-    vScrollbar->setPos(vScrollbar->getPos() - (my - panMY),
-                      canvas->getHeight());
-    canvas->scroll(hScrollbar->getPos(), vScrollbar->getPos());
-    panMX = mx;
-    panMY = my;
-  }
-}
-
-//------------------------------------------------------------------------
-// button callbacks
-//------------------------------------------------------------------------
-
-static void nextPageCbk(LTKWidget *button, int n, GBool on) {
-  gotoNextPage(1, gTrue);
-}
-
-static void nextTenPageCbk(LTKWidget *button, int n, GBool on) {
-  gotoNextPage(10, gTrue);
-}
-
-static void gotoNextPage(int inc, GBool top) {
-  int pg;
-
-  if (!doc || doc->getNumPages() == 0) {
-    return;
-  }
-  if (page < doc->getNumPages()) {
-    if (top && !fullScreen) {
-      vScrollbar->setPos(0, canvas->getHeight());
-      canvas->setScrollPos(hScrollbar->getPos(), vScrollbar->getPos());
-    }
-    if ((pg = page + inc) > doc->getNumPages()) {
-      pg = doc->getNumPages();
-    }
-    displayPage(pg, zoom, rotate, gTrue);
-  } else {
-    XBell(display, 0);
-  }
-}
-
-static void prevPageCbk(LTKWidget *button, int n, GBool on) {
-  gotoPrevPage(1, gTrue, gFalse);
-}
-
-static void prevTenPageCbk(LTKWidget *button, int n, GBool on) {
-  gotoPrevPage(10, gTrue, gFalse);
-}
-
-static void gotoPrevPage(int dec, GBool top, GBool bottom) {
-  int pg;
-
-  if (!doc || doc->getNumPages() == 0) {
-    return;
-  }
-  if (page > 1) {
-    if (top && !fullScreen) {
-      vScrollbar->setPos(0, canvas->getHeight());
-      canvas->setScrollPos(hScrollbar->getPos(), vScrollbar->getPos());
-    } else if (bottom && !fullScreen) {
-      vScrollbar->setPos(canvas->getRealHeight() - canvas->getHeight(),
-                        canvas->getHeight());
-      canvas->scroll(hScrollbar->getPos(), vScrollbar->getPos());
-    }
-    if ((pg = page - dec) < 1) {
-      pg = 1;
-    }
-    displayPage(pg, zoom, rotate, gTrue);
-  } else {
-    XBell(display, 0);
-  }
-}
-
-static void backCbk(LTKWidget *button, int n, GBool on) {
-  if (historyBLen <= 1) {
-    XBell(display, 0);
-    return;
-  }
-  if (--historyCur < 0)
-    historyCur = historySize - 1;
-  --historyBLen;
-  ++historyFLen;
-  if (history[historyCur].fileName->cmp(doc->getFileName()) != 0) {
-    if (!loadFile(history[historyCur].fileName->copy())) {
-      XBell(display, 0);
-      return;
-    }
-  }
-  displayPage(history[historyCur].page, zoom, rotate, gFalse);
-}
-
-static void forwardCbk(LTKWidget *button, int n, GBool on) {
-  if (historyFLen == 0) {
-    XBell(display, 0);
-    return;
-  }
-  if (++historyCur == historySize)
-    historyCur = 0;
-  --historyFLen;
-  ++historyBLen;
-  if (history[historyCur].fileName->cmp(doc->getFileName()) != 0) {
-    if (!loadFile(history[historyCur].fileName->copy())) {
-      XBell(display, 0);
-      return;
-    }
-  }
-  displayPage(history[historyCur].page, zoom, rotate, gFalse);
-}
-
-static void pageNumCbk(LTKWidget *textIn, int n, GString *text) {
-  int page1;
-  char s[20];
-
-  if (!doc || doc->getNumPages() == 0)
-    return;
-  page1 = atoi(text->getCString());
-  if (page1 >= 1 && page1 <= doc->getNumPages()) {
-    if (page1 != page)
-      displayPage(page1, zoom, rotate, gTrue);
-  } else {
-    XBell(display, 0);
-    sprintf(s, "%d", page);
-    pageNumText->setText(s);
-  }
-}
-
-static void zoomMenuCbk(LTKMenuItem *item) {
-  int z;
-
-  if (!doc || doc->getNumPages() == 0) {
-    return;
-  }
-  z = item->getItemNum();
-  if (z != zoom) {
-    displayPage(page, z, rotate, gFalse);
-  }
-}
-
-static void postScriptCbk(LTKWidget *button, int n, GBool on) {
-  if (!doc)
-    return;
-  mapPSDialog();
-}
-
-static void aboutCbk(LTKWidget *button, int n, GBool on) {
-  mapAboutWin();
-}
-
-static void quitCbk(LTKWidget *button, int n, GBool on) {
-  quit = gTrue;
-}
-
-//------------------------------------------------------------------------
-// scrollbar callbacks
-//------------------------------------------------------------------------
-
-static void scrollVertCbk(LTKWidget *scrollbar, int n, int val) {
-  canvas->scroll(hScrollbar->getPos(), val);
-  XSync(display, False);
-}
-
-static void scrollHorizCbk(LTKWidget *scrollbar, int n, int val) {
-  canvas->scroll(val, vScrollbar->getPos());
-  XSync(display, False);
-}
-
-//------------------------------------------------------------------------
-// misc callbacks
-//------------------------------------------------------------------------
-
-static void layoutCbk(LTKWindow *win1) {
-  if (page >= 0 && (zoom == zoomPage || zoom == zoomWidth)) {
-    displayPage(page, zoom, rotate, gFalse);
-  } else {
-    updateScrollbars();
-  }
-}
-
-static void updateScrollbars() {
-  if (fullScreen) {
-    return;
-  }
-  hScrollbar->setLimits(0, canvas->getRealWidth() - 1);
-  hScrollbar->setPos(hScrollbar->getPos(), canvas->getWidth());
-  hScrollbar->setScrollDelta(16);
-  vScrollbar->setLimits(0, canvas->getRealHeight() - 1);
-  vScrollbar->setPos(vScrollbar->getPos(), canvas->getHeight());
-  vScrollbar->setScrollDelta(16);
-  canvas->scroll(hScrollbar->getPos(), vScrollbar->getPos());
-}
-
-static void propChangeCbk(LTKWindow *win1, Atom atom) {
-  Window xwin;
-  char *cmd;
-  Atom type;
-  int format;
-  Gulong size, remain;
-  char *p, *q;
-  GString *newFileName;
-  int newPage;
-  GString *destName;
-  LinkDest *dest;
-
-  // get command
-  xwin = win1->getXWindow();
-  if (XGetWindowProperty(display, xwin, remoteAtom,
-                        0, remoteCmdLength/4, True, remoteAtom,
-                        &type, &format, &size, &remain,
-                        (Guchar **)&cmd) != Success) {
-    return;
-  }
-  if (size == 0) {
-    return;
-  }
-
-  // raise window
-  if (cmd[0] == 'D' || cmd[0] == 'r'){
-    win->raise();
-    XFlush(display);
-  }
-
-  // display file / page
-  if (cmd[0] == 'd' || cmd[0] == 'D') {
-    p = cmd + 2;
-    q = strchr(p, ' ');
-    if (!q) {
-      return;
-    }
-    *q++ = '\0';
-    newPage = 1;
-    destName = NULL;
-    if (*p == '+') {
-      destName = new GString(p + 1);
-    } else {
-      newPage = atoi(p);
-    }
-    if (!q) {
-      return;
-    }
-    newFileName = new GString(q);
-    XFree((XPointer)cmd);
-    if (!doc || newFileName->cmp(doc->getFileName())) {
-      if (!loadFile(newFileName))
-       return;
-    } else {
-      delete newFileName;
-    }
-    if (destName) {
-      if ((dest = doc->findDest(destName))) {
-       displayDest(dest, zoom, rotate, gTrue);
-       delete dest;
-      }
-      delete destName;
-    } else if (newPage != page &&
-              newPage >= 1 && newPage <= doc->getNumPages()) {
-      displayPage(newPage, zoom, rotate, gTrue);
-    }
-
-  // quit
-  } else if (cmd[0] == 'q') {
-    quit = gTrue;
-  }
-}
-
-//------------------------------------------------------------------------
-// selection
-//------------------------------------------------------------------------
-
-static void setSelection(int newXMin, int newYMin, int newXMax, int newYMax) {
-  int x, y, w, h;
-  GBool needRedraw, needScroll;
-  GBool moveLeft, moveRight, moveTop, moveBottom;
-
-  // erase old selection on canvas pixmap
-  needRedraw = gFalse;
-  if (selectXMin < selectXMax && selectYMin < selectYMax) {
-    XFillRectangle(canvas->getDisplay(), canvas->getPixmap(),
-                  selectGC, selectXMin, selectYMin,
-                  selectXMax - selectXMin, selectYMax - selectYMin);
-    needRedraw = gTrue;
-  }
-
-  // draw new selection on canvas pixmap
-  if (newXMin < newXMax && newYMin < newYMax) {
-    XFillRectangle(canvas->getDisplay(), canvas->getPixmap(),
-                  selectGC, newXMin, newYMin,
-                  newXMax - newXMin, newYMax - newYMin);
-    needRedraw = gTrue;
-  }
-
-  // check which edges moved
-  moveLeft = newXMin != selectXMin;
-  moveTop = newYMin != selectYMin;
-  moveRight = newXMax != selectXMax;
-  moveBottom = newYMax != selectYMax;
-
-  // redraw currently visible part of canvas
-  if (needRedraw) {
-    if (moveLeft) {
-      canvas->redrawRect((newXMin < selectXMin) ? newXMin : selectXMin,
-                        (newYMin < selectYMin) ? newYMin : selectYMin,
-                        (newXMin > selectXMin) ? newXMin : selectXMin,
-                        (newYMax > selectYMax) ? newYMax : selectYMax);
-    }
-    if (moveRight) {
-      canvas->redrawRect((newXMax < selectXMax) ? newXMax : selectXMax,
-                        (newYMin < selectYMin) ? newYMin : selectYMin,
-                        (newXMax > selectXMax) ? newXMax : selectXMax,
-                        (newYMax > selectYMax) ? newYMax : selectYMax);
-    }
-    if (moveTop) {
-      canvas->redrawRect((newXMin < selectXMin) ? newXMin : selectXMin,
-                        (newYMin < selectYMin) ? newYMin : selectYMin,
-                        (newXMax > selectXMax) ? newXMax : selectXMax,
-                        (newYMin > selectYMin) ? newYMin : selectYMin);
-    }
-    if (moveBottom) {
-      canvas->redrawRect((newXMin < selectXMin) ? newXMin : selectXMin,
-                        (newYMax < selectYMax) ? newYMax : selectYMax,
-                        (newXMax > selectXMax) ? newXMax : selectXMax,
-                        (newYMax > selectYMax) ? newYMax : selectYMax);
-    }
-  }
-
-  // switch to new selection coords
-  selectXMin = newXMin;
-  selectXMax = newXMax;
-  selectYMin = newYMin;
-  selectYMax = newYMax;
-
-  // scroll canvas if necessary
-  if (fullScreen) {
-    return;
-  }
-  needScroll = gFalse;
-  w = canvas->getWidth();
-  h = canvas->getHeight();
-  x = hScrollbar->getPos();
-  y = vScrollbar->getPos();
-  if (moveLeft && selectXMin < x) {
-    x = selectXMin;
-    needScroll = gTrue;
-  } else if (moveRight && selectXMax >= x + w) {
-    x = selectXMax - w;
-    needScroll = gTrue;
-  } else if (moveLeft && selectXMin >= x + w) {
-    x = selectXMin - w;
-    needScroll = gTrue;
-  } else if (moveRight && selectXMax < x) {
-    x = selectXMax;
-    needScroll = gTrue;
-  }
-  if (moveTop && selectYMin < y) {
-    y = selectYMin;
-    needScroll = gTrue;
-  } else if (moveBottom && selectYMax >= y + h) {
-    y = selectYMax - h;
-    needScroll = gTrue;
-  } else if (moveTop && selectYMin >= y + h) {
-    y = selectYMin - h;
-    needScroll = gTrue;
-  } else if (moveBottom && selectYMax < y) {
-    y = selectYMax;
-    needScroll = gTrue;
-  }
-  if (needScroll) {
-    hScrollbar->setPos(x, w);
-    vScrollbar->setPos(y, h);
-    canvas->scroll(x, y);
-  }
-}
-
-//------------------------------------------------------------------------
-// "Open" dialog
-//------------------------------------------------------------------------
-
-static void mapOpenDialog() {
-  openDialog = makeOpenDialog(app);
-  ((LTKFileReq *)openDialog->findWidget("fileReq"))->setDir(fileReqDir);
-  openDialog->layoutDialog(win, -1, -1);
-  openDialog->map();
-}
-
-static void openButtonCbk(LTKWidget *button, int n, GBool on) {
-  LTKFileReq *fileReq;
-  GString *sel;
-
-  sel = NULL;
-  if (n == 1) {
-    fileReq = (LTKFileReq *)openDialog->findWidget("fileReq");
-    if (!(sel = fileReq->getSelection())) {
-      return;
-    }
-    openSelectCbk(fileReq, 0, sel);
-  }
-  if (openDialog) {
-    if (sel) {
-      delete fileReqDir;
-      fileReqDir = ((LTKFileReq *)openDialog->findWidget("fileReq"))->getDir();
-    }
-    delete openDialog;
-    openDialog = NULL;
-  }
-}
-
-static void openSelectCbk(LTKWidget *widget, int n, GString *name) {
-  GString *name1;
-
-  name1 = name->copy();
-  if (openDialog) {
-    delete fileReqDir;
-    fileReqDir = ((LTKFileReq *)openDialog->findWidget("fileReq"))->getDir();
-    delete openDialog;
-    openDialog = NULL;
-  }
-  if (loadFile(name1)) {
-    if (!fullScreen) {
-      vScrollbar->setPos(0, canvas->getHeight());
-      canvas->scroll(hScrollbar->getPos(), vScrollbar->getPos());
-    }
-    displayPage(1, zoom, rotate, gTrue);
-  }
-}
-
-//------------------------------------------------------------------------
-// "Reload"
-//------------------------------------------------------------------------
-
-static void reloadCbk() {
-  int pg;
-
-  if (!doc) {
-    return;
-  }
-  pg = page;
-  if (loadFile(doc->getFileName()->copy())) {
-    if (pg > doc->getNumPages()) {
-      pg = doc->getNumPages();
-    }
-    displayPage(pg, zoom, rotate, gFalse);
-  }
-}
-
-//------------------------------------------------------------------------
-// "Save PDF" dialog
-//------------------------------------------------------------------------
-
-static void mapSaveDialog() {
-  saveDialog = makeSaveDialog(app);
-  ((LTKFileReq *)saveDialog->findWidget("fileReq"))->setDir(fileReqDir);
-  saveDialog->layoutDialog(win, -1, -1);
-  saveDialog->map();
-}
-
-static void saveButtonCbk(LTKWidget *button, int n, GBool on) {
-  LTKFileReq *fileReq;
-  GString *sel;
-
-  if (!doc)
-    return;
-  sel = NULL;
-  if (n == 1) {
-    fileReq = (LTKFileReq *)saveDialog->findWidget("fileReq");
-    if (!(sel = fileReq->getSelection())) {
-      return;
-    }
-    saveSelectCbk(fileReq, 0, sel);
-  }
-  if (saveDialog) {
-    if (sel) {
-      delete fileReqDir;
-      fileReqDir = ((LTKFileReq *)saveDialog->findWidget("fileReq"))->getDir();
-    }
-    delete saveDialog;
-    saveDialog = NULL;
-  }
-}
-
-static void saveSelectCbk(LTKWidget *widget, int n, GString *name) {
-  GString *name1;
-
-  name1 = name->copy();
-  if (saveDialog) {
-    delete fileReqDir;
-    fileReqDir = ((LTKFileReq *)saveDialog->findWidget("fileReq"))->getDir();
-    delete saveDialog;
-    saveDialog = NULL;
-  }
-  win->setBusyCursor(gTrue);
-  doc->saveAs(name1);
-  delete name1;
-  win->setBusyCursor(gFalse);
-}
-
-//------------------------------------------------------------------------
-// "PostScript" dialog
-//------------------------------------------------------------------------
-
-static void mapPSDialog() {
-  LTKTextIn *widget;
-  char s[20];
-
-  psDialog = makePostScriptDialog(app);
-  sprintf(s, "%d", psFirstPage);
-  widget = (LTKTextIn *)psDialog->findWidget("firstPage");
-  widget->setText(s);
-  sprintf(s, "%d", psLastPage);
-  widget = (LTKTextIn *)psDialog->findWidget("lastPage");
-  widget->setText(s);
-  widget = (LTKTextIn *)psDialog->findWidget("fileName");
-  widget->setText(psFileName->getCString());
-  psDialog->layoutDialog(win, -1, -1);
-  psDialog->map();
-}
-
-static void psButtonCbk(LTKWidget *button, int n, GBool on) {
-  PSOutputDev *psOut;
-  LTKTextIn *widget;
-
-  if (!doc)
-    return;
-
-  // "Ok" button
-  if (n == 1) {
-    // extract params and close the dialog
-    widget = (LTKTextIn *)psDialog->findWidget("firstPage");
-    psFirstPage = atoi(widget->getText()->getCString());
-    if (psFirstPage < 1)
-      psFirstPage = 1;
-    widget = (LTKTextIn *)psDialog->findWidget("lastPage");
-    psLastPage = atoi(widget->getText()->getCString());
-    if (psLastPage < psFirstPage)
-      psLastPage = psFirstPage;
-    else if (psLastPage > doc->getNumPages())
-      psLastPage = doc->getNumPages();
-    widget = (LTKTextIn *)psDialog->findWidget("fileName");
-    if (psFileName)
-      delete psFileName;
-    psFileName = widget->getText()->copy();
-    if (!(psFileName->getChar(0) == '|' ||
-         psFileName->cmp("-") == 0))
-      makePathAbsolute(psFileName);
-
-    // do the PostScript output
-    psDialog->setBusyCursor(gTrue);
-    win->setBusyCursor(gTrue);
-    if (doc->okToPrint()) {
-      psOut = new PSOutputDev(psFileName->getCString(), doc->getXRef(),
-                             doc->getCatalog(), psFirstPage, psLastPage,
-                             psModePS);
-      if (psOut->isOk()) {
-       doc->displayPages(psOut, psFirstPage, psLastPage, 72, 0, gFalse);
-      }
-      delete psOut;
-    } else {
-      error(-1, "Printing this document is not allowed.");
-    }
-
-    delete psDialog;
-    win->setBusyCursor(gFalse);
-
-  // "Cancel" button
-  } else {
-    delete psDialog;
-  }
-}
-
-//------------------------------------------------------------------------
-// "About" window
-//------------------------------------------------------------------------
-
-static void mapAboutWin() {
-  int i;
-
-  if (aboutWin) {
-    aboutWin->raise();
-  } else {
-    aboutWin = makeAboutWindow(app);
-    aboutWin->setLayoutCbk(&aboutLayoutCbk);
-    aboutList = (LTKList *)aboutWin->findWidget("list");
-    aboutHScrollbar = (LTKScrollbar *)aboutWin->findWidget("hScrollbar");
-    aboutVScrollbar = (LTKScrollbar *)aboutWin->findWidget("vScrollbar");
-    for (i = 0; aboutWinText[i]; ++i) {
-      aboutList->addLine(aboutWinText[i]);
-    }
-    aboutWin->layout(-1, -1, -1, -1);
-    aboutWin->map();
-  }
-}
-
-static void closeAboutCbk(LTKWidget *button, int n, GBool on) {
-  delete aboutWin;
-  aboutWin = NULL;
-}
-
-static void aboutLayoutCbk(LTKWindow *winA) {
-  aboutHScrollbar->setLimits(0, aboutList->getMaxWidth() - 1);
-  aboutHScrollbar->setPos(aboutHScrollbar->getPos(), aboutList->getWidth());
-  aboutVScrollbar->setLimits(0, aboutList->getNumLines() - 1);
-  aboutVScrollbar->setPos(aboutVScrollbar->getPos(),
-                         aboutList->getDisplayedLines());
-}
-
-static void aboutScrollVertCbk(LTKWidget *scrollbar, int n, int val) {
-  aboutList->scrollTo(val, aboutHScrollbar->getPos());
-  XSync(display, False);
-}
-
-static void aboutScrollHorizCbk(LTKWidget *scrollbar, int n, int val) {
-  aboutList->scrollTo(aboutVScrollbar->getPos(), val);
-  XSync(display, False);
-}
-
-//------------------------------------------------------------------------
-// "Find" window
-//------------------------------------------------------------------------
-
-static void findCbk(LTKWidget *button, int n, GBool on) {
-  if (!doc || doc->getNumPages() == 0)
-    return;
-  mapFindWin();
-}
-
-static void mapFindWin() {
-  if (findWin) {
-    findWin->raise();
-  } else {
-    findWin = makeFindWindow(app);
-    findWin->layout(-1, -1, -1, -1);
-    findWin->map();
-    findTextIn = (LTKTextIn *)findWin->findWidget("text");
-  }
-  findTextIn->activate(gTrue);
-}
-
-static void findButtonCbk(LTKWidget *button, int n, GBool on) {
-  if (!doc || doc->getNumPages() == 0)
-    return;
-  if (n == 1) {
-    doFind(findTextIn->getText()->getCString());
-  } else {
-    delete findWin;
-    findWin = NULL;
-  }
-}
-
-static void doFind(char *s) {
-  Unicode *u;
-  TextOutputDev *textOut;
-  int xMin, yMin, xMax, yMax;
-  double xMin1, yMin1, xMax1, yMax1;
-  int pg;
-  GBool top;
-  GString *s1;
-  int len, i;
-
-  // check for zero-length string
-  if (!s[0]) {
-    XBell(display, 0);
-    return;
-  }
-
-  // set cursors to watch
-  win->setBusyCursor(gTrue);
-  findWin->setBusyCursor(gTrue);
-
-  // convert to Unicode
-#if 1 //~ should do something more intelligent here
-  len = strlen(s);
-  u = (Unicode *)gmalloc(len * sizeof(Unicode));
-  for (i = 0; i < len; ++i) {
-    u[i] = (Unicode)(s[i] & 0xff);
-  }
-#endif
-
-  // search current page starting at current selection or top of page
-  xMin = yMin = xMax = yMax = 0;
-  if (selectXMin < selectXMax && selectYMin < selectYMax) {
-    xMin = selectXMax;
-    yMin = (selectYMin + selectYMax) / 2;
-    top = gFalse;
-  } else {
-    top = gTrue;
-  }
-  if (out->findText(u, len, top, gTrue, &xMin, &yMin, &xMax, &yMax)) {
-    goto found;
-  }
-
-  // search following pages
-  textOut = new TextOutputDev(NULL, gFalse, gFalse);
-  if (!textOut->isOk()) {
-    delete textOut;
-    goto done;
-  }
-  for (pg = page+1; pg <= doc->getNumPages(); ++pg) {
-    doc->displayPage(textOut, pg, 72, 0, gFalse);
-    if (textOut->findText(u, len, gTrue, gTrue,
-                         &xMin1, &yMin1, &xMax1, &yMax1)) {
-      goto foundPage;
-    }
-  }
-
-  // search previous pages
-  for (pg = 1; pg < page; ++pg) {
-    doc->displayPage(textOut, pg, 72, 0, gFalse);
-    if (textOut->findText(u, len, gTrue, gTrue,
-                         &xMin1, &yMin1, &xMax1, &yMax1)) {
-      goto foundPage;
-    }
-  }
-  delete textOut;
-
-  // search current page ending at current selection
-  if (selectXMin < selectXMax && selectYMin < selectYMax) {
-    xMax = selectXMin;
-    yMax = (selectYMin + selectYMax) / 2;
-    if (out->findText(u, len, gTrue, gFalse, &xMin, &yMin, &xMax, &yMax)) {
-      goto found;
-    }
-  }
-
-  // not found
-  XBell(display, 0);
-  goto done;
-
-  // found on a different page
- foundPage:
-  delete textOut;
-  displayPage(pg, zoom, rotate, gTrue);
-  if (!out->findText(u, len, gTrue, gTrue, &xMin, &yMin, &xMax, &yMax)) {
-    // this can happen if coalescing is bad
-    goto done;
-  }
-
-  // found: change the selection
- found:
-  setSelection(xMin, yMin, xMax, yMax);
-#ifndef NO_TEXT_SELECT
-  if (doc->okToCopy()) {
-    s1 = out->getText(selectXMin, selectYMin, selectXMax, selectYMax);
-    win->setSelection(NULL, s1);
-  }
-#endif
-
- done:
-  gfree(u);
-
-  // reset cursors to normal
-  win->setBusyCursor(gFalse);
-  findWin->setBusyCursor(gFalse);
-}
-
-//------------------------------------------------------------------------
-// app kill callback
-//------------------------------------------------------------------------
-
-static void killCbk(LTKWindow *win1) {
-  if (win1 == win) {
-    quit = gTrue;
-  } else if (win1 == aboutWin) {
-    delete aboutWin;
-    aboutWin = NULL;
-  } else if (win1 == psDialog) {
-    delete psDialog;
-    psDialog = NULL;
-  } else if (win1 == openDialog) {
-    delete openDialog;
-    openDialog = NULL;
-  } else if (win1 == saveDialog) {
-    delete saveDialog;
-    saveDialog = NULL;
-  } else if (win1 == findWin) {
-    delete findWin;
-    findWin = NULL;
-  }
+  return exitCode;
 }
diff --git a/pdf/xpdf/xpdf.ltk b/pdf/xpdf/xpdf.ltk
deleted file mode 100644 (file)
index 1e21032..0000000
+++ /dev/null
@@ -1,267 +0,0 @@
-#========================================================================
-#
-# xpdf.ltk
-#
-# Copyright 1996-2002 Glyph & Cog, LLC
-#
-#========================================================================
-
-#------------------------------------------------------------------------
-# main window
-#------------------------------------------------------------------------
-
-Window(func:makeWindow title:"xpdf" icon:xpdfIcon) {
-  Box(x:1 y:2 xfill:1 yfill:1) {
-
-    # canvas and scrollbars
-    Box(x:2 y:2 xfill:1 yfill:1) {
-      Box1(xfill:1 yfill:1 sunken) {
-        ScrollingCanvas(name:"canvas" w:100 h:100)
-      }
-      Box1(yfill:1) {
-        Scrollbar(name:"vScrollbar" vert min:0 max:100 move:&scrollVertCbk)
-      }
-      Box1(xfill:1) {
-        Scrollbar(name:"hScrollbar" horiz min:0 max:100 move:&scrollHorizCbk)
-      }
-      Box1() { Empty() }
-    }
-
-    # buttons, page number, etc.
-    Box(x:15 y:1 xfill:1) {
-      Box1() {
-        IconButton(bitmap:backArrow_bits w:backArrow_width
-                   h:backArrow_height press:&backCbk)
-      }
-      Box1() {
-        IconButton(bitmap:dblLeftArrow_bits w:dblLeftArrow_width
-                   h:dblLeftArrow_height press:&prevTenPageCbk)
-      }
-      Box1() {
-        IconButton(bitmap:leftArrow_bits w:leftArrow_width
-                   h:leftArrow_height press:&prevPageCbk)
-      }
-      Box1() {
-        IconButton(bitmap:rightArrow_bits w:rightArrow_width
-                   h:rightArrow_height press:&nextPageCbk)
-      }
-      Box1() {
-        IconButton(bitmap:dblRightArrow_bits w:dblRightArrow_width
-                   h:dblRightArrow_height press:&nextTenPageCbk)
-      }
-      Box1() {
-        IconButton(bitmap:forwardArrow_bits w:forwardArrow_width
-                   h:forwardArrow_height press:&forwardCbk)
-      }
-      Box1() {
-        Label(text:"Page"
-              font:"-*-courier-medium-r-normal-*-14-*-*-*-*-*-*-*")
-      }
-      Box1(sunken left:4 right:4) {
-        TextIn(name:"pageNum" mw:6 done:&pageNumCbk
-               font:"-*-courier-medium-r-normal-*-14-*-*-*-*-*-*-*")
-      }
-      Box1() {
-        Label(name:"numPages" maxLength length:9
-              font:"-*-courier-medium-r-normal-*-14-*-*-*-*-*-*-*")
-      }
-      Box1() {
-        MenuButton(name:"zoom" menu:zoomMenu)
-      }
-      Box1() {
-        IconButton(bitmap:find_bits w:find_width
-                   h:find_height press:&findCbk)
-      }
-      Box1() {
-        IconButton(bitmap:postscript_bits w:postscript_width
-                   h:postscript_height press:&postScriptCbk)
-      }
-      Box1() {
-        IconButton(bitmap:about_bits w:about_width h:about_height
-                   press:&aboutCbk)
-      }
-      Box1(xfill:1) {
-        Label(name:"link" fixedWidth
-              font:"-*-helvetica-medium-r-normal-*-12-*-*-*-*-*-*-*")
-      }
-      Box1() { Button(label:"Quit" press:&quitCbk) }
-    }
-  }
-}
-
-#------------------------------------------------------------------------
-# full-screen main window
-#------------------------------------------------------------------------
-
-Window(func:makeFullScreenWindow title:"xpdf" icon:xpdfIcon) {
-  Box(x:1 y:1 xfill:1 yfill:1) {
-    ScrollingCanvas(name:"canvas" w:100 h:100)
-  }
-}
-
-#------------------------------------------------------------------------
-# menu for main window
-#------------------------------------------------------------------------
-
-Menu(func:makeMenu title:"xpdf" n:8) {
-  MenuItem(text:"Open..."      shortcut:"O"     num:menuOpen
-           select:&menuCbk)
-  MenuItem(text:"Reload"       shortcut:"R"     num:menuReload
-           select:&menuCbk)
-  MenuItem(text:"Save as..."                    num:menuSavePDF
-           select:&menuCbk)
-  MenuItem(text:NULL)
-  MenuItem(text:"Rotate counterclockwise"       num:menuRotateCCW
-           select:&menuCbk)
-  MenuItem(text:"Rotate clockwise"              num:menuRotateCW
-           select:&menuCbk)
-  MenuItem(text:NULL)
-  MenuItem(text:"Quit"         shortcut:"Q"     num:menuQuit
-           select:&menuCbk)
-}
-
-#------------------------------------------------------------------------
-# popup zoom menu
-#------------------------------------------------------------------------
-
-Menu(func:makeZoomMenu title:"zoom" n:14) {
-  MenuItem(text:"-5" num:-5 select:&zoomMenuCbk)
-  MenuItem(text:"-4" num:-4 select:&zoomMenuCbk)
-  MenuItem(text:"-3" num:-3 select:&zoomMenuCbk)
-  MenuItem(text:"-2" num:-2 select:&zoomMenuCbk)
-  MenuItem(text:"-1" num:-1 select:&zoomMenuCbk)
-  MenuItem(text:"0"  num: 0 select:&zoomMenuCbk)
-  MenuItem(text:"+1" num: 1 select:&zoomMenuCbk)
-  MenuItem(text:"+2" num: 2 select:&zoomMenuCbk)
-  MenuItem(text:"+3" num: 3 select:&zoomMenuCbk)
-  MenuItem(text:"+4" num: 4 select:&zoomMenuCbk)
-  MenuItem(text:"+5" num: 5 select:&zoomMenuCbk)
-  MenuItem(text:NULL)
-  MenuItem(text:"fit page"  shortcut:"z" num:100 select:&zoomMenuCbk)
-  MenuItem(text:"fit width" shortcut:"w" num:101 select:&zoomMenuCbk)
-}
-
-#------------------------------------------------------------------------
-# "PostScript output" dialog
-#------------------------------------------------------------------------
-
-Window(func:makePostScriptDialog dialog:gTrue defWidget:"ok"
-       title:"xpdf: PostScript output") {
-  Box(x:1 y:3) {
-    Box(x:4 y:1) {
-      Box1() { Label(text:"Pages:") }
-      Box1(sunken) {
-        TextIn(name:"firstPage" mw:6 tab:"lastPage"
-               font:"-*-courier-medium-r-normal-*-14-*-*-*-*-*-*-*")
-      }
-      Box1() { Label(text:"to") }
-      Box1(sunken) {
-        TextIn(name:"lastPage" mw:6 tab:"fileName"
-               font:"-*-courier-medium-r-normal-*-14-*-*-*-*-*-*-*")
-      }
-    }
-    Box(x:2 y:1) {
-      Box1() { Label(text:"File:") }
-      Box1(sunken xfill:1) {
-        TextIn(name:"fileName" mw:32
-               font:"-*-courier-medium-r-normal-*-14-*-*-*-*-*-*-*")
-      }
-    }
-    Box(x:3 y:1 top:8) {
-      Box1(left:8) { Button(name:"ok" label:"Ok" press:&psButtonCbk num:1) }
-      Box1(xfill:1) { Empty() }
-      Box1(right:8) { Button(label:"Cancel" press:&psButtonCbk num:0) }
-    }
-  }
-}
-
-#------------------------------------------------------------------------
-# "open" dialog
-#------------------------------------------------------------------------
-
-Window(func:makeOpenDialog dialog:gTrue defWidget:"open"
-       title:"xpdf: Open...") {
-  Box(x:1 y:2 xfill:1 yfill:1) {
-    Box1(xfill:1 yfill:1) {
-      FileReq(name:"fileReq" select:openSelectCbk
-              font:"-*-courier-medium-r-normal-*-14-*-*-*-*-*-*-*")
-    }
-    Box(x:3 y:1 top:8 xfill:1) {
-      Box1(left:8) { Button(name:"open" label:"Open" press:&openButtonCbk
-                            num:1) }
-      Box1(xfill:1) { Empty() }
-      Box1(right:8) { Button(label:"Cancel" press:&openButtonCbk num:0) }
-    }
-  }
-}
-
-#------------------------------------------------------------------------
-# "save" dialog
-#------------------------------------------------------------------------
-
-Window(func:makeSaveDialog dialog:gTrue defWidget:"save"
-       title:"xpdf: Save as...") {
-  Box(x:1 y:2 xfill:1 yfill:1) {
-    Box1(xfill:1 yfill:1) {
-      FileReq(name:"fileReq" select:saveSelectCbk
-              font:"-*-courier-medium-r-normal-*-14-*-*-*-*-*-*-*")
-    }
-    Box(x:3 y:1 top:8 xfill:1) {
-      Box1(left:8) { Button(name:"save" label:"Save" press:&saveButtonCbk
-                            num:1) }
-      Box1(xfill:1) { Empty() }
-      Box1(right:8) { Button(label:"Cancel" press:&saveButtonCbk num:0) }
-    }
-  }
-}
-
-#------------------------------------------------------------------------
-# "find" window
-#------------------------------------------------------------------------
-
-Window(func:makeFindWindow defWidget:"find" title:"xpdf: Find") {
-  Box(x:1 y:3 xfill:1 yfill:1) {
-    Box(x:2 y:1 xfill:1) {
-      Box1() { Label(text:"Text:") }
-      Box1(xfill:1 sunken) {
-        TextIn(name:"text" mw:32
-               font:"-*-courier-medium-r-normal-*-14-*-*-*-*-*-*-*")
-      }
-    }
-    Box1(xfill:1 yfill:1) { Empty() }
-    Box(x:3 y:1 top:8 xfill:1) {
-      Box1(left:8) { Button(name:"find" label:"Find" press:&findButtonCbk
-                            num:1) }
-      Box1(xfill:1) { Empty() }
-      Box1(right:8) { Button(label:"Close" press:&findButtonCbk num:0) }
-    }
-  }
-}
-
-#------------------------------------------------------------------------
-# "about" window
-#------------------------------------------------------------------------
-
-Window(func:makeAboutWindow defWidget:"close" title:"About xpdf") {
-  Box(x:1 y:2 xfill:1 yfill:1) {
-    Box(x:2 y:2 xfill:1 yfill:1) {
-      Box1(xfill:1 yfill:1 sunken) {
-        List(name:"list" w:400 h:30
-             font:"-*-courier-medium-r-normal-*-12-*-*-*-*-*-*-*")
-      }
-      Box1(yfill:1) {
-        Scrollbar(name:"vScrollbar" vert min:0 max:100
-                  move:&aboutScrollVertCbk)
-      }
-      Box1(xfill:1) {
-        Scrollbar(name:"hScrollbar" horiz min:0 max:100
-                  move:&aboutScrollHorizCbk)
-      }
-      Box1() { Empty() }
-    }
-    Box(x:2 y:1) {
-      Box1(xfill:1) { Empty() }
-      Box1() { Button(name:"close" label:"Close" press:&closeAboutCbk) }
-    }
-  }
-}
index c814b88550b945781b17b9fd3979f378a51a719d..bb6eab933e4b75d9ba5855d3aacaeec17648dd03 100644 (file)
 //------------------------------------------------------------------------
 
 // xpdf version
-
-#define xpdfVersion "1.01.1"
+#define xpdfVersion         "2.00"
+#define xpdfVersionNum      2.00
+#define xpdfMajorVersion    2
+#define xpdfMinorVersion    0
+#define xpdfMajorVersionStr "2"
 
 // supported PDF version
 #define supportedPDFVersionStr "1.4"
 // copyright notice
 #define xpdfCopyright "Copyright 1996-2002 Glyph & Cog, LLC"
 
+// Windows resource file stuff
+#define winxpdfVersion "WinXpdf 2.00"
+#define xpdfCopyrightAmp "Copyright 1996-2002 Glyph && Cog, LLC"
+
 //------------------------------------------------------------------------
 // paper size
 //------------------------------------------------------------------------
 #define pclose _pclose
 #endif
 
-#if defined(VMS) || defined(VMCMS) || defined(DOS) || defined(OS2) || defined(__EMX__) || defined(WIN32) || defined(__DJGPP__) || defined(__CYGWIN32__) || defined(MACOS)
+#if defined(VMS) || defined(VMCMS) || defined(DOS) || defined(OS2) || defined(__EMX__) || defined(WIN32) || defined(__DJGPP__) || defined(MACOS)
 #define POPEN_READ_MODE "rb"
 #else
 #define POPEN_READ_MODE "r"
 #endif
 
-//------------------------------------------------------------------------
-// uncompress program
-//------------------------------------------------------------------------
-
-#ifdef HAVE_POPEN
-
-// command to uncompress to stdout
-#  ifdef USE_GZIP
-#    define uncompressCmd "gzip -d -c -q"
-#  else
-#    ifdef __EMX__
-#      define uncompressCmd "compress -d -c"
-#    else
-#      define uncompressCmd "uncompress -c"
-#    endif // __EMX__
-#  endif // USE_GZIP
-
-#else // HAVE_POPEN
-
-// command to uncompress a file
-#  ifdef USE_GZIP
-#    define uncompressCmd "gzip -d -q"
-#  else
-#    define uncompressCmd "uncompress"
-#  endif // USE_GZIP
-
-#endif // HAVE_POPEN
-
 //------------------------------------------------------------------------
 // Win32 stuff
 //------------------------------------------------------------------------