upgrade to xpdf 1.01.
authorkramm <kramm>
Mon, 27 May 2002 15:50:04 +0000 (15:50 +0000)
committerkramm <kramm>
Mon, 27 May 2002 15:50:04 +0000 (15:50 +0000)
54 files changed:
pdf2swf/xpdf/Array.cc
pdf2swf/xpdf/Array.h
pdf2swf/xpdf/Catalog.cc
pdf2swf/xpdf/Catalog.h
pdf2swf/xpdf/CompactFontInfo.h [deleted file]
pdf2swf/xpdf/Decrypt.cc
pdf2swf/xpdf/Decrypt.h
pdf2swf/xpdf/Dict.cc
pdf2swf/xpdf/Dict.h
pdf2swf/xpdf/Error.cc
pdf2swf/xpdf/Error.h
pdf2swf/xpdf/FontEncoding.cc [deleted file]
pdf2swf/xpdf/FontEncoding.h [deleted file]
pdf2swf/xpdf/FontFile.cc
pdf2swf/xpdf/FontFile.h
pdf2swf/xpdf/FontInfo.h [deleted file]
pdf2swf/xpdf/FormWidget.cc [deleted file]
pdf2swf/xpdf/FormWidget.h [deleted file]
pdf2swf/xpdf/GString.cc
pdf2swf/xpdf/GString.h
pdf2swf/xpdf/Gfx.cc
pdf2swf/xpdf/Gfx.h
pdf2swf/xpdf/GfxFont.cc
pdf2swf/xpdf/GfxFont.h
pdf2swf/xpdf/GfxState.cc
pdf2swf/xpdf/GfxState.h
pdf2swf/xpdf/Lexer.cc
pdf2swf/xpdf/Lexer.h
pdf2swf/xpdf/Link.cc
pdf2swf/xpdf/Link.h
pdf2swf/xpdf/Object.cc
pdf2swf/xpdf/Object.h
pdf2swf/xpdf/OutputDev.cc
pdf2swf/xpdf/OutputDev.h
pdf2swf/xpdf/PDFDoc.cc
pdf2swf/xpdf/PDFDoc.h
pdf2swf/xpdf/Page.cc
pdf2swf/xpdf/Page.h
pdf2swf/xpdf/Params.cc [deleted file]
pdf2swf/xpdf/Params.h [deleted file]
pdf2swf/xpdf/Parser.cc
pdf2swf/xpdf/Parser.h
pdf2swf/xpdf/StdFontInfo.h [deleted file]
pdf2swf/xpdf/Stream-CCITT.h
pdf2swf/xpdf/Stream.cc
pdf2swf/xpdf/Stream.h
pdf2swf/xpdf/XRef.cc
pdf2swf/xpdf/XRef.h
pdf2swf/xpdf/config.h
pdf2swf/xpdf/gfile.cc
pdf2swf/xpdf/gfile.h
pdf2swf/xpdf/gmem.c
pdf2swf/xpdf/gmem.h
pdf2swf/xpdf/gtypes.h

index 9681b68..fbdde49 100644 (file)
@@ -2,7 +2,7 @@
 //
 // Array.cc
 //
-// Copyright 1996 Derek B. Noonburg
+// Copyright 1996-2002 Glyph & Cog, LLC
 //
 //========================================================================
 
@@ -10,6 +10,7 @@
 #pragma implementation
 #endif
 
+#include <aconf.h>
 #include <stddef.h>
 #include "gmem.h"
 #include "Object.h"
@@ -19,7 +20,8 @@
 // Array
 //------------------------------------------------------------------------
 
-Array::Array() {
+Array::Array(XRef *xrefA) {
+  xref = xrefA;
   elems = NULL;
   size = length = 0;
   ref = 1;
@@ -43,7 +45,7 @@ void Array::add(Object *elem) {
 }
 
 Object *Array::get(int i, Object *obj) {
-  return elems[i].fetch(obj);
+  return elems[i].fetch(xref, obj);
 }
 
 Object *Array::getNF(int i, Object *obj) {
index ecf2eea..a118f68 100644 (file)
@@ -2,7 +2,7 @@
 //
 // Array.h
 //
-// Copyright 1996 Derek B. Noonburg
+// Copyright 1996-2002 Glyph & Cog, LLC
 //
 //========================================================================
 
@@ -15,6 +15,8 @@
 
 #include "Object.h"
 
+class XRef;
+
 //------------------------------------------------------------------------
 // Array
 //------------------------------------------------------------------------
@@ -23,7 +25,7 @@ class Array {
 public:
 
   // Constructor.
-  Array();
+  Array(XRef *xrefA);
 
   // Destructor.
   ~Array();
@@ -44,6 +46,7 @@ public:
 
 private:
 
+  XRef *xref;                  // the xref table for this PDF file
   Object *elems;               // array of elements
   int size;                    // size of <elems> array
   int length;                  // number of elements in array
index 815cca3..1212e2e 100644 (file)
@@ -2,7 +2,7 @@
 //
 // Catalog.cc
 //
-// Copyright 1996 Derek B. Noonburg
+// Copyright 1996-2002 Glyph & Cog, LLC
 //
 //========================================================================
 
 #pragma implementation
 #endif
 
+#include <aconf.h>
 #include <stddef.h>
 #include "gmem.h"
 #include "Object.h"
+#include "XRef.h"
 #include "Array.h"
 #include "Dict.h"
 #include "Page.h"
 // Catalog
 //------------------------------------------------------------------------
 
-Catalog::Catalog(Object *catDict) {
-  Object pagesDict;
+Catalog::Catalog(XRef *xrefA, GBool printCommands) {
+  Object catDict, pagesDict;
   Object obj, obj2;
   int numPages0;
   int i;
 
   ok = gTrue;
+  xref = xrefA;
   pages = NULL;
   pageRefs = NULL;
   numPages = pagesSize = 0;
+  baseURI = NULL;
 
-  if (!catDict->isDict()) {
-    error(-1, "Catalog object is wrong type (%s)", catDict->getTypeName());
+  xref->getCatalog(&catDict);
+  if (!catDict.isDict()) {
+    error(-1, "Catalog object is wrong type (%s)", catDict.getTypeName());
     goto err1;
   }
 
   // read page tree
-  catDict->dictLookup("Pages", &pagesDict);
+  catDict.dictLookup("Pages", &pagesDict);
   // This should really be isDict("Pages"), but I've seen at least one
   // PDF file where the /Type entry is missing.
   if (!pagesDict.isDict()) {
@@ -64,25 +69,24 @@ Catalog::Catalog(Object *catDict) {
     pageRefs[i].num = -1;
     pageRefs[i].gen = -1;
   }
-  numPages = readPageTree(pagesDict.getDict(), NULL, 0);
+  numPages = readPageTree(pagesDict.getDict(), NULL, 0, printCommands);
   if (numPages != numPages0) {
     error(-1, "Page count in top-level pages object is incorrect");
   }
   pagesDict.free();
 
   // read named destination dictionary
-  catDict->dictLookup("Dests", &dests);
+  catDict.dictLookup("Dests", &dests);
 
   // read root of named destination tree
-  if (catDict->dictLookup("Names", &obj)->isDict())
+  if (catDict.dictLookup("Names", &obj)->isDict())
     obj.dictLookup("Dests", &nameTree);
   else
     nameTree.initNull();
   obj.free();
 
   // read base URI
-  baseURI = NULL;
-  if (catDict->dictLookup("URI", &obj)->isDict()) {
+  if (catDict.dictLookup("URI", &obj)->isDict()) {
     if (obj.dictLookup("Base", &obj2)->isString()) {
       baseURI = obj2.getString()->copy();
     }
@@ -90,6 +94,13 @@ Catalog::Catalog(Object *catDict) {
   }
   obj.free();
 
+  // get the metadata stream
+  catDict.dictLookup("Metadata", &metadata);
+
+  // get the structure tree root
+  catDict.dictLookup("StructTreeRoot", &structTreeRoot);
+
+  catDict.free();
   return;
 
  err3:
@@ -97,6 +108,7 @@ Catalog::Catalog(Object *catDict) {
  err2:
   pagesDict.free();
  err1:
+  catDict.free();
   dests.initNull();
   nameTree.initNull();
   ok = gFalse;
@@ -119,9 +131,36 @@ Catalog::~Catalog() {
   if (baseURI) {
     delete baseURI;
   }
+  metadata.free();
+  structTreeRoot.free();
+}
+
+GString *Catalog::readMetadata() {
+  GString *s;
+  Dict *dict;
+  Object obj;
+  int c;
+
+  if (!metadata.isStream()) {
+    return NULL;
+  }
+  dict = metadata.streamGetDict();
+  if (!dict->lookup("Subtype", &obj)->isName("XML")) {
+    error(-1, "Unknown Metadata type: '%s'",
+         obj.isName() ? obj.getName() : "???");
+  }
+  obj.free();
+  s = new GString();
+  metadata.streamReset();
+  while ((c = metadata.streamGetChar()) != EOF) {
+    s->append(c);
+  }
+  metadata.streamClose();
+  return s;
 }
 
-int Catalog::readPageTree(Dict *pagesDict, PageAttrs *attrs, int start) {
+int Catalog::readPageTree(Dict *pagesDict, PageAttrs *attrs, int start,
+                         GBool printCommands) {
   Object kids;
   Object kid;
   Object kidRef;
@@ -140,7 +179,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(start+1, kid.getDict(), attrs2);
+      page = new Page(xref, start+1, kid.getDict(), attrs2, printCommands);
       if (!page->isOk()) {
        ++start;
        goto err3;
@@ -166,7 +205,8 @@ 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)) < 0)
+      if ((start = readPageTree(kid.getDict(), attrs1, start, printCommands))
+         < 0)
        goto err2;
     } else {
       error(-1, "Kid object (page %d) is wrong type (%s)",
@@ -225,10 +265,10 @@ LinkDest *Catalog::findDest(GString *name) {
   // construct LinkDest
   dest = NULL;
   if (obj1.isArray()) {
-    dest = new LinkDest(obj1.getArray(), gTrue);
+    dest = new LinkDest(obj1.getArray());
   } else if (obj1.isDict()) {
     if (obj1.dictLookup("D", &obj2)->isArray())
-      dest = new LinkDest(obj2.getArray(), gTrue);
+      dest = new LinkDest(obj2.getArray());
     else
       error(-1, "Bad named destination value");
     obj2.free();
index b0f3143..afad803 100644 (file)
@@ -2,7 +2,7 @@
 //
 // Catalog.h
 //
-// Copyright 1996 Derek B. Noonburg
+// Copyright 1996-2002 Glyph & Cog, LLC
 //
 //========================================================================
 
@@ -13,6 +13,7 @@
 #pragma interface
 #endif
 
+class XRef;
 class Object;
 class Page;
 class PageAttrs;
@@ -27,7 +28,7 @@ class Catalog {
 public:
 
   // Constructor.
-  Catalog(Object *catDict);
+  Catalog(XRef *xrefA, GBool printCommands = gFalse);
 
   // Destructor.
   ~Catalog();
@@ -47,6 +48,13 @@ public:
   // Return base URI, or NULL if none.
   GString *getBaseURI() { return baseURI; }
 
+  // Return the contents of the metadata stream, or NULL if there is
+  // no metadata.
+  GString *readMetadata();
+
+  // Return the structure tree root object.
+  Object *getStructTreeRoot() { return &structTreeRoot; }
+
   // Find a page, given its object ID.  Returns page number, or 0 if
   // not found.
   int findPage(int num, int gen);
@@ -57,6 +65,7 @@ public:
 
 private:
 
+  XRef *xref;                  // the xref table for this PDF file
   Page **pages;                        // array of pages
   Ref *pageRefs;               // object ID for each page
   int numPages;                        // number of pages
@@ -64,9 +73,12 @@ private:
   Object dests;                        // named destination dictionary
   Object nameTree;             // name tree
   GString *baseURI;            // base URI for URI-type links
+  Object metadata;             // metadata stream
+  Object structTreeRoot;       // structure tree root dictionary
   GBool ok;                    // true if catalog is valid
 
-  int readPageTree(Dict *pages, PageAttrs *attrs, int start);
+  int readPageTree(Dict *pages, PageAttrs *attrs, int start,
+                  GBool printCommands);
   Object *findDestInTree(Object *tree, GString *name, Object *obj);
 };
 
diff --git a/pdf2swf/xpdf/CompactFontInfo.h b/pdf2swf/xpdf/CompactFontInfo.h
deleted file mode 100644 (file)
index c642660..0000000
+++ /dev/null
@@ -1,464 +0,0 @@
-//========================================================================
-//
-// CompactFontInfo.h
-//
-// Copyright 1999 Derek B. Noonburg
-//
-//========================================================================
-
-#ifndef COMPACTFONTINFO_H
-#define COMPACTFONTINFO_H
-
-static char *type1CStdStrings[391] = {
-  ".notdef",
-  "space",
-  "exclam",
-  "quotedbl",
-  "numbersign",
-  "dollar",
-  "percent",
-  "ampersand",
-  "quoteright",
-  "parenleft",
-  "parenright",
-  "asterisk",
-  "plus",
-  "comma",
-  "hyphen",
-  "period",
-  "slash",
-  "zero",
-  "one",
-  "two",
-  "three",
-  "four",
-  "five",
-  "six",
-  "seven",
-  "eight",
-  "nine",
-  "colon",
-  "semicolon",
-  "less",
-  "equal",
-  "greater",
-  "question",
-  "at",
-  "A",
-  "B",
-  "C",
-  "D",
-  "E",
-  "F",
-  "G",
-  "H",
-  "I",
-  "J",
-  "K",
-  "L",
-  "M",
-  "N",
-  "O",
-  "P",
-  "Q",
-  "R",
-  "S",
-  "T",
-  "U",
-  "V",
-  "W",
-  "X",
-  "Y",
-  "Z",
-  "bracketleft",
-  "backslash",
-  "bracketright",
-  "asciicircum",
-  "underscore",
-  "quoteleft",
-  "a",
-  "b",
-  "c",
-  "d",
-  "e",
-  "f",
-  "g",
-  "h",
-  "i",
-  "j",
-  "k",
-  "l",
-  "m",
-  "n",
-  "o",
-  "p",
-  "q",
-  "r",
-  "s",
-  "t",
-  "u",
-  "v",
-  "w",
-  "x",
-  "y",
-  "z",
-  "braceleft",
-  "bar",
-  "braceright",
-  "asciitilde",
-  "exclamdown",
-  "cent",
-  "sterling",
-  "fraction",
-  "yen",
-  "florin",
-  "section",
-  "currency",
-  "quotesingle",
-  "quotedblleft",
-  "guillemotleft",
-  "guilsinglleft",
-  "guilsinglright",
-  "fi",
-  "fl",
-  "endash",
-  "dagger",
-  "daggerdbl",
-  "periodcentered",
-  "paragraph",
-  "bullet",
-  "quotesinglbase",
-  "quotedblbase",
-  "quotedblright",
-  "guillemotright",
-  "ellipsis",
-  "perthousand",
-  "questiondown",
-  "grave",
-  "acute",
-  "circumflex",
-  "tilde",
-  "macron",
-  "breve",
-  "dotaccent",
-  "dieresis",
-  "ring",
-  "cedilla",
-  "hungarumlaut",
-  "ogonek",
-  "caron",
-  "emdash",
-  "AE",
-  "ordfeminine",
-  "Lslash",
-  "Oslash",
-  "OE",
-  "ordmasculine",
-  "ae",
-  "dotlessi",
-  "lslash",
-  "oslash",
-  "oe",
-  "germandbls",
-  "onesuperior",
-  "logicalnot",
-  "mu",
-  "trademark",
-  "Eth",
-  "onehalf",
-  "plusminus",
-  "Thorn",
-  "onequarter",
-  "divide",
-  "brokenbar",
-  "degree",
-  "thorn",
-  "threequarters",
-  "twosuperior",
-  "registered",
-  "minus",
-  "eth",
-  "multiply",
-  "threesuperior",
-  "copyright",
-  "Aacute",
-  "Acircumflex",
-  "Adieresis",
-  "Agrave",
-  "Aring",
-  "Atilde",
-  "Ccedilla",
-  "Eacute",
-  "Ecircumflex",
-  "Edieresis",
-  "Egrave",
-  "Iacute",
-  "Icircumflex",
-  "Idieresis",
-  "Igrave",
-  "Ntilde",
-  "Oacute",
-  "Ocircumflex",
-  "Odieresis",
-  "Ograve",
-  "Otilde",
-  "Scaron",
-  "Uacute",
-  "Ucircumflex",
-  "Udieresis",
-  "Ugrave",
-  "Yacute",
-  "Ydieresis",
-  "Zcaron",
-  "aacute",
-  "acircumflex",
-  "adieresis",
-  "agrave",
-  "aring",
-  "atilde",
-  "ccedilla",
-  "eacute",
-  "ecircumflex",
-  "edieresis",
-  "egrave",
-  "iacute",
-  "icircumflex",
-  "idieresis",
-  "igrave",
-  "ntilde",
-  "oacute",
-  "ocircumflex",
-  "odieresis",
-  "ograve",
-  "otilde",
-  "scaron",
-  "uacute",
-  "ucircumflex",
-  "udieresis",
-  "ugrave",
-  "yacute",
-  "ydieresis",
-  "zcaron",
-  "exclamsmall",
-  "Hungarumlautsmall",
-  "dollaroldstyle",
-  "dollarsuperior",
-  "ampersandsmall",
-  "Acutesmall",
-  "parenleftsuperior",
-  "parenrightsuperior",
-  "twodotenleader",
-  "onedotenleader",
-  "zerooldstyle",
-  "oneoldstyle",
-  "twooldstyle",
-  "threeoldstyle",
-  "fouroldstyle",
-  "fiveoldstyle",
-  "sixoldstyle",
-  "sevenoldstyle",
-  "eightoldstyle",
-  "nineoldstyle",
-  "commasuperior",
-  "threequartersemdash",
-  "periodsuperior",
-  "questionsmall",
-  "asuperior",
-  "bsuperior",
-  "centsuperior",
-  "dsuperior",
-  "esuperior",
-  "isuperior",
-  "lsuperior",
-  "msuperior",
-  "nsuperior",
-  "osuperior",
-  "rsuperior",
-  "ssuperior",
-  "tsuperior",
-  "ff",
-  "ffi",
-  "ffl",
-  "parenleftinferior",
-  "parenrightinferior",
-  "Circumflexsmall",
-  "hyphensuperior",
-  "Gravesmall",
-  "Asmall",
-  "Bsmall",
-  "Csmall",
-  "Dsmall",
-  "Esmall",
-  "Fsmall",
-  "Gsmall",
-  "Hsmall",
-  "Ismall",
-  "Jsmall",
-  "Ksmall",
-  "Lsmall",
-  "Msmall",
-  "Nsmall",
-  "Osmall",
-  "Psmall",
-  "Qsmall",
-  "Rsmall",
-  "Ssmall",
-  "Tsmall",
-  "Usmall",
-  "Vsmall",
-  "Wsmall",
-  "Xsmall",
-  "Ysmall",
-  "Zsmall",
-  "colonmonetary",
-  "onefitted",
-  "rupiah",
-  "Tildesmall",
-  "exclamdownsmall",
-  "centoldstyle",
-  "Lslashsmall",
-  "Scaronsmall",
-  "Zcaronsmall",
-  "Dieresissmall",
-  "Brevesmall",
-  "Caronsmall",
-  "Dotaccentsmall",
-  "Macronsmall",
-  "figuredash",
-  "hypheninferior",
-  "Ogoneksmall",
-  "Ringsmall",
-  "Cedillasmall",
-  "questiondownsmall",
-  "oneeighth",
-  "threeeighths",
-  "fiveeighths",
-  "seveneighths",
-  "onethird",
-  "twothirds",
-  "zerosuperior",
-  "foursuperior",
-  "fivesuperior",
-  "sixsuperior",
-  "sevensuperior",
-  "eightsuperior",
-  "ninesuperior",
-  "zeroinferior",
-  "oneinferior",
-  "twoinferior",
-  "threeinferior",
-  "fourinferior",
-  "fiveinferior",
-  "sixinferior",
-  "seveninferior",
-  "eightinferior",
-  "nineinferior",
-  "centinferior",
-  "dollarinferior",
-  "periodinferior",
-  "commainferior",
-  "Agravesmall",
-  "Aacutesmall",
-  "Acircumflexsmall",
-  "Atildesmall",
-  "Adieresissmall",
-  "Aringsmall",
-  "AEsmall",
-  "Ccedillasmall",
-  "Egravesmall",
-  "Eacutesmall",
-  "Ecircumflexsmall",
-  "Edieresissmall",
-  "Igravesmall",
-  "Iacutesmall",
-  "Icircumflexsmall",
-  "Idieresissmall",
-  "Ethsmall",
-  "Ntildesmall",
-  "Ogravesmall",
-  "Oacutesmall",
-  "Ocircumflexsmall",
-  "Otildesmall",
-  "Odieresissmall",
-  "OEsmall",
-  "Oslashsmall",
-  "Ugravesmall",
-  "Uacutesmall",
-  "Ucircumflexsmall",
-  "Udieresissmall",
-  "Yacutesmall",
-  "Thornsmall",
-  "Ydieresissmall",
-  "001.000",
-  "001.001",
-  "001.002",
-  "001.003",
-  "Black",
-  "Bold",
-  "Book",
-  "Light",
-  "Medium",
-  "Regular",
-  "Roman",
-  "Semibold"
-};
-
-static Gushort type1CISOAdobeCharset[229] = {
-    0,   1,   2,   3,   4,   5,   6,   7,   8,   9,
-   10,  11,  12,  13,  14,  15,  16,  17,  18,  19,
-   20,  21,  22,  23,  24,  25,  26,  27,  28,  29,
-   30,  31,  32,  33,  34,  35,  36,  37,  38,  39,
-   40,  41,  42,  43,  44,  45,  46,  47,  48,  49,
-   50,  51,  52,  53,  54,  55,  56,  57,  58,  59,
-   60,  61,  62,  63,  64,  65,  66,  67,  68,  69,
-   70,  71,  72,  73,  74,  75,  76,  77,  78,  79,
-   80,  81,  82,  83,  84,  85,  86,  87,  88,  89,
-   90,  91,  92,  93,  94,  95,  96,  97,  98,  99,
-  100, 101, 102, 103, 104, 105, 106, 107, 108, 109,
-  110, 111, 112, 113, 114, 115, 116, 117, 118, 119,
-  120, 121, 122, 123, 124, 125, 126, 127, 128, 129,
-  130, 131, 132, 133, 134, 135, 136, 137, 138, 139,
-  140, 141, 142, 143, 144, 145, 146, 147, 148, 149,
-  150, 151, 152, 153, 154, 155, 156, 157, 158, 159,
-  160, 161, 162, 163, 164, 165, 166, 167, 168, 169,
-  170, 171, 172, 173, 174, 175, 176, 177, 178, 179,
-  180, 181, 182, 183, 184, 185, 186, 187, 188, 189,
-  190, 191, 192, 193, 194, 195, 196, 197, 198, 199,
-  200, 201, 202, 203, 204, 205, 206, 207, 208, 209,
-  210, 211, 212, 213, 214, 215, 216, 217, 218, 219,
-  220, 221, 222, 223, 224, 225, 226, 227, 228
-};
-
-static Gushort type1CExpertCharset[166] = {
-    0,   1, 229, 230, 231, 232, 233, 234, 235, 236,
-  237, 238,  13,  14,  15,  99, 239, 240, 241, 242,
-  243, 244, 245, 246, 247, 248,  27,  28, 249, 250,
-  251, 252, 253, 254, 255, 256, 257, 258, 259, 260,
-  261, 262, 263, 264, 265, 266, 109, 110, 267, 268,
-  269, 270, 271, 272, 273, 274, 275, 276, 277, 278,
-  279, 280, 281, 282, 283, 284, 285, 286, 287, 288,
-  289, 290, 291, 292, 293, 294, 295, 296, 297, 298,
-  299, 300, 301, 302, 303, 304, 305, 306, 307, 308,
-  309, 310, 311, 312, 313, 314, 315, 316, 317, 318,
-  158, 155, 163, 319, 320, 321, 322, 323, 324, 325,
-  326, 150, 164, 169, 327, 328, 329, 330, 331, 332,
-  333, 334, 335, 336, 337, 338, 339, 340, 341, 342,
-  343, 344, 345, 346, 347, 348, 349, 350, 351, 352,
-  353, 354, 355, 356, 357, 358, 359, 360, 361, 362,
-  363, 364, 365, 366, 367, 368, 369, 370, 371, 372,
-  373, 374, 375, 376, 377, 378
-};
-
-static Gushort type1CExpertSubsetCharset[87] = {
-    0,   1, 231, 232, 235, 236, 237, 238,  13,  14,
-   15,  99, 239, 240, 241, 242, 243, 244, 245, 246,
-  247, 248,  27,  28, 249, 250, 251, 253, 254, 255,
-  256, 257, 258, 259, 260, 261, 262, 263, 264, 265,
-  266, 109, 110, 267, 268, 269, 270, 272, 300, 301,
-  302, 305, 314, 315, 158, 155, 163, 320, 321, 322,
-  323, 324, 325, 326, 150, 164, 169, 327, 328, 329,
-  330, 331, 332, 333, 334, 335, 336, 337, 338, 339,
-  340, 341, 342, 343, 344, 345, 346
-};
-
-#endif
index ae9b732..8de4091 100644 (file)
@@ -2,7 +2,7 @@
 //
 // Decrypt.cc
 //
-// Copyright 1996 Derek B. Noonburg
+// Copyright 1996-2002 Glyph & Cog, LLC
 //
 //========================================================================
 
@@ -10,6 +10,7 @@
 #pragma implementation
 #endif
 
+#include <aconf.h>
 #include "gmem.h"
 #include "Decrypt.h"
 
@@ -28,42 +29,96 @@ static Guchar passwordPad[32] = {
 // Decrypt
 //------------------------------------------------------------------------
 
-Decrypt::Decrypt(Guchar *fileKey, int objNum, int objGen) {
+Decrypt::Decrypt(Guchar *fileKey, int keyLength, int objNum, int objGen) {
+  int i;
+
   // construct object key
-  objKey[0] = fileKey[0];
-  objKey[1] = fileKey[1];
-  objKey[2] = fileKey[2];
-  objKey[3] = fileKey[3];
-  objKey[4] = fileKey[4];
-  objKey[5] = objNum & 0xff;
-  objKey[6] = (objNum >> 8) & 0xff;
-  objKey[7] = (objNum >> 16) & 0xff;
-  objKey[8] = objGen & 0xff;
-  objKey[9] = (objGen >> 8) & 0xff;
-  md5(objKey, 10, objKey);
+  for (i = 0; i < keyLength; ++i) {
+    objKey[i] = fileKey[i];
+  }
+  objKey[keyLength] = objNum & 0xff;
+  objKey[keyLength + 1] = (objNum >> 8) & 0xff;
+  objKey[keyLength + 2] = (objNum >> 16) & 0xff;
+  objKey[keyLength + 3] = objGen & 0xff;
+  objKey[keyLength + 4] = (objGen >> 8) & 0xff;
+  md5(objKey, keyLength + 5, objKey);
 
   // set up for decryption
   x = y = 0;
-  rc4InitKey(objKey, 10, state);
+  if ((objKeyLength = keyLength + 5) > 16) {
+    objKeyLength = 16;
+  }
+  rc4InitKey(objKey, objKeyLength, state);
 }
 
 void Decrypt::reset() {
   x = y = 0;
-  rc4InitKey(objKey, 10, state);
+  rc4InitKey(objKey, objKeyLength, state);
 }
 
 Guchar Decrypt::decryptByte(Guchar c) {
   return rc4DecryptByte(state, &x, &y, c);
 }
 
-GBool Decrypt::makeFileKey(GString *ownerKey, GString *userKey,
+GBool Decrypt::makeFileKey(int encVersion, int encRevision, int keyLength,
+                          GString *ownerKey, GString *userKey,
                           int permissions, GString *fileID,
-                          GString *userPassword, Guchar *fileKey) {
-  Guchar *buf;
-  Guchar userTest[32];
+                          GString *ownerPassword, GString *userPassword,
+                          Guchar *fileKey, GBool *ownerPasswordOk) {
+  Guchar test[32];
+  GString *userPassword2;
   Guchar fState[256];
   Guchar fx, fy;
   int len, i;
+
+  // try using the supplied owner password to generate the user password
+  if (ownerPassword) {
+    len = ownerPassword->getLength();
+    if (len < 32) {
+      memcpy(test, ownerPassword->getCString(), len);
+      memcpy(test + len, passwordPad, 32 - len);
+    } else {
+      memcpy(test, ownerPassword->getCString(), 32);
+    }
+  } else {
+    memcpy(test, passwordPad, 32);
+  }
+  md5(test, 32, test);
+  if (encRevision == 3) {
+    for (i = 0; i < 50; ++i) {
+      md5(test, 16, test);
+    }
+  }
+  rc4InitKey(test, keyLength, fState);
+  fx = fy = 0;
+  for (i = 0; i < 32; ++i) {
+    test[i] = rc4DecryptByte(fState, &fx, &fy, ownerKey->getChar(i));
+  }
+  userPassword2 = new GString((char *)test, 32);
+  if (makeFileKey2(encVersion, encRevision, keyLength, ownerKey, userKey,
+                  permissions, fileID, userPassword2, fileKey)) {
+    *ownerPasswordOk = gTrue;
+    delete userPassword2;
+    return gTrue;
+  }
+  *ownerPasswordOk = gFalse;
+  delete userPassword2;
+
+  // try using the supplied user password
+  return makeFileKey2(encVersion, encRevision, keyLength, ownerKey, userKey,
+                     permissions, fileID, userPassword, fileKey);
+}
+
+GBool Decrypt::makeFileKey2(int encVersion, int encRevision, int keyLength,
+                           GString *ownerKey, GString *userKey,
+                           int permissions, GString *fileID,
+                           GString *userPassword, Guchar *fileKey) {
+  Guchar *buf;
+  Guchar test[32];
+  Guchar fState[256];
+  Guchar tmpKey[16];
+  Guchar fx, fy;
+  int len, i, j;
   GBool ok;
 
   // generate file key
@@ -86,16 +141,41 @@ GBool Decrypt::makeFileKey(GString *ownerKey, GString *userKey,
   buf[67] = (permissions >> 24) & 0xff;
   memcpy(buf + 68, fileID->getCString(), fileID->getLength());
   md5(buf, 68 + fileID->getLength(), fileKey);
+  if (encRevision == 3) {
+    for (i = 0; i < 50; ++i) {
+      md5(fileKey, 16, fileKey);
+    }
+  }
 
-  // test user key
-  fx = fy = 0;
-  rc4InitKey(fileKey, 5, fState);
-  for (i = 0; i < 32; ++i) {
-    userTest[i] = rc4DecryptByte(fState, &fx, &fy, userKey->getChar(i));
+  // test user password
+  if (encRevision == 2) {
+    rc4InitKey(fileKey, keyLength, fState);
+    fx = fy = 0;
+    for (i = 0; i < 32; ++i) {
+      test[i] = rc4DecryptByte(fState, &fx, &fy, userKey->getChar(i));
+    }
+    ok = memcmp(test, passwordPad, 32) == 0;
+  } else if (encRevision == 3) {
+    memcpy(test, userKey->getCString(), 32);
+    for (i = 19; i >= 0; --i) {
+      for (j = 0; j < keyLength; ++j) {
+       tmpKey[j] = fileKey[j] ^ i;
+      }
+      rc4InitKey(tmpKey, keyLength, fState);
+      fx = fy = 0;
+      for (j = 0; j < 32; ++j) {
+       test[j] = rc4DecryptByte(fState, &fx, &fy, test[j]);
+      }
+    }
+    memcpy(buf, passwordPad, 32);
+    memcpy(buf + 32, fileID->getCString(), fileID->getLength());
+    md5(buf, 32 + fileID->getLength(), buf);
+    ok = memcmp(test, buf, 16) == 0;
+  } else {
+    ok = gFalse;
   }
-  ok = memcmp(userTest, passwordPad, 32) == 0;
-  gfree(buf);
 
+  gfree(buf);
   return ok;
 }
 
@@ -136,6 +216,7 @@ static Guchar rc4DecryptByte(Guchar *state, Guchar *x, Guchar *y, Guchar c) {
 // MD5 message digest
 //------------------------------------------------------------------------
 
+// this works around a bug in older Sun compilers
 static inline Gulong rotateLeft(Gulong x, int r) {
   x &= 0xffffffff;
   return ((x << r) | (x >> (32 - r))) & 0xffffffff;
index 3ea4374..52afb2f 100644 (file)
@@ -2,7 +2,7 @@
 //
 // Decrypt.h
 //
-// Copyright 1996 Derek B. Noonburg
+// Copyright 1996-2002 Glyph & Cog, LLC
 //
 //========================================================================
 
@@ -24,7 +24,7 @@ class Decrypt {
 public:
 
   // Initialize the decryptor object.
-  Decrypt(Guchar *fileKey, int objNum, int objGen);
+  Decrypt(Guchar *fileKey, int keyLength, int objNum, int objGen);
 
   // Reset decryption.
   void reset();
@@ -32,16 +32,26 @@ public:
   // Decrypt one byte.
   Guchar decryptByte(Guchar c);
 
-  // Generate a file key.  The <fileKey> buffer must have space for
-  // at least 16 bytes.  Checks user key and returns gTrue if okay.
-  // <userPassword> may be NULL.
-  static GBool makeFileKey(GString *ownerKey, GString *userKey,
+  // Generate a file key.  The <fileKey> buffer must have space for at
+  // least 16 bytes.  Checks <ownerPassword> and then <userPassword>
+  // and returns true if either is correct.  Sets <ownerPasswordOk> if
+  // the owner password was correct.  Either or both of the passwords
+  // may be NULL, which is treated as an empty string.
+  static GBool makeFileKey(int encVersion, int encRevision, int keyLength,
+                          GString *ownerKey, GString *userKey,
                           int permissions, GString *fileID,
-                          GString *userPassword, Guchar *fileKey);
+                          GString *ownerPassword, GString *userPassword,
+                          Guchar *fileKey, GBool *ownerPasswordOk);
 
 private:
 
-  Guchar objKey[16];
+  static GBool makeFileKey2(int encVersion, int encRevision, int keyLength,
+                           GString *ownerKey, GString *userKey,
+                           int permissions, GString *fileID,
+                           GString *userPassword, Guchar *fileKey);
+
+  int objKeyLength;
+  Guchar objKey[21];
   Guchar state[256];
   Guchar x, y;
 };
index 94ca386..5eb077e 100644 (file)
@@ -2,7 +2,7 @@
 //
 // Dict.cc
 //
-// Copyright 1996 Derek B. Noonburg
+// Copyright 1996-2002 Glyph & Cog, LLC
 //
 //========================================================================
 
@@ -10,6 +10,7 @@
 #pragma implementation
 #endif
 
+#include <aconf.h>
 #include <stddef.h>
 #include <string.h>
 #include "gmem.h"
@@ -21,7 +22,8 @@
 // Dict
 //------------------------------------------------------------------------
 
-Dict::Dict() {
+Dict::Dict(XRef *xrefA) {
+  xref = xrefA;
   entries = NULL;
   size = length = 0;
   ref = 1;
@@ -66,7 +68,7 @@ GBool Dict::is(char *type) {
 Object *Dict::lookup(char *key, Object *obj) {
   DictEntry *e;
 
-  return (e = find(key)) ? e->val.fetch(obj) : obj->initNull();
+  return (e = find(key)) ? e->val.fetch(xref, obj) : obj->initNull();
 }
 
 Object *Dict::lookupNF(char *key, Object *obj) {
@@ -80,26 +82,9 @@ char *Dict::getKey(int i) {
 }
 
 Object *Dict::getVal(int i, Object *obj) {
-  return entries[i].val.fetch(obj);
+  return entries[i].val.fetch(xref, obj);
 }
 
 Object *Dict::getValNF(int i, Object *obj) {
   return entries[i].val.copy(obj);
 }
-
-void Dict::dumpEntries()
-{
-    int t;
-    for(t=0;t<size;t++)
-    {
-       printf("key: %s\n", entries[t].key);
-       fflush(stdout);
-       {
-          printf(" value:\n");
-          entries[t].val.print();
-       }
-       fflush(stdout);
-       printf("---\n");
-       fflush(stdout);
-    }
-}
index e392645..b994514 100644 (file)
@@ -2,7 +2,7 @@
 //
 // Dict.h
 //
-// Copyright 1996 Derek B. Noonburg
+// Copyright 1996-2002 Glyph & Cog, LLC
 //
 //========================================================================
 
@@ -28,7 +28,7 @@ class Dict {
 public:
 
   // Constructor.
-  Dict();
+  Dict(XRef *xrefA);
 
   // Destructor.
   ~Dict();
@@ -56,18 +56,20 @@ public:
   Object *getVal(int i, Object *obj);
   Object *getValNF(int i, Object *obj);
 
+  // Set the xref pointer.  This is only used in one special case: the
+  // trailer dictionary, which is read before the xref table is
+  // parsed.
+  void setXRef(XRef *xrefA) { xref = xrefA; }
+
 private:
 
+  XRef *xref;                  // the xref table for this PDF file
   DictEntry *entries;          // array of entries
   int size;                    // size of <entries> array
   int length;                  // number of entries in dictionary
   int ref;                     // reference count
 
   DictEntry *find(char *key);
-
-public:
-  void dumpEntries();
-
 };
 
 #endif
index 485a7cb..3eae5c9 100644 (file)
@@ -2,7 +2,7 @@
 //
 // Error.cc
 //
-// Copyright 1996 Derek B. Noonburg
+// Copyright 1996-2002 Glyph & Cog, LLC
 //
 //========================================================================
 
 #pragma implementation
 #endif
 
+#include <aconf.h>
 #include <stdio.h>
 #include <stddef.h>
 #include <stdarg.h>
-#include "gtypes.h"
-#include "Params.h"
+#include "GlobalParams.h"
 #include "Error.h"
 
-FILE *errFile;
-GBool errQuiet;
-
-void errorInit() {
-  if (errQuiet) {
-    errFile = NULL;
-  } else {
-    errFile = stderr;
-  }
-}
-
 void CDECL error(int pos, char *msg, ...) {
   va_list args;
 
-  if (errQuiet) {
+  // NB: this can be called before the globalParams object is created
+  if (globalParams && globalParams->getErrQuiet()) {
     return;
   }
-  if (printCommands) {
-    fflush(stdout);
-  }
   if (pos >= 0) {
-    fprintf(errFile, "Error (%d): ", pos);
+    fprintf(stderr, "Error (%d): ", pos);
   } else {
-    fprintf(errFile, "Error: ");
+    fprintf(stderr, "Error: ");
   }
   va_start(args, msg);
-  vfprintf(errFile, msg, args);
+  vfprintf(stderr, msg, args);
   va_end(args);
-  fprintf(errFile, "\n");
-  fflush(errFile);
+  fprintf(stderr, "\n");
+  fflush(stderr);
 }
index f651678..77801c5 100644 (file)
@@ -2,7 +2,7 @@
 //
 // Error.h
 //
-// Copyright 1996 Derek B. Noonburg
+// Copyright 1996-2002 Glyph & Cog, LLC
 //
 //========================================================================
 
 #include <stdio.h>
 #include "config.h"
 
-// File to send error (and other) messages to.
-extern FILE *errFile;
-
-extern void errorInit();
-
 extern void CDECL error(int pos, char *msg, ...);
 
 #endif
diff --git a/pdf2swf/xpdf/FontEncoding.cc b/pdf2swf/xpdf/FontEncoding.cc
deleted file mode 100644 (file)
index bf12577..0000000
+++ /dev/null
@@ -1,143 +0,0 @@
-//========================================================================
-//
-// FontEncoding.cc
-//
-// Copyright 1999 Derek B. Noonburg
-//
-//========================================================================
-
-#ifdef __GNUC__
-#pragma implementation
-#endif
-
-#include <stdlib.h>
-#include <stddef.h>
-#include <stdio.h>
-#include <string.h>
-#include "gmem.h"
-#include "FontEncoding.h"
-
-//------------------------------------------------------------------------
-// FontEncoding
-//------------------------------------------------------------------------
-
-inline int FontEncoding::hash(char *name) {
-  Guint h;
-
-  h = (Guint)name[0] & 0xff;
-  if (h && name[1])
-    h = h * 61 + ((Guint)name[1] & 0xff);
-  return (int)(h % (Guint)fontEncHashSize);
-}
-
-FontEncoding::FontEncoding() {
-  int i;
-
-  encoding = (char **)gmalloc(256 * sizeof(char *));
-  size = 256;
-  freeEnc = gTrue;
-  for (i = 0; i < 256; ++i)
-    encoding[i] = NULL;
-  for (i = 0; i < fontEncHashSize; ++i)
-    hashTab[i] = -1;
-}
-
-FontEncoding::FontEncoding(char **encoding, int size) {
-  int i;
-
-  this->encoding = encoding;
-  this->size = size;
-  freeEnc = gFalse;
-  for (i = 0; i < fontEncHashSize; ++i)
-    hashTab[i] = -1;
-  for (i = 0; i < size; ++i) {
-    if (encoding[i])
-      addChar1(i, encoding[i]);
-  }
-}
-
-FontEncoding::FontEncoding(FontEncoding *fontEnc) {
-  int i;
-
-  encoding = (char **)gmalloc(fontEnc->size * sizeof(char *));
-  size = fontEnc->size;
-  freeEnc = gTrue;
-  for (i = 0; i < size; ++i) {
-    encoding[i] =
-      fontEnc->encoding[i] ? copyString(fontEnc->encoding[i]) : (char *)NULL;
-  }
-  memcpy(hashTab, fontEnc->hashTab, fontEncHashSize * sizeof(short));
-}
-
-void FontEncoding::addChar(int code, char *name) {
-  int h, i;
-
-  // replace character associated with code
-  if (encoding[code]) {
-    h = hash(encoding[code]);
-    for (i = 0; i < fontEncHashSize; ++i) {
-      if (hashTab[h] == code) {
-       hashTab[h] = -2;
-       break;
-      }
-      if (++h == fontEncHashSize)
-       h = 0;
-    }
-    gfree(encoding[code]);
-  }
-
-  // associate name with code
-  encoding[code] = name;
-
-  // insert name in hash table
-  addChar1(code, name);
-}
-
-void FontEncoding::addChar1(int code, char *name) {
-  int h, i, code2;
-
-  // insert name in hash table
-  h = hash(name); 
-  for (i = 0; i < fontEncHashSize; ++i) {
-    code2 = hashTab[h];
-    if (code2 < 0) {
-      hashTab[h] = code;
-      break;
-    } else if (encoding[code2] && !strcmp(encoding[code2], name)) {
-      // keep the highest code for each char -- this is needed because
-      // X won't display chars with codes < 32
-      if (code > code2)
-       hashTab[h] = code;
-      break;
-    }
-    if (++h == fontEncHashSize)
-      h = 0;
-  }
-}
-
-FontEncoding::~FontEncoding() {
-  int i;
-
-  if (freeEnc) {
-    for (i = 0; i < size; ++i) {
-      if (encoding[i])
-       gfree(encoding[i]);
-    }
-    gfree(encoding);
-  }
-}
-
-int FontEncoding::getCharCode(char *name) {
-  int h, i, code;
-
-  h = hash(name);
-  for (i = 0; i < fontEncHashSize; ++i) {
-    code = hashTab[h];
-    if (code == -1 ||
-       (code >= 0 && encoding[code] && !strcmp(encoding[code], name)))
-      return code;
-    if (++h >= fontEncHashSize)
-      h = 0;
-  }
-  return -1;
-}
diff --git a/pdf2swf/xpdf/FontEncoding.h b/pdf2swf/xpdf/FontEncoding.h
deleted file mode 100644 (file)
index 10e6fce..0000000
+++ /dev/null
@@ -1,64 +0,0 @@
-//========================================================================
-//
-// FontEncoding.h
-//
-// Copyright 1999 Derek B. Noonburg
-//
-//========================================================================
-
-#ifndef FONTENCODING_H
-#define FONTENCODING_H
-
-#ifdef __GNUC__
-#pragma interface
-#endif
-
-#include "gtypes.h"
-
-//------------------------------------------------------------------------
-// FontEncoding
-//------------------------------------------------------------------------
-
-#define fontEncHashSize 419
-
-class FontEncoding {
-public:
-
-  // Construct an empty encoding.
-  FontEncoding();
-
-  // Construct an encoding from an array of char names.
-  FontEncoding(char **encoding, int size);
-
-  // Destructor.
-  ~FontEncoding();
-
-  // Create a copy of the encoding.
-  FontEncoding *copy() { return new FontEncoding(this); }
-
-  // Return number of codes in encoding, i.e., max code + 1.
-  int getSize() { return size; }
-
-  // Add a char to the encoding.
-  void addChar(int code, char *name);
-
-  // Return the character name associated with <code>.
-  char *getCharName(int code) { return code>=size?0:encoding[code]; }
-
-  // Return the code associated with <name>.
-  int getCharCode(char *name);
-
-private:
-
-  FontEncoding(FontEncoding *fontEnc);
-  int hash(char *name);
-  void addChar1(int code, char *name);
-
-  char **encoding;             // code --> name mapping
-  int size;                    // number of codes
-  GBool freeEnc;               // should we free the encoding array?
-  short                                // name --> code hash table
-    hashTab[fontEncHashSize];
-};
-
-#endif
index 777b56a..ae58547 100644 (file)
@@ -2,7 +2,7 @@
 //
 // FontFile.cc
 //
-// Copyright 1999 Derek B. Noonburg
+// Copyright 1999-2002 Glyph & Cog, LLC
 //
 //========================================================================
 
@@ -10,6 +10,7 @@
 #pragma implementation
 #endif
 
+#include <aconf.h>
 #include <math.h>
 #include <stdlib.h>
 #include <stddef.h>
 #include <ctype.h>
 #include "gmem.h"
 #include "Error.h"
+#include "GlobalParams.h"
+#include "CharCodeToUnicode.h"
+#include "FontEncodingTables.h"
 #include "FontFile.h"
 
-#include "StdFontInfo.h"
-#include "CompactFontInfo.h"
-
-//------------------------------------------------------------------------
-
-static Guint getWord(Guchar *ptr, int size);
-static double getNum(Guchar **ptr, GBool *fp);
-static char *getString(int sid, Guchar *stringIdxPtr,
-                      Guchar *stringStartPtr, int stringOffSize,
-                      char *buf);
+#include "CompactFontTables.h"
 
 //------------------------------------------------------------------------
 
@@ -58,34 +53,46 @@ FontFile::~FontFile() {
 
 Type1FontFile::Type1FontFile(char *file, int len) {
   char *line, *line1, *p, *p2;
+  GBool haveEncoding;
   char buf[256];
   char c;
-  int n, code, i;
+  int n, code, i, j;
 
   name = NULL;
-  encoding = NULL;
-  freeEnc = gTrue;
+  encoding = (char **)gmalloc(256 * sizeof(char *));
+  for (i = 0; i < 256; ++i) {
+    encoding[i] = NULL;
+  }
+  haveEncoding = gFalse;
 
-  for (i = 1, line = file; i <= 100 && line < file + len && !encoding; ++i) {
+  for (i = 1, line = file;
+       i <= 100 && line < file + len && !haveEncoding;
+       ++i) {
 
     // get font name
     if (!strncmp(line, "/FontName", 9)) {
       strncpy(buf, line, 255);
       buf[255] = '\0';
       if ((p = strchr(buf+9, '/')) &&
-         (p = strtok(p+1, " \t\n\r")))
+         (p = strtok(p+1, " \t\n\r"))) {
        name = copyString(p);
+      }
       line = nextLine(line, file + len);
 
     // get encoding
     } else if (!strncmp(line, "/Encoding StandardEncoding def", 30)) {
-      encoding = type1StdEncoding.copy();
+      for (j = 0; j < 256; ++j) {
+       if (standardEncoding[j]) {
+         encoding[j] = copyString(standardEncoding[j]);
+       }
+      }
+      haveEncoding = gTrue;
     } else if (!strncmp(line, "/Encoding 256 array", 19)) {
-      encoding = new FontEncoding();
-      for (i = 0; i < 300; ++i) {
+      for (j = 0; j < 300; ++j) {
        line1 = nextLine(line, file + len);
-       if ((n = line1 - line) > 255)
+       if ((n = line1 - line) > 255) {
          n = 255;
+       }
        strncpy(buf, line, n);
        buf[n] = '\0';
        for (p = buf; *p == ' ' || *p == '\t'; ++p) ;
@@ -102,7 +109,7 @@ Type1FontFile::Type1FontFile(char *file, int len) {
                ++p;
                for (p2 = p; *p2 && *p2 != ' ' && *p2 != '\t'; ++p2) ;
                *p2 = '\0';
-               encoding->addChar(code, copyString(p));
+               encoding[code] = copyString(p);
              }
            }
          }
@@ -115,6 +122,7 @@ Type1FontFile::Type1FontFile(char *file, int len) {
        line = line1;
       }
       //~ check for getinterval/putinterval junk
+      haveEncoding = gTrue;
 
     } else {
       line = nextLine(line, file + len);
@@ -123,77 +131,143 @@ Type1FontFile::Type1FontFile(char *file, int len) {
 }
 
 Type1FontFile::~Type1FontFile() {
-  if (name)
-    gfree(name);
-  if (encoding && freeEnc)
-    delete encoding;
-}
+  int i;
 
-FontEncoding *Type1FontFile::getEncoding(GBool taken) {
-  if (taken)
-    freeEnc = gFalse;
-  return encoding;
+  if (name) {
+    gfree(name);
+  }
+  for (i = 0; i < 256; ++i) {
+    gfree(encoding[i]);
+  }
+  gfree(encoding);
 }
 
 //------------------------------------------------------------------------
 // Type1CFontFile
 //------------------------------------------------------------------------
 
-Type1CFontFile::Type1CFontFile(char *file, int len) {
+struct Type1CTopDict {
+  int version;
+  int notice;
+  int copyright;
+  int fullName;
+  int familyName;
+  int weight;
+  int isFixedPitch;
+  double italicAngle;
+  double underlinePosition;
+  double underlineThickness;
+  int paintType;
+  int charstringType;
+  double fontMatrix[6];
+  int uniqueID;
+  double fontBBox[4];
+  double strokeWidth;
+  int charset;
+  int encoding;
+  int charStrings;
+  int privateSize;
+  int privateOffset;
+
+  //----- CIDFont entries
+  int registry;
+  int ordering;
+  int supplement;
+  int fdArrayOffset;
+  int fdSelectOffset;
+};
+
+struct Type1CPrivateDict {
+  GString *dictData;
+  int subrsOffset;
+  double defaultWidthX;
+  GBool defaultWidthXFP;
+  double nominalWidthX;
+  GBool nominalWidthXFP;
+};
+
+Type1CFontFile::Type1CFontFile(char *fileA, int lenA) {
+  Guchar *nameIdxPtr, *idxPtr0, *idxPtr1;
+
+  file = fileA;
+  len = lenA;
+  name = NULL;
+  encoding = NULL;
+
+  // some tools embed Type 1C fonts with an extra whitespace char at
+  // the beginning
+  if (file[0] != '\x01') {
+    ++file;
+  }
+
+  // read header
+  topOffSize = file[3] & 0xff;
+
+  // read name index (first font only)
+  nameIdxPtr = (Guchar *)file + (file[2] & 0xff);
+  idxPtr0 = getIndexValPtr(nameIdxPtr, 0);
+  idxPtr1 = getIndexValPtr(nameIdxPtr, 1);
+  name = new GString((char *)idxPtr0, idxPtr1 - idxPtr0);
+
+  topDictIdxPtr = getIndexEnd(nameIdxPtr);
+  stringIdxPtr = getIndexEnd(topDictIdxPtr);
+  gsubrIdxPtr = getIndexEnd(stringIdxPtr);
+}
+
+Type1CFontFile::~Type1CFontFile() {
+  int i;
+
+  delete name;
+  if (encoding) {
+    for (i = 0; i < 256; ++i) {
+      gfree(encoding[i]);
+    }
+    gfree(encoding);
+  }
+}
+
+char *Type1CFontFile::getName() {
+  return name->getCString();
+}
+
+char **Type1CFontFile::getEncoding() {
+  if (!encoding) {
+    readNameAndEncoding();
+  }
+  return encoding;
+}
+
+void Type1CFontFile::readNameAndEncoding() {
   char buf[256];
-  Guchar *topPtr, *idxStartPtr, *idxPtr0, *idxPtr1;
-  Guchar *stringIdxPtr, *stringStartPtr;
-  int topOffSize, idxOffSize, stringOffSize;
-  int nFonts, nStrings, nGlyphs;
+  Guchar *idxPtr0, *idxPtr1, *ptr;
+  int nGlyphs;
   int nCodes, nRanges, nLeft, nSups;
   Gushort *glyphNames;
   int charset, enc, charstrings;
-  int charsetFormat, encFormat;
+  int encFormat;
   int c, sid;
-  double op[48];
   double x;
   GBool isFP;
   int key;
-  int i, j, n;
-
-  name = NULL;
-  encoding = NULL;
-  freeEnc = gTrue;
+  int i, j;
 
-  // read header
-  topPtr = (Guchar *)file + (file[2] & 0xff);
-  topOffSize = file[3] & 0xff;
+  encoding = (char **)gmalloc(256 * sizeof(char *));
+  for (i = 0; i < 256; ++i) {
+    encoding[i] = NULL;
+  }
 
-  // read name index (first font only)
-  nFonts = getWord(topPtr, 2);
-  idxOffSize = topPtr[2];
-  topPtr += 3;
-  idxStartPtr = topPtr + (nFonts + 1) * idxOffSize - 1;
-  idxPtr0 = idxStartPtr + getWord(topPtr, idxOffSize);
-  idxPtr1 = idxStartPtr + getWord(topPtr + idxOffSize, idxOffSize);
-  if ((n = idxPtr1 - idxPtr0) > 255)
-    n = 255;
-  strncpy(buf, (char *)idxPtr0, n);
-  buf[n] = '\0';
-  name = copyString(buf);
-  topPtr = idxStartPtr + getWord(topPtr + nFonts * idxOffSize, idxOffSize);
-
-  // read top dict index (first font only)
-  nFonts = getWord(topPtr, 2);
-  idxOffSize = topPtr[2];
-  topPtr += 3;
-  idxStartPtr = topPtr + (nFonts + 1) * idxOffSize - 1;
-  idxPtr0 = idxStartPtr + getWord(topPtr, idxOffSize);
-  idxPtr1 = idxStartPtr + getWord(topPtr + idxOffSize, idxOffSize);
-  charset = 0;
-  enc = 0;
-  charstrings = 0;
+  // read top dict (first font only)
+  idxPtr0 = getIndexValPtr(topDictIdxPtr, 0);
+  idxPtr1 = getIndexValPtr(topDictIdxPtr, 1);
+  charset = enc = charstrings = 0;
   i = 0;
-  while (idxPtr0 < idxPtr1) {
-    if (*idxPtr0 <= 27 || *idxPtr0 == 31) {
-      key = *idxPtr0++;
-      if (key == 0x0c)
-       key = (key << 8) | *idxPtr0++;
+  ptr = idxPtr0;
+  while (ptr < idxPtr1) {
+    if (*ptr <= 27 || *ptr == 31) {
+      key = *ptr++;
+      if (key == 0x0c) {
+       key = (key << 8) | *ptr++;
+      }
       if (key == 0x0f) { // charset
        charset = (int)op[0];
       } else if (key == 0x10) { // encoding
@@ -203,445 +277,130 @@ Type1CFontFile::Type1CFontFile(char *file, int len) {
       }
       i = 0;
     } else {
-      x = getNum(&idxPtr0, &isFP);
-      if (i < 48)
+      x = getNum(&ptr, &isFP);
+      if (i < 48) {
        op[i++] = x;
+      }
     }
   }
-  topPtr = idxStartPtr + getWord(topPtr + nFonts * idxOffSize, idxOffSize);
-
-  // read string index
-  nStrings = getWord(topPtr, 2);
-  stringOffSize = topPtr[2];
-  topPtr += 3;
-  stringIdxPtr = topPtr;
-  stringStartPtr = topPtr + (nStrings + 1) * stringOffSize - 1;
-  topPtr = stringStartPtr + getWord(topPtr + nStrings * stringOffSize,
-                                   stringOffSize);
 
   // get number of glyphs from charstrings index
-  topPtr = (Guchar *)file + charstrings;
-  nGlyphs = getWord(topPtr, 2);
+  nGlyphs = getIndexLen((Guchar *)file + charstrings);
 
-  // read charset
-  if (charset == 0) {
-    glyphNames = type1CISOAdobeCharset;
-  } else if (charset == 1) {
-    glyphNames = type1CExpertCharset;
-  } else if (charset == 2) {
-    glyphNames = type1CExpertSubsetCharset;
-  } else {
-    glyphNames = (Gushort *)gmalloc(nGlyphs * sizeof(Gushort));
-    glyphNames[0] = 0;
-    topPtr = (Guchar *)file + charset;
-    charsetFormat = *topPtr++;
-    if (charsetFormat == 0) {
-      for (i = 1; i < nGlyphs; ++i) {
-       glyphNames[i] = getWord(topPtr, 2);
-       topPtr += 2;
-      }
-    } else if (charsetFormat == 1) {
-      i = 1;
-      while (i < nGlyphs) {
-       c = getWord(topPtr, 2);
-       topPtr += 2;
-       nLeft = *topPtr++;
-       for (j = 0; j <= nLeft; ++j)
-         glyphNames[i++] = c++;
-      }
-    } else if (charsetFormat == 2) {
-      i = 1;
-      while (i < nGlyphs) {
-       c = getWord(topPtr, 2);
-       topPtr += 2;
-       nLeft = getWord(topPtr, 2);
-       topPtr += 2;
-       for (j = 0; j <= nLeft; ++j)
-         glyphNames[i++] = c++;
-      }
-    }
-  }
+  // read charset (GID -> name mapping)
+  glyphNames = readCharset(charset, nGlyphs);
 
-  // read encoding (glyph -> code mapping)
+  // read encoding (GID -> code mapping)
   if (enc == 0) {
-    encoding = type1StdEncoding.copy();
+    for (i = 0; i < 256; ++i) {
+      if (standardEncoding[i]) {
+       encoding[i] = copyString(standardEncoding[i]);
+      }
+    }
   } else if (enc == 1) {
-    encoding = type1ExpertEncoding.copy();
+    for (i = 0; i < 256; ++i) {
+      if (expertEncoding[i]) {
+       encoding[i] = copyString(expertEncoding[i]);
+      }
+    }
   } else {
-    encoding = new FontEncoding();
-    topPtr = (Guchar *)file + enc;
-    encFormat = *topPtr++;
+    ptr = (Guchar *)file + enc;
+    encFormat = *ptr++;
     if ((encFormat & 0x7f) == 0) {
-      nCodes = 1 + *topPtr++;
+      nCodes = 1 + *ptr++;
       if (nCodes > nGlyphs) {
        nCodes = nGlyphs;
       }
       for (i = 1; i < nCodes; ++i) {
-       c = *topPtr++;
-       getString(glyphNames[i], stringIdxPtr, stringStartPtr,
-                 stringOffSize, buf);
-       encoding->addChar(c, copyString(buf));
+       c = *ptr++;
+       encoding[c] = copyString(getString(glyphNames[i], buf));
       }
     } else if ((encFormat & 0x7f) == 1) {
-      nRanges = *topPtr++;
+      nRanges = *ptr++;
       nCodes = 1;
       for (i = 0; i < nRanges; ++i) {
-       c = *topPtr++;
-       nLeft = *topPtr++;
+       c = *ptr++;
+       nLeft = *ptr++;
        for (j = 0; j <= nLeft && nCodes < nGlyphs; ++j) {
-         getString(glyphNames[nCodes], stringIdxPtr, stringStartPtr,
-                   stringOffSize, buf);
-         encoding->addChar(c, copyString(buf));
+         encoding[c] = copyString(getString(glyphNames[nCodes], buf));
          ++nCodes;
          ++c;
        }
       }
     }
     if (encFormat & 0x80) {
-      nSups = *topPtr++;
+      nSups = *ptr++;
       for (i = 0; i < nSups; ++i) {
-       c = *topPtr++;
-       sid = getWord(topPtr, 2);
-       topPtr += 2;
-       getString(sid, stringIdxPtr, stringStartPtr,
-                 stringOffSize, buf);
-       encoding->addChar(c, copyString(buf));
+       c = *ptr++;
+       sid = getWord(ptr, 2);
+       ptr += 2;
+       encoding[c] = copyString(getString(sid, buf));
       }
     }
   }
 
-  if (charset > 2)
+  if (charset > 2) {
     gfree(glyphNames);
-}
-
-Type1CFontFile::~Type1CFontFile() {
-  if (name)
-    gfree(name);
-  if (encoding && freeEnc)
-    delete encoding;
-}
-
-FontEncoding *Type1CFontFile::getEncoding(GBool taken) {
-  if (taken)
-    freeEnc = gFalse;
-  return encoding;
-}
-
-static Guint getWord(Guchar *ptr, int size) {
-  Guint x;
-  int i;
-
-  x = 0;
-  for (i = 0; i < size; ++i)
-    x = (x << 8) + *ptr++;
-  return x;
-}
-
-static double getNum(Guchar **ptr, GBool *fp) {
-  static char nybChars[16] = "0123456789.ee -";
-  int b0, b, nyb0, nyb1;
-  double x;
-  char buf[65];
-  int i;
-
-  x = 0;
-  *fp = gFalse;
-  b0 = (*ptr)[0];
-  if (b0 < 28) {
-    x = 0;
-  } else if (b0 == 28) {
-    x = ((*ptr)[1] << 8) + (*ptr)[2];
-    *ptr += 3;
-  } else if (b0 == 29) {
-    x = ((*ptr)[1] << 24) + ((*ptr)[2] << 16) + ((*ptr)[3] << 8) + (*ptr)[4];
-    *ptr += 5;
-  } else if (b0 == 30) {
-    *ptr += 1;
-    i = 0;
-    do {
-      b = *(*ptr)++;
-      nyb0 = b >> 4;
-      nyb1 = b & 0x0f;
-      if (nyb0 == 0xf)
-       break;
-      buf[i++] = nybChars[nyb0];
-      if (i == 64)
-       break;
-      if (nyb0 == 0xc)
-       buf[i++] = '-';
-      if (i == 64)
-       break;
-      if (nyb1 == 0xf)
-       break;
-      buf[i++] = nybChars[nyb1];
-      if (i == 64)
-       break;
-      if (nyb1 == 0xc)
-       buf[i++] = '-';
-    } while (i < 64);
-    buf[i] = '\0';
-    x = atof(buf);
-    *fp = gTrue;
-  } else if (b0 == 31) {
-    x = 0;
-  } else if (b0 < 247) {
-    x = b0 - 139;
-    *ptr += 1;
-  } else if (b0 < 251) {
-    x = ((b0 - 247) << 8) + (*ptr)[1] + 108;
-    *ptr += 2;
-  } else {
-    x = -((b0 - 251) << 8) - (*ptr)[1] - 108;
-    *ptr += 2;
-  }
-  return x;
-}
-
-static char *getString(int sid, Guchar *stringIdxPtr,
-                      Guchar *stringStartPtr, int stringOffSize,
-                      char *buf) {
-  Guchar *idxPtr0, *idxPtr1;
-  int len;
-
-  if (sid < 391) {
-    strcpy(buf, type1CStdStrings[sid]);
-  } else {
-    sid -= 391;
-    idxPtr0 = stringStartPtr + getWord(stringIdxPtr + sid * stringOffSize,
-                                      stringOffSize);
-    idxPtr1 = stringStartPtr + getWord(stringIdxPtr + (sid+1) * stringOffSize,
-                                      stringOffSize);
-    if ((len = idxPtr1 - idxPtr0) > 255)
-      len = 255;
-    strncpy(buf, (char *)idxPtr0, len);
-    buf[len] = '\0';
   }
-  return buf;
-}
-
-//------------------------------------------------------------------------
-// Type1CFontConverter
-//------------------------------------------------------------------------
-
-Type1CFontConverter::Type1CFontConverter(char *file, int len, FILE *out) {
-  this->file = file;
-  this->len = len;
-  this->out = out;
-  r1 = 55665;
-  line = 0;
-}
-
-Type1CFontConverter::~Type1CFontConverter() {
 }
 
-void Type1CFontConverter::convert() {
-  char *fontName;
-  struct {
-    int version;
-    int notice;
-    int copyright;
-    int fullName;
-    int familyName;
-    int weight;
-    int isFixedPitch;
-    double italicAngle;
-    double underlinePosition;
-    double underlineThickness;
-    int paintType;
-    int charstringType;                //~ ???
-    double fontMatrix[6];
-    int uniqueID;
-    double fontBBox[4];
-    double strokeWidth;                //~ ???
-    int charset;
-    int encoding;
-    int charStrings;
-    int privateSize;
-    int privateOffset;
-  } dict;
+void Type1CFontFile::convertToType1(FILE *outA) {
+  Type1CTopDict dict;
+  Type1CPrivateDict privateDict;
   char buf[256], eBuf[256];
-  Guchar *topPtr, *idxStartPtr, *idxPtr0, *idxPtr1;
-  Guchar *stringIdxPtr, *stringStartPtr;
-  int topOffSize, idxOffSize, stringOffSize;
-  int nFonts, nStrings, nGlyphs;
-  int nCodes, nRanges, nLeft, nSups;
+  Guchar *idxPtr0, *idxPtr1, *subrsIdxPtr, *charStringsIdxPtr, *ptr;
+  int nGlyphs, nCodes, nRanges, nLeft, nSups;
   Gushort *glyphNames;
-  int charsetFormat, encFormat;
-  int subrsOffset, nSubrs;
-  int nCharStrings;
+  int encFormat, nSubrs, nCharStrings;
   int c, sid;
-  double x;
-  GBool isFP;
-  int key;
   int i, j, n;
 
-  // read header
-  topPtr = (Guchar *)file + (file[2] & 0xff);
-  topOffSize = file[3] & 0xff;
-
-  // read name (first font only)
-  nFonts = getWord(topPtr, 2);
-  idxOffSize = topPtr[2];
-  topPtr += 3;
-  idxStartPtr = topPtr + (nFonts + 1) * idxOffSize - 1;
-  idxPtr0 = idxStartPtr + getWord(topPtr, idxOffSize);
-  idxPtr1 = idxStartPtr + getWord(topPtr + idxOffSize, idxOffSize);
-  if ((n = idxPtr1 - idxPtr0) > 255)
-    n = 255;
-  strncpy(buf, (char *)idxPtr0, n);
-  buf[n] = '\0';
-  fontName = copyString(buf);
-  topPtr = idxStartPtr + getWord(topPtr + nFonts * idxOffSize, idxOffSize);
+  out = outA;
 
   // read top dict (first font only)
-  nFonts = getWord(topPtr, 2);
-  idxOffSize = topPtr[2];
-  topPtr += 3;
-  idxStartPtr = topPtr + (nFonts + 1) * idxOffSize - 1;
-  idxPtr0 = idxStartPtr + getWord(topPtr, idxOffSize);
-  idxPtr1 = idxStartPtr + getWord(topPtr + idxOffSize, idxOffSize);
-  dict.version = 0;
-  dict.notice = 0;
-  dict.copyright = 0;
-  dict.fullName = 0;
-  dict.familyName = 0;
-  dict.weight = 0;
-  dict.isFixedPitch = 0;
-  dict.italicAngle = 0;
-  dict.underlinePosition = -100;
-  dict.underlineThickness = 50;
-  dict.paintType = 0;
-  dict.charstringType = 2;
-  dict.fontMatrix[0] = 0.001;
-  dict.fontMatrix[1] = 0;
-  dict.fontMatrix[2] = 0;
-  dict.fontMatrix[3] = 0.001;
-  dict.fontMatrix[4] = 0;
-  dict.fontMatrix[5] = 0;
-  dict.uniqueID = 0;
-  dict.fontBBox[0] = 0;
-  dict.fontBBox[1] = 0;
-  dict.fontBBox[2] = 0;
-  dict.fontBBox[3] = 0;
-  dict.strokeWidth = 0;
-  dict.charset = 0;
-  dict.encoding = 0;
-  dict.charStrings = 0;
-  dict.privateSize = 0;
-  dict.privateOffset = 0;
-  i = 0;
-  while (idxPtr0 < idxPtr1) {
-    if (*idxPtr0 <= 27 || *idxPtr0 == 31) {
-      key = *idxPtr0++;
-      if (key == 0x0c)
-       key = (key << 8) | *idxPtr0++;
-      switch (key) {
-      case 0x0000: dict.version = (int)op[0]; break;
-      case 0x0001: dict.notice = (int)op[0]; break;
-      case 0x0c00: dict.copyright = (int)op[0]; break;
-      case 0x0002: dict.fullName = (int)op[0]; break;
-      case 0x0003: dict.familyName = (int)op[0]; break;
-      case 0x0004: dict.weight = (int)op[0]; break;
-      case 0x0c01: dict.isFixedPitch = (int)op[0]; break;
-      case 0x0c02: dict.italicAngle = op[0]; break;
-      case 0x0c03: dict.underlinePosition = op[0]; break;
-      case 0x0c04: dict.underlineThickness = op[0]; break;
-      case 0x0c05: dict.paintType = (int)op[0]; break;
-      case 0x0c06: dict.charstringType = (int)op[0]; break;
-      case 0x0c07: dict.fontMatrix[0] = op[0];
-                  dict.fontMatrix[1] = op[1];
-                  dict.fontMatrix[2] = op[2];
-                  dict.fontMatrix[3] = op[3];
-                  dict.fontMatrix[4] = op[4];
-                  dict.fontMatrix[5] = op[5]; break;
-      case 0x000d: dict.uniqueID = (int)op[0]; break;
-      case 0x0005: dict.fontBBox[0] = op[0];
-                  dict.fontBBox[1] = op[1];
-                  dict.fontBBox[2] = op[2];
-                  dict.fontBBox[3] = op[3]; break;
-      case 0x0c08: dict.strokeWidth = op[0]; break;
-      case 0x000f: dict.charset = (int)op[0]; break;
-      case 0x0010: dict.encoding = (int)op[0]; break;
-      case 0x0011: dict.charStrings = (int)op[0]; break;
-      case 0x0012: dict.privateSize = (int)op[0];
-                  dict.privateOffset = (int)op[1]; break;
-      }
-      i = 0;
-    } else {
-      x = getNum(&idxPtr0, &isFP);
-      if (i < 48) {
-       op[i] = x;
-       fp[i++] = isFP;
-      }
-    }
-  }
-  topPtr = idxStartPtr + getWord(topPtr + nFonts * idxOffSize, idxOffSize);
-
-  // read string index
-  nStrings = getWord(topPtr, 2);
-  stringOffSize = topPtr[2];
-  topPtr += 3;
-  stringIdxPtr = topPtr;
-  stringStartPtr = topPtr + (nStrings + 1) * stringOffSize - 1;
-  topPtr = stringStartPtr + getWord(topPtr + nStrings * stringOffSize,
-                                   stringOffSize);
+  readTopDict(&dict);
 
-#if 1 //~
   // get global subrs
-  int nGSubrs;
-  int gSubrOffSize;
-
-  nGSubrs = getWord(topPtr, 2);
-  gSubrOffSize = topPtr[2];
-  topPtr += 3;
-#endif
+  //~ ... global subrs are unimplemented
 
   // write header and font dictionary, up to encoding
-  fprintf(out, "%%!FontType1-1.0: %s", fontName);
+  fprintf(out, "%%!FontType1-1.0: %s", name->getCString());
   if (dict.version != 0) {
-    fprintf(out, "%s",
-           getString(dict.version, stringIdxPtr, stringStartPtr,
-                     stringOffSize, buf));
+    fprintf(out, "%s", getString(dict.version, buf));
   }
   fprintf(out, "\n");
   fprintf(out, "11 dict begin\n");
   fprintf(out, "/FontInfo 10 dict dup begin\n");
   if (dict.version != 0) {
     fprintf(out, "/version (%s) readonly def\n",
-           getString(dict.version, stringIdxPtr, stringStartPtr,
-                     stringOffSize, buf));
+           getString(dict.version, buf));
   }
   if (dict.notice != 0) {
     fprintf(out, "/Notice (%s) readonly def\n",
-           getString(dict.notice, stringIdxPtr, stringStartPtr,
-                     stringOffSize, buf));
+           getString(dict.notice, buf));
   }
   if (dict.copyright != 0) {
     fprintf(out, "/Copyright (%s) readonly def\n",
-           getString(dict.copyright, stringIdxPtr, stringStartPtr,
-                     stringOffSize, buf));
+           getString(dict.copyright, buf));
   }
   if (dict.fullName != 0) {
     fprintf(out, "/FullName (%s) readonly def\n",
-           getString(dict.fullName, stringIdxPtr, stringStartPtr,
-                     stringOffSize, buf));
+           getString(dict.fullName, buf));
   }
   if (dict.familyName != 0) {
     fprintf(out, "/FamilyName (%s) readonly def\n",
-           getString(dict.familyName, stringIdxPtr, stringStartPtr,
-                     stringOffSize, buf));
+           getString(dict.familyName, buf));
   }
   if (dict.weight != 0) {
     fprintf(out, "/Weight (%s) readonly def\n",
-           getString(dict.weight, stringIdxPtr, stringStartPtr,
-                     stringOffSize, buf));
+           getString(dict.weight, buf));
   }
   fprintf(out, "/isFixedPitch %s def\n", dict.isFixedPitch ? "true" : "false");
   fprintf(out, "/ItalicAngle %g def\n", dict.italicAngle);
   fprintf(out, "/UnderlinePosition %g def\n", dict.underlinePosition);
   fprintf(out, "/UnderlineThickness %g def\n", dict.underlineThickness);
   fprintf(out, "end readonly def\n");
-  fprintf(out, "/FontName /%s def\n", fontName);
+  fprintf(out, "/FontName /%s def\n", name->getCString());
   fprintf(out, "/PaintType %d def\n", dict.paintType);
   fprintf(out, "/FontType 1 def\n");
   fprintf(out, "/FontMatrix [%g %g %g %g %g %g] readonly def\n",
@@ -650,52 +409,16 @@ void Type1CFontConverter::convert() {
   fprintf(out, "/FontBBox [%g %g %g %g] readonly def\n",
          dict.fontBBox[0], dict.fontBBox[1],
          dict.fontBBox[2], dict.fontBBox[3]);
+  fprintf(out, "/StrokeWidth %g def\n", dict.strokeWidth);
   if (dict.uniqueID != 0) {
     fprintf(out, "/UniqueID %d def\n", dict.uniqueID);
   }
 
   // get number of glyphs from charstrings index
-  topPtr = (Guchar *)file + dict.charStrings;
-  nGlyphs = getWord(topPtr, 2);
+  nGlyphs = getIndexLen((Guchar *)file + dict.charStrings);
 
   // read charset
-  if (dict.charset == 0) {
-    glyphNames = type1CISOAdobeCharset;
-  } else if (dict.charset == 1) {
-    glyphNames = type1CExpertCharset;
-  } else if (dict.charset == 2) {
-    glyphNames = type1CExpertSubsetCharset;
-  } else {
-    glyphNames = (Gushort *)gmalloc(nGlyphs * sizeof(Gushort));
-    glyphNames[0] = 0;
-    topPtr = (Guchar *)file + dict.charset;
-    charsetFormat = *topPtr++;
-    if (charsetFormat == 0) {
-      for (i = 1; i < nGlyphs; ++i) {
-       glyphNames[i] = getWord(topPtr, 2);
-       topPtr += 2;
-      }
-    } else if (charsetFormat == 1) {
-      i = 1;
-      while (i < nGlyphs) {
-       c = getWord(topPtr, 2);
-       topPtr += 2;
-       nLeft = *topPtr++;
-       for (j = 0; j <= nLeft; ++j)
-         glyphNames[i++] = c++;
-      }
-    } else if (charsetFormat == 2) {
-      i = 1;
-      while (i < nGlyphs) {
-       c = getWord(topPtr, 2);
-       topPtr += 2;
-       nLeft = getWord(topPtr, 2);
-       topPtr += 2;
-       for (j = 0; j <= nLeft; ++j)
-         glyphNames[i++] = c++;
-      }
-    }
-  }
+  glyphNames = readCharset(dict.charset, nGlyphs);
 
   // read encoding (glyph -> code mapping), write Type 1 encoding
   fprintf(out, "/Encoding ");
@@ -706,54 +429,55 @@ void Type1CFontConverter::convert() {
     fprintf(out, "0 1 255 {1 index exch /.notdef put} for\n");
     if (dict.encoding == 1) {
       for (i = 0; i < 256; ++i) {
-       if (type1ExpertEncodingNames[i])
-         fprintf(out, "dup %d /%s put\n", i, type1ExpertEncodingNames[i]);
+       if (expertEncoding[i]) {
+         fprintf(out, "dup %d /%s put\n", i, expertEncoding[i]);
+       }
       }
     } else {
-      topPtr = (Guchar *)file + dict.encoding;
-      encFormat = *topPtr++;
+      ptr = (Guchar *)file + dict.encoding;
+      encFormat = *ptr++;
       if ((encFormat & 0x7f) == 0) {
-       nCodes = 1 + *topPtr++;
+       nCodes = 1 + *ptr++;
        if (nCodes > nGlyphs) {
          nCodes = nGlyphs;
        }
        for (i = 1; i < nCodes; ++i) {
-         c = *topPtr++;
-         fprintf(out, "dup %d /%s put\n", c,
-                 getString(glyphNames[i], stringIdxPtr, stringStartPtr,
-                           stringOffSize, buf));
+         c = *ptr++;
+         fprintf(out, "dup %d /%s put\n",
+                 c, getString(glyphNames[i], buf));
        }
       } else if ((encFormat & 0x7f) == 1) {
-       nRanges = *topPtr++;
+       nRanges = *ptr++;
        nCodes = 1;
        for (i = 0; i < nRanges; ++i) {
-         c = *topPtr++;
-         nLeft = *topPtr++;
+         c = *ptr++;
+         nLeft = *ptr++;
          for (j = 0; j <= nLeft && nCodes < nGlyphs; ++j) {
-           fprintf(out, "dup %d /%s put\n", c,
-                   getString(glyphNames[nCodes], stringIdxPtr, stringStartPtr,
-                             stringOffSize, buf));
+           fprintf(out, "dup %d /%s put\n",
+                   c, getString(glyphNames[nCodes], buf));
            ++nCodes;
            ++c;
          }
        }
       }
       if (encFormat & 0x80) {
-       nSups = *topPtr++;
+       nSups = *ptr++;
        for (i = 0; i < nSups; ++i) {
-         c = *topPtr++;
-         sid = getWord(topPtr, 2);
-         topPtr += 2;
-         fprintf(out, "dup %d /%s put\n", c,
-                 getString(sid, stringIdxPtr, stringStartPtr,
-                           stringOffSize, buf));
+         c = *ptr++;
+         sid = getWord(ptr, 2);
+         ptr += 2;
+         fprintf(out, "dup %d /%s put\n", c, getString(sid, buf));
        }
       }
     }
     fprintf(out, "readonly def\n");
   }
   fprintf(out, "currentdict end\n");
+
+  // start the binary section
   fprintf(out, "currentfile eexec\n");
+  r1 = 55665;
+  line = 0;
 
   // get private dictionary
   eexecWrite("\x83\xca\x73\xd5");
@@ -762,128 +486,31 @@ void Type1CFontConverter::convert() {
   eexecWrite("/ND {noaccess def} executeonly def\n");
   eexecWrite("/NP {noaccess put} executeonly def\n");
   eexecWrite("/MinFeature {16 16} ND\n");
-  eexecWrite("/password 5839 def\n");
-  subrsOffset = 0;
-  defaultWidthX = 0;
-  nominalWidthX = 0;
-  topPtr = (Guchar *)file + dict.privateOffset;
-  idxPtr0 = topPtr;
-  idxPtr1 = idxPtr0 + dict.privateSize;
-  i = 0;
-  while (idxPtr0 < idxPtr1) {
-    if (*idxPtr0 <= 27 || *idxPtr0 == 31) {
-      key = *idxPtr0++;
-      if (key == 0x0c)
-       key = (key << 8) | *idxPtr0++;
-      switch (key) {
-      case 0x0006:
-       getDeltaInt(eBuf, "BlueValues", op, i);
-       eexecWrite(eBuf);
-       break;
-      case 0x0007:
-       getDeltaInt(eBuf, "OtherBlues", op, i);
-       eexecWrite(eBuf);
-       break;
-      case 0x0008:
-       getDeltaInt(eBuf, "FamilyBlues", op, i);
-       eexecWrite(eBuf);
-       break;
-      case 0x0009:
-       getDeltaInt(eBuf, "FamilyOtherBlues", op, i);
-       eexecWrite(eBuf);
-       break;
-      case 0x0c09:
-       sprintf(eBuf, "/BlueScale %g def\n", op[0]);
-       eexecWrite(eBuf);
-       break;
-      case 0x0c0a:
-       sprintf(eBuf, "/BlueShift %d def\n", (int)op[0]);
-       eexecWrite(eBuf);
-       break;
-      case 0x0c0b:
-       sprintf(eBuf, "/BlueFuzz %d def\n", (int)op[0]);
-       eexecWrite(eBuf);
-       break;
-      case 0x000a:
-       sprintf(eBuf, "/StdHW [%g] def\n", op[0]);
-       eexecWrite(eBuf);
-       break;
-      case 0x000b:
-       sprintf(eBuf, "/StdVW [%g] def\n", op[0]);
-       eexecWrite(eBuf);
-       break;
-      case 0x0c0c:
-       getDeltaReal(eBuf, "StemSnapH", op, i);
-       eexecWrite(eBuf);
-       break;
-      case 0x0c0d:
-       getDeltaReal(eBuf, "StemSnapV", op, i);
-       eexecWrite(eBuf);
-       break;
-      case 0x0c0e:
-       sprintf(eBuf, "/ForceBold %s def\n", op[0] ? "true" : "false");
-       eexecWrite(eBuf);
-       break;
-      case 0x0c0f:
-       sprintf(eBuf, "/ForceBoldThreshold %g def\n", op[0]);
-       eexecWrite(eBuf);
-       break;
-      case 0x0c11:
-       sprintf(eBuf, "/LanguageGroup %d def\n", (int)op[0]);
-       eexecWrite(eBuf);
-       break;
-      case 0x0c12:
-       sprintf(eBuf, "/ExpansionFactor %g def\n", op[0]);
-       eexecWrite(eBuf);
-       break;
-      case 0x0c13:
-       error(-1, "Got Type 1C InitialRandomSeed");
-       break;
-      case 0x0013:
-       subrsOffset = (int)op[0];
-       break;
-      case 0x0014:
-       defaultWidthX = op[0];
-       defaultWidthXFP = fp[0];
-       break;
-      case 0x0015:
-       nominalWidthX = op[0];
-       nominalWidthXFP = fp[0];
-       break;
-      default:
-       error(-1, "Uknown Type 1C private dict entry %04x", key);
-       break;
-      }
-      i = 0;
-    } else {
-      x = getNum(&idxPtr0, &isFP);
-      if (i < 48) {
-       op[i] = x;
-       fp[i++] = isFP;
-      }
-    }
-  }
+  readPrivateDict(&privateDict, dict.privateOffset, dict.privateSize);
+  eexecWrite(privateDict.dictData->getCString());
+  defaultWidthX = privateDict.defaultWidthX;
+  defaultWidthXFP = privateDict.defaultWidthXFP;
+  nominalWidthX = privateDict.nominalWidthX;
+  nominalWidthXFP = privateDict.nominalWidthXFP;
 
   // get subrs
-  if (subrsOffset != 0) {
-    topPtr += subrsOffset;
-    nSubrs = getWord(topPtr, 2);
-    idxOffSize = topPtr[2];
-    topPtr += 3;
+  if (privateDict.subrsOffset != 0) {
+    subrsIdxPtr = (Guchar *)file + dict.privateOffset +
+                  privateDict.subrsOffset;
+    nSubrs = getIndexLen(subrsIdxPtr);
     sprintf(eBuf, "/Subrs %d array\n", nSubrs);
     eexecWrite(eBuf);
-    idxStartPtr = topPtr + (nSubrs + 1) * idxOffSize - 1;
-    idxPtr1 = idxStartPtr + getWord(topPtr, idxOffSize);
+    idxPtr1 = getIndexValPtr(subrsIdxPtr, 0);
     for (i = 0; i < nSubrs; ++i) {
       idxPtr0 = idxPtr1;
-      idxPtr1 = idxStartPtr + getWord(topPtr + (i+1)*idxOffSize, idxOffSize);
+      idxPtr1 = getIndexValPtr(subrsIdxPtr, i+1);
       n = idxPtr1 - idxPtr0;
-#if 1 //~
+#if 1 //~ Type 2 subrs are unimplemented
       error(-1, "Unimplemented Type 2 subrs");
 #else
       sprintf(eBuf, "dup %d %d RD ", i, n);
       eexecWrite(eBuf);
-      cvtGlyph(idxPtr0, n);
+      eexecCvtGlyph(idxPtr0, n);
       eexecWrite(" NP\n");
 #endif
     }
@@ -891,21 +518,16 @@ void Type1CFontConverter::convert() {
   }
 
   // get CharStrings
-  topPtr = (Guchar *)file + dict.charStrings;
-  nCharStrings = getWord(topPtr, 2);
-  idxOffSize = topPtr[2];
-  topPtr += 3;
+  charStringsIdxPtr = (Guchar *)file + dict.charStrings;
+  nCharStrings = getIndexLen(charStringsIdxPtr);
   sprintf(eBuf, "2 index /CharStrings %d dict dup begin\n", nCharStrings);
   eexecWrite(eBuf);
-  idxStartPtr = topPtr + (nCharStrings + 1) * idxOffSize - 1;
-  idxPtr1 = idxStartPtr + getWord(topPtr, idxOffSize);
+  idxPtr1 = getIndexValPtr(charStringsIdxPtr, 0);
   for (i = 0; i < nCharStrings; ++i) {
     idxPtr0 = idxPtr1;
-    idxPtr1 = idxStartPtr + getWord(topPtr + (i+1)*idxOffSize, idxOffSize);
+    idxPtr1 = getIndexValPtr(charStringsIdxPtr, i+1);
     n = idxPtr1 - idxPtr0;
-    cvtGlyph(getString(glyphNames[i], stringIdxPtr, stringStartPtr,
-                      stringOffSize, buf),
-            idxPtr0, n);
+    eexecCvtGlyph(getString(glyphNames[i], buf), idxPtr0, n);
   }
   eexecWrite("end\n");
   eexecWrite("end\n");
@@ -915,719 +537,3145 @@ void Type1CFontConverter::convert() {
   eexecWrite("mark currentfile closefile\n");
 
   // trailer
-  if (line > 0)
+  if (line > 0) {
     fputc('\n', out);
+  }
   for (i = 0; i < 8; ++i) {
     fprintf(out, "0000000000000000000000000000000000000000000000000000000000000000\n");
   }
   fprintf(out, "cleartomark\n");
 
   // clean up
-  if (dict.charset > 2)
+  delete privateDict.dictData;
+  if (dict.charset > 2) {
     gfree(glyphNames);
-  gfree(fontName);
+  }
 }
 
-void Type1CFontConverter::eexecWrite(char *s) {
-  Guchar *p;
-  Guchar x;
+void Type1CFontFile::convertToCIDType0(char *psName, FILE *outA) {
+  Type1CTopDict dict;
+  Type1CPrivateDict *privateDicts;
+  GString *charStrings;
+  int *charStringOffsets;
+  Gushort *charset;
+  int *cidMap;
+  Guchar *fdSelect;
+  Guchar *charStringsIdxPtr, *fdArrayIdx, *idxPtr0, *idxPtr1, *ptr;
+  char buf[256];
+  int nGlyphs, nCIDs, gdBytes, nFDs;
+  int fdSelectFmt, nRanges, gid0, gid1, fd, offset;
+  int key;
+  double x;
+  GBool isFP;
+  int i, j, k, n;
 
-  for (p = (Guchar *)s; *p; ++p) {
-    x = *p ^ (r1 >> 8);
-    r1 = (x + r1) * 52845 + 22719;
-    fputc(hexChars[x >> 4], out);
-    fputc(hexChars[x & 0x0f], out);
-    line += 2;
-    if (line == 64) {
-      fputc('\n', out);
-      line = 0;
+  out = outA;
+
+  fprintf(out, "/CIDInit /ProcSet findresource begin\n");
+
+  // read top dict (first font only)
+  readTopDict(&dict);
+
+  // read the FDArray dictionaries and Private dictionaries
+  if (dict.fdArrayOffset == 0) {
+    nFDs = 1;
+    privateDicts = (Type1CPrivateDict *)
+                     gmalloc(nFDs * sizeof(Type1CPrivateDict));
+    privateDicts[0].dictData = new GString();
+    privateDicts[0].subrsOffset = 0;
+    privateDicts[0].defaultWidthX = 0;
+    privateDicts[0].defaultWidthXFP = gFalse;
+    privateDicts[0].nominalWidthX = 0;
+    privateDicts[0].nominalWidthXFP = gFalse;
+  } else {
+    fdArrayIdx = (Guchar *)file + dict.fdArrayOffset;
+    nFDs = getIndexLen(fdArrayIdx);
+    privateDicts = (Type1CPrivateDict *)
+                     gmalloc(nFDs * sizeof(Type1CPrivateDict));
+    idxPtr1 = getIndexValPtr(fdArrayIdx, 0);
+    for (i = 0; i < nFDs; ++i) {
+      privateDicts[i].dictData = NULL;
+      idxPtr0 = idxPtr1;
+      idxPtr1 = getIndexValPtr(fdArrayIdx, i + 1);
+      ptr = idxPtr0;
+      j = 0;
+      while (ptr < idxPtr1) {
+       if (*ptr <= 27 || *ptr == 31) {
+         key = *ptr++;
+         if (key == 0x0c) {
+           key = (key << 8) | *ptr++;
+         }
+         if (key == 0x0012) {
+           readPrivateDict(&privateDicts[i], (int)op[1], (int)op[0]);
+         }
+         j = 0;
+       } else {
+         x = getNum(&ptr, &isFP);
+         if (j < 48) {
+           op[j] = x;
+           fp[j++] = isFP;
+         }
+       }
+      }
+      if (!privateDicts[i].dictData) {
+       privateDicts[i].dictData = new GString();
+       privateDicts[i].subrsOffset = 0;
+       privateDicts[i].defaultWidthX = 0;
+       privateDicts[i].defaultWidthXFP = gFalse;
+       privateDicts[i].nominalWidthX = 0;
+       privateDicts[i].nominalWidthXFP = gFalse;
+      }
+    }
+  }
+
+  // get the glyph count
+  charStringsIdxPtr = (Guchar *)file + dict.charStrings;
+  nGlyphs = getIndexLen(charStringsIdxPtr);
+
+  // read the FDSelect table
+  fdSelect = (Guchar *)gmalloc(nGlyphs);
+  if (dict.fdSelectOffset == 0) {
+    for (i = 0; i < nGlyphs; ++i) {
+      fdSelect[i] = 0;
+    }
+  } else {
+    ptr = (Guchar *)file + dict.fdSelectOffset;
+    fdSelectFmt = *ptr++;
+    if (fdSelectFmt == 0) {
+      memcpy(fdSelect, ptr, nGlyphs);
+    } else if (fdSelectFmt == 3) {
+      nRanges = getWord(ptr, 2);
+      ptr += 2;
+      gid0 = getWord(ptr, 2);
+      ptr += 2;
+      for (i = 1; i <= nRanges; ++i) {
+       fd = *ptr++;
+       gid1 = getWord(ptr, 2);
+       ptr += 2;
+       for (j = gid0; j < gid1; ++j) {
+         fdSelect[j] = fd;
+       }
+       gid0 = gid1;
+      }
+    } else {
+      error(-1, "Unknown FDSelect table format in CID font");
+      for (i = 0; i < nGlyphs; ++i) {
+       fdSelect[i] = 0;
+      }
+    }
+  }
+
+  // read the charset, compute the CID-to-GID mapping
+  charset = readCharset(dict.charset, nGlyphs);
+  nCIDs = 0;
+  for (i = 0; i < nGlyphs; ++i) {
+    if (charset[i] >= nCIDs) {
+      nCIDs = charset[i] + 1;
+    }
+  }
+  cidMap = (int *)gmalloc(nCIDs * sizeof(int));
+  for (i = 0; i < nCIDs; ++i) {
+    cidMap[i] = -1;
+  }
+  for (i = 0; i < nGlyphs; ++i) {
+    cidMap[charset[i]] = i;
+  }
+
+  // build the charstrings
+  charStrings = new GString();
+  charStringOffsets = (int *)gmalloc((nCIDs + 1) * sizeof(int));
+  for (i = 0; i < nCIDs; ++i) {
+    charStringOffsets[i] = charStrings->getLength();
+    if (cidMap[i] >= 0) {
+      idxPtr0 = getIndexValPtr(charStringsIdxPtr, cidMap[i]);
+      idxPtr1 = getIndexValPtr(charStringsIdxPtr, cidMap[i]+1);
+      n = idxPtr1 - idxPtr0;
+      j = fdSelect[cidMap[i]];
+      defaultWidthX = privateDicts[j].defaultWidthX;
+      defaultWidthXFP = privateDicts[j].defaultWidthXFP;
+      nominalWidthX = privateDicts[j].nominalWidthX;
+      nominalWidthXFP = privateDicts[j].nominalWidthXFP;
+      cvtGlyph(idxPtr0, n);
+      charStrings->append(charBuf);
+      delete charBuf;
+    }
+  }
+  charStringOffsets[nCIDs] = charStrings->getLength();
+
+  // compute gdBytes = number of bytes needed for charstring offsets
+  // (offset size needs to account for the charstring offset table,
+  // with a worst case of five bytes per entry, plus the charstrings
+  // themselves)
+  i = (nCIDs + 1) * 5 + charStrings->getLength();
+  if (i < 0x100) {
+    gdBytes = 1;
+  } else if (i < 0x10000) {
+    gdBytes = 2;
+  } else if (i < 0x1000000) {
+    gdBytes = 3;
+  } else {
+    gdBytes = 4;
+  }
+
+  // begin the font dictionary
+  fprintf(out, "20 dict begin\n");
+  fprintf(out, "/CIDFontName /%s def\n", psName);
+  fprintf(out, "/CIDFontType 0 def\n");
+  fprintf(out, "/CIDSystemInfo 3 dict dup begin\n");
+  if (dict.registry > 0 && dict.ordering > 0) {
+    fprintf(out, "  /Registry (%s) def\n", getString(dict.registry, buf));
+    fprintf(out, "  /Ordering (%s) def\n", getString(dict.ordering, buf));
+  } else {
+    fprintf(out, "  /Registry (Adobe) def\n");
+    fprintf(out, "  /Ordering (Identity) def\n");
+  }
+  fprintf(out, "  /Supplement %d def\n", dict.supplement);
+  fprintf(out, "end def\n");
+  fprintf(out, "/FontMatrix [%g %g %g %g %g %g] def\n",
+         dict.fontMatrix[0], dict.fontMatrix[1], dict.fontMatrix[2],
+         dict.fontMatrix[3], dict.fontMatrix[4], dict.fontMatrix[5]);
+  fprintf(out, "/FontBBox [%g %g %g %g] def\n",
+         dict.fontBBox[0], dict.fontBBox[1],
+         dict.fontBBox[2], dict.fontBBox[3]);
+  fprintf(out, "/FontInfo 1 dict dup begin\n");
+  fprintf(out, "  /FSType 8 def\n");
+  fprintf(out, "end def\n");
+
+  // CIDFont-specific entries
+  fprintf(out, "/CIDCount %d def\n", nCIDs);
+  fprintf(out, "/FDBytes 1 def\n");
+  fprintf(out, "/GDBytes %d def\n", gdBytes);
+  fprintf(out, "/CIDMapOffset 0 def\n");
+  if (dict.paintType != 0) {
+    fprintf(out, "/PaintType %d def\n", dict.paintType);
+    fprintf(out, "/StrokeWidth %g def\n", dict.strokeWidth);
+  }
+
+  // FDArray entry
+  fprintf(out, "/FDArray %d array\n", nFDs);
+  for (i = 0; i < nFDs; ++i) {
+    fprintf(out, "dup %d 10 dict begin\n", i);
+    fprintf(out, "/FontType 1 def\n");
+    fprintf(out, "/FontMatrix [1 0 0 1 0 0] def\n");
+    fprintf(out, "/PaintType %d def\n", dict.paintType);
+    fprintf(out, "/Private 32 dict begin\n");
+    fwrite(privateDicts[i].dictData->getCString(), 1,
+          privateDicts[i].dictData->getLength(), out);
+    fprintf(out, "currentdict end def\n");
+    fprintf(out, "currentdict end put\n");
+  }
+  fprintf(out, "def\n");
+
+  //~ need to deal with subrs
+  
+  // start the binary section
+  offset = (nCIDs + 1) * (1 + gdBytes);
+  fprintf(out, "(Hex) %d StartData\n",
+         offset + charStrings->getLength());
+
+  // write the charstring offset (CIDMap) table
+  for (i = 0; i <= nCIDs; i += 6) {
+    for (j = 0; j < 6 && i+j <= nCIDs; ++j) {
+      if (cidMap[i+j] >= 0) {
+       buf[0] = (char)fdSelect[cidMap[i+j]];
+      } else {
+       buf[0] = (char)0;
+      }
+      n = offset + charStringOffsets[i+j];
+      for (k = gdBytes; k >= 1; --k) {
+       buf[k] = (char)(n & 0xff);
+       n >>= 8;
+      }
+      for (k = 0; k <= gdBytes; ++k) {
+       fprintf(out, "%02x", buf[k] & 0xff);
+      }
+    }
+    fputc('\n', out);
+  }
+
+  // write the charstring data
+  n = charStrings->getLength();
+  for (i = 0; i < n; i += 32) {
+    for (j = 0; j < 32 && i+j < n; ++j) {
+      fprintf(out, "%02x", charStrings->getChar(i+j) & 0xff);
+    }
+    if (i + 32 >= n) {
+      fputc('>', out);
     }
+    fputc('\n', out);
+  }
+
+  for (i = 0; i < nFDs; ++i) {
+    delete privateDicts[i].dictData;
   }
+  gfree(privateDicts);
+  gfree(cidMap);
+  gfree(charset);
+  gfree(charStringOffsets);
+  delete charStrings;
+  gfree(fdSelect);
 }
 
-void Type1CFontConverter::cvtGlyph(char *name, Guchar *s, int n) {
-  int nHints;
-  int x;
-  GBool first = gTrue;
+void Type1CFontFile::convertToType0(char *psName, FILE *outA) {
+  Type1CTopDict dict;
+  Type1CPrivateDict *privateDicts;
+  Gushort *charset;
+  int *cidMap;
+  Guchar *fdSelect;
+  Guchar *charStringsIdxPtr, *fdArrayIdx, *idxPtr0, *idxPtr1, *ptr;
+  char buf[256];
   char eBuf[256];
-  double d, dx, dy;
-  GBool dFP;
-  int i, k;
+  int nGlyphs, nCIDs, nFDs;
+  int fdSelectFmt, nRanges, gid0, gid1, fd;
+  int key;
+  double x;
+  GBool isFP;
+  int i, j, n;
 
-  charBuf = new GString();
-  charBuf->append((char)73);
-  charBuf->append((char)58);
-  charBuf->append((char)147);
-  charBuf->append((char)134);
+  out = outA;
 
-  i = 0;
-  nOps = 0;
-  nHints = 0;
-  while (i < n) {
-    if (s[i] == 12) {
-      switch (s[i+1]) {
-      case 0:                  // dotsection (should be Type 1 only?)
-       //~ ignored
-       break;
-      case 34:                 // hflex
-       if (nOps != 7) {
-         error(-1, "Wrong number of args (%d) to Type 2 hflex", nOps);
-       }
-       eexecDumpNum(op[0], fp[0]);
-       eexecDumpNum(0, gFalse);
-       eexecDumpNum(op[1], fp[1]);
-       eexecDumpNum(op[2], fp[2]);
-       eexecDumpNum(op[3], fp[3]);
-       eexecDumpNum(0, gFalse);
-       eexecDumpOp1(8);
-       eexecDumpNum(op[4], fp[4]);
-       eexecDumpNum(0, gFalse);
-       eexecDumpNum(op[5], fp[5]);
-       eexecDumpNum(-op[2], fp[2]);
-       eexecDumpNum(op[6], fp[6]);
-       eexecDumpNum(0, gFalse);
-       eexecDumpOp1(8);
-       break;
-      case 35:                 // flex
-       if (nOps != 13) {
-         error(-1, "Wrong number of args (%d) to Type 2 flex", nOps);
-       }
-       eexecDumpNum(op[0], fp[0]);
-       eexecDumpNum(op[1], fp[1]);
-       eexecDumpNum(op[2], fp[2]);
-       eexecDumpNum(op[3], fp[3]);
-       eexecDumpNum(op[4], fp[4]);
-       eexecDumpNum(op[5], fp[5]);
-       eexecDumpOp1(8);
-       eexecDumpNum(op[6], fp[6]);
-       eexecDumpNum(op[7], fp[7]);
-       eexecDumpNum(op[8], fp[8]);
-       eexecDumpNum(op[9], fp[9]);
-       eexecDumpNum(op[10], fp[10]);
-       eexecDumpNum(op[11], fp[11]);
-       eexecDumpOp1(8);
-       break;
-      case 36:                 // hflex1
-       if (nOps != 9) {
-         error(-1, "Wrong number of args (%d) to Type 2 hflex1", nOps);
-       }
-       eexecDumpNum(op[0], fp[0]);
-       eexecDumpNum(op[1], fp[1]);
-       eexecDumpNum(op[2], fp[2]);
-       eexecDumpNum(op[3], fp[3]);
-       eexecDumpNum(op[4], fp[4]);
-       eexecDumpNum(0, gFalse);
-       eexecDumpOp1(8);
-       eexecDumpNum(op[5], fp[5]);
-       eexecDumpNum(0, gFalse);
-       eexecDumpNum(op[6], fp[6]);
-       eexecDumpNum(op[7], fp[7]);
-       eexecDumpNum(op[8], fp[8]);
-       eexecDumpNum(-(op[1] + op[3] + op[7]), fp[1] | fp[3] | fp[7]);
-       eexecDumpOp1(8);
-       break;
-      case 37:                 // flex1
-       if (nOps != 11) {
-         error(-1, "Wrong number of args (%d) to Type 2 flex1", nOps);
-       }
-       eexecDumpNum(op[0], fp[0]);
-       eexecDumpNum(op[1], fp[1]);
-       eexecDumpNum(op[2], fp[2]);
-       eexecDumpNum(op[3], fp[3]);
-       eexecDumpNum(op[4], fp[4]);
-       eexecDumpNum(op[5], fp[5]);
-       eexecDumpOp1(8);
-       eexecDumpNum(op[6], fp[6]);
-       eexecDumpNum(op[7], fp[7]);
-       eexecDumpNum(op[8], fp[8]);
-       eexecDumpNum(op[9], fp[9]);
-       dx = op[0] + op[2] + op[4] + op[6] + op[8];
-       dy = op[1] + op[3] + op[5] + op[7] + op[9];
-       if (fabs(dx) > fabs(dy)) {
-         eexecDumpNum(op[10], fp[10]);
-         eexecDumpNum(-dy, fp[1] | fp[3] | fp[5] | fp[7] | fp[9]);
+  // read top dict (first font only)
+  readTopDict(&dict);
+
+  // read the FDArray dictionaries and Private dictionaries
+  if (dict.fdArrayOffset == 0) {
+    nFDs = 1;
+    privateDicts = (Type1CPrivateDict *)
+                     gmalloc(nFDs * sizeof(Type1CPrivateDict));
+    privateDicts[0].dictData = new GString();
+    privateDicts[0].subrsOffset = 0;
+    privateDicts[0].defaultWidthX = 0;
+    privateDicts[0].defaultWidthXFP = gFalse;
+    privateDicts[0].nominalWidthX = 0;
+    privateDicts[0].nominalWidthXFP = gFalse;
+  } else {
+    fdArrayIdx = (Guchar *)file + dict.fdArrayOffset;
+    nFDs = getIndexLen(fdArrayIdx);
+    privateDicts = (Type1CPrivateDict *)
+                     gmalloc(nFDs * sizeof(Type1CPrivateDict));
+    idxPtr1 = getIndexValPtr(fdArrayIdx, 0);
+    for (i = 0; i < nFDs; ++i) {
+      privateDicts[i].dictData = NULL;
+      idxPtr0 = idxPtr1;
+      idxPtr1 = getIndexValPtr(fdArrayIdx, i + 1);
+      ptr = idxPtr0;
+      j = 0;
+      while (ptr < idxPtr1) {
+       if (*ptr <= 27 || *ptr == 31) {
+         key = *ptr++;
+         if (key == 0x0c) {
+           key = (key << 8) | *ptr++;
+         }
+         if (key == 0x0012) {
+           readPrivateDict(&privateDicts[i], (int)op[1], (int)op[0]);
+         }
+         j = 0;
        } else {
-         eexecDumpNum(-dx, fp[0] | fp[2] | fp[4] | fp[6] | fp[8]);
-         eexecDumpNum(op[10], fp[10]);
+         x = getNum(&ptr, &isFP);
+         if (j < 48) {
+           op[j] = x;
+           fp[j++] = isFP;
+         }
        }
-       eexecDumpOp1(8);
-       break;
-      case 3:                  // and
-      case 4:                  // or
-      case 5:                  // not
-      case 8:                  // store
-      case 9:                  // abs
-      case 10:                 // add
-      case 11:                 // sub
-      case 12:                 // div
-      case 13:                 // load
-      case 14:                 // neg
-      case 15:                 // eq
-      case 18:                 // drop
-      case 20:                 // put
-      case 21:                 // get
-      case 22:                 // ifelse
-      case 23:                 // random
-      case 24:                 // mul
-      case 26:                 // sqrt
-      case 27:                 // dup
-      case 28:                 // exch
-      case 29:                 // index
-      case 30:                 // roll
-       error(-1, "Unimplemented Type 2 charstring op: 12.%d", s[i+1]);
-       break;
-      default:
-       error(-1, "Illegal Type 2 charstring op: 12.%d", s[i+1]);
-       break;
       }
-      i += 2;
-      nOps = 0;
-    } else if (s[i] == 19) {   // hintmask
-      //~ ignored
-      if (first) {
-       cvtGlyphWidth(nOps == 1);
-       first = gFalse;
+      if (!privateDicts[i].dictData) {
+       privateDicts[i].dictData = new GString();
+       privateDicts[i].subrsOffset = 0;
+       privateDicts[i].defaultWidthX = 0;
+       privateDicts[i].defaultWidthXFP = gFalse;
+       privateDicts[i].nominalWidthX = 0;
+       privateDicts[i].nominalWidthXFP = gFalse;
       }
-      if (nOps > 0) {
-       if (nOps & 1) {
-         error(-1, "Wrong number of args (%d) to Type 2 hintmask/vstemhm",
-               nOps);
+    }
+  }
+
+  // get the glyph count
+  charStringsIdxPtr = (Guchar *)file + dict.charStrings;
+  nGlyphs = getIndexLen(charStringsIdxPtr);
+
+  // read the FDSelect table
+  fdSelect = (Guchar *)gmalloc(nGlyphs);
+  if (dict.fdSelectOffset == 0) {
+    for (i = 0; i < nGlyphs; ++i) {
+      fdSelect[i] = 0;
+    }
+  } else {
+    ptr = (Guchar *)file + dict.fdSelectOffset;
+    fdSelectFmt = *ptr++;
+    if (fdSelectFmt == 0) {
+      memcpy(fdSelect, ptr, nGlyphs);
+    } else if (fdSelectFmt == 3) {
+      nRanges = getWord(ptr, 2);
+      ptr += 2;
+      gid0 = getWord(ptr, 2);
+      ptr += 2;
+      for (i = 1; i <= nRanges; ++i) {
+       fd = *ptr++;
+       gid1 = getWord(ptr, 2);
+       ptr += 2;
+       for (j = gid0; j < gid1; ++j) {
+         fdSelect[j] = fd;
        }
-       nHints += nOps / 2;
+       gid0 = gid1;
       }
-      i += 1 + ((nHints + 7) >> 3);
-      nOps = 0;
-    } else if (s[i] == 20) {   // cntrmask
-      //~ ignored
-      if (first) {
-       cvtGlyphWidth(nOps == 1);
-       first = gFalse;
+    } else {
+      error(-1, "Unknown FDSelect table format in CID font");
+      for (i = 0; i < nGlyphs; ++i) {
+       fdSelect[i] = 0;
       }
-      if (nOps > 0) {
-       if (nOps & 1) {
-         error(-1, "Wrong number of args (%d) to Type 2 cntrmask/vstemhm",
-               nOps);
-       }
-       nHints += nOps / 2;
+    }
+  }
+
+  // read the charset, compute the CID-to-GID mapping
+  charset = readCharset(dict.charset, nGlyphs);
+  nCIDs = 0;
+  for (i = 0; i < nGlyphs; ++i) {
+    if (charset[i] >= nCIDs) {
+      nCIDs = charset[i] + 1;
+    }
+  }
+  cidMap = (int *)gmalloc(nCIDs * sizeof(int));
+  for (i = 0; i < nCIDs; ++i) {
+    cidMap[i] = -1;
+  }
+  for (i = 0; i < nGlyphs; ++i) {
+    cidMap[charset[i]] = i;
+  }
+
+  // write the descendant Type 1 fonts
+  for (i = 0; i < nCIDs; i += 256) {
+
+    //~ this assumes that all CIDs in this block have the same FD --
+    //~ to handle multiple FDs correctly, need to somehow divide the
+    //~ font up by FD
+    fd = 0;
+    for (j = 0; j < 256 && i+j < nCIDs; ++j) {
+      if (cidMap[i+j] >= 0) {
+       fd = fdSelect[cidMap[i+j]];
+       break;
       }
-      i += 1 + ((nHints + 7) >> 3);
-      nOps = 0;
-    } else if (s[i] == 28) {
-      x = (s[i+1] << 8) + s[i+2];
-      if (x & 0x8000)
-       x |= -1 << 15;
-      if (nOps < 48) {
-       fp[nOps] = gFalse;
-       op[nOps++] = x;
+    }
+
+    // font dictionary (unencrypted section)
+    fprintf(out, "16 dict begin\n");
+    fprintf(out, "/FontName /%s_%02x def\n", psName, i >> 8);
+    fprintf(out, "/FontType 1 def\n");
+    fprintf(out, "/FontMatrix [%g %g %g %g %g %g] def\n",
+           dict.fontMatrix[0], dict.fontMatrix[1], dict.fontMatrix[2],
+           dict.fontMatrix[3], dict.fontMatrix[4], dict.fontMatrix[5]);
+    fprintf(out, "/FontBBox [%g %g %g %g] def\n",
+           dict.fontBBox[0], dict.fontBBox[1],
+           dict.fontBBox[2], dict.fontBBox[3]);
+    fprintf(out, "/PaintType %d def\n", dict.paintType);
+    if (dict.paintType != 0) {
+      fprintf(out, "/StrokeWidth %g def\n", dict.strokeWidth);
+    }
+    fprintf(out, "/Encoding 256 array\n");
+    for (j = 0; j < 256 && i+j < nCIDs; ++j) {
+      fprintf(out, "dup %d /c%02x put\n", j, j);
+    }
+    fprintf(out, "readonly def\n");
+    fprintf(out, "currentdict end\n");
+
+    // start the binary section
+    fprintf(out, "currentfile eexec\n");
+    r1 = 55665;
+    line = 0;
+
+    // start the private dictionary
+    eexecWrite("\x83\xca\x73\xd5");
+    eexecWrite("dup /Private 32 dict dup begin\n");
+    eexecWrite("/RD {string currentfile exch readstring pop} executeonly def\n");
+    eexecWrite("/ND {noaccess def} executeonly def\n");
+    eexecWrite("/NP {noaccess put} executeonly def\n");
+    eexecWrite("/MinFeature {16 16} ND\n");
+    eexecWrite(privateDicts[fd].dictData->getCString());
+    defaultWidthX = privateDicts[fd].defaultWidthX;
+    defaultWidthXFP = privateDicts[fd].defaultWidthXFP;
+    nominalWidthX = privateDicts[fd].nominalWidthX;
+    nominalWidthXFP = privateDicts[fd].nominalWidthXFP;
+
+    // start the CharStrings
+    sprintf(eBuf, "2 index /CharStrings 256 dict dup begin\n");
+    eexecWrite(eBuf);
+
+    // write the .notdef CharString
+    idxPtr0 = getIndexValPtr(charStringsIdxPtr, 0);
+    idxPtr1 = getIndexValPtr(charStringsIdxPtr, 1);
+    n = idxPtr1 - idxPtr0;
+    eexecCvtGlyph(".notdef", idxPtr0, n);
+
+    // write the CharStrings
+    for (j = 0; j < 256 && i+j < nCIDs; ++j) {
+      if (cidMap[i+j] >= 0) {
+       idxPtr0 = getIndexValPtr(charStringsIdxPtr, cidMap[i+j]);
+       idxPtr1 = getIndexValPtr(charStringsIdxPtr, cidMap[i+j]+1);
+       n = idxPtr1 - idxPtr0;
+       sprintf(buf, "c%02x", j);
+       eexecCvtGlyph(buf, idxPtr0, n);
       }
-      i += 3;
-    } else if (s[i] <= 31) {
-      switch (s[i]) {
-      case 4:                  // vmoveto
-       if (first) {
-         cvtGlyphWidth(nOps == 2);
-         first = gFalse;
-       }
-       if (nOps != 1)
-         error(-1, "Wrong number of args (%d) to Type 2 vmoveto", nOps);
-       eexecDumpNum(op[0], fp[0]);
-       eexecDumpOp1(4);
+    }
+    eexecWrite("end\n");
+    eexecWrite("end\n");
+    eexecWrite("readonly put\n");
+    eexecWrite("noaccess put\n");
+    eexecWrite("dup /FontName get exch definefont pop\n");
+    eexecWrite("mark currentfile closefile\n");
+
+    // trailer
+    if (line > 0) {
+      fputc('\n', out);
+    }
+    for (j = 0; j < 8; ++j) {
+      fprintf(out, "0000000000000000000000000000000000000000000000000000000000000000\n");
+    }
+    fprintf(out, "cleartomark\n");
+  }
+
+  // write the Type 0 parent font
+  fprintf(out, "16 dict begin\n");
+  fprintf(out, "/FontName /%s def\n", psName);
+  fprintf(out, "/FontType 0 def\n");
+  fprintf(out, "/FontMatrix [1 0 0 1 0 0] def\n");
+  fprintf(out, "/FMapType 2 def\n");
+  fprintf(out, "/Encoding [\n");
+  for (i = 0; i < nCIDs; i += 256) {
+    fprintf(out, "%d\n", i >> 8);
+  }
+  fprintf(out, "] def\n");
+  fprintf(out, "/FDepVector [\n");
+  for (i = 0; i < nCIDs; i += 256) {
+    fprintf(out, "/%s_%02x findfont\n", psName, i >> 8);
+  }
+  fprintf(out, "] def\n");
+  fprintf(out, "FontName currentdict end definefont pop\n");
+
+  // clean up
+  for (i = 0; i < nFDs; ++i) {
+    delete privateDicts[i].dictData;
+  }
+  gfree(privateDicts);
+  gfree(cidMap);
+  gfree(charset);
+  gfree(fdSelect);
+}
+
+void Type1CFontFile::readTopDict(Type1CTopDict *dict) {
+  Guchar *idxPtr0, *idxPtr1, *ptr;
+  double x;
+  GBool isFP;
+  int key;
+  int i;
+
+  idxPtr0 = getIndexValPtr(topDictIdxPtr, 0);
+  idxPtr1 = getIndexValPtr(topDictIdxPtr, 1);
+  dict->version = 0;
+  dict->notice = 0;
+  dict->copyright = 0;
+  dict->fullName = 0;
+  dict->familyName = 0;
+  dict->weight = 0;
+  dict->isFixedPitch = 0;
+  dict->italicAngle = 0;
+  dict->underlinePosition = -100;
+  dict->underlineThickness = 50;
+  dict->paintType = 0;
+  dict->charstringType = 2;
+  dict->fontMatrix[0] = 0.001;
+  dict->fontMatrix[1] = 0;
+  dict->fontMatrix[2] = 0;
+  dict->fontMatrix[3] = 0.001;
+  dict->fontMatrix[4] = 0;
+  dict->fontMatrix[5] = 0;
+  dict->uniqueID = 0;
+  dict->fontBBox[0] = 0;
+  dict->fontBBox[1] = 0;
+  dict->fontBBox[2] = 0;
+  dict->fontBBox[3] = 0;
+  dict->strokeWidth = 0;
+  dict->charset = 0;
+  dict->encoding = 0;
+  dict->charStrings = 0;
+  dict->privateSize = 0;
+  dict->privateOffset = 0;
+  dict->registry = 0;
+  dict->ordering = 0;
+  dict->supplement = 0;
+  dict->fdArrayOffset = 0;
+  dict->fdSelectOffset = 0;
+  i = 0;
+  ptr = idxPtr0;
+  while (ptr < idxPtr1) {
+    if (*ptr <= 27 || *ptr == 31) {
+      key = *ptr++;
+      if (key == 0x0c) {
+       key = (key << 8) | *ptr++;
+      }
+      switch (key) {
+      case 0x0000: dict->version = (int)op[0]; break;
+      case 0x0001: dict->notice = (int)op[0]; break;
+      case 0x0c00: dict->copyright = (int)op[0]; break;
+      case 0x0002: dict->fullName = (int)op[0]; break;
+      case 0x0003: dict->familyName = (int)op[0]; break;
+      case 0x0004: dict->weight = (int)op[0]; break;
+      case 0x0c01: dict->isFixedPitch = (int)op[0]; break;
+      case 0x0c02: dict->italicAngle = op[0]; break;
+      case 0x0c03: dict->underlinePosition = op[0]; break;
+      case 0x0c04: dict->underlineThickness = op[0]; break;
+      case 0x0c05: dict->paintType = (int)op[0]; break;
+      case 0x0c06: dict->charstringType = (int)op[0]; break;
+      case 0x0c07: dict->fontMatrix[0] = op[0];
+                  dict->fontMatrix[1] = op[1];
+                  dict->fontMatrix[2] = op[2];
+                  dict->fontMatrix[3] = op[3];
+                  dict->fontMatrix[4] = op[4];
+                  dict->fontMatrix[5] = op[5]; break;
+      case 0x000d: dict->uniqueID = (int)op[0]; break;
+      case 0x0005: dict->fontBBox[0] = op[0];
+                  dict->fontBBox[1] = op[1];
+                  dict->fontBBox[2] = op[2];
+                  dict->fontBBox[3] = op[3]; break;
+      case 0x0c08: dict->strokeWidth = op[0]; break;
+      case 0x000f: dict->charset = (int)op[0]; break;
+      case 0x0010: dict->encoding = (int)op[0]; break;
+      case 0x0011: dict->charStrings = (int)op[0]; break;
+      case 0x0012: dict->privateSize = (int)op[0];
+                  dict->privateOffset = (int)op[1]; break;
+      case 0x0c1e: dict->registry = (int)op[0];
+                  dict->ordering = (int)op[1];
+                  dict->supplement = (int)op[2]; break;
+      case 0x0c24: dict->fdArrayOffset = (int)op[0]; break;
+      case 0x0c25: dict->fdSelectOffset = (int)op[0]; break;
+      }
+      i = 0;
+    } else {
+      x = getNum(&ptr, &isFP);
+      if (i < 48) {
+       op[i] = x;
+       fp[i++] = isFP;
+      }
+    }
+  }
+}
+
+void Type1CFontFile::readPrivateDict(Type1CPrivateDict *privateDict,
+                                    int offset, int size) {
+  Guchar *idxPtr0, *idxPtr1, *ptr;
+  char eBuf[256];
+  int key;
+  double x;
+  GBool isFP;
+  int i;
+
+  privateDict->dictData = new GString();
+  privateDict->subrsOffset = 0;
+  privateDict->defaultWidthX = 0;
+  privateDict->defaultWidthXFP = gFalse;
+  privateDict->nominalWidthX = 0;
+  privateDict->nominalWidthXFP = gFalse;
+  idxPtr0 = (Guchar *)file + offset;
+  idxPtr1 = idxPtr0 + size;
+  ptr = idxPtr0;
+  i = 0;
+  while (ptr < idxPtr1) {
+    if (*ptr <= 27 || *ptr == 31) {
+      key = *ptr++;
+      if (key == 0x0c) {
+       key = (key << 8) | *ptr++;
+      }
+      switch (key) {
+      case 0x0006:
+       getDeltaInt(eBuf, "BlueValues", op, i);
+       privateDict->dictData->append(eBuf);
        break;
-      case 5:                  // rlineto
-       if (nOps < 2 || nOps % 2 != 0)
-         error(-1, "Wrong number of args (%d) to Type 2 rlineto", nOps);
-       for (k = 0; k < nOps; k += 2) {
-         eexecDumpNum(op[k], fp[k]);
-         eexecDumpNum(op[k+1], fp[k+1]);
-         eexecDumpOp1(5);
-       }
+      case 0x0007:
+       getDeltaInt(eBuf, "OtherBlues", op, i);
+       privateDict->dictData->append(eBuf);
        break;
-      case 6:                  // hlineto
-       if (nOps < 1)
-         error(-1, "Wrong number of args (%d) to Type 2 hlineto", nOps);
-       for (k = 0; k < nOps; ++k) {
-         eexecDumpNum(op[k], fp[k]);
-         eexecDumpOp1((k & 1) ? 7 : 6);
-       }
+      case 0x0008:
+       getDeltaInt(eBuf, "FamilyBlues", op, i);
+       privateDict->dictData->append(eBuf);
        break;
-      case 7:                  // vlineto
-       if (nOps < 1)
-         error(-1, "Wrong number of args (%d) to Type 2 vlineto", nOps);
-       for (k = 0; k < nOps; ++k) {
-         eexecDumpNum(op[k], fp[k]);
-         eexecDumpOp1((k & 1) ? 6 : 7);
-       }
+      case 0x0009:
+       getDeltaInt(eBuf, "FamilyOtherBlues", op, i);
+       privateDict->dictData->append(eBuf);
        break;
-      case 8:                  // rrcurveto
-       if (nOps < 6 || nOps % 6 != 0)
-         error(-1, "Wrong number of args (%d) to Type 2 rrcurveto", nOps);
-       for (k = 0; k < nOps; k += 6) {
-         eexecDumpNum(op[k], fp[k]);
-         eexecDumpNum(op[k+1], fp[k+1]);
-         eexecDumpNum(op[k+2], fp[k+2]);
-         eexecDumpNum(op[k+3], fp[k+3]);
-         eexecDumpNum(op[k+4], fp[k+4]);
-         eexecDumpNum(op[k+5], fp[k+5]);
-         eexecDumpOp1(8);
-       }
+      case 0x0c09:
+       sprintf(eBuf, "/BlueScale %g def\n", op[0]);
+       privateDict->dictData->append(eBuf);
        break;
-      case 14:                 // endchar / seac
-       if (first) {
-         cvtGlyphWidth(nOps == 1 || nOps == 5);
-         first = gFalse;
-       }
-       if (nOps == 4) {
-         eexecDumpNum(0, 0);
-         eexecDumpNum(op[0], fp[0]);
-         eexecDumpNum(op[1], fp[1]);
-         eexecDumpNum(op[2], fp[2]);
-         eexecDumpNum(op[3], fp[3]);
-         eexecDumpOp2(6);
-       } else if (nOps == 0) {
-         eexecDumpOp1(14);
-       } else {
-         error(-1, "Wrong number of args (%d) to Type 2 endchar", nOps);
-       }
+      case 0x0c0a:
+       sprintf(eBuf, "/BlueShift %d def\n", (int)op[0]);
+       privateDict->dictData->append(eBuf);
        break;
-      case 21:                 // rmoveto
-       if (first) {
-         cvtGlyphWidth(nOps == 3);
-         first = gFalse;
-       }
-       if (nOps != 2)
-         error(-1, "Wrong number of args (%d) to Type 2 rmoveto", nOps);
-       eexecDumpNum(op[0], fp[0]);
-       eexecDumpNum(op[1], fp[1]);
-       eexecDumpOp1(21);
+      case 0x0c0b:
+       sprintf(eBuf, "/BlueFuzz %d def\n", (int)op[0]);
+       privateDict->dictData->append(eBuf);
        break;
-      case 22:                 // hmoveto
-       if (first) {
-         cvtGlyphWidth(nOps == 2);
-         first = gFalse;
-       }
-       if (nOps != 1)
-         error(-1, "Wrong number of args (%d) to Type 2 hmoveto", nOps);
-       eexecDumpNum(op[0], fp[0]);
-       eexecDumpOp1(22);
+      case 0x000a:
+       sprintf(eBuf, "/StdHW [%g] def\n", op[0]);
+       privateDict->dictData->append(eBuf);
        break;
-      case 24:                 // rcurveline
-       if (nOps < 8 || (nOps - 2) % 6 != 0)
-         error(-1, "Wrong number of args (%d) to Type 2 rcurveline", nOps);
-       for (k = 0; k < nOps - 2; k += 6) {
-         eexecDumpNum(op[k], fp[k]);
-         eexecDumpNum(op[k+1], fp[k+1]);
-         eexecDumpNum(op[k+2], fp[k+2]);
-         eexecDumpNum(op[k+3], fp[k+3]);
-         eexecDumpNum(op[k+4], fp[k+4]);
-         eexecDumpNum(op[k+5], fp[k+5]);
-         eexecDumpOp1(8);
-       }
-       eexecDumpNum(op[k], fp[k]);
-       eexecDumpNum(op[k+1], fp[k]);
-       eexecDumpOp1(5);
+      case 0x000b:
+       sprintf(eBuf, "/StdVW [%g] def\n", op[0]);
+       privateDict->dictData->append(eBuf);
        break;
-      case 25:                 // rlinecurve
-       if (nOps < 8 || (nOps - 6) % 2 != 0)
-         error(-1, "Wrong number of args (%d) to Type 2 rlinecurve", nOps);
-       for (k = 0; k < nOps - 6; k += 2) {
-         eexecDumpNum(op[k], fp[k]);
-         eexecDumpNum(op[k+1], fp[k]);
-         eexecDumpOp1(5);
-       }
-       eexecDumpNum(op[k], fp[k]);
-       eexecDumpNum(op[k+1], fp[k+1]);
-       eexecDumpNum(op[k+2], fp[k+2]);
-       eexecDumpNum(op[k+3], fp[k+3]);
-       eexecDumpNum(op[k+4], fp[k+4]);
-       eexecDumpNum(op[k+5], fp[k+5]);
-       eexecDumpOp1(8);
+      case 0x0c0c:
+       getDeltaReal(eBuf, "StemSnapH", op, i);
+       privateDict->dictData->append(eBuf);
        break;
-      case 26:                 // vvcurveto
-       if (nOps < 4 || !(nOps % 4 == 0 || (nOps-1) % 4 == 0))
-         error(-1, "Wrong number of args (%d) to Type 2 vvcurveto", nOps);
-       if (nOps % 2 == 1) {
-         eexecDumpNum(op[0], fp[0]);
-         eexecDumpNum(op[1], fp[1]);
-         eexecDumpNum(op[2], fp[2]);
-         eexecDumpNum(op[3], fp[3]);
-         eexecDumpNum(0, gFalse);
-         eexecDumpNum(op[4], fp[4]);
-         eexecDumpOp1(8);
-         k = 5;
-       } else {
-         k = 0;
-       }
-       for (; k < nOps; k += 4) {
-         eexecDumpNum(0, gFalse);
-         eexecDumpNum(op[k], fp[k]);
-         eexecDumpNum(op[k+1], fp[k+1]);
-         eexecDumpNum(op[k+2], fp[k+2]);
-         eexecDumpNum(0, gFalse);
-         eexecDumpNum(op[k+3], fp[k+3]);
-         eexecDumpOp1(8);
-       }
+      case 0x0c0d:
+       getDeltaReal(eBuf, "StemSnapV", op, i);
+       privateDict->dictData->append(eBuf);
        break;
-      case 27:                 // hhcurveto
-       if (nOps < 4 || !(nOps % 4 == 0 || (nOps-1) % 4 == 0))
-         error(-1, "Wrong number of args (%d) to Type 2 hhcurveto", nOps);
-       if (nOps % 2 == 1) {
-         eexecDumpNum(op[1], fp[1]);
-         eexecDumpNum(op[0], fp[0]);
-         eexecDumpNum(op[2], fp[2]);
-         eexecDumpNum(op[3], fp[3]);
-         eexecDumpNum(op[4], fp[4]);
-         eexecDumpNum(0, gFalse);
-         eexecDumpOp1(8);
-         k = 5;
-       } else {
-         k = 0;
-       }
-       for (; k < nOps; k += 4) {
-         eexecDumpNum(op[k], fp[k]);
-         eexecDumpNum(0, gFalse);
-         eexecDumpNum(op[k+1], fp[k+1]);
-         eexecDumpNum(op[k+2], fp[k+2]);
-         eexecDumpNum(op[k+3], fp[k+3]);
-         eexecDumpNum(0, gFalse);
-         eexecDumpOp1(8);
-       }
+      case 0x0c0e:
+       sprintf(eBuf, "/ForceBold %s def\n", op[0] ? "true" : "false");
+       privateDict->dictData->append(eBuf);
        break;
-      case 30:                 // vhcurveto
-       if (nOps < 4 || !(nOps % 4 == 0 || (nOps-1) % 4 == 0))
-         error(-1, "Wrong number of args (%d) to Type 2 vhcurveto", nOps);
-       for (k = 0; k < nOps && k != nOps-5; k += 4) {
-         if (k % 8 == 0) {
-           eexecDumpNum(op[k], fp[k]);
-           eexecDumpNum(op[k+1], fp[k+1]);
-           eexecDumpNum(op[k+2], fp[k+2]);
+      case 0x0c0f:
+       sprintf(eBuf, "/ForceBoldThreshold %g def\n", op[0]);
+       privateDict->dictData->append(eBuf);
+       break;
+      case 0x0c11:
+       sprintf(eBuf, "/LanguageGroup %d def\n", (int)op[0]);
+       privateDict->dictData->append(eBuf);
+       break;
+      case 0x0c12:
+       sprintf(eBuf, "/ExpansionFactor %g def\n", op[0]);
+       privateDict->dictData->append(eBuf);
+       break;
+      case 0x0c13:
+       error(-1, "Got Type 1C InitialRandomSeed");
+       break;
+      case 0x0013:
+       privateDict->subrsOffset = (int)op[0];
+       break;
+      case 0x0014:
+       privateDict->defaultWidthX = op[0];
+       privateDict->defaultWidthXFP = fp[0];
+       break;
+      case 0x0015:
+       privateDict->nominalWidthX = op[0];
+       privateDict->nominalWidthXFP = fp[0];
+       break;
+      default:
+       error(-1, "Unknown Type 1C private dict entry %04x", key);
+       break;
+      }
+      i = 0;
+    } else {
+      x = getNum(&ptr, &isFP);
+      if (i < 48) {
+       op[i] = x;
+       fp[i++] = isFP;
+      }
+    }
+  }
+}
+
+Gushort *Type1CFontFile::readCharset(int charset, int nGlyphs) {
+  Gushort *glyphNames;
+  Guchar *ptr;
+  int charsetFormat, c;
+  int nLeft, i, j;
+
+  if (charset == 0) {
+    glyphNames = type1CISOAdobeCharset;
+  } else if (charset == 1) {
+    glyphNames = type1CExpertCharset;
+  } else if (charset == 2) {
+    glyphNames = type1CExpertSubsetCharset;
+  } else {
+    glyphNames = (Gushort *)gmalloc(nGlyphs * sizeof(Gushort));
+    glyphNames[0] = 0;
+    ptr = (Guchar *)file + charset;
+    charsetFormat = *ptr++;
+    if (charsetFormat == 0) {
+      for (i = 1; i < nGlyphs; ++i) {
+       glyphNames[i] = getWord(ptr, 2);
+       ptr += 2;
+      }
+    } else if (charsetFormat == 1) {
+      i = 1;
+      while (i < nGlyphs) {
+       c = getWord(ptr, 2);
+       ptr += 2;
+       nLeft = *ptr++;
+       for (j = 0; j <= nLeft && i < nGlyphs; ++j) {
+         glyphNames[i++] = c++;
+       }
+      }
+    } else if (charsetFormat == 2) {
+      i = 1;
+      while (i < nGlyphs) {
+       c = getWord(ptr, 2);
+       ptr += 2;
+       nLeft = getWord(ptr, 2);
+       ptr += 2;
+       for (j = 0; j <= nLeft && i < nGlyphs; ++j) {
+         glyphNames[i++] = c++;
+       }
+      }
+    }
+  }
+  return glyphNames;
+}
+
+void Type1CFontFile::eexecWrite(char *s) {
+  Guchar *p;
+  Guchar x;
+
+  for (p = (Guchar *)s; *p; ++p) {
+    x = *p ^ (r1 >> 8);
+    r1 = (x + r1) * 52845 + 22719;
+    fputc(hexChars[x >> 4], out);
+    fputc(hexChars[x & 0x0f], out);
+    line += 2;
+    if (line == 64) {
+      fputc('\n', out);
+      line = 0;
+    }
+  }
+}
+
+void Type1CFontFile::eexecCvtGlyph(char *glyphName, Guchar *s, int n) {
+  char eBuf[256];
+
+  cvtGlyph(s, n);
+  sprintf(eBuf, "/%s %d RD ", glyphName, charBuf->getLength());
+  eexecWrite(eBuf);
+  eexecWriteCharstring((Guchar *)charBuf->getCString(), charBuf->getLength());
+  eexecWrite(" ND\n");
+  delete charBuf;
+}
+
+void Type1CFontFile::cvtGlyph(Guchar *s, int n) {
+  int nHints;
+  int x;
+  GBool first = gTrue;
+  double d, dx, dy;
+  GBool dFP;
+  Gushort r2;
+  Guchar byte;
+  int i, k;
+
+  charBuf = new GString();
+  charBuf->append((char)73);
+  charBuf->append((char)58);
+  charBuf->append((char)147);
+  charBuf->append((char)134);
+
+  i = 0;
+  nOps = 0;
+  nHints = 0;
+  while (i < n) {
+    if (s[i] == 12) {
+      switch (s[i+1]) {
+      case 0:                  // dotsection (should be Type 1 only?)
+       // ignored
+       break;
+      case 34:                 // hflex
+       if (nOps != 7) {
+         error(-1, "Wrong number of args (%d) to Type 2 hflex", nOps);
+       }
+       eexecDumpNum(op[0], fp[0]);
+       eexecDumpNum(0, gFalse);
+       eexecDumpNum(op[1], fp[1]);
+       eexecDumpNum(op[2], fp[2]);
+       eexecDumpNum(op[3], fp[3]);
+       eexecDumpNum(0, gFalse);
+       eexecDumpOp1(8);
+       eexecDumpNum(op[4], fp[4]);
+       eexecDumpNum(0, gFalse);
+       eexecDumpNum(op[5], fp[5]);
+       eexecDumpNum(-op[2], fp[2]);
+       eexecDumpNum(op[6], fp[6]);
+       eexecDumpNum(0, gFalse);
+       eexecDumpOp1(8);
+       break;
+      case 35:                 // flex
+       if (nOps != 13) {
+         error(-1, "Wrong number of args (%d) to Type 2 flex", nOps);
+       }
+       eexecDumpNum(op[0], fp[0]);
+       eexecDumpNum(op[1], fp[1]);
+       eexecDumpNum(op[2], fp[2]);
+       eexecDumpNum(op[3], fp[3]);
+       eexecDumpNum(op[4], fp[4]);
+       eexecDumpNum(op[5], fp[5]);
+       eexecDumpOp1(8);
+       eexecDumpNum(op[6], fp[6]);
+       eexecDumpNum(op[7], fp[7]);
+       eexecDumpNum(op[8], fp[8]);
+       eexecDumpNum(op[9], fp[9]);
+       eexecDumpNum(op[10], fp[10]);
+       eexecDumpNum(op[11], fp[11]);
+       eexecDumpOp1(8);
+       break;
+      case 36:                 // hflex1
+       if (nOps != 9) {
+         error(-1, "Wrong number of args (%d) to Type 2 hflex1", nOps);
+       }
+       eexecDumpNum(op[0], fp[0]);
+       eexecDumpNum(op[1], fp[1]);
+       eexecDumpNum(op[2], fp[2]);
+       eexecDumpNum(op[3], fp[3]);
+       eexecDumpNum(op[4], fp[4]);
+       eexecDumpNum(0, gFalse);
+       eexecDumpOp1(8);
+       eexecDumpNum(op[5], fp[5]);
+       eexecDumpNum(0, gFalse);
+       eexecDumpNum(op[6], fp[6]);
+       eexecDumpNum(op[7], fp[7]);
+       eexecDumpNum(op[8], fp[8]);
+       eexecDumpNum(-(op[1] + op[3] + op[7]), fp[1] | fp[3] | fp[7]);
+       eexecDumpOp1(8);
+       break;
+      case 37:                 // flex1
+       if (nOps != 11) {
+         error(-1, "Wrong number of args (%d) to Type 2 flex1", nOps);
+       }
+       eexecDumpNum(op[0], fp[0]);
+       eexecDumpNum(op[1], fp[1]);
+       eexecDumpNum(op[2], fp[2]);
+       eexecDumpNum(op[3], fp[3]);
+       eexecDumpNum(op[4], fp[4]);
+       eexecDumpNum(op[5], fp[5]);
+       eexecDumpOp1(8);
+       eexecDumpNum(op[6], fp[6]);
+       eexecDumpNum(op[7], fp[7]);
+       eexecDumpNum(op[8], fp[8]);
+       eexecDumpNum(op[9], fp[9]);
+       dx = op[0] + op[2] + op[4] + op[6] + op[8];
+       dy = op[1] + op[3] + op[5] + op[7] + op[9];
+       if (fabs(dx) > fabs(dy)) {
+         eexecDumpNum(op[10], fp[10]);
+         eexecDumpNum(-dy, fp[1] | fp[3] | fp[5] | fp[7] | fp[9]);
+       } else {
+         eexecDumpNum(-dx, fp[0] | fp[2] | fp[4] | fp[6] | fp[8]);
+         eexecDumpNum(op[10], fp[10]);
+       }
+       eexecDumpOp1(8);
+       break;
+      case 3:                  // and
+      case 4:                  // or
+      case 5:                  // not
+      case 8:                  // store
+      case 9:                  // abs
+      case 10:                 // add
+      case 11:                 // sub
+      case 12:                 // div
+      case 13:                 // load
+      case 14:                 // neg
+      case 15:                 // eq
+      case 18:                 // drop
+      case 20:                 // put
+      case 21:                 // get
+      case 22:                 // ifelse
+      case 23:                 // random
+      case 24:                 // mul
+      case 26:                 // sqrt
+      case 27:                 // dup
+      case 28:                 // exch
+      case 29:                 // index
+      case 30:                 // roll
+       error(-1, "Unimplemented Type 2 charstring op: 12.%d", s[i+1]);
+       break;
+      default:
+       error(-1, "Illegal Type 2 charstring op: 12.%d", s[i+1]);
+       break;
+      }
+      i += 2;
+      nOps = 0;
+    } else if (s[i] == 19) {   // hintmask
+      // ignored
+      if (first) {
+       cvtGlyphWidth(nOps == 1);
+       first = gFalse;
+      }
+      if (nOps > 0) {
+       if (nOps & 1) {
+         error(-1, "Wrong number of args (%d) to Type 2 hintmask/vstemhm",
+               nOps);
+       }
+       nHints += nOps / 2;
+      }
+      i += 1 + ((nHints + 7) >> 3);
+      nOps = 0;
+    } else if (s[i] == 20) {   // cntrmask
+      // ignored
+      if (first) {
+       cvtGlyphWidth(nOps == 1);
+       first = gFalse;
+      }
+      if (nOps > 0) {
+       if (nOps & 1) {
+         error(-1, "Wrong number of args (%d) to Type 2 cntrmask/vstemhm",
+               nOps);
+       }
+       nHints += nOps / 2;
+      }
+      i += 1 + ((nHints + 7) >> 3);
+      nOps = 0;
+    } else if (s[i] == 28) {
+      x = (s[i+1] << 8) + s[i+2];
+      if (x & 0x8000) {
+       x |= -1 << 15;
+      }
+      if (nOps < 48) {
+       fp[nOps] = gFalse;
+       op[nOps++] = x;
+      }
+      i += 3;
+    } else if (s[i] <= 31) {
+      switch (s[i]) {
+      case 4:                  // vmoveto
+       if (first) {
+         cvtGlyphWidth(nOps == 2);
+         first = gFalse;
+       }
+       if (nOps != 1) {
+         error(-1, "Wrong number of args (%d) to Type 2 vmoveto", nOps);
+       }
+       eexecDumpNum(op[0], fp[0]);
+       eexecDumpOp1(4);
+       break;
+      case 5:                  // rlineto
+       if (nOps < 2 || nOps % 2 != 0) {
+         error(-1, "Wrong number of args (%d) to Type 2 rlineto", nOps);
+       }
+       for (k = 0; k < nOps; k += 2) {
+         eexecDumpNum(op[k], fp[k]);
+         eexecDumpNum(op[k+1], fp[k+1]);
+         eexecDumpOp1(5);
+       }
+       break;
+      case 6:                  // hlineto
+       if (nOps < 1) {
+         error(-1, "Wrong number of args (%d) to Type 2 hlineto", nOps);
+       }
+       for (k = 0; k < nOps; ++k) {
+         eexecDumpNum(op[k], fp[k]);
+         eexecDumpOp1((k & 1) ? 7 : 6);
+       }
+       break;
+      case 7:                  // vlineto
+       if (nOps < 1) {
+         error(-1, "Wrong number of args (%d) to Type 2 vlineto", nOps);
+       }
+       for (k = 0; k < nOps; ++k) {
+         eexecDumpNum(op[k], fp[k]);
+         eexecDumpOp1((k & 1) ? 6 : 7);
+       }
+       break;
+      case 8:                  // rrcurveto
+       if (nOps < 6 || nOps % 6 != 0) {
+         error(-1, "Wrong number of args (%d) to Type 2 rrcurveto", nOps);
+       }
+       for (k = 0; k < nOps; k += 6) {
+         eexecDumpNum(op[k], fp[k]);
+         eexecDumpNum(op[k+1], fp[k+1]);
+         eexecDumpNum(op[k+2], fp[k+2]);
+         eexecDumpNum(op[k+3], fp[k+3]);
+         eexecDumpNum(op[k+4], fp[k+4]);
+         eexecDumpNum(op[k+5], fp[k+5]);
+         eexecDumpOp1(8);
+       }
+       break;
+      case 14:                 // endchar / seac
+       if (first) {
+         cvtGlyphWidth(nOps == 1 || nOps == 5);
+         first = gFalse;
+       }
+       if (nOps == 4) {
+         eexecDumpNum(0, 0);
+         eexecDumpNum(op[0], fp[0]);
+         eexecDumpNum(op[1], fp[1]);
+         eexecDumpNum(op[2], fp[2]);
+         eexecDumpNum(op[3], fp[3]);
+         eexecDumpOp2(6);
+       } else if (nOps == 0) {
+         eexecDumpOp1(14);
+       } else {
+         error(-1, "Wrong number of args (%d) to Type 2 endchar", nOps);
+       }
+       break;
+      case 21:                 // rmoveto
+       if (first) {
+         cvtGlyphWidth(nOps == 3);
+         first = gFalse;
+       }
+       if (nOps != 2) {
+         error(-1, "Wrong number of args (%d) to Type 2 rmoveto", nOps);
+       }
+       eexecDumpNum(op[0], fp[0]);
+       eexecDumpNum(op[1], fp[1]);
+       eexecDumpOp1(21);
+       break;
+      case 22:                 // hmoveto
+       if (first) {
+         cvtGlyphWidth(nOps == 2);
+         first = gFalse;
+       }
+       if (nOps != 1) {
+         error(-1, "Wrong number of args (%d) to Type 2 hmoveto", nOps);
+       }
+       eexecDumpNum(op[0], fp[0]);
+       eexecDumpOp1(22);
+       break;
+      case 24:                 // rcurveline
+       if (nOps < 8 || (nOps - 2) % 6 != 0) {
+         error(-1, "Wrong number of args (%d) to Type 2 rcurveline", nOps);
+       }
+       for (k = 0; k < nOps - 2; k += 6) {
+         eexecDumpNum(op[k], fp[k]);
+         eexecDumpNum(op[k+1], fp[k+1]);
+         eexecDumpNum(op[k+2], fp[k+2]);
+         eexecDumpNum(op[k+3], fp[k+3]);
+         eexecDumpNum(op[k+4], fp[k+4]);
+         eexecDumpNum(op[k+5], fp[k+5]);
+         eexecDumpOp1(8);
+       }
+       eexecDumpNum(op[k], fp[k]);
+       eexecDumpNum(op[k+1], fp[k]);
+       eexecDumpOp1(5);
+       break;
+      case 25:                 // rlinecurve
+       if (nOps < 8 || (nOps - 6) % 2 != 0) {
+         error(-1, "Wrong number of args (%d) to Type 2 rlinecurve", nOps);
+       }
+       for (k = 0; k < nOps - 6; k += 2) {
+         eexecDumpNum(op[k], fp[k]);
+         eexecDumpNum(op[k+1], fp[k]);
+         eexecDumpOp1(5);
+       }
+       eexecDumpNum(op[k], fp[k]);
+       eexecDumpNum(op[k+1], fp[k+1]);
+       eexecDumpNum(op[k+2], fp[k+2]);
+       eexecDumpNum(op[k+3], fp[k+3]);
+       eexecDumpNum(op[k+4], fp[k+4]);
+       eexecDumpNum(op[k+5], fp[k+5]);
+       eexecDumpOp1(8);
+       break;
+      case 26:                 // vvcurveto
+       if (nOps < 4 || !(nOps % 4 == 0 || (nOps-1) % 4 == 0)) {
+         error(-1, "Wrong number of args (%d) to Type 2 vvcurveto", nOps);
+       }
+       if (nOps % 2 == 1) {
+         eexecDumpNum(op[0], fp[0]);
+         eexecDumpNum(op[1], fp[1]);
+         eexecDumpNum(op[2], fp[2]);
+         eexecDumpNum(op[3], fp[3]);
+         eexecDumpNum(0, gFalse);
+         eexecDumpNum(op[4], fp[4]);
+         eexecDumpOp1(8);
+         k = 5;
+       } else {
+         k = 0;
+       }
+       for (; k < nOps; k += 4) {
+         eexecDumpNum(0, gFalse);
+         eexecDumpNum(op[k], fp[k]);
+         eexecDumpNum(op[k+1], fp[k+1]);
+         eexecDumpNum(op[k+2], fp[k+2]);
+         eexecDumpNum(0, gFalse);
+         eexecDumpNum(op[k+3], fp[k+3]);
+         eexecDumpOp1(8);
+       }
+       break;
+      case 27:                 // hhcurveto
+       if (nOps < 4 || !(nOps % 4 == 0 || (nOps-1) % 4 == 0)) {
+         error(-1, "Wrong number of args (%d) to Type 2 hhcurveto", nOps);
+       }
+       if (nOps % 2 == 1) {
+         eexecDumpNum(op[1], fp[1]);
+         eexecDumpNum(op[0], fp[0]);
+         eexecDumpNum(op[2], fp[2]);
+         eexecDumpNum(op[3], fp[3]);
+         eexecDumpNum(op[4], fp[4]);
+         eexecDumpNum(0, gFalse);
+         eexecDumpOp1(8);
+         k = 5;
+       } else {
+         k = 0;
+       }
+       for (; k < nOps; k += 4) {
+         eexecDumpNum(op[k], fp[k]);
+         eexecDumpNum(0, gFalse);
+         eexecDumpNum(op[k+1], fp[k+1]);
+         eexecDumpNum(op[k+2], fp[k+2]);
+         eexecDumpNum(op[k+3], fp[k+3]);
+         eexecDumpNum(0, gFalse);
+         eexecDumpOp1(8);
+       }
+       break;
+      case 30:                 // vhcurveto
+       if (nOps < 4 || !(nOps % 4 == 0 || (nOps-1) % 4 == 0)) {
+         error(-1, "Wrong number of args (%d) to Type 2 vhcurveto", nOps);
+       }
+       for (k = 0; k < nOps && k != nOps-5; k += 4) {
+         if (k % 8 == 0) {
+           eexecDumpNum(op[k], fp[k]);
+           eexecDumpNum(op[k+1], fp[k+1]);
+           eexecDumpNum(op[k+2], fp[k+2]);
+           eexecDumpNum(op[k+3], fp[k+3]);
+           eexecDumpOp1(30);
+         } else {
+           eexecDumpNum(op[k], fp[k]);
+           eexecDumpNum(op[k+1], fp[k+1]);
+           eexecDumpNum(op[k+2], fp[k+2]);
+           eexecDumpNum(op[k+3], fp[k+3]);
+           eexecDumpOp1(31);
+         }
+       }
+       if (k == nOps-5) {
+         if (k % 8 == 0) {
+           eexecDumpNum(0, gFalse);
+           eexecDumpNum(op[k], fp[k]);
+           eexecDumpNum(op[k+1], fp[k+1]);
+           eexecDumpNum(op[k+2], fp[k+2]);
+           eexecDumpNum(op[k+3], fp[k+3]);
+           eexecDumpNum(op[k+4], fp[k+4]);
+         } else {
+           eexecDumpNum(op[k], fp[k]);
+           eexecDumpNum(0, gFalse);
+           eexecDumpNum(op[k+1], fp[k+1]);
+           eexecDumpNum(op[k+2], fp[k+2]);
+           eexecDumpNum(op[k+4], fp[k+4]);
+           eexecDumpNum(op[k+3], fp[k+3]);
+         }
+         eexecDumpOp1(8);
+       }
+       break;
+      case 31:                 // hvcurveto
+       if (nOps < 4 || !(nOps % 4 == 0 || (nOps-1) % 4 == 0)) {
+         error(-1, "Wrong number of args (%d) to Type 2 hvcurveto", nOps);
+       }
+       for (k = 0; k < nOps && k != nOps-5; k += 4) {
+         if (k % 8 == 0) {
+           eexecDumpNum(op[k], fp[k]);
+           eexecDumpNum(op[k+1], fp[k+1]);
+           eexecDumpNum(op[k+2], fp[k+2]);
+           eexecDumpNum(op[k+3], fp[k+3]);
+           eexecDumpOp1(31);
+         } else {
+           eexecDumpNum(op[k], fp[k]);
+           eexecDumpNum(op[k+1], fp[k+1]);
+           eexecDumpNum(op[k+2], fp[k+2]);
            eexecDumpNum(op[k+3], fp[k+3]);
            eexecDumpOp1(30);
+         }
+       }
+       if (k == nOps-5) {
+         if (k % 8 == 0) {
+           eexecDumpNum(op[k], fp[k]);
+           eexecDumpNum(0, gFalse);
+           eexecDumpNum(op[k+1], fp[k+1]);
+           eexecDumpNum(op[k+2], fp[k+2]);
+           eexecDumpNum(op[k+4], fp[k+4]);
+           eexecDumpNum(op[k+3], fp[k+3]);
+         } else {
+           eexecDumpNum(0, gFalse);
+           eexecDumpNum(op[k], fp[k]);
+           eexecDumpNum(op[k+1], fp[k+1]);
+           eexecDumpNum(op[k+2], fp[k+2]);
+           eexecDumpNum(op[k+3], fp[k+3]);
+           eexecDumpNum(op[k+4], fp[k+4]);
+         }
+         eexecDumpOp1(8);
+       }
+       break;
+      case 1:                  // hstem
+       if (first) {
+         cvtGlyphWidth(nOps & 1);
+         first = gFalse;
+       }
+       if (nOps & 1) {
+         error(-1, "Wrong number of args (%d) to Type 2 hstem", nOps);
+       }
+       d = 0;
+       dFP = gFalse;
+       for (k = 0; k < nOps; k += 2) {
+         if (op[k+1] < 0) {
+           d += op[k] + op[k+1];
+           dFP |= fp[k] | fp[k+1];
+           eexecDumpNum(d, dFP);
+           eexecDumpNum(-op[k+1], fp[k+1]);
          } else {
-           eexecDumpNum(op[k], fp[k]);
+           d += op[k];
+           dFP |= fp[k];
+           eexecDumpNum(d, dFP);
            eexecDumpNum(op[k+1], fp[k+1]);
-           eexecDumpNum(op[k+2], fp[k+2]);
-           eexecDumpNum(op[k+3], fp[k+3]);
-           eexecDumpOp1(31);
+           d += op[k+1];
+           dFP |= fp[k+1];
          }
+         eexecDumpOp1(1);
        }
-       if (k == nOps-5) {
-         if (k % 8 == 0) {
-           eexecDumpNum(0, gFalse);
-           eexecDumpNum(op[k], fp[k]);
-           eexecDumpNum(op[k+1], fp[k+1]);
-           eexecDumpNum(op[k+2], fp[k+2]);
-           eexecDumpNum(op[k+3], fp[k+3]);
-           eexecDumpNum(op[k+4], fp[k+4]);
+       nHints += nOps / 2;
+       break;
+      case 3:                  // vstem
+       if (first) {
+         cvtGlyphWidth(nOps & 1);
+         first = gFalse;
+       }
+       if (nOps & 1) {
+         error(-1, "Wrong number of args (%d) to Type 2 vstem", nOps);
+       }
+       d = 0;
+       dFP = gFalse;
+       for (k = 0; k < nOps; k += 2) {
+         if (op[k+1] < 0) {
+           d += op[k] + op[k+1];
+           dFP |= fp[k] | fp[k+1];
+           eexecDumpNum(d, dFP);
+           eexecDumpNum(-op[k+1], fp[k+1]);
          } else {
-           eexecDumpNum(op[k], fp[k]);
-           eexecDumpNum(0, gFalse);
+           d += op[k];
+           dFP |= fp[k];
+           eexecDumpNum(d, dFP);
            eexecDumpNum(op[k+1], fp[k+1]);
-           eexecDumpNum(op[k+2], fp[k+2]);
-           eexecDumpNum(op[k+4], fp[k+4]);
-           eexecDumpNum(op[k+3], fp[k+3]);
+           d += op[k+1];
+           dFP |= fp[k+1];
          }
-         eexecDumpOp1(8);
+         eexecDumpOp1(3);
+       }
+       nHints += nOps / 2;
+       break;
+      case 18:                 // hstemhm
+       // ignored
+       if (first) {
+         cvtGlyphWidth(nOps & 1);
+         first = gFalse;
+       }
+       if (nOps & 1) {
+         error(-1, "Wrong number of args (%d) to Type 2 hstemhm", nOps);
+       }
+       nHints += nOps / 2;
+       break;
+      case 23:                 // vstemhm
+       // ignored
+       if (first) {
+         cvtGlyphWidth(nOps & 1);
+         first = gFalse;
+       }
+       if (nOps & 1) {
+         error(-1, "Wrong number of args (%d) to Type 2 vstemhm", nOps);
        }
+       nHints += nOps / 2;
+       break;
+      case 10:                 // callsubr
+      case 11:                 // return
+      case 16:                 // blend
+      case 29:                 // callgsubr
+       error(-1, "Unimplemented Type 2 charstring op: %d", s[i]);
+       break;
+      default:
+       error(-1, "Illegal Type 2 charstring op: %d", s[i]);
+       break;
+      }
+      ++i;
+      nOps = 0;
+    } else if (s[i] <= 246) {
+      if (nOps < 48) {
+       fp[nOps] = gFalse;
+       op[nOps++] = (int)s[i] - 139;
+      }
+      ++i;
+    } else if (s[i] <= 250) {
+      if (nOps < 48) {
+       fp[nOps] = gFalse;
+       op[nOps++] = (((int)s[i] - 247) << 8) + (int)s[i+1] + 108;
+      }
+      i += 2;
+    } else if (s[i] <= 254) {
+      if (nOps < 48) {
+       fp[nOps] = gFalse;
+       op[nOps++] = -(((int)s[i] - 251) << 8) - (int)s[i+1] - 108;
+      }
+      i += 2;
+    } else {
+      x = (s[i+1] << 24) | (s[i+2] << 16) | (s[i+3] << 8) | s[i+4];
+      if (x & 0x80000000)
+       x |= -1 << 31;
+      if (nOps < 48) {
+       fp[nOps] = gTrue;
+       op[nOps++] = (double)x / 65536.0;
+      }
+      i += 5;
+    }
+  }
+
+  // charstring encryption
+  r2 = 4330;
+  for (i = 0; i < charBuf->getLength(); ++i) {
+    byte = charBuf->getChar(i) ^ (r2 >> 8);
+    charBuf->setChar(i, byte);
+    r2 = (byte + r2) * 52845 + 22719;
+  }
+}
+
+void Type1CFontFile::cvtGlyphWidth(GBool useOp) {
+  double w;
+  GBool wFP;
+  int i;
+
+  if (useOp) {
+    w = nominalWidthX + op[0];
+    wFP = nominalWidthXFP | fp[0];
+    for (i = 1; i < nOps; ++i) {
+      op[i-1] = op[i];
+      fp[i-1] = fp[i];
+    }
+    --nOps;
+  } else {
+    w = defaultWidthX;
+    wFP = defaultWidthXFP;
+  }
+  eexecDumpNum(0, gFalse);
+  eexecDumpNum(w, wFP);
+  eexecDumpOp1(13);
+}
+
+void Type1CFontFile::eexecDumpNum(double x, GBool fpA) {
+  Guchar buf[12];
+  int y, n;
+
+  n = 0;
+  if (fpA) {
+    if (x >= -32768 && x < 32768) {
+      y = (int)(x * 256.0);
+      buf[0] = 255;
+      buf[1] = (Guchar)(y >> 24);
+      buf[2] = (Guchar)(y >> 16);
+      buf[3] = (Guchar)(y >> 8);
+      buf[4] = (Guchar)y;
+      buf[5] = 255;
+      buf[6] = 0;
+      buf[7] = 0;
+      buf[8] = 1;
+      buf[9] = 0;
+      buf[10] = 12;
+      buf[11] = 12;
+      n = 12;
+    } else {
+      error(-1, "Type 2 fixed point constant out of range");
+    }
+  } else {
+    y = (int)x;
+    if (y >= -107 && y <= 107) {
+      buf[0] = (Guchar)(y + 139);
+      n = 1;
+    } else if (y > 107 && y <= 1131) {
+      y -= 108;
+      buf[0] = (Guchar)((y >> 8) + 247);
+      buf[1] = (Guchar)(y & 0xff);
+      n = 2;
+    } else if (y < -107 && y >= -1131) {
+      y = -y - 108;
+      buf[0] = (Guchar)((y >> 8) + 251);
+      buf[1] = (Guchar)(y & 0xff);
+      n = 2;
+    } else {
+      buf[0] = 255;
+      buf[1] = (Guchar)(y >> 24);
+      buf[2] = (Guchar)(y >> 16);
+      buf[3] = (Guchar)(y >> 8);
+      buf[4] = (Guchar)y;
+      n = 5;
+    }
+  }
+  charBuf->append((char *)buf, n);
+}
+
+void Type1CFontFile::eexecDumpOp1(int opA) {
+  charBuf->append((char)opA);
+}
+
+void Type1CFontFile::eexecDumpOp2(int opA) {
+  charBuf->append((char)12);
+  charBuf->append((char)opA);
+}
+
+void Type1CFontFile::eexecWriteCharstring(Guchar *s, int n) {
+  Guchar x;
+  int i;
+
+  // eexec encryption
+  for (i = 0; i < n; ++i) {
+    x = s[i] ^ (r1 >> 8);
+    r1 = (x + r1) * 52845 + 22719;
+    fputc(hexChars[x >> 4], out);
+    fputc(hexChars[x & 0x0f], out);
+    line += 2;
+    if (line == 64) {
+      fputc('\n', out);
+      line = 0;
+    }
+  }
+}
+
+void Type1CFontFile::getDeltaInt(char *buf, char *key, double *opA,
+                                int n) {
+  int x, i;
+
+  sprintf(buf, "/%s [", key);
+  buf += strlen(buf);
+  x = 0;
+  for (i = 0; i < n; ++i) {
+    x += (int)opA[i];
+    sprintf(buf, "%s%d", i > 0 ? " " : "", x);
+    buf += strlen(buf);
+  }
+  sprintf(buf, "] def\n");
+}
+
+void Type1CFontFile::getDeltaReal(char *buf, char *key, double *opA,
+                                 int n) {
+  double x;
+  int i;
+
+  sprintf(buf, "/%s [", key);
+  buf += strlen(buf);
+  x = 0;
+  for (i = 0; i < n; ++i) {
+    x += opA[i];
+    sprintf(buf, "%s%g", i > 0 ? " " : "", x);
+    buf += strlen(buf);
+  }
+  sprintf(buf, "] def\n");
+}
+
+int Type1CFontFile::getIndexLen(Guchar *indexPtr) {
+  return (int)getWord(indexPtr, 2);
+}
+
+Guchar *Type1CFontFile::getIndexValPtr(Guchar *indexPtr, int i) {
+  int n, offSize;
+  Guchar *idxStartPtr;
+
+  n = (int)getWord(indexPtr, 2);
+  offSize = indexPtr[2];
+  idxStartPtr = indexPtr + 3 + (n + 1) * offSize - 1;
+  return idxStartPtr + getWord(indexPtr + 3 + i * offSize, offSize);
+}
+
+Guchar *Type1CFontFile::getIndexEnd(Guchar *indexPtr) {
+  int n, offSize;
+  Guchar *idxStartPtr;
+
+  n = (int)getWord(indexPtr, 2);
+  offSize = indexPtr[2];
+  idxStartPtr = indexPtr + 3 + (n + 1) * offSize - 1;
+  return idxStartPtr + getWord(indexPtr + 3 + n * offSize, offSize);
+}
+
+Guint Type1CFontFile::getWord(Guchar *ptr, int size) {
+  Guint x;
+  int i;
+
+  x = 0;
+  for (i = 0; i < size; ++i) {
+    x = (x << 8) + *ptr++;
+  }
+  return x;
+}
+
+double Type1CFontFile::getNum(Guchar **ptr, GBool *isFP) {
+  static char nybChars[16] = "0123456789.ee -";
+  int b0, b, nyb0, nyb1;
+  double x;
+  char buf[65];
+  int i;
+
+  x = 0;
+  *isFP = gFalse;
+  b0 = (*ptr)[0];
+  if (b0 < 28) {
+    x = 0;
+  } else if (b0 == 28) {
+    x = ((*ptr)[1] << 8) + (*ptr)[2];
+    *ptr += 3;
+  } else if (b0 == 29) {
+    x = ((*ptr)[1] << 24) + ((*ptr)[2] << 16) + ((*ptr)[3] << 8) + (*ptr)[4];
+    *ptr += 5;
+  } else if (b0 == 30) {
+    *ptr += 1;
+    i = 0;
+    do {
+      b = *(*ptr)++;
+      nyb0 = b >> 4;
+      nyb1 = b & 0x0f;
+      if (nyb0 == 0xf) {
        break;
-      case 31:                 // hvcurveto
-       if (nOps < 4 || !(nOps % 4 == 0 || (nOps-1) % 4 == 0))
-         error(-1, "Wrong number of args (%d) to Type 2 hvcurveto", nOps);
-       for (k = 0; k < nOps && k != nOps-5; k += 4) {
-         if (k % 8 == 0) {
-           eexecDumpNum(op[k], fp[k]);
-           eexecDumpNum(op[k+1], fp[k+1]);
-           eexecDumpNum(op[k+2], fp[k+2]);
-           eexecDumpNum(op[k+3], fp[k+3]);
-           eexecDumpOp1(31);
-         } else {
-           eexecDumpNum(op[k], fp[k]);
-           eexecDumpNum(op[k+1], fp[k+1]);
-           eexecDumpNum(op[k+2], fp[k+2]);
-           eexecDumpNum(op[k+3], fp[k+3]);
-           eexecDumpOp1(30);
-         }
-       }
-       if (k == nOps-5) {
-         if (k % 8 == 0) {
-           eexecDumpNum(op[k], fp[k]);
-           eexecDumpNum(0, gFalse);
-           eexecDumpNum(op[k+1], fp[k+1]);
-           eexecDumpNum(op[k+2], fp[k+2]);
-           eexecDumpNum(op[k+4], fp[k+4]);
-           eexecDumpNum(op[k+3], fp[k+3]);
-         } else {
-           eexecDumpNum(0, gFalse);
-           eexecDumpNum(op[k], fp[k]);
-           eexecDumpNum(op[k+1], fp[k+1]);
-           eexecDumpNum(op[k+2], fp[k+2]);
-           eexecDumpNum(op[k+3], fp[k+3]);
-           eexecDumpNum(op[k+4], fp[k+4]);
-         }
-         eexecDumpOp1(8);
-       }
+      }
+      buf[i++] = nybChars[nyb0];
+      if (i == 64) {
        break;
-      case 1:                  // hstem
-       if (first) {
-         cvtGlyphWidth(nOps & 1);
-         first = gFalse;
-       }
-       if (nOps & 1) {
-         error(-1, "Wrong number of args (%d) to Type 2 hstem", nOps);
+      }
+      if (nyb0 == 0xc) {
+       buf[i++] = '-';
+      }
+      if (i == 64) {
+       break;
+      }
+      if (nyb1 == 0xf) {
+       break;
+      }
+      buf[i++] = nybChars[nyb1];
+      if (i == 64) {
+       break;
+      }
+      if (nyb1 == 0xc) {
+       buf[i++] = '-';
+      }
+    } while (i < 64);
+    buf[i] = '\0';
+    x = atof(buf);
+    *isFP = gTrue;
+  } else if (b0 == 31) {
+    x = 0;
+  } else if (b0 < 247) {
+    x = b0 - 139;
+    *ptr += 1;
+  } else if (b0 < 251) {
+    x = ((b0 - 247) << 8) + (*ptr)[1] + 108;
+    *ptr += 2;
+  } else {
+    x = -((b0 - 251) << 8) - (*ptr)[1] - 108;
+    *ptr += 2;
+  }
+  return x;
+}
+
+char *Type1CFontFile::getString(int sid, char *buf) {
+  Guchar *idxPtr0, *idxPtr1;
+  int n;
+
+  if (sid < 391) {
+    strcpy(buf, type1CStdStrings[sid]);
+  } else {
+    sid -= 391;
+    idxPtr0 = getIndexValPtr(stringIdxPtr, sid);
+    idxPtr1 = getIndexValPtr(stringIdxPtr, sid + 1);
+    if ((n = idxPtr1 - idxPtr0) > 255) {
+      n = 255;
+    }
+    strncpy(buf, (char *)idxPtr0, n);
+    buf[n] = '\0';
+  }
+  return buf;
+}
+
+//------------------------------------------------------------------------
+// TrueTypeFontFile
+//------------------------------------------------------------------------
+
+//
+// Terminology
+// -----------
+//
+// character code = number used as an element of a text string
+//
+// character name = glyph name = name for a particular glyph within a
+//                  font
+//
+// glyph index = position (within some internal table in the font)
+//               where the instructions to draw a particular glyph are
+//               stored
+//
+// Type 1 fonts
+// ------------
+//
+// Type 1 fonts contain:
+//
+// Encoding: array of glyph names, maps char codes to glyph names
+//
+//           Encoding[charCode] = charName
+//
+// CharStrings: dictionary of instructions, keyed by character names,
+//              maps character name to glyph data
+//
+//              CharStrings[charName] = glyphData
+//
+// TrueType fonts
+// --------------
+//
+// TrueType fonts contain:
+//
+// 'cmap' table: mapping from character code to glyph index; there may
+//               be multiple cmaps in a TrueType font
+//
+//               cmap[charCode] = glyphIdx
+//
+// 'post' table: mapping from glyph index to glyph name
+//
+//               post[glyphIdx] = glyphName
+//
+// Type 42 fonts
+// -------------
+//
+// Type 42 fonts contain:
+//
+// Encoding: array of glyph names, maps char codes to glyph names
+//
+//           Encoding[charCode] = charName
+//
+// CharStrings: dictionary of glyph indexes, keyed by character names,
+//              maps character name to glyph index
+//
+//              CharStrings[charName] = glyphIdx
+//
+
+struct TTFontTableHdr {
+  char tag[4];
+  Guint checksum;
+  Guint offset;
+  Guint length;
+};
+
+struct T42Table {
+  char *tag;                   // 4-byte tag
+  GBool required;              // required by the TrueType spec?
+};
+
+// TrueType tables to be embedded in Type 42 fonts.
+// NB: the table names must be in alphabetical order here.
+#define nT42Tables 11
+static T42Table t42Tables[nT42Tables] = {
+  { "cvt ", gTrue  },
+  { "fpgm", gTrue  },
+  { "glyf", gTrue  },
+  { "head", gTrue  },
+  { "hhea", gTrue  },
+  { "hmtx", gTrue  },
+  { "loca", gTrue  },
+  { "maxp", gTrue  },
+  { "prep", gTrue  },
+  { "vhea", gFalse },
+  { "vmtx", gFalse }
+};
+#define t42HeadTable 3
+#define t42LocaTable 6
+#define t42GlyfTable 2
+
+// Glyph names in some arbitrary standard that Apple uses for their
+// TrueType fonts.
+static char *macGlyphNames[258] = {
+  ".notdef",
+  "null",
+  "CR",
+  "space",
+  "exclam",
+  "quotedbl",
+  "numbersign",
+  "dollar",
+  "percent",
+  "ampersand",
+  "quotesingle",
+  "parenleft",
+  "parenright",
+  "asterisk",
+  "plus",
+  "comma",
+  "hyphen",
+  "period",
+  "slash",
+  "zero",
+  "one",
+  "two",
+  "three",
+  "four",
+  "five",
+  "six",
+  "seven",
+  "eight",
+  "nine",
+  "colon",
+  "semicolon",
+  "less",
+  "equal",
+  "greater",
+  "question",
+  "at",
+  "A",
+  "B",
+  "C",
+  "D",
+  "E",
+  "F",
+  "G",
+  "H",
+  "I",
+  "J",
+  "K",
+  "L",
+  "M",
+  "N",
+  "O",
+  "P",
+  "Q",
+  "R",
+  "S",
+  "T",
+  "U",
+  "V",
+  "W",
+  "X",
+  "Y",
+  "Z",
+  "bracketleft",
+  "backslash",
+  "bracketright",
+  "asciicircum",
+  "underscore",
+  "grave",
+  "a",
+  "b",
+  "c",
+  "d",
+  "e",
+  "f",
+  "g",
+  "h",
+  "i",
+  "j",
+  "k",
+  "l",
+  "m",
+  "n",
+  "o",
+  "p",
+  "q",
+  "r",
+  "s",
+  "t",
+  "u",
+  "v",
+  "w",
+  "x",
+  "y",
+  "z",
+  "braceleft",
+  "bar",
+  "braceright",
+  "asciitilde",
+  "Adieresis",
+  "Aring",
+  "Ccedilla",
+  "Eacute",
+  "Ntilde",
+  "Odieresis",
+  "Udieresis",
+  "aacute",
+  "agrave",
+  "acircumflex",
+  "adieresis",
+  "atilde",
+  "aring",
+  "ccedilla",
+  "eacute",
+  "egrave",
+  "ecircumflex",
+  "edieresis",
+  "iacute",
+  "igrave",
+  "icircumflex",
+  "idieresis",
+  "ntilde",
+  "oacute",
+  "ograve",
+  "ocircumflex",
+  "odieresis",
+  "otilde",
+  "uacute",
+  "ugrave",
+  "ucircumflex",
+  "udieresis",
+  "dagger",
+  "degree",
+  "cent",
+  "sterling",
+  "section",
+  "bullet",
+  "paragraph",
+  "germandbls",
+  "registered",
+  "copyright",
+  "trademark",
+  "acute",
+  "dieresis",
+  "notequal",
+  "AE",
+  "Oslash",
+  "infinity",
+  "plusminus",
+  "lessequal",
+  "greaterequal",
+  "yen",
+  "mu1",
+  "partialdiff",
+  "summation",
+  "product",
+  "pi",
+  "integral",
+  "ordfeminine",
+  "ordmasculine",
+  "Ohm",
+  "ae",
+  "oslash",
+  "questiondown",
+  "exclamdown",
+  "logicalnot",
+  "radical",
+  "florin",
+  "approxequal",
+  "increment",
+  "guillemotleft",
+  "guillemotright",
+  "ellipsis",
+  "nbspace",
+  "Agrave",
+  "Atilde",
+  "Otilde",
+  "OE",
+  "oe",
+  "endash",
+  "emdash",
+  "quotedblleft",
+  "quotedblright",
+  "quoteleft",
+  "quoteright",
+  "divide",
+  "lozenge",
+  "ydieresis",
+  "Ydieresis",
+  "fraction",
+  "currency",
+  "guilsinglleft",
+  "guilsinglright",
+  "fi",
+  "fl",
+  "daggerdbl",
+  "periodcentered",
+  "quotesinglbase",
+  "quotedblbase",
+  "perthousand",
+  "Acircumflex",
+  "Ecircumflex",
+  "Aacute",
+  "Edieresis",
+  "Egrave",
+  "Iacute",
+  "Icircumflex",
+  "Idieresis",
+  "Igrave",
+  "Oacute",
+  "Ocircumflex",
+  "applelogo",
+  "Ograve",
+  "Uacute",
+  "Ucircumflex",
+  "Ugrave",
+  "dotlessi",
+  "circumflex",
+  "tilde",
+  "overscore",
+  "breve",
+  "dotaccent",
+  "ring",
+  "cedilla",
+  "hungarumlaut",
+  "ogonek",
+  "caron",
+  "Lslash",
+  "lslash",
+  "Scaron",
+  "scaron",
+  "Zcaron",
+  "zcaron",
+  "brokenbar",
+  "Eth",
+  "eth",
+  "Yacute",
+  "yacute",
+  "Thorn",
+  "thorn",
+  "minus",
+  "multiply",
+  "onesuperior",
+  "twosuperior",
+  "threesuperior",
+  "onehalf",
+  "onequarter",
+  "threequarters",
+  "franc",
+  "Gbreve",
+  "gbreve",
+  "Idot",
+  "Scedilla",
+  "scedilla",
+  "Cacute",
+  "cacute",
+  "Ccaron",
+  "ccaron",
+  "dmacron"
+};
+
+enum T42FontIndexMode {
+  t42FontModeUnicode,
+  t42FontModeCharCode,
+  t42FontModeCharCodeOffset,
+  t42FontModeMacRoman
+};
+
+TrueTypeFontFile::TrueTypeFontFile(char *fileA, int lenA) {
+  int pos, i;
+
+  file = fileA;
+  len = lenA;
+
+  encoding = NULL;
+
+  // read table directory
+  nTables = getUShort(4);
+  tableHdrs = (TTFontTableHdr *)gmalloc(nTables * sizeof(TTFontTableHdr));
+  pos = 12;
+  for (i = 0; i < nTables; ++i) {
+    tableHdrs[i].tag[0] = getByte(pos+0);
+    tableHdrs[i].tag[1] = getByte(pos+1);
+    tableHdrs[i].tag[2] = getByte(pos+2);
+    tableHdrs[i].tag[3] = getByte(pos+3);
+    tableHdrs[i].checksum = getULong(pos+4);
+    tableHdrs[i].offset = getULong(pos+8);
+    tableHdrs[i].length = getULong(pos+12);
+    pos += 16;
+  }
+
+  // check for tables that are required by both the TrueType spec
+  // and the Type 42 spec
+  if (seekTable("head") < 0 ||
+      seekTable("hhea") < 0 ||
+      seekTable("loca") < 0 ||
+      seekTable("maxp") < 0 ||
+      seekTable("glyf") < 0 ||
+      seekTable("hmtx") < 0) {
+    error(-1, "TrueType font file is missing a required table");
+    return;
+  }
+
+  // read the 'head' table
+  pos = seekTable("head");
+  bbox[0] = getShort(pos + 36);
+  bbox[1] = getShort(pos + 38);
+  bbox[2] = getShort(pos + 40);
+  bbox[3] = getShort(pos + 42);
+  locaFmt = getShort(pos + 50);
+
+  // read the 'maxp' table
+  pos = seekTable("maxp");
+  nGlyphs = getUShort(pos + 4);
+}
+
+TrueTypeFontFile::~TrueTypeFontFile() {
+  int i;
+
+  if (encoding) {
+    for (i = 0; i < 256; ++i) {
+      gfree(encoding[i]);
+    }
+    gfree(encoding);
+  }
+  gfree(tableHdrs);
+}
+
+char *TrueTypeFontFile::getName() {
+  return NULL;
+}
+
+char **TrueTypeFontFile::getEncoding() {
+  int cmap[256];
+  int nCmaps, cmapPlatform, cmapEncoding, cmapFmt;
+  int cmapLen, cmapOffset, cmapFirst;
+  int segCnt, segStart, segEnd, segDelta, segOffset;
+  int pos, i, j, k;
+  Guint fmt;
+  GString *s;
+  int stringIdx, stringPos, n;
+
+  if (encoding) {
+    return encoding;
+  }
+
+  //----- construct the (char code) -> (glyph idx) mapping
+
+  // map everything to the missing glyph
+  for (i = 0; i < 256; ++i) {
+    cmap[i] = 0;
+  }
+
+  // look for the 'cmap' table
+  if ((pos = seekTable("cmap")) >= 0) {
+    nCmaps = getUShort(pos+2);
+
+    // if the font has a Windows-symbol cmap, use it;
+    // otherwise, use the first cmap in the table
+    for (i = 0; i < nCmaps; ++i) {
+      cmapPlatform = getUShort(pos + 4 + 8*i);
+      cmapEncoding = getUShort(pos + 4 + 8*i + 2);
+      if (cmapPlatform == 3 && cmapEncoding == 0) {
+       break;
+      }
+    }
+    if (i >= nCmaps) {
+      i = 0;
+      cmapPlatform = getUShort(pos + 4);
+      cmapEncoding = getUShort(pos + 4 + 2);
+    }
+    pos += getULong(pos + 4 + 8*i + 4);
+
+    // read the cmap
+    cmapFmt = getUShort(pos);
+    switch (cmapFmt) {
+    case 0: // byte encoding table (Apple standard)
+      cmapLen = getUShort(pos + 2);
+      for (i = 0; i < cmapLen && i < 256; ++i) {
+       cmap[i] = getByte(pos + 6 + i);
+      }
+      break;
+    case 4: // segment mapping to delta values (Microsoft standard)
+      if (cmapPlatform == 3 && cmapEncoding == 0) {
+       // Windows-symbol uses char codes 0xf000 - 0xf0ff
+       cmapOffset = 0xf000;
+      } else {
+       cmapOffset = 0;
+      }
+      segCnt = getUShort(pos + 6) / 2;
+      for (i = 0; i < segCnt; ++i) {
+       segEnd = getUShort(pos + 14 + 2*i);
+       segStart = getUShort(pos + 16 + 2*segCnt + 2*i);
+       segDelta = getUShort(pos + 16 + 4*segCnt + 2*i);
+       segOffset = getUShort(pos + 16 + 6*segCnt + 2*i);
+       if (segStart - cmapOffset <= 0xff &&
+           segEnd - cmapOffset >= 0) {
+         for (j = (segStart - cmapOffset >= 0) ? segStart : cmapOffset;
+              j <= segEnd && j - cmapOffset <= 0xff;
+              ++j) {
+           if (segOffset == 0) {
+             k = (j + segDelta) & 0xffff;
+           } else {
+             k = getUShort(pos + 16 + 6*segCnt + 2*i +
+                           segOffset + 2 * (j - segStart));
+             if (k != 0) {
+               k = (k + segDelta) & 0xffff;
+             }
+           }
+           cmap[j - cmapOffset] = k;
+         }
        }
-       d = 0;
-       dFP = gFalse;
-       for (k = 0; k < nOps; k += 2) {
-         if (op[k+1] < 0) {
-           d += op[k] + op[k+1];
-           dFP |= fp[k] | fp[k+1];
-           eexecDumpNum(d, dFP);
-           eexecDumpNum(-op[k+1], fp[k+1]);
+      }
+      break;
+    case 6: // trimmed table mapping
+      cmapFirst = getUShort(pos + 6);
+      cmapLen = getUShort(pos + 8);
+      for (i = cmapFirst; i < 256 && i < cmapFirst + cmapLen; ++i) {
+       cmap[i] = getUShort(pos + 10 + 2*i);
+      }
+      break;
+    default:
+      error(-1, "Unimplemented cmap format (%d) in TrueType font file",
+           cmapFmt);
+      break;
+    }
+  }
+
+  //----- construct the (glyph idx) -> (glyph name) mapping
+  //----- and compute the (char code) -> (glyph name) mapping
+
+  encoding = (char **)gmalloc(256 * sizeof(char *));
+  for (i = 0; i < 256; ++i) {
+    encoding[i] = NULL;
+  }
+
+  if ((pos = seekTable("post")) >= 0) {
+    fmt = getULong(pos);
+
+    // Apple font
+    if (fmt == 0x00010000) {
+      for (i = 0; i < 256; ++i) {
+       j = (cmap[i] < 258) ? cmap[i] : 0;
+       encoding[i] = copyString(macGlyphNames[j]);
+      }
+
+    // Microsoft font
+    } else if (fmt == 0x00020000) {
+      stringIdx = 0;
+      stringPos = pos + 34 + 2*nGlyphs;
+      for (i = 0; i < 256; ++i) {
+       if (cmap[i] < nGlyphs) {
+         j = getUShort(pos + 34 + 2 * cmap[i]);
+         if (j < 258) {
+           encoding[i] = copyString(macGlyphNames[j]);
          } else {
-           d += op[k];
-           dFP |= fp[k];
-           eexecDumpNum(d, dFP);
-           eexecDumpNum(op[k+1], fp[k+1]);
-           d += op[k+1];
-           dFP |= fp[k+1];
+           j -= 258;
+           if (j != stringIdx) {
+             for (stringIdx = 0, stringPos = pos + 34 + 2*nGlyphs;
+                  stringIdx < j;
+                  ++stringIdx, stringPos += 1 + getByte(stringPos)) ;
+           }
+           n = getByte(stringPos);
+           s = new GString(file + stringPos + 1, n);
+           encoding[i] = copyString(s->getCString());
+           delete s;
+           ++stringIdx;
+           stringPos += 1 + n;
          }
-         eexecDumpOp1(1);
-       }
-       nHints += nOps / 2;
-       break;
-      case 3:                  // vstem
-       if (first) {
-         cvtGlyphWidth(nOps & 1);
-         first = gFalse;
+       } else {
+         encoding[i] = copyString(macGlyphNames[0]);
        }
-       if (nOps & 1) {
-         error(-1, "Wrong number of args (%d) to Type 2 vstem", nOps);
+      }
+
+    // Apple subset
+    } else if (fmt == 0x000280000) {
+      for (i = 0; i < 256; ++i) {
+       if (cmap[i] < nGlyphs) {
+         j = i + getChar(pos + 32 + cmap[i]);
+       } else {
+         j = 0;
        }
-       d = 0;
-       dFP = gFalse;
-       for (k = 0; k < nOps; k += 2) {
-         if (op[k+1] < 0) {
-           d += op[k] + op[k+1];
-           dFP |= fp[k] | fp[k+1];
-           eexecDumpNum(d, dFP);
-           eexecDumpNum(-op[k+1], fp[k+1]);
-         } else {
-           d += op[k];
-           dFP |= fp[k];
-           eexecDumpNum(d, dFP);
-           eexecDumpNum(op[k+1], fp[k+1]);
-           d += op[k+1];
-           dFP |= fp[k+1];
+       encoding[i] = copyString(macGlyphNames[j]);
+      }
+
+    // Ugh, just assume the Apple glyph set
+    } else {
+      for (i = 0; i < 256; ++i) {
+       j = (cmap[i] < 258) ? cmap[i] : 0;
+       encoding[i] = copyString(macGlyphNames[j]);
+      }
+    }
+
+  // no "post" table: assume the Apple glyph set
+  } else {
+    for (i = 0; i < 256; ++i) {
+      j = (cmap[i] < 258) ? cmap[i] : 0;
+      encoding[i] = copyString(macGlyphNames[j]);
+    }
+  }
+
+  return encoding;
+}
+
+void TrueTypeFontFile::convertToType42(char *name, char **encodingA,
+                                      CharCodeToUnicode *toUnicode,
+                                      GBool pdfFontHasEncoding, FILE *out) {
+  // write the header
+  fprintf(out, "%%!PS-TrueTypeFont-%g\n", getFixed(0));
+
+  // begin the font dictionary
+  fprintf(out, "10 dict begin\n");
+  fprintf(out, "/FontName /%s def\n", name);
+  fprintf(out, "/FontType 42 def\n");
+  fprintf(out, "/FontMatrix [1 0 0 1 0 0] def\n");
+  fprintf(out, "/FontBBox [%d %d %d %d] def\n",
+         bbox[0], bbox[1], bbox[2], bbox[3]);
+  fprintf(out, "/PaintType 0 def\n");
+
+  // write the guts of the dictionary
+  cvtEncoding(encodingA, out);
+  cvtCharStrings(encodingA, toUnicode, pdfFontHasEncoding, out);
+  cvtSfnts(out, NULL);
+
+  // end the dictionary and define the font
+  fprintf(out, "FontName currentdict end definefont pop\n");
+}
+
+void TrueTypeFontFile::convertToCIDType2(char *name, Gushort *cidMap,
+                                        int nCIDs, FILE *out) {
+  Gushort cid;
+  int i, j, k;
+
+  // write the header
+  fprintf(out, "%%!PS-TrueTypeFont-%g\n", getFixed(0));
+
+  // begin the font dictionary
+  fprintf(out, "20 dict begin\n");
+  fprintf(out, "/CIDFontName /%s def\n", name);
+  fprintf(out, "/CIDFontType 2 def\n");
+  fprintf(out, "/FontType 42 def\n");
+  fprintf(out, "/CIDSystemInfo 3 dict dup begin\n");
+  fprintf(out, "  /Registry (Adobe) def\n");
+  fprintf(out, "  /Ordering (Identity) def\n");
+  fprintf(out, "  /Supplement 0 def\n");
+  fprintf(out, "  end def\n");
+  fprintf(out, "/GDBytes 2 def\n");
+  if (cidMap) {
+    fprintf(out, "/CIDCount %d def\n", nCIDs);
+    if (nCIDs > 32767) {
+      fprintf(out, "/CIDMap [");
+      for (i = 0; i < nCIDs; i += 32768 - 16) {
+       fprintf(out, "<\n");
+       for (j = 0; j < 32768 - 16 && i+j < nCIDs; j += 16) {
+         fprintf(out, "  ");
+         for (k = 0; k < 16 && i+j+k < nCIDs; ++k) {
+           cid = cidMap[i+j+k];
+           fprintf(out, "%02x%02x", (cid >> 8) & 0xff, cid & 0xff);
          }
-         eexecDumpOp1(3);
-       }
-       nHints += nOps / 2;
-       break;
-      case 18:                 // hstemhm
-       //~ ignored
-       if (first) {
-         cvtGlyphWidth(nOps & 1);
-         first = gFalse;
+         fprintf(out, "\n");
        }
-       if (nOps & 1) {
-         error(-1, "Wrong number of args (%d) to Type 2 hstemhm", nOps);
+       fprintf(out, "  >");
+      }
+      fprintf(out, "\n");
+      fprintf(out, "] def\n");
+    } else {
+      fprintf(out, "/CIDMap <\n");
+      for (i = 0; i < nCIDs; i += 16) {
+       fprintf(out, "  ");
+       for (j = 0; j < 16 && i+j < nCIDs; ++j) {
+         cid = cidMap[i+j];
+         fprintf(out, "%02x%02x", (cid >> 8) & 0xff, cid & 0xff);
        }
-       nHints += nOps / 2;
+       fprintf(out, "\n");
+      }
+      fprintf(out, "> def\n");
+    }
+  } else {
+    // direct mapping - just fill the string(s) with s[i]=i
+    fprintf(out, "/CIDCount %d def\n", nGlyphs);
+    if (nGlyphs > 32767) {
+      fprintf(out, "/CIDMap [\n");
+      for (i = 0; i < nGlyphs; i += 32767) {
+       j = nGlyphs - i < 32767 ? nGlyphs - i : 32767;
+       fprintf(out, "  %d string 0 1 %d {\n", 2 * j, j - 1);
+       fprintf(out, "    2 copy dup 2 mul exch %d add -8 bitshift put\n", i);
+       fprintf(out, "    1 index exch dup 2 mul 1 add exch %d add"
+               " 255 and put\n", i);
+       fprintf(out, "  } for\n");
+      }
+      fprintf(out, "] def\n");
+    } else {
+      fprintf(out, "/CIDMap %d string\n", 2 * nGlyphs);
+      fprintf(out, "  0 1 %d {\n", nGlyphs - 1);
+      fprintf(out, "    2 copy dup 2 mul exch -8 bitshift put\n");
+      fprintf(out, "    1 index exch dup 2 mul 1 add exch 255 and put\n");
+      fprintf(out, "  } for\n");
+      fprintf(out, "def\n");
+    }
+  }
+  fprintf(out, "/FontMatrix [1 0 0 1 0 0] def\n");
+  fprintf(out, "/FontBBox [%d %d %d %d] def\n",
+         bbox[0], bbox[1], bbox[2], bbox[3]);
+  fprintf(out, "/PaintType 0 def\n");
+  fprintf(out, "/Encoding [] readonly def\n");
+  fprintf(out, "/CharStrings 1 dict dup begin\n");
+  fprintf(out, "  /.notdef 0 def\n");
+  fprintf(out, "  end readonly def\n");
+
+  // write the guts of the dictionary
+  cvtSfnts(out, NULL);
+
+  // end the dictionary and define the font
+  fprintf(out, "CIDFontName currentdict end /CIDFont defineresource pop\n");
+}
+
+void TrueTypeFontFile::convertToType0(char *name, Gushort *cidMap,
+                                     int nCIDs, FILE *out) {
+  GString *sfntsName;
+  int n, i, j;
+
+  // write the Type 42 sfnts array
+  sfntsName = (new GString(name))->append("_sfnts");
+  cvtSfnts(out, sfntsName);
+  delete sfntsName;
+
+  // write the descendant Type 42 fonts
+  n = cidMap ? nCIDs : nGlyphs;
+  for (i = 0; i < n; i += 256) {
+    fprintf(out, "10 dict begin\n");
+    fprintf(out, "/FontName /%s_%02x def\n", name, i >> 8);
+    fprintf(out, "/FontType 42 def\n");
+    fprintf(out, "/FontMatrix [1 0 0 1 0 0] def\n");
+    fprintf(out, "/FontBBox [%d %d %d %d] def\n",
+           bbox[0], bbox[1], bbox[2], bbox[3]);
+    fprintf(out, "/PaintType 0 def\n");
+    fprintf(out, "/sfnts %s_sfnts def\n", name);
+    fprintf(out, "/Encoding 256 array\n");
+    for (j = 0; j < 256 && i+j < n; ++j) {
+      fprintf(out, "dup %d /c%02x put\n", j, j);
+    }
+    fprintf(out, "readonly def\n");
+    fprintf(out, "/CharStrings 257 dict dup begin\n");
+    fprintf(out, "/.notdef 0 def\n");
+    for (j = 0; j < 256 && i+j < n; ++j) {
+      fprintf(out, "/c%02x %d def\n", j, cidMap ? cidMap[i+j] : i+j);
+    }
+    fprintf(out, "end readonly def\n");
+    fprintf(out, "FontName currentdict end definefont pop\n");
+  }
+
+  // write the Type 0 parent font
+  fprintf(out, "16 dict begin\n");
+  fprintf(out, "/FontName /%s def\n", name);
+  fprintf(out, "/FontType 0 def\n");
+  fprintf(out, "/FontMatrix [1 0 0 1 0 0] def\n");
+  fprintf(out, "/FMapType 2 def\n");
+  fprintf(out, "/Encoding [\n");
+  for (i = 0; i < n; i += 256) {
+    fprintf(out, "%d\n", i >> 8);
+  }
+  fprintf(out, "] def\n");
+  fprintf(out, "/FDepVector [\n");
+  for (i = 0; i < n; i += 256) {
+    fprintf(out, "/%s_%02x findfont\n", name, i >> 8);
+  }
+  fprintf(out, "] def\n");
+  fprintf(out, "FontName currentdict end definefont pop\n");
+}
+
+int TrueTypeFontFile::getByte(int pos) {
+  if (pos < 0 || pos >= len) {
+    return 0;
+  }
+  return file[pos] & 0xff;
+}
+
+int TrueTypeFontFile::getChar(int pos) {
+  int x;
+
+  if (pos < 0 || pos >= len) {
+    return 0;
+  }
+  x = file[pos] & 0xff;
+  if (x & 0x80)
+    x |= 0xffffff00;
+  return x;
+}
+
+int TrueTypeFontFile::getUShort(int pos) {
+  int x;
+
+  if (pos < 0 || pos+1 >= len) {
+    return 0;
+  }
+  x = file[pos] & 0xff;
+  x = (x << 8) + (file[pos+1] & 0xff);
+  return x;
+}
+
+int TrueTypeFontFile::getShort(int pos) {
+  int x;
+
+  if (pos < 0 || pos+1 >= len) {
+    return 0;
+  }
+  x = file[pos] & 0xff;
+  x = (x << 8) + (file[pos+1] & 0xff);
+  if (x & 0x8000)
+    x |= 0xffff0000;
+  return x;
+}
+
+Guint TrueTypeFontFile::getULong(int pos) {
+  int x;
+
+  if (pos < 0 || pos+3 >= len) {
+    return 0;
+  }
+  x = file[pos] & 0xff;
+  x = (x << 8) + (file[pos+1] & 0xff);
+  x = (x << 8) + (file[pos+2] & 0xff);
+  x = (x << 8) + (file[pos+3] & 0xff);
+  return x;
+}
+
+double TrueTypeFontFile::getFixed(int pos) {
+  int x, y;
+
+  x = getShort(pos);
+  y = getUShort(pos+2);
+  return (double)x + (double)y / 65536;
+}
+
+int TrueTypeFontFile::seekTable(char *tag) {
+  int i;
+
+  for (i = 0; i < nTables; ++i) {
+    if (!strncmp(tableHdrs[i].tag, tag, 4)) {
+      return tableHdrs[i].offset;
+    }
+  }
+  return -1;
+}
+
+int TrueTypeFontFile::seekTableIdx(char *tag) {
+  int i;
+
+  for (i = 0; i < nTables; ++i) {
+    if (!strncmp(tableHdrs[i].tag, tag, 4)) {
+      return i;
+    }
+  }
+  return -1;
+}
+
+void TrueTypeFontFile::cvtEncoding(char **encodingA, FILE *out) {
+  char *name;
+  int i;
+
+  fprintf(out, "/Encoding 256 array\n");
+  for (i = 0; i < 256; ++i) {
+    if (!(name = encodingA[i])) {
+      name = ".notdef";
+    }
+    fprintf(out, "dup %d /%s put\n", i, name);
+  }
+  fprintf(out, "readonly def\n");
+}
+
+void TrueTypeFontFile::cvtCharStrings(char **encodingA,
+                                     CharCodeToUnicode *toUnicode,
+                                     GBool pdfFontHasEncoding, FILE *out) {
+  int unicodeCmap, macRomanCmap, msSymbolCmap;
+  int nCmaps, cmapPlatform, cmapEncoding, cmapFmt, cmapOffset;
+  T42FontIndexMode mode;
+  char *name;
+  Unicode u;
+  int pos, i, j, k;
+
+  // always define '.notdef'
+  fprintf(out, "/CharStrings 256 dict dup begin\n");
+  fprintf(out, "/.notdef 0 def\n");
+
+  // if there's no 'cmap' table, punt
+  if ((pos = seekTable("cmap")) < 0) {
+    goto err;
+  }
+
+  // To match up with the Adobe-defined behaviour, we choose a cmap
+  // like this:
+  // 1. If the PDF font has an encoding:
+  //    1a. If the TrueType font has a Microsoft Unicode cmap, use it,
+  //        and use the Unicode indexes, not the char codes.
+  //    1b. If the TrueType font has a Macintosh Roman cmap, use it,
+  //        and reverse map the char names through MacRomanEncoding to
+  //        get char codes.
+  // 2. If the PDF font does not have an encoding:
+  //    2a. If the TrueType font has a Macintosh Roman cmap, use it,
+  //        and use char codes directly.
+  //    2b. If the TrueType font has a Microsoft Symbol cmap, use it,
+  //        and use (0xf000 + char code).
+  // 3. If none of these rules apply, use the first cmap and hope for
+  //    the best (this shouldn't happen).
+  nCmaps = getUShort(pos+2);
+  unicodeCmap = macRomanCmap = msSymbolCmap = -1;
+  cmapOffset = 0;
+  for (i = 0; i < nCmaps; ++i) {
+    cmapPlatform = getUShort(pos + 4 + 8*i);
+    cmapEncoding = getUShort(pos + 4 + 8*i + 2);
+    if (cmapPlatform == 3 && cmapEncoding == 1) {
+      unicodeCmap = i;
+    } else if (cmapPlatform == 1 && cmapEncoding == 0) {
+      macRomanCmap = i;
+    } else if (cmapPlatform == 3 && cmapEncoding == 0) {
+      msSymbolCmap = i;
+    }
+  }
+  i = 0;
+  mode = t42FontModeCharCode;
+  if (pdfFontHasEncoding) {
+    if (unicodeCmap >= 0) {
+      i = unicodeCmap;
+      mode = t42FontModeUnicode;
+    } else if (macRomanCmap >= 0) {
+      i = macRomanCmap;
+      mode = t42FontModeMacRoman;
+    }
+  } else {
+    if (macRomanCmap >= 0) {
+      i = macRomanCmap;
+      mode = t42FontModeCharCode;
+    } else if (msSymbolCmap >= 0) {
+      i = msSymbolCmap;
+      mode = t42FontModeCharCodeOffset;
+      cmapOffset = 0xf000;
+    }
+  }
+  cmapPlatform = getUShort(pos + 4 + 8*i);
+  cmapEncoding = getUShort(pos + 4 + 8*i + 2);
+  pos += getULong(pos + 4 + 8*i + 4);
+  cmapFmt = getUShort(pos);
+  if (cmapFmt != 0 && cmapFmt != 4 && cmapFmt != 6) {
+    error(-1, "Unimplemented cmap format (%d) in TrueType font file",
+         cmapFmt);
+    goto err;
+  }
+
+  // map char name to glyph index:
+  // 1. use encoding to map name to char code
+  // 2. use cmap to map char code to glyph index
+  j = 0; // make gcc happy
+  for (i = 0; i < 256; ++i) {
+    name = encodingA[i];
+    if (name && strcmp(name, ".notdef")) {
+      switch (mode) {
+      case t42FontModeUnicode:
+       toUnicode->mapToUnicode((CharCode)i, &u, 1);
+       j = (int)u;
        break;
-      case 23:                 // vstemhm
-       //~ ignored
-       if (first) {
-         cvtGlyphWidth(nOps & 1);
-         first = gFalse;
-       }
-       if (nOps & 1) {
-         error(-1, "Wrong number of args (%d) to Type 2 vstemhm", nOps);
-       }
-       nHints += nOps / 2;
+      case t42FontModeCharCode:
+       j = i;
        break;
-      case 10:                 // callsubr
-      case 11:                 // return
-      case 16:                 // blend
-      case 29:                 // callgsubr
-       error(-1, "Unimplemented Type 2 charstring op: %d", s[i]);
+      case t42FontModeCharCodeOffset:
+       j = cmapOffset + i;
        break;
-      default:
-       error(-1, "Illegal Type 2 charstring op: %d", s[i]);
+      case t42FontModeMacRoman:
+       j = globalParams->getMacRomanCharCode(name);
        break;
       }
-      ++i;
-      nOps = 0;
-    } else if (s[i] <= 246) {
-      if (nOps < 48) {
-       fp[nOps] = gFalse;
-       op[nOps++] = (int)s[i] - 139;
-      }
-      ++i;
-    } else if (s[i] <= 250) {
-      if (nOps < 48) {
-       fp[nOps] = gFalse;
-       op[nOps++] = (((int)s[i] - 247) << 8) + (int)s[i+1] + 108;
+      // note: Distiller (maybe Adobe's PS interpreter in general)
+      // doesn't like TrueType fonts that have CharStrings entries
+      // which point to nonexistent glyphs, hence the (k < nGlyphs)
+      // test
+      if ((k = getCmapEntry(cmapFmt, pos, j)) > 0 &&
+         k < nGlyphs) {
+       fprintf(out, "/%s %d def\n", name, k);
       }
-      i += 2;
-    } else if (s[i] <= 254) {
-      if (nOps < 48) {
-       fp[nOps] = gFalse;
-       op[nOps++] = -(((int)s[i] - 251) << 8) - (int)s[i+1] - 108;
+    }
+  }
+
+ err:
+  fprintf(out, "end readonly def\n");
+}
+
+int TrueTypeFontFile::getCmapEntry(int cmapFmt, int pos, int code) {
+  int cmapLen, cmapFirst;
+  int segCnt, segEnd, segStart, segDelta, segOffset;
+  int a, b, m, i;
+
+  switch (cmapFmt) {
+  case 0: // byte encoding table (Apple standard)
+    cmapLen = getUShort(pos + 2);
+    if (code >= cmapLen) {
+      return 0;
+    }
+    return getByte(pos + 6 + code);
+
+  case 4: // segment mapping to delta values (Microsoft standard)
+    segCnt = getUShort(pos + 6) / 2;
+    a = -1;
+    b = segCnt - 1;
+    segEnd = getUShort(pos + 14 + 2*b);
+    if (code > segEnd) {
+      // malformed font -- the TrueType spec requires the last segEnd
+      // to be 0xffff
+      return 0;
+    }
+    // invariant: seg[a].end < code <= seg[b].end
+    while (b - a > 1) {
+      m = (a + b) / 2;
+      segEnd = getUShort(pos + 14 + 2*m);
+      if (segEnd < code) {
+       a = m;
+      } else {
+       b = m;
       }
-      i += 2;
+    }
+    segStart = getUShort(pos + 16 + 2*segCnt + 2*b);
+    segDelta = getUShort(pos + 16 + 4*segCnt + 2*b);
+    segOffset = getUShort(pos + 16 + 6*segCnt + 2*b);
+    if (segOffset == 0) {
+      i = (code + segDelta) & 0xffff;
     } else {
-      x = (s[i+1] << 24) | (s[i+2] << 16) | (s[i+3] << 8) | s[i+4];
-      if (x & 0x80000000)
-       x |= -1 << 31;
-      if (nOps < 48) {
-       fp[nOps] = gTrue;
-       op[nOps++] = (double)x / 65536.0;
+      i = getUShort(pos + 16 + 6*segCnt + 2*b +
+                   segOffset + 2 * (code - segStart));
+      if (i != 0) {
+       i = (i + segDelta) & 0xffff;
       }
-      i += 5;
     }
-  }
+    return i;
 
-  sprintf(eBuf, "/%s %d RD ", name, charBuf->getLength());
-  eexecWrite(eBuf);
-  eexecWriteCharstring((Guchar *)charBuf->getCString(), charBuf->getLength());
-  eexecWrite(" ND\n");
-  delete charBuf;
-}
+  case 6: // trimmed table mapping
+    cmapFirst = getUShort(pos + 6);
+    cmapLen = getUShort(pos + 8);
+    if (code < cmapFirst || code >= cmapFirst + cmapLen) {
+      return 0;
+    }
+    return getUShort(pos + 10 + 2*(code - cmapFirst));
 
-void Type1CFontConverter::cvtGlyphWidth(GBool useOp) {
-  double w;
-  GBool wFP;
-  int i;
+  default:
+    // shouldn't happen - this is checked earlier
+    break;
+  }
+  return 0;
+}
 
-  if (useOp) {
-    w = nominalWidthX + op[0];
-    wFP = nominalWidthXFP | fp[0];
-    for (i = 1; i < nOps; ++i) {
-      op[i-1] = op[i];
-      fp[i-1] = fp[i];
+void TrueTypeFontFile::cvtSfnts(FILE *out, GString *name) {
+  TTFontTableHdr newTableHdrs[nT42Tables];
+  char tableDir[12 + nT42Tables*16];
+  char headTable[54];
+  int *origLocaTable;
+  char *locaTable;
+  int nNewTables;
+  Guint checksum;
+  int pos, glyfPos, length, glyphLength, pad;
+  int i, j, k;
+
+  // construct the 'head' table, zero out the font checksum
+  memcpy(headTable, file + seekTable("head"), 54);
+  headTable[8] = headTable[9] = headTable[10] = headTable[11] = (char)0;
+
+  // read the original 'loca' table and construct the new one
+  // (pad each glyph out to a multiple of 4 bytes)
+  origLocaTable = (int *)gmalloc((nGlyphs + 1) * sizeof(int));
+  pos = seekTable("loca");
+  for (i = 0; i <= nGlyphs; ++i) {
+    if (locaFmt) {
+      origLocaTable[i] = getULong(pos + 4*i);
+    } else {
+      origLocaTable[i] = 2 * getUShort(pos + 2*i);
     }
-    --nOps;
+  }
+  locaTable = (char *)gmalloc((nGlyphs + 1) * (locaFmt ? 4 : 2));
+  if (locaFmt) {
+    locaTable[0] = locaTable[1] = locaTable[2] = locaTable[3] = 0;
   } else {
-    w = defaultWidthX;
-    wFP = defaultWidthXFP;
+    locaTable[0] = locaTable[1] = 0;
+  }
+  pos = 0;
+  for (i = 1; i <= nGlyphs; ++i) {
+    length = origLocaTable[i] - origLocaTable[i-1];
+    if (length & 3) {
+      length += 4 - (length & 3);
+    }
+    pos += length;
+    if (locaFmt) {
+      locaTable[4*i  ] = (char)(pos >> 24);
+      locaTable[4*i+1] = (char)(pos >> 16);
+      locaTable[4*i+2] = (char)(pos >>  8);
+      locaTable[4*i+3] = (char) pos;
+    } else {
+      locaTable[2*i  ] = (char)(pos >> 9);
+      locaTable[2*i+1] = (char)(pos >> 1);
+    }
   }
-  eexecDumpNum(0, gFalse);
-  eexecDumpNum(w, wFP);
-  eexecDumpOp1(13);
-}
 
-void Type1CFontConverter::eexecDumpNum(double x, GBool fp) {
-  Guchar buf[12];
-  int y, n;
+  // count the number of tables
+  nNewTables = 0;
+  for (i = 0; i < nT42Tables; ++i) {
+    if (t42Tables[i].required ||
+       seekTable(t42Tables[i].tag) >= 0) {
+      ++nNewTables;
+    }
+  }
 
-  n = 0;
-  if (fp) {
-    if (x >= -32768 && x < 32768) {
-      y = (int)(x * 256.0);
-      buf[0] = 255;
-      buf[1] = (Guchar)(y >> 24);
-      buf[2] = (Guchar)(y >> 16);
-      buf[3] = (Guchar)(y >> 8);
-      buf[4] = (Guchar)y;
-      buf[5] = 255;
-      buf[6] = 0;
-      buf[7] = 0;
-      buf[8] = 1;
-      buf[9] = 0;
-      buf[10] = 12;
-      buf[11] = 12;
-      n = 12;
+  // construct the new table headers, including table checksums
+  // (pad each table out to a multiple of 4 bytes)
+  pos = 12 + nNewTables*16;
+  k = 0;
+  for (i = 0; i < nT42Tables; ++i) {
+    length = -1;
+    checksum = 0; // make gcc happy
+    if (i == t42HeadTable) {
+      length = 54;
+      checksum = computeTableChecksum(headTable, 54);
+    } else if (i == t42LocaTable) {
+      length = (nGlyphs + 1) * (locaFmt ? 4 : 2);
+      checksum = computeTableChecksum(locaTable, length);
+    } else if (i == t42GlyfTable) {
+      length = 0;
+      checksum = 0;
+      glyfPos = seekTable("glyf");
+      for (j = 0; j < nGlyphs; ++j) {
+       glyphLength = origLocaTable[j+1] - origLocaTable[j];
+       pad = (glyphLength & 3) ? 4 - (glyphLength & 3) : 0;
+       length += glyphLength + pad;
+       checksum += computeTableChecksum(file + glyfPos + origLocaTable[j],
+                                        glyphLength);
+      }
     } else {
-      error(-1, "Type 2 fixed point constant out of range");
+      if ((j = seekTableIdx(t42Tables[i].tag)) >= 0) {
+       length = tableHdrs[j].length;
+       checksum = computeTableChecksum(file + tableHdrs[j].offset, length);
+      } else if (t42Tables[i].required) {
+       error(-1, "Embedded TrueType font is missing a required table ('%s')",
+             t42Tables[i].tag);
+       length = 0;
+       checksum = 0;
+      }
+    }
+    if (length >= 0) {
+      strncpy(newTableHdrs[k].tag, t42Tables[i].tag, 4);
+      newTableHdrs[k].checksum = checksum;
+      newTableHdrs[k].offset = pos;
+      newTableHdrs[k].length = length;
+      pad = (length & 3) ? 4 - (length & 3) : 0;
+      pos += length + pad;
+      ++k;
     }
+  }
+
+  // construct the table directory
+  tableDir[0] = 0x00;          // sfnt version
+  tableDir[1] = 0x01;
+  tableDir[2] = 0x00;
+  tableDir[3] = 0x00;
+  tableDir[4] = 0;             // numTables
+  tableDir[5] = nNewTables;
+  tableDir[6] = 0;             // searchRange
+  tableDir[7] = (char)128;
+  tableDir[8] = 0;             // entrySelector
+  tableDir[9] = 3;
+  tableDir[10] = 0;            // rangeShift
+  tableDir[11] = (char)(16 * nNewTables - 128);
+  pos = 12;
+  for (i = 0; i < nNewTables; ++i) {
+    tableDir[pos   ] = newTableHdrs[i].tag[0];
+    tableDir[pos+ 1] = newTableHdrs[i].tag[1];
+    tableDir[pos+ 2] = newTableHdrs[i].tag[2];
+    tableDir[pos+ 3] = newTableHdrs[i].tag[3];
+    tableDir[pos+ 4] = (char)(newTableHdrs[i].checksum >> 24);
+    tableDir[pos+ 5] = (char)(newTableHdrs[i].checksum >> 16);
+    tableDir[pos+ 6] = (char)(newTableHdrs[i].checksum >>  8);
+    tableDir[pos+ 7] = (char) newTableHdrs[i].checksum;
+    tableDir[pos+ 8] = (char)(newTableHdrs[i].offset >> 24);
+    tableDir[pos+ 9] = (char)(newTableHdrs[i].offset >> 16);
+    tableDir[pos+10] = (char)(newTableHdrs[i].offset >>  8);
+    tableDir[pos+11] = (char) newTableHdrs[i].offset;
+    tableDir[pos+12] = (char)(newTableHdrs[i].length >> 24);
+    tableDir[pos+13] = (char)(newTableHdrs[i].length >> 16);
+    tableDir[pos+14] = (char)(newTableHdrs[i].length >>  8);
+    tableDir[pos+15] = (char) newTableHdrs[i].length;
+    pos += 16;
+  }
+
+  // compute the font checksum and store it in the head table
+  checksum = computeTableChecksum(tableDir, 12 + nNewTables*16);
+  for (i = 0; i < nNewTables; ++i) {
+    checksum += newTableHdrs[i].checksum;
+  }
+  checksum = 0xb1b0afba - checksum; // because the TrueType spec says so
+  headTable[ 8] = (char)(checksum >> 24);
+  headTable[ 9] = (char)(checksum >> 16);
+  headTable[10] = (char)(checksum >>  8);
+  headTable[11] = (char) checksum;
+
+  // start the sfnts array
+  if (name) {
+    fprintf(out, "/%s [\n", name->getCString());
   } else {
-    y = (int)x;
-    if (y >= -107 && y <= 107) {
-      buf[0] = (Guchar)(y + 139);
-      n = 1;
-    } else if (y > 107 && y <= 1131) {
-      y -= 108;
-      buf[0] = (Guchar)((y >> 8) + 247);
-      buf[1] = (Guchar)(y & 0xff);
-      n = 2;
-    } else if (y < -107 && y >= -1131) {
-      y = -y - 108;
-      buf[0] = (Guchar)((y >> 8) + 251);
-      buf[1] = (Guchar)(y & 0xff);
-      n = 2;
+    fprintf(out, "/sfnts [\n");
+  }
+
+  // write the table directory
+  dumpString(tableDir, 12 + nNewTables*16, out);
+
+  // write the tables
+  for (i = 0; i < nNewTables; ++i) {
+    if (i == t42HeadTable) {
+      dumpString(headTable, 54, out);
+    } else if (i == t42LocaTable) {
+      length = (nGlyphs + 1) * (locaFmt ? 4 : 2);
+      dumpString(locaTable, length, out);
+    } else if (i == t42GlyfTable) {
+      glyfPos = seekTable("glyf");
+      for (j = 0; j < nGlyphs; ++j) {
+       length = origLocaTable[j+1] - origLocaTable[j];
+       if (length > 0) {
+         dumpString(file + glyfPos + origLocaTable[j], length, out);
+       }
+      }
     } else {
-      buf[0] = 255;
-      buf[1] = (Guchar)(y >> 24);
-      buf[2] = (Guchar)(y >> 16);
-      buf[3] = (Guchar)(y >> 8);
-      buf[4] = (Guchar)y;
-      n = 5;
+      // length == 0 means the table is missing and the error was
+      // already reported during the construction of the table
+      // headers
+      if ((length = newTableHdrs[i].length) > 0) {
+       dumpString(file + seekTable(t42Tables[i].tag), length, out);
+      }
     }
   }
-  charBuf->append((char *)buf, n);
-}
 
-void Type1CFontConverter::eexecDumpOp1(int op) {
-  charBuf->append((char)op);
-}
+  // end the sfnts array
+  fprintf(out, "] def\n");
 
-void Type1CFontConverter::eexecDumpOp2(int op) {
-  charBuf->append((char)12);
-  charBuf->append((char)op);
+  gfree(origLocaTable);
+  gfree(locaTable);
 }
 
-void Type1CFontConverter::eexecWriteCharstring(Guchar *s, int n) {
-  Gushort r2;
-  Guchar x;
-  int i;
+void TrueTypeFontFile::dumpString(char *s, int length, FILE *out) {
+  int pad, i, j;
 
-  r2 = 4330;
+  fprintf(out, "<");
+  for (i = 0; i < length; i += 32) {
+    for (j = 0; j < 32 && i+j < length; ++j) {
+      fprintf(out, "%02X", s[i+j] & 0xff);
+    }
+    if (i % (65536 - 32) == 65536 - 64) {
+      fprintf(out, ">\n<");
+    } else if (i+32 < length) {
+      fprintf(out, "\n");
+    }
+  }
+  if (length & 3) {
+    pad = 4 - (length & 3);
+    for (i = 0; i < pad; ++i) {
+      fprintf(out, "00");
+    }
+  }
+  // add an extra zero byte because the Adobe Type 42 spec says so
+  fprintf(out, "00>\n");
+}
 
-  for (i = 0; i < n; ++i) {
-    // charstring encryption
-    x = s[i];
-    x ^= (r2 >> 8);
-    r2 = (x + r2) * 52845 + 22719;
+Guint TrueTypeFontFile::computeTableChecksum(char *data, int length) {
+  Guint checksum, word;
+  int i;
 
-    // eexec encryption
-    x ^= (r1 >> 8);
-    r1 = (x + r1) * 52845 + 22719;
-    fputc(hexChars[x >> 4], out);
-    fputc(hexChars[x & 0x0f], out);
-    line += 2;
-    if (line == 64) {
-      fputc('\n', out);
-      line = 0;
+  checksum = 0;
+  for (i = 0; i+3 < length; i += 4) {
+    word = ((data[i  ] & 0xff) << 24) +
+           ((data[i+1] & 0xff) << 16) +
+           ((data[i+2] & 0xff) <<  8) +
+            (data[i+3] & 0xff);
+    checksum += word;
+  }
+  if (length & 3) {
+    word = 0;
+    i = length & ~3;
+    switch (length & 3) {
+    case 3:
+      word |= (data[i+2] & 0xff) <<  8;
+    case 2:
+      word |= (data[i+1] & 0xff) << 16;
+    case 1:
+      word |= (data[i  ] & 0xff) << 24;
+      break;
     }
+    checksum += word;
   }
+  return checksum;
 }
 
-void Type1CFontConverter::getDeltaInt(char *buf, char *name, double *op,
-                                     int n) {
-  int x, i;
+void TrueTypeFontFile::writeTTF(FILE *out) {
+  static char cmapTab[20] = {
+    0, 0,                      // table version number
+    0, 1,                      // number of encoding tables
+    0, 1,                      // platform ID
+    0, 0,                      // encoding ID
+    0, 0, 0, 12,               // offset of subtable
+    0, 0,                      // subtable format
+    0, 1,                      // subtable length
+    0, 1,                      // subtable version
+    0,                         // map char 0 -> glyph 0
+    0                          // pad to multiple of four bytes
+  };
+  static char nameTab[8] = {
+    0, 0,                      // format
+    0, 0,                      // number of name records
+    0, 6,                      // offset to start of string storage
+    0, 0                       // pad to multiple of four bytes
+  };
+  static char postTab[32] = {
+    0, 1, 0, 0,                        // format
+    0, 0, 0, 0,                        // italic angle
+    0, 0,                      // underline position
+    0, 0,                      // underline thickness
+    0, 0, 0, 0,                        // fixed pitch
+    0, 0, 0, 0,                        // min Type 42 memory
+    0, 0, 0, 0,                        // max Type 42 memory
+    0, 0, 0, 0,                        // min Type 1 memory
+    0, 0, 0, 0                 // max Type 1 memory
+  };
+  GBool haveCmap, haveName, havePost;
+  GBool dirCmap, dirName, dirPost;
+  int nNewTables, nAllTables, pad;
+  char *tableDir;
+  Guint t, pos;
+  int i, j;
+
+  // check for missing tables
+  haveCmap = seekTable("cmap") >= 0;
+  haveName = seekTable("name") >= 0;
+  havePost = seekTable("post") >= 0;
+  nNewTables = (haveCmap ? 0 : 1) + (haveName ? 0 : 1) + (havePost ? 0 : 1);
+  if (!nNewTables) {
+    // none are missing - write the TTF file as is
+    fwrite(file, 1, len, out);
+    return;
+  }
 
-  sprintf(buf, "/%s [", name);
-  buf += strlen(buf);
-  x = 0;
-  for (i = 0; i < n; ++i) {
-    x += (int)op[i];
-    sprintf(buf, "%s%d", i > 0 ? " " : "", x);
-    buf += strlen(buf);
+  // construct the new table directory
+  nAllTables = nTables + nNewTables;
+  tableDir = (char *)gmalloc(12 + nAllTables * 16);
+  memcpy(tableDir, file, 12 + nTables * 16);
+  tableDir[4] = (char)((nAllTables >> 8) & 0xff);
+  tableDir[5] = (char)(nAllTables & 0xff);
+  for (i = -1, t = (Guint)nAllTables; t; ++i, t >>= 1) ;
+  t = 1 << (4 + i);
+  tableDir[6] = (char)((t >> 8) & 0xff);
+  tableDir[7] = (char)(t & 0xff);
+  tableDir[8] = (char)((i >> 8) & 0xff);
+  tableDir[9] = (char)(i & 0xff);
+  t = nAllTables * 16 - t;
+  tableDir[10] = (char)((t >> 8) & 0xff);
+  tableDir[11] = (char)(t & 0xff);
+  dirCmap = haveCmap;
+  dirName = haveName;
+  dirPost = havePost;
+  j = 0;
+  pad = (len & 3) ? 4 - (len & 3) : 0;
+  pos = len + pad + 16 * nNewTables;
+  for (i = 0; i < nTables; ++i) {
+    if (!dirCmap && strncmp(tableHdrs[i].tag, "cmap", 4) > 0) {
+      tableDir[12 + 16*j     ] = 'c';
+      tableDir[12 + 16*j +  1] = 'm';
+      tableDir[12 + 16*j +  2] = 'a';
+      tableDir[12 + 16*j +  3] = 'p';
+      tableDir[12 + 16*j +  4] = (char)0; //~ should compute the checksum
+      tableDir[12 + 16*j +  5] = (char)0;
+      tableDir[12 + 16*j +  6] = (char)0;
+      tableDir[12 + 16*j +  7] = (char)0;
+      tableDir[12 + 16*j +  8] = (char)((pos >> 24) & 0xff);
+      tableDir[12 + 16*j +  9] = (char)((pos >> 16) & 0xff);
+      tableDir[12 + 16*j + 10] = (char)((pos >>  8) & 0xff);
+      tableDir[12 + 16*j + 11] = (char)( pos        & 0xff);
+      tableDir[12 + 16*j + 12] = (char)((sizeof(cmapTab) >> 24) & 0xff);
+      tableDir[12 + 16*j + 13] = (char)((sizeof(cmapTab) >> 16) & 0xff);
+      tableDir[12 + 16*j + 14] = (char)((sizeof(cmapTab) >>  8) & 0xff);
+      tableDir[12 + 16*j + 15] = (char)( sizeof(cmapTab)        & 0xff);
+      pos += sizeof(cmapTab);
+      ++j;
+      dirCmap = gTrue;
+    }
+    if (!dirName && strncmp(tableHdrs[i].tag, "name", 4) > 0) {
+      tableDir[12 + 16*j     ] = 'n';
+      tableDir[12 + 16*j +  1] = 'a';
+      tableDir[12 + 16*j +  2] = 'm';
+      tableDir[12 + 16*j +  3] = 'e';
+      tableDir[12 + 16*j +  4] = (char)0; //~ should compute the checksum
+      tableDir[12 + 16*j +  5] = (char)0;
+      tableDir[12 + 16*j +  6] = (char)0;
+      tableDir[12 + 16*j +  7] = (char)0;
+      tableDir[12 + 16*j +  8] = (char)((pos >> 24) & 0xff);
+      tableDir[12 + 16*j +  9] = (char)((pos >> 16) & 0xff);
+      tableDir[12 + 16*j + 10] = (char)((pos >>  8) & 0xff);
+      tableDir[12 + 16*j + 11] = (char)( pos        & 0xff);
+      tableDir[12 + 16*j + 12] = (char)((sizeof(nameTab) >> 24) & 0xff);
+      tableDir[12 + 16*j + 13] = (char)((sizeof(nameTab) >> 16) & 0xff);
+      tableDir[12 + 16*j + 14] = (char)((sizeof(nameTab) >>  8) & 0xff);
+      tableDir[12 + 16*j + 15] = (char)( sizeof(nameTab)        & 0xff);
+      pos += sizeof(nameTab);
+      ++j;
+      dirName = gTrue;
+    }
+    if (!dirName && strncmp(tableHdrs[i].tag, "post", 4) > 0) {
+      tableDir[12 + 16*j     ] = 'p';
+      tableDir[12 + 16*j +  1] = 'o';
+      tableDir[12 + 16*j +  2] = 's';
+      tableDir[12 + 16*j +  3] = 't';
+      tableDir[12 + 16*j +  4] = (char)0; //~ should compute the checksum
+      tableDir[12 + 16*j +  5] = (char)0;
+      tableDir[12 + 16*j +  6] = (char)0;
+      tableDir[12 + 16*j +  7] = (char)0;
+      tableDir[12 + 16*j +  8] = (char)((pos >> 24) & 0xff);
+      tableDir[12 + 16*j +  9] = (char)((pos >> 16) & 0xff);
+      tableDir[12 + 16*j + 10] = (char)((pos >>  8) & 0xff);
+      tableDir[12 + 16*j + 11] = (char)( pos        & 0xff);
+      tableDir[12 + 16*j + 12] = (char)((sizeof(postTab) >> 24) & 0xff);
+      tableDir[12 + 16*j + 13] = (char)((sizeof(postTab) >> 16) & 0xff);
+      tableDir[12 + 16*j + 14] = (char)((sizeof(postTab) >>  8) & 0xff);
+      tableDir[12 + 16*j + 15] = (char)( sizeof(postTab)        & 0xff);
+      pos += sizeof(postTab);
+      ++j;
+      dirPost = gTrue;
+    }
+    memcpy(&tableDir[12 + 16*j], file + 12 + 16*i, 16);
+    t = tableHdrs[i].offset + nNewTables * 16;
+    tableDir[12 + 16*j +  8] = (char)((t >> 24) & 0xff);
+    tableDir[12 + 16*j +  9] = (char)((t >> 16) & 0xff);
+    tableDir[12 + 16*j + 10] = (char)((t >>  8) & 0xff);
+    tableDir[12 + 16*j + 11] = (char)( t        & 0xff);
+    ++j;
+  }
+  if (!dirCmap) {
+    tableDir[12 + 16*j     ] = 'c';
+    tableDir[12 + 16*j +  1] = 'm';
+    tableDir[12 + 16*j +  2] = 'a';
+    tableDir[12 + 16*j +  3] = 'p';
+    tableDir[12 + 16*j +  4] = (char)0; //~ should compute the checksum
+    tableDir[12 + 16*j +  5] = (char)0;
+    tableDir[12 + 16*j +  6] = (char)0;
+    tableDir[12 + 16*j +  7] = (char)0;
+    tableDir[12 + 16*j +  8] = (char)((pos >> 24) & 0xff);
+    tableDir[12 + 16*j +  9] = (char)((pos >> 16) & 0xff);
+    tableDir[12 + 16*j + 10] = (char)((pos >>  8) & 0xff);
+    tableDir[12 + 16*j + 11] = (char)( pos        & 0xff);
+    tableDir[12 + 16*j + 12] = (char)((sizeof(cmapTab) >> 24) & 0xff);
+    tableDir[12 + 16*j + 13] = (char)((sizeof(cmapTab) >> 16) & 0xff);
+    tableDir[12 + 16*j + 14] = (char)((sizeof(cmapTab) >>  8) & 0xff);
+    tableDir[12 + 16*j + 15] = (char)( sizeof(cmapTab)        & 0xff);
+    pos += sizeof(cmapTab);
+    ++j;
+    dirCmap = gTrue;
+  }
+  if (!dirName) {
+    tableDir[12 + 16*j     ] = 'n';
+    tableDir[12 + 16*j +  1] = 'a';
+    tableDir[12 + 16*j +  2] = 'm';
+    tableDir[12 + 16*j +  3] = 'e';
+    tableDir[12 + 16*j +  4] = (char)0; //~ should compute the checksum
+    tableDir[12 + 16*j +  5] = (char)0;
+    tableDir[12 + 16*j +  6] = (char)0;
+    tableDir[12 + 16*j +  7] = (char)0;
+    tableDir[12 + 16*j +  8] = (char)((pos >> 24) & 0xff);
+    tableDir[12 + 16*j +  9] = (char)((pos >> 16) & 0xff);
+    tableDir[12 + 16*j + 10] = (char)((pos >>  8) & 0xff);
+    tableDir[12 + 16*j + 11] = (char)( pos        & 0xff);
+    tableDir[12 + 16*j + 12] = (char)((sizeof(nameTab) >> 24) & 0xff);
+    tableDir[12 + 16*j + 13] = (char)((sizeof(nameTab) >> 16) & 0xff);
+    tableDir[12 + 16*j + 14] = (char)((sizeof(nameTab) >>  8) & 0xff);
+    tableDir[12 + 16*j + 15] = (char)( sizeof(nameTab)        & 0xff);
+    pos += sizeof(nameTab);
+    ++j;
+    dirName = gTrue;
+  }
+  if (!dirPost) {
+    tableDir[12 + 16*j     ] = 'p';
+    tableDir[12 + 16*j +  1] = 'o';
+    tableDir[12 + 16*j +  2] = 's';
+    tableDir[12 + 16*j +  3] = 't';
+    tableDir[12 + 16*j +  4] = (char)0; //~ should compute the checksum
+    tableDir[12 + 16*j +  5] = (char)0;
+    tableDir[12 + 16*j +  6] = (char)0;
+    tableDir[12 + 16*j +  7] = (char)0;
+    tableDir[12 + 16*j +  8] = (char)((pos >> 24) & 0xff);
+    tableDir[12 + 16*j +  9] = (char)((pos >> 16) & 0xff);
+    tableDir[12 + 16*j + 10] = (char)((pos >>  8) & 0xff);
+    tableDir[12 + 16*j + 11] = (char)( pos        & 0xff);
+    tableDir[12 + 16*j + 12] = (char)((sizeof(postTab) >> 24) & 0xff);
+    tableDir[12 + 16*j + 13] = (char)((sizeof(postTab) >> 16) & 0xff);
+    tableDir[12 + 16*j + 14] = (char)((sizeof(postTab) >>  8) & 0xff);
+    tableDir[12 + 16*j + 15] = (char)( sizeof(postTab)        & 0xff);
+    pos += sizeof(postTab);
+    ++j;
+    dirPost = gTrue;
   }
-  sprintf(buf, "] def\n");
-}
 
-void Type1CFontConverter::getDeltaReal(char *buf, char *name, double *op,
-                                      int n) {
-  double x;
-  int i;
+  // write the table directory
+  fwrite(tableDir, 1, 12 + 16 * nAllTables, out);
 
-  sprintf(buf, "/%s [", name);
-  buf += strlen(buf);
-  x = 0;
-  for (i = 0; i < n; ++i) {
-    x += op[i];
-    sprintf(buf, "%s%g", i > 0 ? " " : "", x);
-    buf += strlen(buf);
+  // write the original tables
+  fwrite(file + 12 + 16*nTables, 1, len - (12 + 16*nTables), out);
+
+  // write the new tables
+  for (i = 0; i < pad; ++i) {
+    fputc((char)0, out);
   }
-  sprintf(buf, "] def\n");
+  if (!haveCmap) {
+    fwrite(cmapTab, 1, sizeof(cmapTab), out);
+  }
+  if (!haveName) {
+    fwrite(nameTab, 1, sizeof(nameTab), out);
+  }
+  if (!havePost) {
+    fwrite(postTab, 1, sizeof(postTab), out);
+  }
+
+  gfree(tableDir);
 }
index ec625ef..d5de25c 100644 (file)
@@ -2,7 +2,7 @@
 //
 // FontFile.h
 //
-// Copyright 1999 Derek B. Noonburg
+// Copyright 1999-2002 Glyph & Cog, LLC
 //
 //========================================================================
 
@@ -16,7 +16,9 @@
 #include <stdio.h>
 #include "gtypes.h"
 #include "GString.h"
-#include "FontEncoding.h"
+#include "CharTypes.h"
+
+class CharCodeToUnicode;
 
 //------------------------------------------------------------------------
 // FontFile
@@ -32,10 +34,9 @@ public:
   // Returns NULL if no name is available.
   virtual char *getName() = 0;
 
-  // Returns the custom font encoding, or NULL if the encoding is
-  // not available.  If <taken> is set, the caller of this function
-  // will be responsible for freeing the encoding object.
-  virtual FontEncoding *getEncoding(GBool taken) = 0;
+  // Returns the custom font encoding, or NULL if the encoding is not
+  // available.
+  virtual char **getEncoding() = 0;
 };
 
 //------------------------------------------------------------------------
@@ -48,59 +49,79 @@ public:
   Type1FontFile(char *file, int len);
   virtual ~Type1FontFile();
   virtual char *getName() { return name; }
-  virtual FontEncoding *getEncoding(GBool taken);
+  virtual char **getEncoding() { return encoding; }
 
 private:
 
   char *name;
-  FontEncoding *encoding;
-  GBool freeEnc;
+  char **encoding;
 };
 
 //------------------------------------------------------------------------
 // Type1CFontFile
 //------------------------------------------------------------------------
 
+struct Type1CTopDict;
+struct Type1CPrivateDict;
+
 class Type1CFontFile: public FontFile {
 public:
 
-  Type1CFontFile(char *file, int len);
+  Type1CFontFile(char *fileA, int lenA);
   virtual ~Type1CFontFile();
-  virtual char *getName() { return name; }
-  virtual FontEncoding *getEncoding(GBool taken);
 
-private:
-
-  char *name;
-  FontEncoding *encoding;
-  GBool freeEnc;
-};
+  virtual char *getName();
+  virtual char **getEncoding();
 
-//------------------------------------------------------------------------
-// Type1CFontConverter
-//------------------------------------------------------------------------
+  // Convert to a Type 1 font, suitable for embedding in a PostScript
+  // file.  The name will be used as the PostScript font name.
+  void convertToType1(FILE *outA);
 
-class Type1CFontConverter {
-public:
+  // Convert to a Type 0 CIDFont, suitable for embedding in a
+  // PostScript file.  The name will be used as the PostScript font
+  // name.
+  void convertToCIDType0(char *psName, FILE *outA);
 
-  Type1CFontConverter(char *file, int len, FILE *out);
-  ~Type1CFontConverter();
-  void convert();
+  // Convert to a Type 0 (but non-CID) composite font, suitable for
+  // embedding in a PostScript file.  The name will be used as the
+  // PostScript font name.
+  void convertToType0(char *psName, FILE *outA);
 
 private:
 
+  void readNameAndEncoding();
+  void readTopDict(Type1CTopDict *dict);
+  void readPrivateDict(Type1CPrivateDict *privateDict,
+                      int offset, int size);
+  Gushort *readCharset(int charset, int nGlyphs);
   void eexecWrite(char *s);
-  void cvtGlyph(char *name, Guchar *s, int n);
+  void eexecCvtGlyph(char *glyphName, Guchar *s, int n);
+  void cvtGlyph(Guchar *s, int n);
   void cvtGlyphWidth(GBool useOp);
-  void eexecDumpNum(double x, GBool fp);
-  void eexecDumpOp1(int op);
-  void eexecDumpOp2(int op);
+  void eexecDumpNum(double x, GBool fpA);
+  void eexecDumpOp1(int opA);
+  void eexecDumpOp2(int opA);
   void eexecWriteCharstring(Guchar *s, int n);
-  void getDeltaInt(char *buf, char *name, double *op, int n);
-  void getDeltaReal(char *buf, char *name, double *op, int n);
+  void getDeltaInt(char *buf, char *key, double *opA, int n);
+  void getDeltaReal(char *buf, char *key, double *opA, int n);
+  int getIndexLen(Guchar *indexPtr);
+  Guchar *getIndexValPtr(Guchar *indexPtr, int i);
+  Guchar *getIndexEnd(Guchar *indexPtr);
+  Guint getWord(Guchar *ptr, int size);
+  double getNum(Guchar **ptr, GBool *fp);
+  char *getString(int sid, char *buf);
 
   char *file;
   int len;
+
+  GString *name;
+  char **encoding;
+
+  int topOffSize;
+  Guchar *topDictIdxPtr;
+  Guchar *stringIdxPtr;
+  Guchar *gsubrIdxPtr;
+
   FILE *out;
   double op[48];               // operands
   GBool fp[48];                        // true if operand is fixed point
@@ -114,4 +135,81 @@ private:
   int line;                    // number of eexec chars on current line
 };
 
+//------------------------------------------------------------------------
+// TrueTypeFontFile
+//------------------------------------------------------------------------
+
+struct TTFontTableHdr;
+
+class TrueTypeFontFile: public FontFile {
+public:
+
+  TrueTypeFontFile(char *fileA, int lenA);
+  ~TrueTypeFontFile();
+
+  // This always returns NULL, since it's probably better to trust the
+  // font name in the PDF file rather than the one in the TrueType
+  // font file.
+  virtual char *getName();
+
+  virtual char **getEncoding();
+
+  // Convert to a Type 42 font, suitable for embedding in a PostScript
+  // file.  The name will be used as the PostScript font name (so we
+  // don't need to depend on the 'name' table in the font).  The
+  // encoding is needed because the PDF Font object can modify the
+  // encoding.
+  void convertToType42(char *name, char **encodingA,
+                      CharCodeToUnicode *toUnicode,
+                      GBool pdfFontHasEncoding, FILE *out);
+
+  // Convert to a Type 2 CIDFont, suitable for embedding in a
+  // PostScript file.  The name will be used as the PostScript font
+  // name (so we don't need to depend on the 'name' table in the
+  // font).
+  void convertToCIDType2(char *name, Gushort *cidMap,
+                        int nCIDs, FILE *out);
+
+  // Convert to a Type 0 (but non-CID) composite font, suitable for
+  // embedding in a PostScript file.  The name will be used as the
+  // PostScript font name (so we don't need to depend on the 'name'
+  // table in the font).
+  void convertToType0(char *name, Gushort *cidMap,
+                     int nCIDs, FILE *out);
+
+  // Write a TTF file, filling in any missing tables that are required
+  // by the TrueType spec.  If the font already has all the required
+  // tables, it will be written unmodified.
+  void writeTTF(FILE *out);
+
+private:
+
+  char *file;
+  int len;
+
+  char **encoding;
+
+  TTFontTableHdr *tableHdrs;
+  int nTables;
+  int bbox[4];
+  int locaFmt;
+  int nGlyphs;
+
+  int getByte(int pos);
+  int getChar(int pos);
+  int getUShort(int pos);
+  int getShort(int pos);
+  Guint getULong(int pos);
+  double getFixed(int pos);
+  int seekTable(char *tag);
+  int seekTableIdx(char *tag);
+  void cvtEncoding(char **encodingA, FILE *out);
+  void cvtCharStrings(char **encodingA, CharCodeToUnicode *toUnicode,
+                     GBool pdfFontHasEncoding, FILE *out);
+  int getCmapEntry(int cmapFmt, int pos, int code);
+  void cvtSfnts(FILE *out, GString *name);
+  void dumpString(char *s, int length, FILE *out);
+  Guint computeTableChecksum(char *data, int length);
+};
+
 #endif
diff --git a/pdf2swf/xpdf/FontInfo.h b/pdf2swf/xpdf/FontInfo.h
deleted file mode 100644 (file)
index ed768d6..0000000
+++ /dev/null
@@ -1,2068 +0,0 @@
-//========================================================================
-//
-// FontInfo.h
-//
-// This file was automatically generated by makeFontInfo.
-//
-// Copyright 1996 Derek B. Noonburg
-//
-//========================================================================
-
-#ifndef FONTINFO_H
-#define FONTINFO_H
-
-//------------------------------------------------------------------------
-// Character encodings.
-//------------------------------------------------------------------------
-
-#define standardEncodingSize 335
-char *standardEncodingNames[standardEncodingSize] = {
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  "space",
-  "exclam",
-  "quotedbl",
-  "numbersign",
-  "dollar",
-  "percent",
-  "ampersand",
-  "quoteright",
-  "parenleft",
-  "parenright",
-  "asterisk",
-  "plus",
-  "comma",
-  "hyphen",
-  "period",
-  "slash",
-  "zero",
-  "one",
-  "two",
-  "three",
-  "four",
-  "five",
-  "six",
-  "seven",
-  "eight",
-  "nine",
-  "colon",
-  "semicolon",
-  "less",
-  "equal",
-  "greater",
-  "question",
-  "at",
-  "A",
-  "B",
-  "C",
-  "D",
-  "E",
-  "F",
-  "G",
-  "H",
-  "I",
-  "J",
-  "K",
-  "L",
-  "M",
-  "N",
-  "O",
-  "P",
-  "Q",
-  "R",
-  "S",
-  "T",
-  "U",
-  "V",
-  "W",
-  "X",
-  "Y",
-  "Z",
-  "bracketleft",
-  "backslash",
-  "bracketright",
-  "asciicircum",
-  "underscore",
-  "quoteleft",
-  "a",
-  "b",
-  "c",
-  "d",
-  "e",
-  "f",
-  "g",
-  "h",
-  "i",
-  "j",
-  "k",
-  "l",
-  "m",
-  "n",
-  "o",
-  "p",
-  "q",
-  "r",
-  "s",
-  "t",
-  "u",
-  "v",
-  "w",
-  "x",
-  "y",
-  "z",
-  "braceleft",
-  "bar",
-  "braceright",
-  "asciitilde",
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  "exclamdown",
-  "cent",
-  "sterling",
-  "fraction",
-  "yen",
-  "florin",
-  "section",
-  "currency",
-  "quotesingle",
-  "quotedblleft",
-  "guillemotleft",
-  "guilsinglleft",
-  "guilsinglright",
-  "fi",
-  "fl",
-  NULL,
-  "endash",
-  "dagger",
-  "daggerdbl",
-  "periodcentered",
-  NULL,
-  "paragraph",
-  "bullet",
-  "quotesinglbase",
-  "quotedblbase",
-  "quotedblright",
-  "guillemotright",
-  "ellipsis",
-  "perthousand",
-  NULL,
-  "questiondown",
-  NULL,
-  "grave",
-  "acute",
-  "circumflex",
-  "tilde",
-  "macron",
-  "breve",
-  "dotaccent",
-  "dieresis",
-  NULL,
-  "ring",
-  "cedilla",
-  NULL,
-  "hungarumlaut",
-  "ogonek",
-  "caron",
-  "emdash",
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  "AE",
-  NULL,
-  "ordfeminine",
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  "Lslash",
-  "Oslash",
-  "OE",
-  "ordmasculine",
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  "ae",
-  NULL,
-  NULL,
-  NULL,
-  "dotlessi",
-  NULL,
-  NULL,
-  "lslash",
-  "oslash",
-  "oe",
-  "germandbls",
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  "Aacute",
-  "Acircumflex",
-  "Adieresis",
-  "Agrave",
-  "Aring",
-  "Atilde",
-  "Ccedilla",
-  "Eacute",
-  "Ecircumflex",
-  "Edieresis",
-  "Egrave",
-  "Eth",
-  "Iacute",
-  "Icircumflex",
-  "Idieresis",
-  "Igrave",
-  "Ntilde",
-  "Oacute",
-  "Ocircumflex",
-  "Odieresis",
-  "Ograve",
-  "Otilde",
-  "Scaron",
-  "Thorn",
-  "Uacute",
-  "Ucircumflex",
-  "Udieresis",
-  "Ugrave",
-  "Yacute",
-  "Ydieresis",
-  "Zcaron",
-  "aacute",
-  "acircumflex",
-  "adieresis",
-  "agrave",
-  "aring",
-  "atilde",
-  "brokenbar",
-  "ccedilla",
-  "copyright",
-  "degree",
-  "divide",
-  "eacute",
-  "ecircumflex",
-  "edieresis",
-  "egrave",
-  "eth",
-  "iacute",
-  "icircumflex",
-  "idieresis",
-  "igrave",
-  "logicalnot",
-  "minus",
-  "mu",
-  "multiply",
-  "ntilde",
-  "oacute",
-  "ocircumflex",
-  "odieresis",
-  "ograve",
-  "onehalf",
-  "onequarter",
-  "onesuperior",
-  "otilde",
-  "plusminus",
-  "registered",
-  "scaron",
-  "thorn",
-  "threequarters",
-  "threesuperior",
-  "trademark",
-  "twosuperior",
-  "uacute",
-  "ucircumflex",
-  "udieresis",
-  "ugrave",
-  "yacute",
-  "ydieresis",
-  "zcaron"
-};
-static FontEncoding standardEncoding(standardEncodingNames,
-                                     standardEncodingSize);
-
-#define symbolEncodingSize 257
-char *symbolEncodingNames[symbolEncodingSize] = {
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  "space",
-  "exclam",
-  "universal",
-  "numbersign",
-  "existential",
-  "percent",
-  "ampersand",
-  "suchthat",
-  "parenleft",
-  "parenright",
-  "asteriskmath",
-  "plus",
-  "comma",
-  "minus",
-  "period",
-  "slash",
-  "zero",
-  "one",
-  "two",
-  "three",
-  "four",
-  "five",
-  "six",
-  "seven",
-  "eight",
-  "nine",
-  "colon",
-  "semicolon",
-  "less",
-  "equal",
-  "greater",
-  "question",
-  "congruent",
-  "Alpha",
-  "Beta",
-  "Chi",
-  "Delta",
-  "Epsilon",
-  "Phi",
-  "Gamma",
-  "Eta",
-  "Iota",
-  "theta1",
-  "Kappa",
-  "Lambda",
-  "Mu",
-  "Nu",
-  "Omicron",
-  "Pi",
-  "Theta",
-  "Rho",
-  "Sigma",
-  "Tau",
-  "Upsilon",
-  "sigma1",
-  "Omega",
-  "Xi",
-  "Psi",
-  "Zeta",
-  "bracketleft",
-  "therefore",
-  "bracketright",
-  "perpendicular",
-  "underscore",
-  "radicalex",
-  "alpha",
-  "beta",
-  "chi",
-  "delta",
-  "epsilon",
-  "phi",
-  "gamma",
-  "eta",
-  "iota",
-  "phi1",
-  "kappa",
-  "lambda",
-  "mu",
-  "nu",
-  "omicron",
-  "pi",
-  "theta",
-  "rho",
-  "sigma",
-  "tau",
-  "upsilon",
-  "omega1",
-  "omega",
-  "xi",
-  "psi",
-  "zeta",
-  "braceleft",
-  "bar",
-  "braceright",
-  "similar",
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  "Upsilon1",
-  "minute",
-  "lessequal",
-  "fraction",
-  "infinity",
-  "florin",
-  "club",
-  "diamond",
-  "heart",
-  "spade",
-  "arrowboth",
-  "arrowleft",
-  "arrowup",
-  "arrowright",
-  "arrowdown",
-  "degree",
-  "plusminus",
-  "second",
-  "greaterequal",
-  "multiply",
-  "proportional",
-  "partialdiff",
-  "bullet",
-  "divide",
-  "notequal",
-  "equivalence",
-  "approxequal",
-  "ellipsis",
-  "arrowvertex",
-  "arrowhorizex",
-  "carriagereturn",
-  "aleph",
-  "Ifraktur",
-  "Rfraktur",
-  "weierstrass",
-  "circlemultiply",
-  "circleplus",
-  "emptyset",
-  "intersection",
-  "union",
-  "propersuperset",
-  "reflexsuperset",
-  "notsubset",
-  "propersubset",
-  "reflexsubset",
-  "element",
-  "notelement",
-  "angle",
-  "gradient",
-  "registerserif",
-  "copyrightserif",
-  "trademarkserif",
-  "product",
-  "radical",
-  "dotmath",
-  "logicalnot",
-  "logicaland",
-  "logicalor",
-  "arrowdblboth",
-  "arrowdblleft",
-  "arrowdblup",
-  "arrowdblright",
-  "arrowdbldown",
-  "lozenge",
-  "angleleft",
-  "registersans",
-  "copyrightsans",
-  "trademarksans",
-  "summation",
-  "parenlefttp",
-  "parenleftex",
-  "parenleftbt",
-  "bracketlefttp",
-  "bracketleftex",
-  "bracketleftbt",
-  "bracelefttp",
-  "braceleftmid",
-  "braceleftbt",
-  "braceex",
-  NULL,
-  "angleright",
-  "integral",
-  "integraltp",
-  "integralex",
-  "integralbt",
-  "parenrighttp",
-  "parenrightex",
-  "parenrightbt",
-  "bracketrighttp",
-  "bracketrightex",
-  "bracketrightbt",
-  "bracerighttp",
-  "bracerightmid",
-  "bracerightbt",
-  NULL,
-  "apple"
-};
-static FontEncoding symbolEncoding(symbolEncodingNames,
-                                   symbolEncodingSize);
-
-#define zapfDingbatsEncodingSize 270
-char *zapfDingbatsEncodingNames[zapfDingbatsEncodingSize] = {
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  "space",
-  "a1",
-  "a2",
-  "a202",
-  "a3",
-  "a4",
-  "a5",
-  "a119",
-  "a118",
-  "a117",
-  "a11",
-  "a12",
-  "a13",
-  "a14",
-  "a15",
-  "a16",
-  "a105",
-  "a17",
-  "a18",
-  "a19",
-  "a20",
-  "a21",
-  "a22",
-  "a23",
-  "a24",
-  "a25",
-  "a26",
-  "a27",
-  "a28",
-  "a6",
-  "a7",
-  "a8",
-  "a9",
-  "a10",
-  "a29",
-  "a30",
-  "a31",
-  "a32",
-  "a33",
-  "a34",
-  "a35",
-  "a36",
-  "a37",
-  "a38",
-  "a39",
-  "a40",
-  "a41",
-  "a42",
-  "a43",
-  "a44",
-  "a45",
-  "a46",
-  "a47",
-  "a48",
-  "a49",
-  "a50",
-  "a51",
-  "a52",
-  "a53",
-  "a54",
-  "a55",
-  "a56",
-  "a57",
-  "a58",
-  "a59",
-  "a60",
-  "a61",
-  "a62",
-  "a63",
-  "a64",
-  "a65",
-  "a66",
-  "a67",
-  "a68",
-  "a69",
-  "a70",
-  "a71",
-  "a72",
-  "a73",
-  "a74",
-  "a203",
-  "a75",
-  "a204",
-  "a76",
-  "a77",
-  "a78",
-  "a79",
-  "a81",
-  "a82",
-  "a83",
-  "a84",
-  "a97",
-  "a98",
-  "a99",
-  "a100",
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  "a101",
-  "a102",
-  "a103",
-  "a104",
-  "a106",
-  "a107",
-  "a108",
-  "a112",
-  "a111",
-  "a110",
-  "a109",
-  "a120",
-  "a121",
-  "a122",
-  "a123",
-  "a124",
-  "a125",
-  "a126",
-  "a127",
-  "a128",
-  "a129",
-  "a130",
-  "a131",
-  "a132",
-  "a133",
-  "a134",
-  "a135",
-  "a136",
-  "a137",
-  "a138",
-  "a139",
-  "a140",
-  "a141",
-  "a142",
-  "a143",
-  "a144",
-  "a145",
-  "a146",
-  "a147",
-  "a148",
-  "a149",
-  "a150",
-  "a151",
-  "a152",
-  "a153",
-  "a154",
-  "a155",
-  "a156",
-  "a157",
-  "a158",
-  "a159",
-  "a160",
-  "a161",
-  "a163",
-  "a164",
-  "a196",
-  "a165",
-  "a192",
-  "a166",
-  "a167",
-  "a168",
-  "a169",
-  "a170",
-  "a171",
-  "a172",
-  "a173",
-  "a162",
-  "a174",
-  "a175",
-  "a176",
-  "a177",
-  "a178",
-  "a179",
-  "a193",
-  "a180",
-  "a199",
-  "a181",
-  "a200",
-  "a182",
-  NULL,
-  "a201",
-  "a183",
-  "a184",
-  "a197",
-  "a185",
-  "a194",
-  "a198",
-  "a186",
-  "a195",
-  "a187",
-  "a188",
-  "a189",
-  "a190",
-  "a191",
-  NULL,
-  "a205",
-  "a206",
-  "a85",
-  "a86",
-  "a87",
-  "a88",
-  "a89",
-  "a90",
-  "a91",
-  "a92",
-  "a93",
-  "a94",
-  "a95",
-  "a96"
-};
-static FontEncoding zapfDingbatsEncoding(zapfDingbatsEncodingNames,
-                                         zapfDingbatsEncodingSize);
-
-#define macRomanEncodingSize 256
-char *macRomanEncodingNames[macRomanEncodingSize] = {
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  "space",
-  "exclam",
-  "quotedbl",
-  "numbersign",
-  "dollar",
-  "percent",
-  "ampersand",
-  "quotesingle",
-  "parenleft",
-  "parenright",
-  "asterisk",
-  "plus",
-  "comma",
-  "hyphen",
-  "period",
-  "slash",
-  "zero",
-  "one",
-  "two",
-  "three",
-  "four",
-  "five",
-  "six",
-  "seven",
-  "eight",
-  "nine",
-  "colon",
-  "semicolon",
-  "less",
-  "equal",
-  "greater",
-  "question",
-  "at",
-  "A",
-  "B",
-  "C",
-  "D",
-  "E",
-  "F",
-  "G",
-  "H",
-  "I",
-  "J",
-  "K",
-  "L",
-  "M",
-  "N",
-  "O",
-  "P",
-  "Q",
-  "R",
-  "S",
-  "T",
-  "U",
-  "V",
-  "W",
-  "X",
-  "Y",
-  "Z",
-  "bracketleft",
-  "backslash",
-  "bracketright",
-  "asciicircum",
-  "underscore",
-  "grave",
-  "a",
-  "b",
-  "c",
-  "d",
-  "e",
-  "f",
-  "g",
-  "h",
-  "i",
-  "j",
-  "k",
-  "l",
-  "m",
-  "n",
-  "o",
-  "p",
-  "q",
-  "r",
-  "s",
-  "t",
-  "u",
-  "v",
-  "w",
-  "x",
-  "y",
-  "z",
-  "braceleft",
-  "bar",
-  "braceright",
-  "asciitilde",
-  NULL,
-  "Adieresis",
-  "Aring",
-  "Ccedilla",
-  "Eacute",
-  "Ntilde",
-  "Odieresis",
-  "Udieresis",
-  "aacute",
-  "agrave",
-  "acircumflex",
-  "adieresis",
-  "atilde",
-  "aring",
-  "ccedilla",
-  "eacute",
-  "egrave",
-  "ecircumflex",
-  "edieresis",
-  "iacute",
-  "igrave",
-  "icircumflex",
-  "idieresis",
-  "ntilde",
-  "oacute",
-  "ograve",
-  "ocircumflex",
-  "odieresis",
-  "otilde",
-  "uacute",
-  "ugrave",
-  "ucircumflex",
-  "udieresis",
-  "dagger",
-  "degree",
-  "cent",
-  "sterling",
-  "section",
-  "bullet",
-  "paragraph",
-  "germandbls",
-  "registered",
-  "copyright",
-  "trademark",
-  "acute",
-  "dieresis",
-  NULL,
-  "AE",
-  "Oslash",
-  NULL,
-  "plusminus",
-  NULL,
-  NULL,
-  "yen",
-  "mu",
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  "ordfeminine",
-  "ordmasculine",
-  NULL,
-  "ae",
-  "oslash",
-  "questiondown",
-  "exclamdown",
-  "logicalnot",
-  NULL,
-  "florin",
-  NULL,
-  NULL,
-  "guillemotleft",
-  "guillemotright",
-  "ellipsis",
-  "space",
-  "Agrave",
-  "Atilde",
-  "Otilde",
-  "OE",
-  "oe",
-  "endash",
-  "emdash",
-  "quotedblleft",
-  "quotedblright",
-  "quoteleft",
-  "quoteright",
-  "divide",
-  NULL,
-  "ydieresis",
-  "Ydieresis",
-  "fraction",
-  "currency",
-  "guilsinglleft",
-  "guilsinglright",
-  "fi",
-  "fl",
-  "daggerdbl",
-  "periodcentered",
-  "quotesinglbase",
-  "quotedblbase",
-  "perthousand",
-  "Acircumflex",
-  "Ecircumflex",
-  "Aacute",
-  "Edieresis",
-  "Egrave",
-  "Iacute",
-  "Icircumflex",
-  "Idieresis",
-  "Igrave",
-  "Oacute",
-  "Ocircumflex",
-  NULL,
-  "Ograve",
-  "Uacute",
-  "Ucircumflex",
-  "Ugrave",
-  "dotlessi",
-  "circumflex",
-  "tilde",
-  "macron",
-  "breve",
-  "dotaccent",
-  "ring",
-  "cedilla",
-  "hungarumlaut",
-  "ogonek",
-  "caron"
-};
-static FontEncoding macRomanEncoding(macRomanEncodingNames,
-                                     macRomanEncodingSize);
-
-#define winAnsiEncodingSize 256
-static char *winAnsiEncodingNames[winAnsiEncodingSize] = {
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  "space",
-  "exclam",
-  "quotedbl",
-  "numbersign",
-  "dollar",
-  "percent",
-  "ampersand",
-  "quotesingle",
-  "parenleft",
-  "parenright",
-  "asterisk",
-  "plus",
-  "comma",
-  "hyphen",
-  "period",
-  "slash",
-  "zero",
-  "one",
-  "two",
-  "three",
-  "four",
-  "five",
-  "six",
-  "seven",
-  "eight",
-  "nine",
-  "colon",
-  "semicolon",
-  "less",
-  "equal",
-  "greater",
-  "question",
-  "at",
-  "A",
-  "B",
-  "C",
-  "D",
-  "E",
-  "F",
-  "G",
-  "H",
-  "I",
-  "J",
-  "K",
-  "L",
-  "M",
-  "N",
-  "O",
-  "P",
-  "Q",
-  "R",
-  "S",
-  "T",
-  "U",
-  "V",
-  "W",
-  "X",
-  "Y",
-  "Z",
-  "bracketleft",
-  "backslash",
-  "bracketright",
-  "asciicircum",
-  "underscore",
-  "grave",
-  "a",
-  "b",
-  "c",
-  "d",
-  "e",
-  "f",
-  "g",
-  "h",
-  "i",
-  "j",
-  "k",
-  "l",
-  "m",
-  "n",
-  "o",
-  "p",
-  "q",
-  "r",
-  "s",
-  "t",
-  "u",
-  "v",
-  "w",
-  "x",
-  "y",
-  "z",
-  "braceleft",
-  "bar",
-  "braceright",
-  "asciitilde",
-  "bullet",
-  "bullet",
-  "bullet",
-  "quotesinglbase",
-  "florin",
-  "quotedblbase",
-  "ellipsis",
-  "dagger",
-  "daggerdbl",
-  "circumflex",
-  "perthousand",
-  "Scaron",
-  "guilsinglleft",
-  "OE",
-  "bullet",
-  "bullet",
-  "bullet",
-  "bullet",
-  "quoteleft",
-  "quoteright",
-  "quotedblleft",
-  "quotedblright",
-  "bullet",
-  "endash",
-  "emdash",
-  "tilde",
-  "trademark",
-  "scaron",
-  "guilsinglright",
-  "oe",
-  "bullet",
-  "bullet",
-  "Ydieresis",
-  "space",
-  "exclamdown",
-  "cent",
-  "sterling",
-  "currency",
-  "yen",
-  "brokenbar",
-  "section",
-  "dieresis",
-  "copyright",
-  "ordfeminine",
-  "guillemotleft",
-  "logicalnot",
-  "hyphen",
-  "registered",
-  "macron",
-  "degree",
-  "plusminus",
-  "twosuperior",
-  "threesuperior",
-  "acute",
-  "mu",
-  "paragraph",
-  "periodcentered",
-  "cedilla",
-  "onesuperior",
-  "ordmasculine",
-  "guillemotright",
-  "onequarter",
-  "onehalf",
-  "threequarters",
-  "questiondown",
-  "Agrave",
-  "Aacute",
-  "Acircumflex",
-  "Atilde",
-  "Adieresis",
-  "Aring",
-  "AE",
-  "Ccedilla",
-  "Egrave",
-  "Eacute",
-  "Ecircumflex",
-  "Edieresis",
-  "Igrave",
-  "Iacute",
-  "Icircumflex",
-  "Idieresis",
-  "Eth",
-  "Ntilde",
-  "Ograve",
-  "Oacute",
-  "Ocircumflex",
-  "Otilde",
-  "Odieresis",
-  "multiply",
-  "Oslash",
-  "Ugrave",
-  "Uacute",
-  "Ucircumflex",
-  "Udieresis",
-  "Yacute",
-  "Thorn",
-  "germandbls",
-  "agrave",
-  "aacute",
-  "acircumflex",
-  "atilde",
-  "adieresis",
-  "aring",
-  "ae",
-  "ccedilla",
-  "egrave",
-  "eacute",
-  "ecircumflex",
-  "edieresis",
-  "igrave",
-  "iacute",
-  "icircumflex",
-  "idieresis",
-  "eth",
-  "ntilde",
-  "ograve",
-  "oacute",
-  "ocircumflex",
-  "otilde",
-  "odieresis",
-  "divide",
-  "oslash",
-  "ugrave",
-  "uacute",
-  "ucircumflex",
-  "udieresis",
-  "yacute",
-  "thorn",
-  "ydieresis"
-};
-static FontEncoding winAnsiEncoding(winAnsiEncodingNames,
-                                    winAnsiEncodingSize);
-
-//------------------------------------------------------------------------
-// Character widths for built-in fonts.
-//------------------------------------------------------------------------
-
-static Gushort courierWidths[335] = {
-     0,    0,    0,    0,    0,    0,    0,    0,
-     0,    0,    0,    0,    0,    0,    0,    0,
-     0,    0,    0,    0,    0,    0,    0,    0,
-     0,    0,    0,    0,    0,    0,    0,    0,
-   600,  600,  600,  600,  600,  600,  600,  600,
-   600,  600,  600,  600,  600,  600,  600,  600,
-   600,  600,  600,  600,  600,  600,  600,  600,
-   600,  600,  600,  600,  600,  600,  600,  600,
-   600,  600,  600,  600,  600,  600,  600,  600,
-   600,  600,  600,  600,  600,  600,  600,  600,
-   600,  600,  600,  600,  600,  600,  600,  600,
-   600,  600,  600,  600,  600,  600,  600,  600,
-   600,  600,  600,  600,  600,  600,  600,  600,
-   600,  600,  600,  600,  600,  600,  600,  600,
-   600,  600,  600,  600,  600,  600,  600,  600,
-   600,  600,  600,  600,  600,  600,  600,    0,
-     0,    0,    0,    0,    0,    0,    0,    0,
-     0,    0,    0,    0,    0,    0,    0,    0,
-     0,    0,    0,    0,    0,    0,    0,    0,
-     0,    0,    0,    0,    0,    0,    0,    0,
-     0,  600,  600,  600,  600,  600,  600,  600,
-   600,  600,  600,  600,  600,  600,  600,  600,
-     0,  600,  600,  600,  600,    0,  600,  600,
-   600,  600,  600,  600,  600,  600,    0,  600,
-     0,  600,  600,  600,  600,  600,  600,  600,
-   600,    0,  600,  600,    0,  600,  600,  600,
-   600,    0,    0,    0,    0,    0,    0,    0,
-     0,    0,    0,    0,    0,    0,    0,    0,
-     0,  600,    0,  600,    0,    0,    0,    0,
-   600,  600,  600,  600,    0,    0,    0,    0,
-     0,  600,    0,    0,    0,  600,    0,    0,
-   600,  600,  600,  600,    0,    0,    0,    0,
-   600,  600,  600,  600,  600,  600,  600,  600,
-   600,  600,  600,  600,  600,  600,  600,  600,
-   600,  600,  600,  600,  600,  600,  600,  600,
-   600,  600,  600,  600,  600,  600,  600,  600,
-   600,  600,  600,  600,  600,  600,  600,  600,
-   600,  600,  600,  600,  600,  600,  600,  600,
-   600,  600,  600,  600,  600,  600,  600,  600,
-   600,  600,  600,  600,  600,  600,  600,  600,
-   600,  600,  600,  600,  600,  600,  600,  600,
-   600,  600,  600,  600,  600,  600,  600
-};
-
-static Gushort courierBoldWidths[335] = {
-     0,    0,    0,    0,    0,    0,    0,    0,
-     0,    0,    0,    0,    0,    0,    0,    0,
-     0,    0,    0,    0,    0,    0,    0,    0,
-     0,    0,    0,    0,    0,    0,    0,    0,
-   600,  600,  600,  600,  600,  600,  600,  600,
-   600,  600,  600,  600,  600,  600,  600,  600,
-   600,  600,  600,  600,  600,  600,  600,  600,
-   600,  600,  600,  600,  600,  600,  600,  600,
-   600,  600,  600,  600,  600,  600,  600,  600,
-   600,  600,  600,  600,  600,  600,  600,  600,
-   600,  600,  600,  600,  600,  600,  600,  600,
-   600,  600,  600,  600,  600,  600,  600,  600,
-   600,  600,  600,  600,  600,  600,  600,  600,
-   600,  600,  600,  600,  600,  600,  600,  600,
-   600,  600,  600,  600,  600,  600,  600,  600,
-   600,  600,  600,  600,  600,  600,  600,    0,
-     0,    0,    0,    0,    0,    0,    0,    0,
-     0,    0,    0,    0,    0,    0,    0,    0,
-     0,    0,    0,    0,    0,    0,    0,    0,
-     0,    0,    0,    0,    0,    0,    0,    0,
-     0,  600,  600,  600,  600,  600,  600,  600,
-   600,  600,  600,  600,  600,  600,  600,  600,
-     0,  600,  600,  600,  600,    0,  600,  600,
-   600,  600,  600,  600,  600,  600,    0,  600,
-     0,  600,  600,  600,  600,  600,  600,  600,
-   600,    0,  600,  600,    0,  600,  600,  600,
-   600,    0,    0,    0,    0,    0,    0,    0,
-     0,    0,    0,    0,    0,    0,    0,    0,
-     0,  600,    0,  600,    0,    0,    0,    0,
-   600,  600,  600,  600,    0,    0,    0,    0,
-     0,  600,    0,    0,    0,  600,    0,    0,
-   600,  600,  600,  600,    0,    0,    0,    0,
-   600,  600,  600,  600,  600,  600,  600,  600,
-   600,  600,  600,  600,  600,  600,  600,  600,
-   600,  600,  600,  600,  600,  600,  600,  600,
-   600,  600,  600,  600,  600,  600,  600,  600,
-   600,  600,  600,  600,  600,  600,  600,  600,
-   600,  600,  600,  600,  600,  600,  600,  600,
-   600,  600,  600,  600,  600,  600,  600,  600,
-   600,  600,  600,  600,  600,  600,  600,  600,
-   600,  600,  600,  600,  600,  600,  600,  600,
-   600,  600,  600,  600,  600,  600,  600
-};
-
-static Gushort courierBoldObliqueWidths[335] = {
-     0,    0,    0,    0,    0,    0,    0,    0,
-     0,    0,    0,    0,    0,    0,    0,    0,
-     0,    0,    0,    0,    0,    0,    0,    0,
-     0,    0,    0,    0,    0,    0,    0,    0,
-   600,  600,  600,  600,  600,  600,  600,  600,
-   600,  600,  600,  600,  600,  600,  600,  600,
-   600,  600,  600,  600,  600,  600,  600,  600,
-   600,  600,  600,  600,  600,  600,  600,  600,
-   600,  600,  600,  600,  600,  600,  600,  600,
-   600,  600,  600,  600,  600,  600,  600,  600,
-   600,  600,  600,  600,  600,  600,  600,  600,
-   600,  600,  600,  600,  600,  600,  600,  600,
-   600,  600,  600,  600,  600,  600,  600,  600,
-   600,  600,  600,  600,  600,  600,  600,  600,
-   600,  600,  600,  600,  600,  600,  600,  600,
-   600,  600,  600,  600,  600,  600,  600,    0,
-     0,    0,    0,    0,    0,    0,    0,    0,
-     0,    0,    0,    0,    0,    0,    0,    0,
-     0,    0,    0,    0,    0,    0,    0,    0,
-     0,    0,    0,    0,    0,    0,    0,    0,
-     0,  600,  600,  600,  600,  600,  600,  600,
-   600,  600,  600,  600,  600,  600,  600,  600,
-     0,  600,  600,  600,  600,    0,  600,  600,
-   600,  600,  600,  600,  600,  600,    0,  600,
-     0,  600,  600,  600,  600,  600,  600,  600,
-   600,    0,  600,  600,    0,  600,  600,  600,
-   600,    0,    0,    0,    0,    0,    0,    0,
-     0,    0,    0,    0,    0,    0,    0,    0,
-     0,  600,    0,  600,    0,    0,    0,    0,
-   600,  600,  600,  600,    0,    0,    0,    0,
-     0,  600,    0,    0,    0,  600,    0,    0,
-   600,  600,  600,  600,    0,    0,    0,    0,
-   600,  600,  600,  600,  600,  600,  600,  600,
-   600,  600,  600,  600,  600,  600,  600,  600,
-   600,  600,  600,  600,  600,  600,  600,  600,
-   600,  600,  600,  600,  600,  600,  600,  600,
-   600,  600,  600,  600,  600,  600,  600,  600,
-   600,  600,  600,  600,  600,  600,  600,  600,
-   600,  600,  600,  600,  600,  600,  600,  600,
-   600,  600,  600,  600,  600,  600,  600,  600,
-   600,  600,  600,  600,  600,  600,  600,  600,
-   600,  600,  600,  600,  600,  600,  600
-};
-
-static Gushort courierObliqueWidths[335] = {
-     0,    0,    0,    0,    0,    0,    0,    0,
-     0,    0,    0,    0,    0,    0,    0,    0,
-     0,    0,    0,    0,    0,    0,    0,    0,
-     0,    0,    0,    0,    0,    0,    0,    0,
-   600,  600,  600,  600,  600,  600,  600,  600,
-   600,  600,  600,  600,  600,  600,  600,  600,
-   600,  600,  600,  600,  600,  600,  600,  600,
-   600,  600,  600,  600,  600,  600,  600,  600,
-   600,  600,  600,  600,  600,  600,  600,  600,
-   600,  600,  600,  600,  600,  600,  600,  600,
-   600,  600,  600,  600,  600,  600,  600,  600,
-   600,  600,  600,  600,  600,  600,  600,  600,
-   600,  600,  600,  600,  600,  600,  600,  600,
-   600,  600,  600,  600,  600,  600,  600,  600,
-   600,  600,  600,  600,  600,  600,  600,  600,
-   600,  600,  600,  600,  600,  600,  600,    0,
-     0,    0,    0,    0,    0,    0,    0,    0,
-     0,    0,    0,    0,    0,    0,    0,    0,
-     0,    0,    0,    0,    0,    0,    0,    0,
-     0,    0,    0,    0,    0,    0,    0,    0,
-     0,  600,  600,  600,  600,  600,  600,  600,
-   600,  600,  600,  600,  600,  600,  600,  600,
-     0,  600,  600,  600,  600,    0,  600,  600,
-   600,  600,  600,  600,  600,  600,    0,  600,
-     0,  600,  600,  600,  600,  600,  600,  600,
-   600,    0,  600,  600,    0,  600,  600,  600,
-   600,    0,    0,    0,    0,    0,    0,    0,
-     0,    0,    0,    0,    0,    0,    0,    0,
-     0,  600,    0,  600,    0,    0,    0,    0,
-   600,  600,  600,  600,    0,    0,    0,    0,
-     0,  600,    0,    0,    0,  600,    0,    0,
-   600,  600,  600,  600,    0,    0,    0,    0,
-   600,  600,  600,  600,  600,  600,  600,  600,
-   600,  600,  600,  600,  600,  600,  600,  600,
-   600,  600,  600,  600,  600,  600,  600,  600,
-   600,  600,  600,  600,  600,  600,  600,  600,
-   600,  600,  600,  600,  600,  600,  600,  600,
-   600,  600,  600,  600,  600,  600,  600,  600,
-   600,  600,  600,  600,  600,  600,  600,  600,
-   600,  600,  600,  600,  600,  600,  600,  600,
-   600,  600,  600,  600,  600,  600,  600,  600,
-   600,  600,  600,  600,  600,  600,  600
-};
-
-static Gushort helveticaWidths[335] = {
-     0,    0,    0,    0,    0,    0,    0,    0,
-     0,    0,    0,    0,    0,    0,    0,    0,
-     0,    0,    0,    0,    0,    0,    0,    0,
-     0,    0,    0,    0,    0,    0,    0,    0,
-   278,  278,  355,  556,  556,  889,  667,  222,
-   333,  333,  389,  584,  278,  333,  278,  278,
-   556,  556,  556,  556,  556,  556,  556,  556,
-   556,  556,  278,  278,  584,  584,  584,  556,
-  1015,  667,  667,  722,  722,  667,  611,  778,
-   722,  278,  500,  667,  556,  833,  722,  778,
-   667,  778,  722,  667,  611,  722,  667,  944,
-   667,  667,  611,  278,  278,  278,  469,  556,
-   222,  556,  556,  500,  556,  556,  278,  556,
-   556,  222,  222,  500,  222,  833,  556,  556,
-   556,  556,  333,  500,  278,  556,  500,  722,
-   500,  500,  500,  334,  260,  334,  584,    0,
-     0,    0,    0,    0,    0,    0,    0,    0,
-     0,    0,    0,    0,    0,    0,    0,    0,
-     0,    0,    0,    0,    0,    0,    0,    0,
-     0,    0,    0,    0,    0,    0,    0,    0,
-     0,  333,  556,  556,  167,  556,  556,  556,
-   556,  191,  333,  556,  333,  333,  500,  500,
-     0,  556,  556,  556,  278,    0,  537,  350,
-   222,  333,  333,  556, 1000, 1000,    0,  611,
-     0,  333,  333,  333,  333,  333,  333,  333,
-   333,    0,  333,  333,    0,  333,  333,  333,
-  1000,    0,    0,    0,    0,    0,    0,    0,
-     0,    0,    0,    0,    0,    0,    0,    0,
-     0, 1000,    0,  370,    0,    0,    0,    0,
-   556,  778, 1000,  365,    0,    0,    0,    0,
-     0,  889,    0,    0,    0,  278,    0,    0,
-   222,  611,  944,  611,    0,    0,    0,    0,
-   667,  667,  667,  667,  667,  667,  722,  667,
-   667,  667,  667,  722,  278,  278,  278,  278,
-   722,  778,  778,  778,  778,  778,  667,  667,
-   722,  722,  722,  722,  667,  667,  611,  556,
-   556,  556,  556,  556,  556,  260,  500,  737,
-   400,  584,  556,  556,  556,  556,  556,  278,
-   278,  278,  278,  584,  584,  556,  584,  556,
-   556,  556,  556,  556,  834,  834,  333,  556,
-   584,  737,  500,  556,  834,  333, 1000,  333,
-   556,  556,  556,  556,  500,  500,  500
-};
-
-static Gushort helveticaBoldWidths[335] = {
-     0,    0,    0,    0,    0,    0,    0,    0,
-     0,    0,    0,    0,    0,    0,    0,    0,
-     0,    0,    0,    0,    0,    0,    0,    0,
-     0,    0,    0,    0,    0,    0,    0,    0,
-   278,  333,  474,  556,  556,  889,  722,  278,
-   333,  333,  389,  584,  278,  333,  278,  278,
-   556,  556,  556,  556,  556,  556,  556,  556,
-   556,  556,  333,  333,  584,  584,  584,  611,
-   975,  722,  722,  722,  722,  667,  611,  778,
-   722,  278,  556,  722,  611,  833,  722,  778,
-   667,  778,  722,  667,  611,  722,  667,  944,
-   667,  667,  611,  333,  278,  333,  584,  556,
-   278,  556,  611,  556,  611,  556,  333,  611,
-   611,  278,  278,  556,  278,  889,  611,  611,
-   611,  611,  389,  556,  333,  611,  556,  778,
-   556,  556,  500,  389,  280,  389,  584,    0,
-     0,    0,    0,    0,    0,    0,    0,    0,
-     0,    0,    0,    0,    0,    0,    0,    0,
-     0,    0,    0,    0,    0,    0,    0,    0,
-     0,    0,    0,    0,    0,    0,    0,    0,
-     0,  333,  556,  556,  167,  556,  556,  556,
-   556,  238,  500,  556,  333,  333,  611,  611,
-     0,  556,  556,  556,  278,    0,  556,  350,
-   278,  500,  500,  556, 1000, 1000,    0,  611,
-     0,  333,  333,  333,  333,  333,  333,  333,
-   333,    0,  333,  333,    0,  333,  333,  333,
-  1000,    0,    0,    0,    0,    0,    0,    0,
-     0,    0,    0,    0,    0,    0,    0,    0,
-     0, 1000,    0,  370,    0,    0,    0,    0,
-   611,  778, 1000,  365,    0,    0,    0,    0,
-     0,  889,    0,    0,    0,  278,    0,    0,
-   278,  611,  944,  611,    0,    0,    0,    0,
-   722,  722,  722,  722,  722,  722,  722,  667,
-   667,  667,  667,  722,  278,  278,  278,  278,
-   722,  778,  778,  778,  778,  778,  667,  667,
-   722,  722,  722,  722,  667,  667,  611,  556,
-   556,  556,  556,  556,  556,  280,  556,  737,
-   400,  584,  556,  556,  556,  556,  611,  278,
-   278,  278,  278,  584,  584,  611,  584,  611,
-   611,  611,  611,  611,  834,  834,  333,  611,
-   584,  737,  556,  611,  834,  333, 1000,  333,
-   611,  611,  611,  611,  556,  556,  500
-};
-
-static Gushort helveticaBoldObliqueWidths[335] = {
-     0,    0,    0,    0,    0,    0,    0,    0,
-     0,    0,    0,    0,    0,    0,    0,    0,
-     0,    0,    0,    0,    0,    0,    0,    0,
-     0,    0,    0,    0,    0,    0,    0,    0,
-   278,  333,  474,  556,  556,  889,  722,  278,
-   333,  333,  389,  584,  278,  333,  278,  278,
-   556,  556,  556,  556,  556,  556,  556,  556,
-   556,  556,  333,  333,  584,  584,  584,  611,
-   975,  722,  722,  722,  722,  667,  611,  778,
-   722,  278,  556,  722,  611,  833,  722,  778,
-   667,  778,  722,  667,  611,  722,  667,  944,
-   667,  667,  611,  333,  278,  333,  584,  556,
-   278,  556,  611,  556,  611,  556,  333,  611,
-   611,  278,  278,  556,  278,  889,  611,  611,
-   611,  611,  389,  556,  333,  611,  556,  778,
-   556,  556,  500,  389,  280,  389,  584,    0,
-     0,    0,    0,    0,    0,    0,    0,    0,
-     0,    0,    0,    0,    0,    0,    0,    0,
-     0,    0,    0,    0,    0,    0,    0,    0,
-     0,    0,    0,    0,    0,    0,    0,    0,
-     0,  333,  556,  556,  167,  556,  556,  556,
-   556,  238,  500,  556,  333,  333,  611,  611,
-     0,  556,  556,  556,  278,    0,  556,  350,
-   278,  500,  500,  556, 1000, 1000,    0,  611,
-     0,  333,  333,  333,  333,  333,  333,  333,
-   333,    0,  333,  333,    0,  333,  333,  333,
-  1000,    0,    0,    0,    0,    0,    0,    0,
-     0,    0,    0,    0,    0,    0,    0,    0,
-     0, 1000,    0,  370,    0,    0,    0,    0,
-   611,  778, 1000,  365,    0,    0,    0,    0,
-     0,  889,    0,    0,    0,  278,    0,    0,
-   278,  611,  944,  611,    0,    0,    0,    0,
-   722,  722,  722,  722,  722,  722,  722,  667,
-   667,  667,  667,  722,  278,  278,  278,  278,
-   722,  778,  778,  778,  778,  778,  667,  667,
-   722,  722,  722,  722,  667,  667,  611,  556,
-   556,  556,  556,  556,  556,  280,  556,  737,
-   400,  584,  556,  556,  556,  556,  611,  278,
-   278,  278,  278,  584,  584,  611,  584,  611,
-   611,  611,  611,  611,  834,  834,  333,  611,
-   584,  737,  556,  611,  834,  333, 1000,  333,
-   611,  611,  611,  611,  556,  556,  500
-};
-
-static Gushort helveticaObliqueWidths[335] = {
-     0,    0,    0,    0,    0,    0,    0,    0,
-     0,    0,    0,    0,    0,    0,    0,    0,
-     0,    0,    0,    0,    0,    0,    0,    0,
-     0,    0,    0,    0,    0,    0,    0,    0,
-   278,  278,  355,  556,  556,  889,  667,  222,
-   333,  333,  389,  584,  278,  333,  278,  278,
-   556,  556,  556,  556,  556,  556,  556,  556,
-   556,  556,  278,  278,  584,  584,  584,  556,
-  1015,  667,  667,  722,  722,  667,  611,  778,
-   722,  278,  500,  667,  556,  833,  722,  778,
-   667,  778,  722,  667,  611,  722,  667,  944,
-   667,  667,  611,  278,  278,  278,  469,  556,
-   222,  556,  556,  500,  556,  556,  278,  556,
-   556,  222,  222,  500,  222,  833,  556,  556,
-   556,  556,  333,  500,  278,  556,  500,  722,
-   500,  500,  500,  334,  260,  334,  584,    0,
-     0,    0,    0,    0,    0,    0,    0,    0,
-     0,    0,    0,    0,    0,    0,    0,    0,
-     0,    0,    0,    0,    0,    0,    0,    0,
-     0,    0,    0,    0,    0,    0,    0,    0,
-     0,  333,  556,  556,  167,  556,  556,  556,
-   556,  191,  333,  556,  333,  333,  500,  500,
-     0,  556,  556,  556,  278,    0,  537,  350,
-   222,  333,  333,  556, 1000, 1000,    0,  611,
-     0,  333,  333,  333,  333,  333,  333,  333,
-   333,    0,  333,  333,    0,  333,  333,  333,
-  1000,    0,    0,    0,    0,    0,    0,    0,
-     0,    0,    0,    0,    0,    0,    0,    0,
-     0, 1000,    0,  370,    0,    0,    0,    0,
-   556,  778, 1000,  365,    0,    0,    0,    0,
-     0,  889,    0,    0,    0,  278,    0,    0,
-   222,  611,  944,  611,    0,    0,    0,    0,
-   667,  667,  667,  667,  667,  667,  722,  667,
-   667,  667,  667,  722,  278,  278,  278,  278,
-   722,  778,  778,  778,  778,  778,  667,  667,
-   722,  722,  722,  722,  667,  667,  611,  556,
-   556,  556,  556,  556,  556,  260,  500,  737,
-   400,  584,  556,  556,  556,  556,  556,  278,
-   278,  278,  278,  584,  584,  556,  584,  556,
-   556,  556,  556,  556,  834,  834,  333,  556,
-   584,  737,  500,  556,  834,  333, 1000,  333,
-   556,  556,  556,  556,  500,  500,  500
-};
-
-static Gushort symbolWidths[257] = {
-     0,    0,    0,    0,    0,    0,    0,    0,
-     0,    0,    0,    0,    0,    0,    0,    0,
-     0,    0,    0,    0,    0,    0,    0,    0,
-     0,    0,    0,    0,    0,    0,    0,    0,
-   250,  333,  713,  500,  549,  833,  778,  439,
-   333,  333,  500,  549,  250,  549,  250,  278,
-   500,  500,  500,  500,  500,  500,  500,  500,
-   500,  500,  278,  278,  549,  549,  549,  444,
-   549,  722,  667,  722,  612,  611,  763,  603,
-   722,  333,  631,  722,  686,  889,  722,  722,
-   768,  741,  556,  592,  611,  690,  439,  768,
-   645,  795,  611,  333,  863,  333,  658,  500,
-   500,  631,  549,  549,  494,  439,  521,  411,
-   603,  329,  603,  549,  549,  576,  521,  549,
-   549,  521,  549,  603,  439,  576,  713,  686,
-   493,  686,  494,  480,  200,  480,  549,    0,
-     0,    0,    0,    0,    0,    0,    0,    0,
-     0,    0,    0,    0,    0,    0,    0,    0,
-     0,    0,    0,    0,    0,    0,    0,    0,
-     0,    0,    0,    0,    0,    0,    0,    0,
-     0,  620,  247,  549,  167,  713,  500,  753,
-   753,  753,  753, 1042,  987,  603,  987,  603,
-   400,  549,  411,  549,  549,  713,  494,  460,
-   549,  549,  549,  549, 1000,  603, 1000,  658,
-   823,  686,  795,  987,  768,  768,  823,  768,
-   768,  713,  713,  713,  713,  713,  713,  713,
-   768,  713,  790,  790,  890,  823,  549,  250,
-   713,  603,  603, 1042,  987,  603,  987,  603,
-   494,  329,  790,  790,  786,  713,  384,  384,
-   384,  384,  384,  384,  494,  494,  494,  494,
-     0,  329,  274,  686,  686,  686,  384,  384,
-   384,  384,  384,  384,  494,  494,  494,    0,
-   790
-};
-
-static Gushort timesBoldWidths[335] = {
-     0,    0,    0,    0,    0,    0,    0,    0,
-     0,    0,    0,    0,    0,    0,    0,    0,
-     0,    0,    0,    0,    0,    0,    0,    0,
-     0,    0,    0,    0,    0,    0,    0,    0,
-   250,  333,  555,  500,  500, 1000,  833,  333,
-   333,  333,  500,  570,  250,  333,  250,  278,
-   500,  500,  500,  500,  500,  500,  500,  500,
-   500,  500,  333,  333,  570,  570,  570,  500,
-   930,  722,  667,  722,  722,  667,  611,  778,
-   778,  389,  500,  778,  667,  944,  722,  778,
-   611,  778,  722,  556,  667,  722,  722, 1000,
-   722,  722,  667,  333,  278,  333,  581,  500,
-   333,  500,  556,  444,  556,  444,  333,  500,
-   556,  278,  333,  556,  278,  833,  556,  500,
-   556,  556,  444,  389,  333,  556,  500,  722,
-   500,  500,  444,  394,  220,  394,  520,    0,
-     0,    0,    0,    0,    0,    0,    0,    0,
-     0,    0,    0,    0,    0,    0,    0,    0,
-     0,    0,    0,    0,    0,    0,    0,    0,
-     0,    0,    0,    0,    0,    0,    0,    0,
-     0,  333,  500,  500,  167,  500,  500,  500,
-   500,  278,  500,  500,  333,  333,  556,  556,
-     0,  500,  500,  500,  250,    0,  540,  350,
-   333,  500,  500,  500, 1000, 1000,    0,  500,
-     0,  333,  333,  333,  333,  333,  333,  333,
-   333,    0,  333,  333,    0,  333,  333,  333,
-  1000,    0,    0,    0,    0,    0,    0,    0,
-     0,    0,    0,    0,    0,    0,    0,    0,
-     0, 1000,    0,  300,    0,    0,    0,    0,
-   667,  778, 1000,  330,    0,    0,    0,    0,
-     0,  722,    0,    0,    0,  278,    0,    0,
-   278,  500,  722,  556,    0,    0,    0,    0,
-   722,  722,  722,  722,  722,  722,  722,  667,
-   667,  667,  667,  722,  389,  389,  389,  389,
-   722,  778,  778,  778,  778,  778,  556,  611,
-   722,  722,  722,  722,  722,  722,  667,  500,
-   500,  500,  500,  500,  500,  220,  444,  747,
-   400,  570,  444,  444,  444,  444,  500,  278,
-   278,  278,  278,  570,  570,  556,  570,  556,
-   500,  500,  500,  500,  750,  750,  300,  500,
-   570,  747,  389,  556,  750,  300, 1000,  300,
-   556,  556,  556,  556,  500,  500,  444
-};
-
-static Gushort timesBoldItalicWidths[335] = {
-     0,    0,    0,    0,    0,    0,    0,    0,
-     0,    0,    0,    0,    0,    0,    0,    0,
-     0,    0,    0,    0,    0,    0,    0,    0,
-     0,    0,    0,    0,    0,    0,    0,    0,
-   250,  389,  555,  500,  500,  833,  778,  333,
-   333,  333,  500,  570,  250,  333,  250,  278,
-   500,  500,  500,  500,  500,  500,  500,  500,
-   500,  500,  333,  333,  570,  570,  570,  500,
-   832,  667,  667,  667,  722,  667,  667,  722,
-   778,  389,  500,  667,  611,  889,  722,  722,
-   611,  722,  667,  556,  611,  722,  667,  889,
-   667,  611,  611,  333,  278,  333,  570,  500,
-   333,  500,  500,  444,  500,  444,  333,  500,
-   556,  278,  278,  500,  278,  778,  556,  500,
-   500,  500,  389,  389,  278,  556,  444,  667,
-   500,  444,  389,  348,  220,  348,  570,    0,
-     0,    0,    0,    0,    0,    0,    0,    0,
-     0,    0,    0,    0,    0,    0,    0,    0,
-     0,    0,    0,    0,    0,    0,    0,    0,
-     0,    0,    0,    0,    0,    0,    0,    0,
-     0,  389,  500,  500,  167,  500,  500,  500,
-   500,  278,  500,  500,  333,  333,  556,  556,
-     0,  500,  500,  500,  250,    0,  500,  350,
-   333,  500,  500,  500, 1000, 1000,    0,  500,
-     0,  333,  333,  333,  333,  333,  333,  333,
-   333,    0,  333,  333,    0,  333,  333,  333,
-  1000,    0,    0,    0,    0,    0,    0,    0,
-     0,    0,    0,    0,    0,    0,    0,    0,
-     0,  944,    0,  266,    0,    0,    0,    0,
-   611,  722,  944,  300,    0,    0,    0,    0,
-     0,  722,    0,    0,    0,  278,    0,    0,
-   278,  500,  722,  500,    0,    0,    0,    0,
-   667,  667,  667,  667,  667,  667,  667,  667,
-   667,  667,  667,  722,  389,  389,  389,  389,
-   722,  722,  722,  722,  722,  722,  556,  611,
-   722,  722,  722,  722,  611,  611,  611,  500,
-   500,  500,  500,  500,  500,  220,  444,  747,
-   400,  570,  444,  444,  444,  444,  500,  278,
-   278,  278,  278,  606,  606,  576,  570,  556,
-   500,  500,  500,  500,  750,  750,  300,  500,
-   570,  747,  389,  500,  750,  300, 1000,  300,
-   556,  556,  556,  556,  444,  444,  389
-};
-
-static Gushort timesItalicWidths[335] = {
-     0,    0,    0,    0,    0,    0,    0,    0,
-     0,    0,    0,    0,    0,    0,    0,    0,
-     0,    0,    0,    0,    0,    0,    0,    0,
-     0,    0,    0,    0,    0,    0,    0,    0,
-   250,  333,  420,  500,  500,  833,  778,  333,
-   333,  333,  500,  675,  250,  333,  250,  278,
-   500,  500,  500,  500,  500,  500,  500,  500,
-   500,  500,  333,  333,  675,  675,  675,  500,
-   920,  611,  611,  667,  722,  611,  611,  722,
-   722,  333,  444,  667,  556,  833,  667,  722,
-   611,  722,  611,  500,  556,  722,  611,  833,
-   611,  556,  556,  389,  278,  389,  422,  500,
-   333,  500,  500,  444,  500,  444,  278,  500,
-   500,  278,  278,  444,  278,  722,  500,  500,
-   500,  500,  389,  389,  278,  500,  444,  667,
-   444,  444,  389,  400,  275,  400,  541,    0,
-     0,    0,    0,    0,    0,    0,    0,    0,
-     0,    0,    0,    0,    0,    0,    0,    0,
-     0,    0,    0,    0,    0,    0,    0,    0,
-     0,    0,    0,    0,    0,    0,    0,    0,
-     0,  389,  500,  500,  167,  500,  500,  500,
-   500,  214,  556,  500,  333,  333,  500,  500,
-     0,  500,  500,  500,  250,    0,  523,  350,
-   333,  556,  556,  500,  889, 1000,    0,  500,
-     0,  333,  333,  333,  333,  333,  333,  333,
-   333,    0,  333,  333,    0,  333,  333,  333,
-   889,    0,    0,    0,    0,    0,    0,    0,
-     0,    0,    0,    0,    0,    0,    0,    0,
-     0,  889,    0,  276,    0,    0,    0,    0,
-   556,  722,  944,  310,    0,    0,    0,    0,
-     0,  667,    0,    0,    0,  278,    0,    0,
-   278,  500,  667,  500,    0,    0,    0,    0,
-   611,  611,  611,  611,  611,  611,  667,  611,
-   611,  611,  611,  722,  333,  333,  333,  333,
-   667,  722,  722,  722,  722,  722,  500,  611,
-   722,  722,  722,  722,  556,  556,  556,  500,
-   500,  500,  500,  500,  500,  275,  444,  760,
-   400,  675,  444,  444,  444,  444,  500,  278,
-   278,  278,  278,  675,  675,  500,  675,  500,
-   500,  500,  500,  500,  750,  750,  300,  500,
-   675,  760,  389,  500,  750,  300,  980,  300,
-   500,  500,  500,  500,  444,  444,  389
-};
-
-static Gushort timesRomanWidths[335] = {
-     0,    0,    0,    0,    0,    0,    0,    0,
-     0,    0,    0,    0,    0,    0,    0,    0,
-     0,    0,    0,    0,    0,    0,    0,    0,
-     0,    0,    0,    0,    0,    0,    0,    0,
-   250,  333,  408,  500,  500,  833,  778,  333,
-   333,  333,  500,  564,  250,  333,  250,  278,
-   500,  500,  500,  500,  500,  500,  500,  500,
-   500,  500,  278,  278,  564,  564,  564,  444,
-   921,  722,  667,  667,  722,  611,  556,  722,
-   722,  333,  389,  722,  611,  889,  722,  722,
-   556,  722,  667,  556,  611,  722,  722,  944,
-   722,  722,  611,  333,  278,  333,  469,  500,
-   333,  444,  500,  444,  500,  444,  333,  500,
-   500,  278,  278,  500,  278,  778,  500,  500,
-   500,  500,  333,  389,  278,  500,  500,  722,
-   500,  500,  444,  480,  200,  480,  541,    0,
-     0,    0,    0,    0,    0,    0,    0,    0,
-     0,    0,    0,    0,    0,    0,    0,    0,
-     0,    0,    0,    0,    0,    0,    0,    0,
-     0,    0,    0,    0,    0,    0,    0,    0,
-     0,  333,  500,  500,  167,  500,  500,  500,
-   500,  180,  444,  500,  333,  333,  556,  556,
-     0,  500,  500,  500,  250,    0,  453,  350,
-   333,  444,  444,  500, 1000, 1000,    0,  444,
-     0,  333,  333,  333,  333,  333,  333,  333,
-   333,    0,  333,  333,    0,  333,  333,  333,
-  1000,    0,    0,    0,    0,    0,    0,    0,
-     0,    0,    0,    0,    0,    0,    0,    0,
-     0,  889,    0,  276,    0,    0,    0,    0,
-   611,  722,  889,  310,    0,    0,    0,    0,
-     0,  667,    0,    0,    0,  278,    0,    0,
-   278,  500,  722,  500,    0,    0,    0,    0,
-   722,  722,  722,  722,  722,  722,  667,  611,
-   611,  611,  611,  722,  333,  333,  333,  333,
-   722,  722,  722,  722,  722,  722,  556,  556,
-   722,  722,  722,  722,  722,  722,  611,  444,
-   444,  444,  444,  444,  444,  200,  444,  760,
-   400,  564,  444,  444,  444,  444,  500,  278,
-   278,  278,  278,  564,  564,  500,  564,  500,
-   500,  500,  500,  500,  750,  750,  300,  500,
-   564,  760,  389,  500,  750,  300,  980,  300,
-   500,  500,  500,  500,  500,  500,  444
-};
-
-static Gushort zapfDingbatsWidths[270] = {
-     0,    0,    0,    0,    0,    0,    0,    0,
-     0,    0,    0,    0,    0,    0,    0,    0,
-     0,    0,    0,    0,    0,    0,    0,    0,
-     0,    0,    0,    0,    0,    0,    0,    0,
-   278,  974,  961,  974,  980,  719,  789,  790,
-   791,  690,  960,  939,  549,  855,  911,  933,
-   911,  945,  974,  755,  846,  762,  761,  571,
-   677,  763,  760,  759,  754,  494,  552,  537,
-   577,  692,  786,  788,  788,  790,  793,  794,
-   816,  823,  789,  841,  823,  833,  816,  831,
-   923,  744,  723,  749,  790,  792,  695,  776,
-   768,  792,  759,  707,  708,  682,  701,  826,
-   815,  789,  789,  707,  687,  696,  689,  786,
-   787,  713,  791,  785,  791,  873,  761,  762,
-   762,  759,  759,  892,  892,  788,  784,  438,
-   138,  277,  415,  392,  392,  668,  668,    0,
-     0,    0,    0,    0,    0,    0,    0,    0,
-     0,    0,    0,    0,    0,    0,    0,    0,
-     0,    0,    0,    0,    0,    0,    0,    0,
-     0,    0,    0,    0,    0,    0,    0,    0,
-     0,  732,  544,  544,  910,  667,  760,  760,
-   776,  595,  694,  626,  788,  788,  788,  788,
-   788,  788,  788,  788,  788,  788,  788,  788,
-   788,  788,  788,  788,  788,  788,  788,  788,
-   788,  788,  788,  788,  788,  788,  788,  788,
-   788,  788,  788,  788,  788,  788,  788,  788,
-   788,  788,  788,  788,  894,  838, 1016,  458,
-   748,  924,  748,  918,  927,  928,  928,  834,
-   873,  828,  924,  924,  917,  930,  931,  463,
-   883,  836,  836,  867,  867,  696,  696,  874,
-     0,  874,  760,  946,  771,  865,  771,  888,
-   967,  888,  831,  873,  927,  970,  918,    0,
-   509,  410,  509,  410,  234,  234,  390,  390,
-   276,  276,  317,  317,  334,  334
-};
-
-//------------------------------------------------------------------------
-// Built-in font table.
-//------------------------------------------------------------------------
-
-struct BuiltinFont {
-  char *name;
-  Gushort *widths;
-  FontEncoding *encoding;
-};
-
-#define numBuiltinFonts ((int)(sizeof(builtinFonts)/sizeof(BuiltinFont)))
-
-static BuiltinFont builtinFonts[] = {
-  {"Courier",               courierWidths,              &standardEncoding},
-  {"Courier-Bold",          courierBoldWidths,          &standardEncoding},
-  {"Courier-BoldOblique",   courierBoldObliqueWidths,   &standardEncoding},
-  {"Courier-Oblique",       courierObliqueWidths,       &standardEncoding},
-  {"Helvetica",             helveticaWidths,            &standardEncoding},
-  {"Helvetica-Bold",        helveticaBoldWidths,        &standardEncoding},
-  {"Helvetica-BoldOblique", helveticaBoldObliqueWidths, &standardEncoding},
-  {"Helvetica-Oblique",     helveticaObliqueWidths,     &standardEncoding},
-  {"Symbol",                symbolWidths,               &symbolEncoding},
-  {"Times-Bold",            timesBoldWidths,            &standardEncoding},
-  {"Times-BoldItalic",      timesBoldItalicWidths,      &standardEncoding},
-  {"Times-Italic",          timesItalicWidths,          &standardEncoding},
-  {"Times-Roman",           timesRomanWidths,           &standardEncoding},
-  {"ZapfDingbats",          zapfDingbatsWidths,         &zapfDingbatsEncoding}
-};
-
-#endif
diff --git a/pdf2swf/xpdf/FormWidget.cc b/pdf2swf/xpdf/FormWidget.cc
deleted file mode 100644 (file)
index 76428d0..0000000
+++ /dev/null
@@ -1,129 +0,0 @@
-//========================================================================
-//
-// FormWidget.cc
-//
-// Copyright 2000 Derek B. Noonburg
-//
-//========================================================================
-
-#ifdef __GNUC__
-#pragma implementation
-#endif
-
-#include "gmem.h"
-#include "Object.h"
-#include "Gfx.h"
-#include "FormWidget.h"
-
-//------------------------------------------------------------------------
-// FormWidget
-//------------------------------------------------------------------------
-
-FormWidget::FormWidget(Dict *dict) {
-  Object obj1, obj2;
-  double t;
-
-  ok = gFalse;
-
-  if (dict->lookup("AP", &obj1)->isDict()) {
-    obj1.dictLookupNF("N", &obj2);
-    //~ this doesn't handle appearances with multiple states --
-    //~ need to look at AS key to get state and then get the
-    //~ corresponding entry from the N dict
-    if (obj2.isRef()) {
-      obj2.copy(&appearance);
-      ok = gTrue;
-    }
-    obj2.free();
-  }
-  obj1.free();
-
-  if (dict->lookup("Rect", &obj1)->isArray() &&
-      obj1.arrayGetLength() == 4) {
-    //~ should check object types here
-    obj1.arrayGet(0, &obj2);
-    xMin = obj2.getNum();
-    obj2.free();
-    obj1.arrayGet(1, &obj2);
-    yMin = obj2.getNum();
-    obj2.free();
-    obj1.arrayGet(2, &obj2);
-    xMax = obj2.getNum();
-    obj2.free();
-    obj1.arrayGet(3, &obj2);
-    yMax = obj2.getNum();
-    obj2.free();
-    if (xMin > xMax) {
-      t = xMin; xMin = xMax; xMax = t;
-    }
-    if (yMin > yMax) {
-      t = yMin; yMin = yMax; yMax = t;
-    }
-  } else {
-    //~ this should return an error
-    xMin = yMin = 0;
-    xMax = yMax = 1;
-  }
-  obj1.free();
-}
-
-FormWidget::~FormWidget() {
-  appearance.free();
-}
-
-void FormWidget::draw(Gfx *gfx) {
-  Object obj;
-
-  if (appearance.fetch(&obj)->isStream()) {
-    gfx->doWidgetForm(&obj, xMin, yMin, xMax, yMax);
-  }
-  obj.free();
-}
-
-//------------------------------------------------------------------------
-// FormWidgets
-//------------------------------------------------------------------------
-
-FormWidgets::FormWidgets(Object *annots) {
-  FormWidget *widget;
-  Object obj1, obj2;
-  int size;
-  int i;
-
-  widgets = NULL;
-  size = 0;
-  nWidgets = 0;
-
-  if (annots->isArray()) {
-    for (i = 0; i < annots->arrayGetLength(); ++i) {
-      if (annots->arrayGet(i, &obj1)->isDict()) {
-       obj1.dictLookup("Subtype", &obj2);
-       if (obj2.isName("Widget") ||
-           obj2.isName("Stamp")) {
-         widget = new FormWidget(obj1.getDict());
-         if (widget->isOk()) {
-           if (nWidgets >= size) {
-             size += 16;
-             widgets = (FormWidget **)grealloc(widgets,
-                                               size * sizeof(FormWidget *));
-           }
-           widgets[nWidgets++] = widget;
-         } else {
-           delete widget;
-         }
-       }
-       obj2.free();
-      }
-      obj1.free();
-    }
-  }
-}
-
-FormWidgets::~FormWidgets() {
-  int i;
-
-  for (i = 0; i < nWidgets; ++i) {
-    delete widgets[i];
-  }
-  gfree(widgets);
-}
diff --git a/pdf2swf/xpdf/FormWidget.h b/pdf2swf/xpdf/FormWidget.h
deleted file mode 100644 (file)
index d746083..0000000
+++ /dev/null
@@ -1,65 +0,0 @@
-//========================================================================
-//
-// FormWidget.h
-//
-// Copyright 2000 Derek B. Noonburg
-//
-//========================================================================
-
-#ifndef FORMWIDGET_H
-#define FORMWIDGET_H
-
-#ifdef __GNUC__
-#pragma interface
-#endif
-
-class Gfx;
-
-//------------------------------------------------------------------------
-// FormWidget
-//------------------------------------------------------------------------
-
-class FormWidget {
-public:
-
-  FormWidget(Dict *dict);
-  ~FormWidget();
-  GBool isOk() { return ok; }
-
-  void draw(Gfx *gfx);
-
-  // Get appearance object.
-  Object *getAppearance(Object *obj) { return appearance.fetch(obj); }
-
-private:
-
-  Object appearance;           // a reference to the Form XObject stream
-                               //   for the normal appearance
-  double xMin, yMin,           // widget rectangle
-         xMax, yMax;
-  GBool ok;
-};
-
-//------------------------------------------------------------------------
-// FormWidgets
-//------------------------------------------------------------------------
-
-class FormWidgets {
-public:
-
-  // Extract widgets from array of annotations.
-  FormWidgets(Object *annots);
-
-  ~FormWidgets();
-
-  // Iterate through list of widgets.
-  int getNumWidgets() { return nWidgets; }
-  FormWidget *getWidget(int i) { return widgets[i]; }
-
-private:
-
-  FormWidget **widgets;
-  int nWidgets;
-};
-
-#endif
index 7b8f271..3bf626a 100644 (file)
@@ -4,7 +4,7 @@
 //
 // Simple variable-length string type.
 //
-// Copyright 1996 Derek B. Noonburg
+// Copyright 1996-2002 Glyph & Cog, LLC
 //
 //========================================================================
 
@@ -12,6 +12,7 @@
 #pragma implementation
 #endif
 
+#include <aconf.h>
 #include <stdlib.h>
 #include <stddef.h>
 #include <string.h>
@@ -45,18 +46,25 @@ GString::GString() {
   s[0] = '\0';
 }
 
-GString::GString(const char *s1) {
-  int n = strlen(s1);
+GString::GString(const char *sA) {
+  int n = strlen(sA);
 
   s = NULL;
   resize(length = n);
-  memcpy(s, s1, n + 1);
+  memcpy(s, sA, n + 1);
 }
 
-GString::GString(const char *s1, int length1) {
+GString::GString(const char *sA, int lengthA) {
   s = NULL;
-  resize(length = length1);
-  memcpy(s, s1, length * sizeof(char));
+  resize(length = lengthA);
+  memcpy(s, sA, length * sizeof(char));
+  s[length] = '\0';
+}
+
+GString::GString(GString *str, int idx, int lengthA) {
+  s = NULL;
+  resize(length = lengthA);
+  memcpy(s, str->getCString() + idx, length);
   s[length] = '\0';
 }
 
@@ -137,10 +145,10 @@ GString *GString::append(const char *str) {
   return this;
 }
 
-GString *GString::append(const char *str, int length1) {
-  resize(length + length1);
-  memcpy(s + length, str, length1);
-  length += length1;
+GString *GString::append(const char *str, int lengthA) {
+  resize(length + lengthA);
+  memcpy(s + length, str, lengthA);
+  length += lengthA;
   s[length] = '\0';
   return this;
 }
@@ -180,14 +188,14 @@ GString *GString::insert(int i, const char *str) {
   return this;
 }
 
-GString *GString::insert(int i, const char *str, int length1) {
+GString *GString::insert(int i, const char *str, int lengthA) {
   int j;
 
-  resize(length + length1);
+  resize(length + lengthA);
   for (j = length; j >= i; --j)
-    s[j+length1] = s[j];
-  memcpy(s+i, str, length1);
-  length += length1;
+    s[j+lengthA] = s[j];
+  memcpy(s+i, str, lengthA);
+  length += lengthA;
   return this;
 }
 
index 4c3b95f..93796cb 100644 (file)
@@ -4,7 +4,7 @@
 //
 // Simple variable-length string type.
 //
-// Copyright 1996 Derek B. Noonburg
+// Copyright 1996-2002 Glyph & Cog, LLC
 //
 //========================================================================
 
@@ -24,11 +24,14 @@ public:
   GString();
 
   // Create a string from a C string.
-  GString(const char *s1);
+  GString(const char *sA);
 
-  // Create a string from <length1> chars at <s1>.  This string
+  // Create a string from <lengthA> chars at <sA>.  This string
   // can contain null characters.
-  GString (const char *s1, int length1);
+  GString(const char *sA, int lengthA);
+
+  // Create a string from <lengthA> chars at <idx> in <str>.
+  GString(GString *str, int idx, int lengthA);
 
   // Copy a string.
   GString(GString *str);
@@ -62,13 +65,13 @@ public:
   GString *append(char c);
   GString *append(GString *str);
   GString *append(const char *str);
-  GString *append(const char *str, int length1);
+  GString *append(const char *str, int lengthA);
 
   // Insert a character or string.
   GString *insert(int i, char c);
   GString *insert(int i, GString *str);
   GString *insert(int i, const char *str);
-  GString *insert(int i, const char *str, int length1);
+  GString *insert(int i, const char *str, int lengthA);
 
   // Delete a character or range of characters.
   GString *del(int i, int n = 1);
@@ -81,8 +84,8 @@ public:
   // These functions assume the strings do not contain null characters.
   int cmp(GString *str) { return strcmp(s, str->getCString()); }
   int cmpN(GString *str, int n) { return strncmp(s, str->getCString(), n); }
-  int cmp(const char *s1) { return strcmp(s, s1); }
-  int cmpN(const char *s1, int n) { return strncmp(s, s1, n); }
+  int cmp(const char *sA) { return strcmp(s, sA); }
+  int cmpN(const char *sA, int n) { return strncmp(s, sA, n); }
 
 private:
 
index 0096d4b..0b00f91 100644 (file)
@@ -2,7 +2,7 @@
 //
 // Gfx.cc
 //
-// Copyright 1996 Derek B. Noonburg
+// Copyright 1996-2002 Glyph & Cog, LLC
 //
 //========================================================================
 
 #pragma implementation
 #endif
 
+#include <aconf.h>
 #include <stdio.h>
 #include <stddef.h>
 #include <string.h>
 #include <math.h>
 #include "gmem.h"
+#include "CharTypes.h"
 #include "Object.h"
 #include "Array.h"
 #include "Dict.h"
 #include "GfxFont.h"
 #include "GfxState.h"
 #include "OutputDev.h"
-#include "Params.h"
+#include "Page.h"
 #include "Error.h"
 #include "Gfx.h"
 
+// the MSVC math.h doesn't define this
+#ifndef M_PI
+#define M_PI 3.14159265358979323846
+#endif
+
+//------------------------------------------------------------------------
+// constants
+//------------------------------------------------------------------------
+
+// Max number of splits along the t axis for an axial shading fill.
+#define axialMaxSplits 256
+
+// Max delta allowed in any color component for an axial shading fill.
+#define axialColorDelta (1 / 256.0)
+
+// Max number of splits along the t axis for a radial shading fill.
+#define radialMaxSplits 256
+
+// Max delta allowed in any color component for a radial shading fill.
+#define radialColorDelta (1 / 256.0)
+
 //------------------------------------------------------------------------
 // Operator table
 //------------------------------------------------------------------------
@@ -190,14 +213,10 @@ Operator Gfx::opTab[] = {
 #define numOps (sizeof(opTab) / sizeof(Operator))
 
 //------------------------------------------------------------------------
-
-GBool printCommands = gFalse;
-
-//------------------------------------------------------------------------
 // GfxResources
 //------------------------------------------------------------------------
 
-GfxResources::GfxResources(Dict *resDict, GfxResources *next) {
+GfxResources::GfxResources(XRef *xref, Dict *resDict, GfxResources *nextA) {
   Object obj1;
 
   if (resDict) {
@@ -206,7 +225,7 @@ GfxResources::GfxResources(Dict *resDict, GfxResources *next) {
     fonts = NULL;
     resDict->lookup("Font", &obj1);
     if (obj1.isDict()) {
-      fonts = new GfxFontDict(obj1.getDict());
+      fonts = new GfxFontDict(xref, obj1.getDict());
     }
     obj1.free();
 
@@ -219,6 +238,9 @@ GfxResources::GfxResources(Dict *resDict, GfxResources *next) {
     // get pattern dictionary
     resDict->lookup("Pattern", &patternDict);
 
+    // get shading dictionary
+    resDict->lookup("Shading", &shadingDict);
+
     // get graphics state parameter dictionary
     resDict->lookup("ExtGState", &gStateDict);
 
@@ -230,7 +252,7 @@ GfxResources::GfxResources(Dict *resDict, GfxResources *next) {
     gStateDict.initNull();
   }
 
-  this->next = next;
+  next = nextA;
 }
 
 GfxResources::~GfxResources() {
@@ -240,6 +262,7 @@ GfxResources::~GfxResources() {
   xObjDict.free();
   colorSpaceDict.free();
   patternDict.free();
+  shadingDict.free();
   gStateDict.free();
 }
 
@@ -318,6 +341,25 @@ GfxPattern *GfxResources::lookupPattern(char *name) {
   return NULL;
 }
 
+GfxShading *GfxResources::lookupShading(char *name) {
+  GfxResources *resPtr;
+  GfxShading *shading;
+  Object obj;
+
+  for (resPtr = this; resPtr; resPtr = resPtr->next) {
+    if (resPtr->shadingDict.isDict()) {
+      if (!resPtr->shadingDict.dictLookup(name, &obj)->isNull()) {
+       shading = GfxShading::parse(&obj);
+       obj.free();
+       return shading;
+      }
+      obj.free();
+    }
+  }
+  error(-1, "Unknown shading '%s'", name);
+  return NULL;
+}
+
 GBool GfxResources::lookupGState(char *name, Object *obj) {
   GfxResources *resPtr;
 
@@ -337,18 +379,21 @@ GBool GfxResources::lookupGState(char *name, Object *obj) {
 // Gfx
 //------------------------------------------------------------------------
 
-Gfx::Gfx(OutputDev *out1, int pageNum, Dict *resDict,
-        double dpi, double x1, double y1, double x2, double y2, GBool crop,
-        double cropX1, double cropY1, double cropX2, double cropY2,
-        int rotate) {
+Gfx::Gfx(XRef *xrefA, OutputDev *outA, int pageNum, Dict *resDict, double dpi,
+        PDFRectangle *box, GBool crop, PDFRectangle *cropBox, int rotate,
+        GBool printCommandsA) {
   int i;
 
+  xref = xrefA;
+  subPage = gFalse;
+  printCommands = printCommandsA;
+
   // start the resource stack
-  res = new GfxResources(resDict, NULL);
+  res = new GfxResources(xref, resDict, NULL);
 
   // initialize
-  out = out1;
-  state = new GfxState(dpi, x1, y1, x2, y2, rotate, out->upsideDown());
+  out = outA;
+  state = new GfxState(dpi, box, rotate, out->upsideDown());
   fontChanged = gFalse;
   clip = clipNone;
   ignoreUndef = 0;
@@ -361,31 +406,65 @@ Gfx::Gfx(OutputDev *out1, int pageNum, Dict *resDict,
 
   // set crop box
   if (crop) {
-    state->moveTo(cropX1, cropY1);
-    state->lineTo(cropX2, cropY1);
-    state->lineTo(cropX2, cropY2);
-    state->lineTo(cropX1, cropY2);
+    state->moveTo(cropBox->x1, cropBox->y1);
+    state->lineTo(cropBox->x2, cropBox->y1);
+    state->lineTo(cropBox->x2, cropBox->y2);
+    state->lineTo(cropBox->x1, cropBox->y2);
     state->closePath();
+    state->clip();
     out->clip(state);
     state->clearPath();
   }
 }
 
-Gfx::~Gfx() {
-  GfxResources *resPtr;
+Gfx::Gfx(XRef *xrefA, OutputDev *outA, Dict *resDict,
+        PDFRectangle *box, GBool crop, PDFRectangle *cropBox) {
+  int i;
+
+  xref = xrefA;
+  subPage = gTrue;
+  printCommands = gFalse;
+
+  // start the resource stack
+  res = new GfxResources(xref, resDict, NULL);
 
+  // initialize
+  out = outA;
+  state = new GfxState(72, box, 0, gFalse);
+  fontChanged = gFalse;
+  clip = clipNone;
+  ignoreUndef = 0;
+  for (i = 0; i < 6; ++i) {
+    baseMatrix[i] = state->getCTM()[i];
+  }
+
+  // set crop box
+  if (crop) {
+    state->moveTo(cropBox->x1, cropBox->y1);
+    state->lineTo(cropBox->x2, cropBox->y1);
+    state->lineTo(cropBox->x2, cropBox->y2);
+    state->lineTo(cropBox->x1, cropBox->y2);
+    state->closePath();
+    state->clip();
+    out->clip(state);
+    state->clearPath();
+  }
+}
+
+Gfx::~Gfx() {
   while (state->hasSaves()) {
     state = state->restore();
     out->restoreState(state);
   }
-  out->endPage();
+  if (!subPage) {
+    out->endPage();
+  }
   while (res) {
-    resPtr = res->getNext();
-    delete res;
-    res = resPtr;
+    popResources();
   }
-  if (state)
+  if (state) {
     delete state;
+  }
 }
 
 void Gfx::display(Object *obj, GBool topLevel) {
@@ -406,7 +485,7 @@ void Gfx::display(Object *obj, GBool topLevel) {
     error(-1, "Weird page contents");
     return;
   }
-  parser = new Parser(new Lexer(obj));
+  parser = new Parser(xref, new Lexer(xref, obj));
   go(topLevel);
   delete parser;
   parser = NULL;
@@ -415,11 +494,11 @@ void Gfx::display(Object *obj, GBool topLevel) {
 void Gfx::go(GBool topLevel) {
   Object obj;
   Object args[maxArgs];
-  int numCmds, numArgs;
+  int numArgs;
   int i;
 
   // scan a sequence of objects
-  numCmds = 0;
+  updateLevel = 0;
   numArgs = 0;
   parser->getObj(&obj);
   while (!obj.isEOF()) {
@@ -433,6 +512,7 @@ void Gfx::go(GBool topLevel) {
          args[i].print(stdout);
        }
        printf("\n");
+       fflush(stdout);
       }
       execOp(&obj, args, numArgs);
       obj.free();
@@ -441,9 +521,9 @@ void Gfx::go(GBool topLevel) {
       numArgs = 0;
 
       // periodically update display
-      if (++numCmds == 200) {
+      if (++updateLevel >= 20000) {
        out->dump();
-       numCmds = 0;
+       updateLevel = 0;
       }
 
     // got an argument - save it
@@ -457,6 +537,7 @@ void Gfx::go(GBool topLevel) {
        printf("throwing away arg: ");
        obj.print(stdout);
        printf("\n");
+       fflush(stdout);
       }
       obj.free();
     }
@@ -476,20 +557,16 @@ void Gfx::go(GBool topLevel) {
        args[i].print(stdout);
       }
       printf("\n");
+      fflush(stdout);
     }
     for (i = 0; i < numArgs; ++i)
       args[i].free();
   }
 
   // update display
-  if (topLevel && numCmds > 0) {
+  if (topLevel && updateLevel > 0) {
     out->dump();
   }
-
-  // clean up
-  if (printCommands) {
-    fflush(stdout);
-  }
 }
 
 void Gfx::execOp(Object *cmd, Object args[], int numArgs) {
@@ -583,12 +660,6 @@ void Gfx::opSave(Object args[], int numArgs) {
 void Gfx::opRestore(Object args[], int numArgs) {
   state = state->restore();
   out->restoreState(state);
-
-  // Some PDF producers (Macromedia FreeHand) generate a save (q) and
-  // restore (Q) inside a path sequence.  The PDF spec seems to imply
-  // that this is illegal.  Calling clearPath() here implements the
-  // behavior apparently expected by this software.
-  state->clearPath();
 }
 
 void Gfx::opConcat(Object args[], int numArgs) {
@@ -768,7 +839,7 @@ void Gfx::opSetFillColorSpace(Object args[], int numArgs) {
   if (colorSpace) {
     state->setFillColorSpace(colorSpace);
   } else {
-    error(getPos(), "Bad color space");
+    error(getPos(), "Bad color space (fill)");
   }
   for (i = 0; i < gfxColorMaxComps; ++i) {
     color.c[i] = 0;
@@ -794,7 +865,7 @@ void Gfx::opSetStrokeColorSpace(Object args[], int numArgs) {
   if (colorSpace) {
     state->setStrokeColorSpace(colorSpace);
   } else {
-    error(getPos(), "Bad color space");
+    error(getPos(), "Bad color space (stroke)");
   }
   for (i = 0; i < gfxColorMaxComps; ++i) {
     color.c[i] = 0;
@@ -970,7 +1041,7 @@ void Gfx::opRectangle(Object args[], int numArgs) {
 }
 
 void Gfx::opClosePath(Object args[], int numArgs) {
-  if (!state->isPath()) {
+  if (!state->isCurPt()) {
     error(getPos(), "No current point in closepath");
     return;
   }
@@ -1103,23 +1174,26 @@ void Gfx::opCloseEOFillStroke(Object args[], int numArgs) {
   doEndPath();
 }
 
-void Gfx::opShFill(Object args[], int numArgs) {
-}
-
 void Gfx::doPatternFill(GBool eoFill) {
   GfxPatternColorSpace *patCS;
   GfxPattern *pattern;
   GfxTilingPattern *tPat;
   GfxColorSpace *cs;
-  GfxPath *path;
-  GfxSubpath *subpath;
   double xMin, yMin, xMax, yMax, x, y, x1, y1;
+  double cxMin, cyMin, cxMax, cyMax;
   int xi0, yi0, xi1, yi1, xi, yi;
   double *ctm, *btm, *ptm;
-  double m[6], ictm[6], m1[6], im[6];
+  double m[6], ictm[6], m1[6], imb[6];
   double det;
   double xstep, ystep;
-  int i, j;
+  int i;
+
+  // this is a bit of a kludge -- patterns can be really slow, so we
+  // skip them if we're only doing text extraction, since they almost
+  // certainly don't contain any text
+  if (!out->needNonText()) {
+    return;
+  }
 
   // get color space
   patCS = (GfxPatternColorSpace *)state->getFillColorSpace();
@@ -1160,42 +1234,14 @@ void Gfx::doPatternFill(GBool eoFill) {
   m[4] = m1[4] * ictm[0] + m1[5] * ictm[2] + ictm[4];
   m[5] = m1[4] * ictm[1] + m1[5] * ictm[3] + ictm[5];
 
-  // construct a (current space) -> (pattern space) transform matrix
-  det = 1 / (m[0] * m[3] - m[1] * m[2]);
-  im[0] = m[3] * det;
-  im[1] = -m[1] * det;
-  im[2] = -m[2] * det;
-  im[3] = m[0] * det;
-  im[4] = (m[2] * m[5] - m[3] * m[4]) * det;
-  im[5] = (m[1] * m[4] - m[0] * m[5]) * det;
-
-  // compute bounding box of current path, in pattern space
-  xMin = xMax = yMin = yMax = 0; // make gcc happy
-  path = state->getPath();
-  for (i = 0; i < path->getNumSubpaths(); ++i) {
-    subpath = path->getSubpath(i);
-    for (j = 0; j < subpath->getNumPoints(); ++j) {
-      x = subpath->getX(j);
-      y = subpath->getY(j);
-      x1 = x * im[0] + y * im[2] + im[4];
-      y1 = x * im[1] + y * im[3] + im[5];
-      if (i == 0 && j == 0) {
-       xMin = xMax = x1;
-       yMin = yMax = y1;
-      } else {
-       if (x1 < xMin) {
-         xMin = x1;
-       } else if (x1 > xMax) {
-         xMax = x1;
-       }
-       if (y1 < yMin) {
-         yMin = y1;
-       } else if (y1 > yMax) {
-         yMax = y1;
-       }
-      }
-    }
-  }
+  // construct a (base space) -> (pattern space) transform matrix
+  det = 1 / (m1[0] * m1[3] - m1[1] * m1[2]);
+  imb[0] = m1[3] * det;
+  imb[1] = -m1[1] * det;
+  imb[2] = -m1[2] * det;
+  imb[3] = m1[0] * det;
+  imb[4] = (m1[2] * m1[5] - m1[3] * m1[4]) * det;
+  imb[5] = (m1[1] * m1[4] - m1[0] * m1[5]) * det;
 
   // save current graphics state
   out->saveState(state);
@@ -1211,6 +1257,7 @@ void Gfx::doPatternFill(GBool eoFill) {
   out->updateFillColor(state);
 
   // clip to current path
+  state->clip();
   if (eoFill) {
     out->eoClip(state);
   } else {
@@ -1218,6 +1265,47 @@ void Gfx::doPatternFill(GBool eoFill) {
   }
   state->clearPath();
 
+  // transform clip region bbox to pattern space
+  state->getClipBBox(&cxMin, &cyMin, &cxMax, &cyMax);
+  xMin = xMax = cxMin * imb[0] + cyMin * imb[2] + imb[4];
+  yMin = yMax = cxMin * imb[1] + cyMin * imb[3] + imb[5];
+  x1 = cxMin * imb[0] + cyMax * imb[2] + imb[4];
+  y1 = cxMin * imb[1] + cyMax * imb[3] + imb[5];
+  if (x1 < xMin) {
+    xMin = x1;
+  } else if (x1 > xMax) {
+    xMax = x1;
+  }
+  if (y1 < yMin) {
+    yMin = y1;
+  } else if (y1 > yMax) {
+    yMax = y1;
+  }
+  x1 = cxMax * imb[0] + cyMin * imb[2] + imb[4];
+  y1 = cxMax * imb[1] + cyMin * imb[3] + imb[5];
+  if (x1 < xMin) {
+    xMin = x1;
+  } else if (x1 > xMax) {
+    xMax = x1;
+  }
+  if (y1 < yMin) {
+    yMin = y1;
+  } else if (y1 > yMax) {
+    yMax = y1;
+  }
+  x1 = cxMax * imb[0] + cyMax * imb[2] + imb[4];
+  y1 = cxMax * imb[1] + cyMax * imb[3] + imb[5];
+  if (x1 < xMin) {
+    xMin = x1;
+  } else if (x1 > xMax) {
+    xMax = x1;
+  }
+  if (y1 < yMin) {
+    yMin = y1;
+  } else if (y1 > yMax) {
+    yMax = y1;
+  }
+
   // draw the pattern
   //~ this should treat negative steps differently -- start at right/top
   //~ edge instead of left/bottom (?)
@@ -1246,12 +1334,485 @@ void Gfx::doPatternFill(GBool eoFill) {
   out->restoreState(state);
 }
 
+void Gfx::opShFill(Object args[], int numArgs) {
+  GfxShading *shading;
+  double xMin, yMin, xMax, yMax;
+
+  if (!(shading = res->lookupShading(args[0].getName()))) {
+    return;
+  }
+
+  // save current graphics state
+  out->saveState(state);
+  state = state->save();
+
+  // clip to bbox
+  if (shading->getHasBBox()) {
+    shading->getBBox(&xMin, &yMin, &xMax, &yMax);
+    state->moveTo(xMin, yMin);
+    state->lineTo(xMax, yMin);
+    state->lineTo(xMax, yMax);
+    state->lineTo(xMin, yMax);
+    state->closePath();
+    state->clip();
+    out->clip(state);
+    state->clearPath();
+  }
+
+  // set the color space
+  state->setFillColorSpace(shading->getColorSpace()->copy());
+
+  // do shading type-specific operations
+  switch (shading->getType()) {
+  case 2:
+    doAxialShFill((GfxAxialShading *)shading);
+    break;
+  case 3:
+    doRadialShFill((GfxRadialShading *)shading);
+    break;
+  }
+
+  // restore graphics state
+  state = state->restore();
+  out->restoreState(state);
+
+  delete shading;
+}
+
+void Gfx::doAxialShFill(GfxAxialShading *shading) {
+  double xMin, yMin, xMax, yMax;
+  double x0, y0, x1, y1;
+  double dx, dy, mul;
+  double tMin, tMax, t, tx, ty;
+  double s[4], sMin, sMax, tmp;
+  double ux0, uy0, ux1, uy1, vx0, vy0, vx1, vy1;
+  double t0, t1, tt;
+  double ta[axialMaxSplits + 1];
+  int next[axialMaxSplits + 1];
+  GfxColor color0, color1;
+  int nComps;
+  int i, j, k, kk;
+
+  // get the clip region bbox
+  state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax);
+
+  // compute min and max t values, based on the four corners of the
+  // clip region bbox
+  shading->getCoords(&x0, &y0, &x1, &y1);
+  dx = x1 - x0;
+  dy = y1 - y0;
+  mul = 1 / (dx * dx + dy * dy);
+  tMin = tMax = ((xMin - x0) * dx + (yMin - y0) * dy) * mul;
+  t = ((xMin - x0) * dx + (yMax - y0) * dy) * mul;
+  if (t < tMin) {
+    tMin = t;
+  } else if (t > tMax) {
+    tMax = t;
+  }
+  t = ((xMax - x0) * dx + (yMin - y0) * dy) * mul;
+  if (t < tMin) {
+    tMin = t;
+  } else if (t > tMax) {
+    tMax = t;
+  }
+  t = ((xMax - x0) * dx + (yMax - y0) * dy) * mul;
+  if (t < tMin) {
+    tMin = t;
+  } else if (t > tMax) {
+    tMax = t;
+  }
+  if (tMin < 0 && !shading->getExtend0()) {
+    tMin = 0;
+  }
+  if (tMax > 1 && !shading->getExtend1()) {
+    tMax = 1;
+  }
+
+  // get the function domain
+  t0 = shading->getDomain0();
+  t1 = shading->getDomain1();
+
+  // Traverse the t axis and do the shading.
+  //
+  // For each point (tx, ty) on the t axis, consider a line through
+  // that point perpendicular to the t axis:
+  //
+  //     x(s) = tx + s * -dy   -->   s = (x - tx) / -dy
+  //     y(s) = ty + s * dx    -->   s = (y - ty) / dx
+  //
+  // Then look at the intersection of this line with the bounding box
+  // (xMin, yMin, xMax, yMax).  In the general case, there are four
+  // intersection points:
+  //
+  //     s0 = (xMin - tx) / -dy
+  //     s1 = (xMax - tx) / -dy
+  //     s2 = (yMin - ty) / dx
+  //     s3 = (yMax - ty) / dx
+  //
+  // and we want the middle two s values.
+  //
+  // In the case where dx = 0, take s0 and s1; in the case where dy =
+  // 0, take s2 and s3.
+  //
+  // Each filled polygon is bounded by two of these line segments
+  // perpdendicular to the t axis.
+  //
+  // The t axis is bisected into smaller regions until the color
+  // difference across a region is small enough, and then the region
+  // is painted with a single color.
+
+  // set up
+  nComps = shading->getColorSpace()->getNComps();
+  ta[0] = tMin;
+  ta[axialMaxSplits] = tMax;
+  next[0] = axialMaxSplits;
+
+  // compute the color at t = tMin
+  if (tMin < 0) {
+    tt = t0;
+  } else if (tMin > 1) {
+    tt = t1;
+  } else {
+    tt = t0 + (t1 - t0) * tMin;
+  }
+  shading->getColor(tt, &color0);
+
+  // compute the coordinates of the point on the t axis at t = tMin;
+  // then compute the intersection of the perpendicular line with the
+  // bounding box
+  tx = x0 + tMin * dx;
+  ty = y0 + tMin * dy;
+  if (dx == 0 && dy == 0) {
+    sMin = sMax = 0;
+  } if (dx == 0) {
+    sMin = (xMin - tx) / -dy;
+    sMax = (xMax - tx) / -dy;
+    if (sMin > sMax) { tmp = sMin; sMin = sMax; sMax = tmp; }
+  } else if (dy == 0) {
+    sMin = (yMin - ty) / dx;
+    sMax = (yMax - ty) / dx;
+    if (sMin > sMax) { tmp = sMin; sMin = sMax; sMax = tmp; }
+  } else {
+    s[0] = (yMin - ty) / dx;
+    s[1] = (yMax - ty) / dx;
+    s[2] = (xMin - tx) / -dy;
+    s[3] = (xMax - tx) / -dy;
+    for (j = 0; j < 3; ++j) {
+      kk = j;
+      for (k = j + 1; k < 4; ++k) {
+       if (s[k] < s[kk]) {
+         kk = k;
+       }
+      }
+      tmp = s[j]; s[j] = s[kk]; s[kk] = tmp;
+    }
+    sMin = s[1];
+    sMax = s[2];
+  }
+  ux0 = tx - sMin * dy;
+  uy0 = ty + sMin * dx;
+  vx0 = tx - sMax * dy;
+  vy0 = ty + sMax * dx;
+
+  i = 0;
+  while (i < axialMaxSplits) {
+
+    // bisect until color difference is small enough or we hit the
+    // bisection limit
+    j = next[i];
+    while (j > i + 1) {
+      if (ta[j] < 0) {
+       tt = t0;
+      } else if (ta[j] > 1) {
+       tt = t1;
+      } else {
+       tt = t0 + (t1 - t0) * ta[j];
+      }
+      shading->getColor(tt, &color1);
+      for (k = 0; k < nComps; ++k) {
+       if (fabs(color1.c[k] - color0.c[k]) > axialColorDelta) {
+         break;
+       }
+      }
+      if (k == nComps) {
+       break;
+      }
+      k = (i + j) / 2;
+      ta[k] = 0.5 * (ta[i] + ta[j]);
+      next[i] = k;
+      next[k] = j;
+      j = k;
+    }
+
+    // use the average of the colors of the two sides of the region
+    for (k = 0; k < nComps; ++k) {
+      color0.c[k] = 0.5 * (color0.c[k] + color1.c[k]);
+    }
+
+    // compute the coordinates of the point on the t axis; then
+    // compute the intersection of the perpendicular line with the
+    // bounding box
+    tx = x0 + ta[j] * dx;
+    ty = y0 + ta[j] * dy;
+    if (dx == 0 && dy == 0) {
+      sMin = sMax = 0;
+    } if (dx == 0) {
+      sMin = (xMin - tx) / -dy;
+      sMax = (xMax - tx) / -dy;
+      if (sMin > sMax) { tmp = sMin; sMin = sMax; sMax = tmp; }
+    } else if (dy == 0) {
+      sMin = (yMin - ty) / dx;
+      sMax = (yMax - ty) / dx;
+      if (sMin > sMax) { tmp = sMin; sMin = sMax; sMax = tmp; }
+    } else {
+      s[0] = (yMin - ty) / dx;
+      s[1] = (yMax - ty) / dx;
+      s[2] = (xMin - tx) / -dy;
+      s[3] = (xMax - tx) / -dy;
+      for (j = 0; j < 3; ++j) {
+       kk = j;
+       for (k = j + 1; k < 4; ++k) {
+         if (s[k] < s[kk]) {
+           kk = k;
+         }
+       }
+       tmp = s[j]; s[j] = s[kk]; s[kk] = tmp;
+      }
+      sMin = s[1];
+      sMax = s[2];
+    }
+    ux1 = tx - sMin * dy;
+    uy1 = ty + sMin * dx;
+    vx1 = tx - sMax * dy;
+    vy1 = ty + sMax * dx;
+
+    // set the color
+    state->setFillColor(&color0);
+    out->updateFillColor(state);
+
+    // fill the region
+    state->moveTo(ux0, uy0);
+    state->lineTo(vx0, vy0);
+    state->lineTo(vx1, vy1);
+    state->lineTo(ux1, uy1);
+    state->closePath();
+    out->fill(state);
+    state->clearPath();
+
+    // set up for next region
+    ux0 = ux1;
+    uy0 = uy1;
+    vx0 = vx1;
+    vy0 = vy1;
+    color0 = color1;
+    i = next[i];
+  }
+}
+
+void Gfx::doRadialShFill(GfxRadialShading *shading) {
+  double sMin, sMax, xMin, yMin, xMax, yMax;
+  double x0, y0, r0, x1, y1, r1, t0, t1;
+  int nComps;
+  GfxColor colorA, colorB;
+  double xa, ya, xb, yb, ra, rb;
+  double ta, tb, sa, sb;
+  int ia, ib, k, n;
+  double *ctm;
+  double angle, t;
+
+  // get the shading info
+  shading->getCoords(&x0, &y0, &r0, &x1, &y1, &r1);
+  t0 = shading->getDomain0();
+  t1 = shading->getDomain1();
+  nComps = shading->getColorSpace()->getNComps();
+
+  // compute the (possibly extended) s range
+  sMin = 0;
+  sMax = 1;
+  if (shading->getExtend0()) {
+    if (r0 < r1) {
+      // extend the smaller end
+      sMin = -r0 / (r1 - r0);
+    } else {
+      // extend the larger end
+      //~ this computes the diagonal of the bounding box -- we should
+      //~ really compute the intersection of the moving/expanding
+      //~ circles with each of the four corners and look for the max
+      //~ radius
+      state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax);
+      sMin = (sqrt((xMax - xMin) * (xMax - xMin) +
+                  (yMax - yMin) * (yMax - yMin)) - r0) / (r1 - r0);
+      if (sMin > 0) {
+       sMin = 0;
+      } else if (sMin < -20) {
+       // sanity check
+       sMin = -20;
+      }
+    }
+  }
+  if (shading->getExtend1()) {
+    if (r1 < r0) {
+      // extend the smaller end
+      sMax = -r0 / (r1 - r0);
+    } else if (r1 > r0) {
+      // extend the larger end
+      state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax);
+      sMax = (sqrt((xMax - xMin) * (xMax - xMin) +
+                  (yMax - yMin) * (yMax - yMin)) - r0) / (r1 - r0);
+      if (sMax < 1) {
+       sMin = 1;
+      } else if (sMax > 20) {
+       // sanity check
+       sMax = 20;
+      }
+    }
+  }
+
+  // compute the number of steps into which circles must be divided to
+  // achieve a curve flatness of 0.1 pixel in device space for the
+  // largest circle (note that "device space" is 72 dpi when generating
+  // PostScript, hence the relatively small 0.1 pixel accuracy)
+  ctm = state->getCTM();
+  t = fabs(ctm[0]);
+  if (fabs(ctm[1]) > t) {
+    t = fabs(ctm[1]);
+  }
+  if (fabs(ctm[2]) > t) {
+    t = fabs(ctm[2]);
+  }
+  if (fabs(ctm[3]) > t) {
+    t = fabs(ctm[3]);
+  }
+  if (r0 > r1) {
+    t *= r0;
+  } else {
+    t *= r1;
+  }
+  if (t < 1) {
+    n = 3;
+  } else {
+    n = (int)(M_PI / acos(1 - 0.1 / t));
+    if (n < 3) {
+      n = 3;
+    } else if (n > 200) {
+      n = 200;
+    }
+  }
+
+  // Traverse the t axis and do the shading.
+  //
+  // This generates and fills a series of rings.  Each ring is defined
+  // by two circles:
+  //   sa, ta, xa, ya, ra, colorA
+  //   sb, tb, xb, yb, rb, colorB
+  //
+  // The s/t axis is divided into radialMaxSplits parts; these parts
+  // are combined as much as possible while respecting the
+  // radialColorDelta parameter.
+
+  // setup for the start circle
+  ia = 0;
+  sa = sMin;
+  ta = t0 + sa * (t1 - t0);
+  xa = x0 + sa * (x1 - x0);
+  ya = y0 + sa * (y1 - y0);
+  ra = r0 + sa * (r1 - r0);
+  if (ta < t0) {
+    shading->getColor(t0, &colorA);
+  } else if (ta > t1) {
+    shading->getColor(t1, &colorA);
+  } else {
+    shading->getColor(ta, &colorA);
+  }
+
+  while (ia < radialMaxSplits) {
+
+    // go as far along the t axis (toward t1) as we can, such that the
+    // color difference is within the tolerance (radialColorDelta) --
+    // this uses bisection (between the current value, t, and t1),
+    // limited to radialMaxSplits points along the t axis
+    ib = radialMaxSplits;
+    sb = sMin + ((double)ib / (double)radialMaxSplits) * (sMax - sMin);
+    tb = t0 + sb * (t1 - t0);
+    if (tb < t0) {
+      shading->getColor(t0, &colorB);
+    } else if (tb > t1) {
+      shading->getColor(t1, &colorB);
+    } else {
+      shading->getColor(tb, &colorB);
+    }
+    while (ib - ia > 1) {
+      for (k = 0; k < nComps; ++k) {
+       if (fabs(colorB.c[k] - colorA.c[k]) > radialColorDelta) {
+         break;
+       }
+      }
+      if (k == nComps) {
+       break;
+      }
+      ib = (ia + ib) / 2;
+      sb = sMin + ((double)ib / (double)radialMaxSplits) * (sMax - sMin);
+      tb = t0 + sb * (t1 - t0);
+      if (tb < t0) {
+       shading->getColor(t0, &colorB);
+      } else if (tb > t1) {
+       shading->getColor(t1, &colorB);
+      } else {
+       shading->getColor(tb, &colorB);
+      }
+    }
+
+    // compute center and radius of the circle
+    xb = x0 + sb * (x1 - x0);
+    yb = y0 + sb * (y1 - y0);
+    rb = r0 + sb * (r1 - r0);
+
+    // use the average of the colors at the two circles
+    for (k = 0; k < nComps; ++k) {
+      colorA.c[k] = 0.5 * (colorA.c[k] + colorB.c[k]);
+    }
+    state->setFillColor(&colorA);
+    out->updateFillColor(state);
+
+    // construct path for first circle
+    state->moveTo(xa + ra, ya);
+    for (k = 1; k < n; ++k) {
+      angle = ((double)k / (double)n) * 2 * M_PI;
+      state->lineTo(xa + ra * cos(angle), ya + ra * sin(angle));
+    }
+    state->closePath();
+
+    // construct and append path for second circle
+    state->moveTo(xb + rb, yb);
+    for (k = 1; k < n; ++k) {
+      angle = ((double)k / (double)n) * 2 * M_PI;
+      state->lineTo(xb + rb * cos(angle), yb + rb * sin(angle));
+    }
+    state->closePath();
+
+    // fill the ring
+    out->eoFill(state);
+    state->clearPath();
+
+    // step to the next value of t
+    ia = ib;
+    sa = sb;
+    ta = tb;
+    xa = xb;
+    ya = yb;
+    ra = rb;
+    colorA = colorB;
+  }
+}
+
 void Gfx::doEndPath() {
-  if (state->isPath()) {
-    if (clip == clipNormal)
+  if (state->isPath() && clip != clipNone) {
+    state->clip();
+    if (clip == clipNormal) {
       out->clip(state);
-    else if (clip == clipEO)
+    } else {
       out->eoClip(state);
+    }
   }
   clip = clipNone;
   state->clearPath();
@@ -1300,9 +1861,11 @@ void Gfx::opSetFont(Object args[], int numArgs) {
     return;
   }
   if (printCommands) {
-    printf("  font: '%s' %g\n",
+    printf("  font: tag=%s name='%s' %g\n",
+          font->getTag()->getCString(),
           font->getName() ? font->getName()->getCString() : "???",
           args[1].getNum());
+    fflush(stdout);
   }
   state->setFont(font, args[1].getNum());
   fontChanged = gTrue;
@@ -1330,6 +1893,7 @@ void Gfx::opSetWordSpacing(Object args[], int numArgs) {
 void Gfx::opSetHorizScaling(Object args[], int numArgs) {
   state->setHorizScaling(args[0].getNum());
   out->updateHorizScaling(state);
+  fontChanged = gTrue;
 }
 
 //------------------------------------------------------------------------
@@ -1422,17 +1986,23 @@ void Gfx::opMoveSetShowText(Object args[], int numArgs) {
 void Gfx::opShowSpaceText(Object args[], int numArgs) {
   Array *a;
   Object obj;
+  int wMode;
   int i;
 
   if (!state->getFont()) {
     error(getPos(), "No font in show/space");
     return;
   }
+  wMode = state->getFont()->getWMode();
   a = args[0].getArray();
   for (i = 0; i < a->getLength(); ++i) {
     a->get(i, &obj);
     if (obj.isNum()) {
-      state->textShift(-obj.getNum() * 0.001 * state->getFontSize());
+      if (wMode) {
+       state->textShift(0, -obj.getNum() * 0.001 * state->getFontSize());
+      } else {
+       state->textShift(-obj.getNum() * 0.001 * state->getFontSize(), 0);
+      }
       out->updateTextShift(state, obj.getNum());
     } else if (obj.isString()) {
       doShowText(obj.getString());
@@ -1445,188 +2015,174 @@ void Gfx::opShowSpaceText(Object args[], int numArgs) {
 
 void Gfx::doShowText(GString *s) {
   GfxFont *font;
-  GfxFontEncoding16 *enc;
-  Guchar *p;
-  Guchar c8;
-  int c16;
-  GString *s16;
-  char s16a[2];
-  int m, n;
-#if 0 //~type3
-  double dx, dy, width, height, w, h, x, y;
+  int wMode;
+  double riseX, riseY;
+  CharCode code;
+  Unicode u[8];
+  double x, y, dx, dy, dx2, dy2, curX, curY, tdx, tdy;
+  double originX, originY, tOriginX, tOriginY;
   double oldCTM[6], newCTM[6];
   double *mat;
   Object charProc;
+  Dict *resDict;
   Parser *oldParser;
-  int i;
-#else
-  double dx, dy, width, height, w, h, sWidth, sHeight;
-#endif
+  char *p;
+  int len, n, uLen, nChars, nSpaces, i;
 
   if (fontChanged) {
     out->updateFont(state);
     fontChanged = gFalse;
   }
   font = state->getFont();
+  wMode = font->getWMode();
 
-  //----- 16-bit font
-  if (font->is16Bit()) {
-    enc = font->getEncoding16();
-    if (out->useDrawChar()) {
-      out->beginString(state, s);
-      s16 = NULL;
-    } else {
-      s16 = new GString();
-    }
-    sWidth = sHeight = 0;
-    state->textTransformDelta(0, state->getRise(), &dx, &dy);
-    p = (Guchar *)s->getCString();
-    n = s->getLength();
-    while (n > 0) {
-      m = getNextChar16(enc, p, &c16);
-      if (enc->wMode == 0) {
-       width = state->getFontSize() * state->getHorizScaling() *
-               font->getWidth16(c16) +
-               state->getCharSpace();
-       if (c16 == ' ') {
-         width += state->getWordSpace();
-       }
-       height = 0;
-      } else {
-       width = 0;
-       height = state->getFontSize() * font->getHeight16(c16);
-      }
-      state->textTransformDelta(width, height, &w, &h);
-      if (out->useDrawChar()) {
-       out->drawChar16(state, state->getCurX() + dx, state->getCurY() + dy,
-                       w, h, c16);
-       state->textShift(width, height);
-      } else {
-       s16a[0] = (char)(c16 >> 8);
-       s16a[1] = (char)c16;
-       s16->append(s16a, 2);
-       sWidth += w;
-       sHeight += h;
-      }
-      n -= m;
-      p += m;
-    }
-    if (out->useDrawChar()) {
-      out->endString(state);
-    } else {
-      out->drawString16(state, s16);
-      delete s16;
-      state->textShift(sWidth, sHeight);
-    }
+  if (out->useDrawChar()) {
+    out->beginString(state, s);
+  }
 
-  //----- 8-bit font
-  } else {
-#if 0 //~type3
-    //~ also check out->renderType3()
-    if (font->getType() == fontType3) {
-      out->beginString(state, s);
-      mat = state->getCTM();
-      for (i = 0; i < 6; ++i) {
-       oldCTM[i] = mat[i];
+  // handle a Type 3 char
+  if (font->getType() == fontType3 && out->interpretType3Chars()) {
+    mat = state->getCTM();
+    for (i = 0; i < 6; ++i) {
+      oldCTM[i] = mat[i];
+    }
+    mat = state->getTextMat();
+    newCTM[0] = mat[0] * oldCTM[0] + mat[1] * oldCTM[2];
+    newCTM[1] = mat[0] * oldCTM[1] + mat[1] * oldCTM[3];
+    newCTM[2] = mat[2] * oldCTM[0] + mat[3] * oldCTM[2];
+    newCTM[3] = mat[2] * oldCTM[1] + mat[3] * oldCTM[3];
+    mat = font->getFontMatrix();
+    newCTM[0] = mat[0] * newCTM[0] + mat[1] * newCTM[2];
+    newCTM[1] = mat[0] * newCTM[1] + mat[1] * newCTM[3];
+    newCTM[2] = mat[2] * newCTM[0] + mat[3] * newCTM[2];
+    newCTM[3] = mat[2] * newCTM[1] + mat[3] * newCTM[3];
+    newCTM[0] *= state->getFontSize();
+    newCTM[3] *= state->getFontSize();
+    newCTM[0] *= state->getHorizScaling();
+    newCTM[2] *= state->getHorizScaling();
+    state->textTransformDelta(0, state->getRise(), &riseX, &riseY);
+    curX = state->getCurX();
+    curY = state->getCurY();
+    oldParser = parser;
+    p = s->getCString();
+    len = s->getLength();
+    while (len > 0) {
+      n = font->getNextChar(p, len, &code,
+                           u, (int)(sizeof(u) / sizeof(Unicode)), &uLen,
+                           &dx, &dy, &originX, &originY);
+      dx = dx * state->getFontSize() + state->getCharSpace();
+      if (n == 1 && *p == ' ') {
+       dx += state->getWordSpace();
       }
-      mat = state->getTextMat();
-      newCTM[0] = mat[0] * oldCTM[0] + mat[1] * oldCTM[2];
-      newCTM[1] = mat[0] * oldCTM[1] + mat[1] * oldCTM[3];
-      newCTM[2] = mat[2] * oldCTM[0] + mat[3] * oldCTM[2];
-      newCTM[3] = mat[2] * oldCTM[1] + mat[3] * oldCTM[3];
-      mat = font->getFontMatrix();
-      newCTM[0] = mat[0] * newCTM[0] + mat[1] * newCTM[2];
-      newCTM[1] = mat[0] * newCTM[1] + mat[1] * newCTM[3];
-      newCTM[2] = mat[2] * newCTM[0] + mat[3] * newCTM[2];
-      newCTM[3] = mat[2] * newCTM[1] + mat[3] * newCTM[3];
-      newCTM[0] *= state->getFontSize();
-      newCTM[3] *= state->getFontSize();
-      newCTM[0] *= state->getHorizScaling();
-      newCTM[2] *= state->getHorizScaling();
-      state->textTransformDelta(0, state->getRise(), &dx, &dy);
-      oldParser = parser;
-      for (p = (Guchar *)s->getCString(), n = s->getLength(); n; ++p, --n) {
-       c8 = *p;
-       font->getCharProc(c8, &charProc);
-       state->transform(state->getCurX() + dx, state->getCurY() + dy, &x, &y);
-       state->setCTM(newCTM[0], newCTM[1], newCTM[2], newCTM[3], x, y);
-       //~ out->updateCTM(???)
+      dx *= state->getHorizScaling();
+      dy *= state->getFontSize();
+      state->textTransformDelta(dx, dy, &tdx, &tdy);
+      state->transform(curX + riseX, curY + riseY, &x, &y);
+      out->saveState(state);
+      state = state->save();
+      state->setCTM(newCTM[0], newCTM[1], newCTM[2], newCTM[3], x, y);
+      //~ out->updateCTM(???)
+      if (!out->beginType3Char(state, code, u, uLen)) {
+       ((Gfx8BitFont *)font)->getCharProc(code, &charProc);
+       if ((resDict = ((Gfx8BitFont *)font)->getResources())) {
+         pushResources(resDict);
+       }
        if (charProc.isStream()) {
          display(&charProc, gFalse);
        } else {
          error(getPos(), "Missing or bad Type3 CharProc entry");
        }
-       state->setCTM(oldCTM[0], oldCTM[1], oldCTM[2],
-                     oldCTM[3], oldCTM[4], oldCTM[5]);
-       //~ out->updateCTM(???) - use gsave/grestore instead?
+       out->endType3Char(state);
+       if (resDict) {
+         popResources();
+       }
        charProc.free();
-       width = state->getFontSize() * state->getHorizScaling() *
-               font->getWidth(c8) +
-               state->getCharSpace();
-       if (c8 == ' ') {
-         width += state->getWordSpace();
+      }
+      state = state->restore();
+      out->restoreState(state);
+      // GfxState::restore() does *not* restore the current position,
+      // so we track it here with (curX, curY)
+      curX += tdx;
+      curY += tdy;
+      state->moveTo(curX, curY);
+      p += n;
+      len -= n;
+    }
+    parser = oldParser;
+
+  } else if (out->useDrawChar()) {
+    state->textTransformDelta(0, state->getRise(), &riseX, &riseY);
+    p = s->getCString();
+    len = s->getLength();
+    while (len > 0) {
+      n = font->getNextChar(p, len, &code,
+                           u, (int)(sizeof(u) / sizeof(Unicode)), &uLen,
+                           &dx, &dy, &originX, &originY);
+      if (wMode) {
+       dx *= state->getFontSize();
+       dy = dy * state->getFontSize() + state->getCharSpace();
+       if (n == 1 && *p == ' ') {
+         dy += state->getWordSpace();
+       }
+      } else {
+       dx = dx * state->getFontSize() + state->getCharSpace();
+       if (n == 1 && *p == ' ') {
+         dx += state->getWordSpace();
        }
-       state->textShift(width);
+       dx *= state->getHorizScaling();
+       dy *= state->getFontSize();
       }
-      parser = oldParser;
-      out->endString(state);
-    } else
-#endif
-    if (out->useDrawChar()) {
-      out->beginString(state, s);
-      state->textTransformDelta(0, state->getRise(), &dx, &dy);
-      for (p = (Guchar *)s->getCString(), n = s->getLength(); n; ++p, --n) {
-       c8 = *p;
-       width = state->getFontSize() * state->getHorizScaling() *
-               font->getWidth(c8) +
-               state->getCharSpace();
-       if (c8 == ' ')
-         width += state->getWordSpace();
-       state->textTransformDelta(width, 0, &w, &h);
-       out->drawChar(state, state->getCurX() + dx, state->getCurY() + dy,
-                     w, h, c8);
-       state->textShift(width);
+      state->textTransformDelta(dx, dy, &tdx, &tdy);
+      originX *= state->getFontSize();
+      originY *= state->getFontSize();
+      state->textTransformDelta(originX, originY, &tOriginX, &tOriginY);
+      out->drawChar(state, state->getCurX() + riseX, state->getCurY() + riseY,
+                   tdx, tdy, tOriginX, tOriginY, code, u, uLen);
+      state->shift(tdx, tdy);
+      p += n;
+      len -= n;
+    }
+
+  } else {
+    dx = dy = 0;
+    p = s->getCString();
+    len = s->getLength();
+    nChars = nSpaces = 0;
+    while (len > 0) {
+      n = font->getNextChar(p, len, &code,
+                           u, (int)(sizeof(u) / sizeof(Unicode)), &uLen,
+                           &dx2, &dy2, &originX, &originY);
+      dx += dx2;
+      dy += dy2;
+      if (n == 1 && *p == ' ') {
+       ++nSpaces;
       }
-      out->endString(state);
+      ++nChars;
+      p += n;
+      len -= n;
+    }
+    if (wMode) {
+      dx *= state->getFontSize();
+      dy = dy * state->getFontSize()
+          + nChars * state->getCharSpace()
+          + nSpaces * state->getWordSpace();
     } else {
-      out->drawString(state, s);
-      width = state->getFontSize() * state->getHorizScaling() *
-             font->getWidth(s) +
-             s->getLength() * state->getCharSpace();
-      for (p = (Guchar *)s->getCString(), n = s->getLength(); n; ++p, --n) {
-       if (*p == ' ')
-         width += state->getWordSpace();
-      }
-      state->textShift(width);
+      dx = dx * state->getFontSize()
+          + nChars * state->getCharSpace()
+          + nSpaces * state->getWordSpace();
+      dx *= state->getHorizScaling();
+      dy *= state->getFontSize();
     }
+    state->textTransformDelta(dx, dy, &tdx, &tdy);
+    out->drawString(state, s);
+    state->shift(tdx, tdy);
   }
-}
 
-int Gfx::getNextChar16(GfxFontEncoding16 *enc, Guchar *p, int *c16) {
-  int n;
-  int code;
-  int a, b, m;
-
-  n = enc->codeLen[*p];
-  if (n == 1) {
-    *c16 = enc->map1[*p];
-  } else {
-    code = (p[0] << 8) + p[1];
-    a = 0;
-    b = enc->map2Len;
-    // invariant: map2[2*a] <= code < map2[2*b]
-    while (b - a > 1) {
-      m = (a + b) / 2;
-      if (enc->map2[2*m] <= code)
-       a = m;
-      else if (enc->map2[2*m] > code)
-       b = m;
-      else
-       break;
-    }
-    *c16 = enc->map2[2*a+1] + (code - enc->map2[2*a]);
+  if (out->useDrawChar()) {
+    out->endString(state);
   }
-  return n;
+
+  updateLevel += 10 * s->getLength();
 }
 
 //------------------------------------------------------------------------
@@ -1634,7 +2190,7 @@ int Gfx::getNextChar16(GfxFontEncoding16 *enc, Guchar *p, int *c16) {
 //------------------------------------------------------------------------
 
 void Gfx::opXObject(Object args[], int numArgs) {
-  Object obj1, obj2, refObj;
+  Object obj1, obj2, obj3, refObj;
 #if OPI_SUPPORT
   Object opiDict;
 #endif
@@ -1660,6 +2216,10 @@ void Gfx::opXObject(Object args[], int numArgs) {
     refObj.free();
   } else if (obj2.isName("Form")) {
     doForm(&obj1);
+  } else if (obj2.isName("PS")) {
+    obj1.streamGetDict()->lookup("Level1", &obj3);
+    out->psXObject(obj1.getStream(),
+                  obj3.isStream() ? obj3.getStream() : (Stream *)NULL);
   } else if (obj2.isName()) {
     error(getPos(), "Unknown XObject subtype '%s'", obj2.getName());
   } else {
@@ -1677,13 +2237,17 @@ void Gfx::opXObject(Object args[], int numArgs) {
 
 void Gfx::doImage(Object *ref, Stream *str, GBool inlineImg) {
   Dict *dict;
-  Object obj1, obj2;
   int width, height;
   int bits;
   GBool mask;
+  GBool invert;
   GfxColorSpace *colorSpace;
   GfxImageColorMap *colorMap;
-  GBool invert;
+  Object maskObj;
+  GBool haveMask;
+  int maskColors[2*gfxColorMaxComps];
+  Object obj1, obj2;
+  int i;
 
   // get stream dict
   dict = str->getDict();
@@ -1791,11 +2355,30 @@ void Gfx::doImage(Object *ref, Stream *str, GBool inlineImg) {
       goto err1;
     }
 
+    // get the mask
+    haveMask = gFalse;
+    dict->lookup("Mask", &maskObj);
+    if (maskObj.isArray()) {
+      for (i = 0; i < maskObj.arrayGetLength(); ++i) {
+       maskObj.arrayGet(i, &obj1);
+       maskColors[i] = obj1.getInt();
+       obj1.free();
+      }
+      haveMask = gTrue;
+    }
+
     // draw it
-    out->drawImage(state, ref, str, width, height, colorMap, inlineImg);
+    out->drawImage(state, ref, str, width, height, colorMap,
+                  haveMask ? maskColors : (int *)NULL,  inlineImg);
     delete colorMap;
-    str->close();
+
+    maskObj.free();
+  }
+
+  if ((i = width * height) > 1000) {
+    i = 1000;
   }
+  updateLevel += i;
 
   return;
 
@@ -1856,7 +2439,7 @@ void Gfx::doForm(Object *str) {
 
   // get resources
   dict->lookup("Resources", &resObj);
-  resDict = resObj.isDict() ? resObj.getDict() : NULL;
+  resDict = resObj.isDict() ? resObj.getDict() : (Dict *)NULL;
 
   // draw it
   doForm1(str, resDict, m, bbox);
@@ -1864,19 +2447,22 @@ void Gfx::doForm(Object *str) {
   resObj.free();
 }
 
-void Gfx::doWidgetForm(Object *str, double xMin, double yMin,
-                      double xMax, double yMax) {
+void Gfx::doAnnot(Object *str, double xMin, double yMin,
+                 double xMax, double yMax) {
   Dict *dict, *resDict;
   Object matrixObj, bboxObj, resObj;
   Object obj1;
-  double m[6], bbox[6];
-  double sx, sy;
+  double m[6], bbox[6], ictm[6];
+  double *ctm;
+  double formX0, formY0, formX1, formY1;
+  double annotX0, annotY0, annotX1, annotY1;
+  double det, x, y, sx, sy;
   int i;
 
   // get stream dict
   dict = str->streamGetDict();
 
-  // get bounding box
+  // get the form bounding box
   dict->lookup("BBox", &bboxObj);
   if (!bboxObj.isArray()) {
     bboxObj.free();
@@ -1890,7 +2476,7 @@ void Gfx::doWidgetForm(Object *str, double xMin, double yMin,
   }
   bboxObj.free();
 
-  // get matrix
+  // get the form matrix
   dict->lookup("Matrix", &matrixObj);
   if (matrixObj.isArray()) {
     for (i = 0; i < 6; ++i) {
@@ -1905,20 +2491,68 @@ void Gfx::doWidgetForm(Object *str, double xMin, double yMin,
   }
   matrixObj.free();
 
-  // scale form bbox to widget rectangle
-  sx = fabs((xMax - xMin) / (bbox[2] - bbox[0]));
-  sy = fabs((yMax - yMin) / (bbox[3] - bbox[1]));
-  m[0] *= sx;  m[1] *= sy;
-  m[2] *= sx;  m[3] *= sy;
-  m[4] *= sx;  m[5] *= sy;
+  // transform the form bbox from form space to user space
+  formX0 = bbox[0] * m[0] + bbox[1] * m[2] + m[4];
+  formY0 = bbox[0] * m[1] + bbox[1] * m[3] + m[5];
+  formX1 = bbox[2] * m[0] + bbox[3] * m[2] + m[4];
+  formY1 = bbox[2] * m[1] + bbox[3] * m[3] + m[5];
+
+  // transform the annotation bbox from default user space to user
+  // space: (bbox * baseMatrix) * iCTM
+  ctm = state->getCTM();
+  det = 1 / (ctm[0] * ctm[3] - ctm[1] * ctm[2]);
+  ictm[0] = ctm[3] * det;
+  ictm[1] = -ctm[1] * det;
+  ictm[2] = -ctm[2] * det;
+  ictm[3] = ctm[0] * det;
+  ictm[4] = (ctm[2] * ctm[5] - ctm[3] * ctm[4]) * det;
+  ictm[5] = (ctm[1] * ctm[4] - ctm[0] * ctm[5]) * det;
+  x = baseMatrix[0] * xMin + baseMatrix[2] * yMin + baseMatrix[4];
+  y = baseMatrix[1] * xMin + baseMatrix[3] * yMin + baseMatrix[5];
+  annotX0 = ictm[0] * x + ictm[2] * y + ictm[4];
+  annotY0 = ictm[1] * x + ictm[3] * y + ictm[5];
+  x = baseMatrix[0] * xMax + baseMatrix[2] * yMax + baseMatrix[4];
+  y = baseMatrix[1] * xMax + baseMatrix[3] * yMax + baseMatrix[5];
+  annotX1 = ictm[0] * x + ictm[2] * y + ictm[4];
+  annotY1 = ictm[1] * x + ictm[3] * y + ictm[5];
+
+  // swap min/max coords
+  if (formX0 > formX1) {
+    x = formX0; formX0 = formX1; formX1 = x;
+  }
+  if (formY0 > formY1) {
+    y = formY0; formY0 = formY1; formY1 = y;
+  }
+  if (annotX0 > annotX1) {
+    x = annotX0; annotX0 = annotX1; annotX1 = x;
+  }
+  if (annotY0 > annotY1) {
+    y = annotY0; annotY0 = annotY1; annotY1 = y;
+  }
 
-  // translate to widget rectangle
-  m[4] += xMin;
-  m[5] += yMin;
+  // scale the form to fit the annotation bbox
+  if (formX1 == formX0) {
+    // this shouldn't happen
+    sx = 1;
+  } else {
+    sx = (annotX1 - annotX0) / (formX1 - formX0);
+  }
+  if (formY1 == formY0) {
+    // this shouldn't happen
+    sy = 1;
+  } else {
+    sy = (annotY1 - annotY0) / (formY1 - formY0);
+  }
+  m[0] *= sx;
+  m[2] *= sx;
+  m[4] = (m[4] - formX0) * sx + annotX0;
+  m[1] *= sy;
+  m[3] *= sy;
+  m[5] = (m[5] - formY0) * sy + annotY0;
 
   // get resources
   dict->lookup("Resources", &resObj);
-  resDict = resObj.isDict() ? resObj.getDict() : NULL;
+  resDict = resObj.isDict() ? resObj.getDict() : (Dict *)NULL;
 
   // draw it
   doForm1(str, resDict, m, bbox);
@@ -1930,11 +2564,10 @@ void Gfx::doWidgetForm(Object *str, double xMin, double yMin,
 void Gfx::doForm1(Object *str, Dict *resDict, double *matrix, double *bbox) {
   Parser *oldParser;
   double oldBaseMatrix[6];
-  GfxResources *resPtr;
   int i;
 
   // push new resources on stack
-  res = new GfxResources(resDict, res);
+  pushResources(resDict);
 
   // save current graphics state
   out->saveState(state);
@@ -1961,6 +2594,7 @@ void Gfx::doForm1(Object *str, Dict *resDict, double *matrix, double *bbox) {
   state->lineTo(bbox[2], bbox[3]);
   state->lineTo(bbox[0], bbox[3]);
   state->closePath();
+  state->clip();
   out->clip(state);
   state->clearPath();
 
@@ -1980,11 +2614,21 @@ void Gfx::doForm1(Object *str, Dict *resDict, double *matrix, double *bbox) {
   out->restoreState(state);
 
   // pop resource stack
+  popResources();
+
+  return;
+}
+
+void Gfx::pushResources(Dict *resDict) {
+  res = new GfxResources(xref, resDict, res);
+}
+
+void Gfx::popResources() {
+  GfxResources *resPtr;
+
   resPtr = res->getNext();
   delete res;
   res = resPtr;
-
-  return;
 }
 
 //------------------------------------------------------------------------
@@ -2020,25 +2664,30 @@ Stream *Gfx::buildImageStream() {
   Stream *str;
 
   // build dictionary
-  dict.initDict();
+  dict.initDict(xref);
   parser->getObj(&obj);
   while (!obj.isCmd("ID") && !obj.isEOF()) {
     if (!obj.isName()) {
       error(getPos(), "Inline image dictionary key must be a name object");
       obj.free();
-      parser->getObj(&obj);
     } else {
       key = copyString(obj.getName());
       obj.free();
       parser->getObj(&obj);
-      if (obj.isEOF() || obj.isError())
+      if (obj.isEOF() || obj.isError()) {
+       gfree(key);
        break;
+      }
       dict.dictAdd(key, &obj);
     }
     parser->getObj(&obj);
   }
-  if (obj.isEOF())
+  if (obj.isEOF()) {
     error(getPos(), "End of file in inline image");
+    obj.free();
+    dict.free();
+    return NULL;
+  }
   obj.free();
 
   // make stream
@@ -2061,11 +2710,13 @@ void Gfx::opEndImage(Object args[], int numArgs) {
 //------------------------------------------------------------------------
 
 void Gfx::opSetCharWidth(Object args[], int numArgs) {
-  error(getPos(), "Encountered 'd0' operator in content stream");
+  out->type3D0(state, args[0].getNum(), args[1].getNum());
 }
 
 void Gfx::opSetCacheDevice(Object args[], int numArgs) {
-  error(getPos(), "Encountered 'd1' operator in content stream");
+  out->type3D1(state, args[0].getNum(), args[1].getNum(),
+              args[2].getNum(), args[3].getNum(),
+              args[4].getNum(), args[5].getNum());
 }
 
 //------------------------------------------------------------------------
@@ -2091,6 +2742,7 @@ void Gfx::opBeginMarkedContent(Object args[], int numArgs) {
     if (numArgs == 2)
       args[2].print(stdout);
     printf("\n");
+    fflush(stdout);
   }
 }
 
@@ -2103,5 +2755,6 @@ void Gfx::opMarkPoint(Object args[], int numArgs) {
     if (numArgs == 2)
       args[2].print(stdout);
     printf("\n");
+    fflush(stdout);
   }
 }
index 34d8f99..b4da531 100644 (file)
@@ -2,7 +2,7 @@
 //
 // Gfx.h
 //
-// Copyright 1996 Derek B. Noonburg
+// Copyright 1996-2002 Glyph & Cog, LLC
 //
 //========================================================================
 
@@ -16,6 +16,7 @@
 #include "gtypes.h"
 
 class GString;
+class XRef;
 class Array;
 class Stream;
 class Parser;
@@ -23,10 +24,13 @@ class Dict;
 class OutputDev;
 class GfxFontDict;
 class GfxFont;
-struct GfxFontEncoding16;
 class GfxPattern;
+class GfxShading;
+class GfxAxialShading;
+class GfxRadialShading;
 class GfxState;
 class Gfx;
+struct PDFRectangle;
 
 //------------------------------------------------------------------------
 // Gfx
@@ -62,7 +66,7 @@ struct Operator {
 class GfxResources {
 public:
 
-  GfxResources(Dict *resDict, GfxResources *next);
+  GfxResources(XRef *xref, Dict *resDict, GfxResources *nextA);
   ~GfxResources();
 
   GfxFont *lookupFont(char *name);
@@ -70,6 +74,7 @@ public:
   GBool lookupXObjectNF(char *name, Object *obj);
   void lookupColorSpace(char *name, Object *obj);
   GfxPattern *lookupPattern(char *name);
+  GfxShading *lookupShading(char *name);
   GBool lookupGState(char *name, Object *obj);
 
   GfxResources *getNext() { return next; }
@@ -80,6 +85,7 @@ private:
   Object xObjDict;
   Object colorSpaceDict;
   Object patternDict;
+  Object shadingDict;
   Object gStateDict;
   GfxResources *next;
 };
@@ -88,24 +94,35 @@ class Gfx {
 public:
 
   // Constructor for regular output.
-  Gfx(OutputDev *out1, int pageNum, Dict *resDict,
-      double dpi, double x1, double y1, double x2, double y2, GBool crop,
-      double cropX1, double cropY1, double cropX2, double cropY2,
-      int rotate);
+  Gfx(XRef *xrefA, OutputDev *outA, int pageNum, Dict *resDict, double dpi,
+      PDFRectangle *box, GBool crop, PDFRectangle *cropBox, int rotate,
+      GBool printCommandsA);
+
+  // Constructor for a sub-page object.
+  Gfx(XRef *xrefA, OutputDev *outA, Dict *resDict,
+      PDFRectangle *box, GBool crop, PDFRectangle *cropBox);
 
-  // Destructor.
   ~Gfx();
 
   // Interpret a stream or array of streams.
   void display(Object *obj, GBool topLevel = gTrue);
 
-  void doWidgetForm(Object *str, double xMin, double yMin,
-                   double xMax, double yMax);
+  // Display an annotation, given its appearance (a Form XObject) and
+  // bounding box (in default user space).
+  void doAnnot(Object *str, double xMin, double yMin,
+              double xMax, double yMax);
+
+  void pushResources(Dict *resDict);
+  void popResources();
 
 private:
 
+  XRef *xref;                  // the xref table for this PDF file
   OutputDev *out;              // output device
+  GBool subPage;               // is this a sub-page object?
+  GBool printCommands;         // print the drawing commands (for debugging)
   GfxResources *res;           // resource stack
+  int updateLevel;
 
   GfxState *state;             // current graphics state
   GBool fontChanged;           // set if font or text matrix has changed
@@ -170,8 +187,10 @@ private:
   void opCloseFillStroke(Object args[], int numArgs);
   void opEOFillStroke(Object args[], int numArgs);
   void opCloseEOFillStroke(Object args[], int numArgs);
-  void opShFill(Object args[], int numArgs);
   void doPatternFill(GBool eoFill);
+  void opShFill(Object args[], int numArgs);
+  void doAxialShFill(GfxAxialShading *shading);
+  void doRadialShFill(GfxRadialShading *shading);
   void doEndPath();
 
   // path clipping operators
@@ -203,7 +222,6 @@ private:
   void opMoveSetShowText(Object args[], int numArgs);
   void opShowSpaceText(Object args[], int numArgs);
   void doShowText(GString *s);
-  int getNextChar16(GfxFontEncoding16 *enc, Guchar *p, int *c16);
 
   // XObject operators
   void opXObject(Object args[], int numArgs);
index 16b311b..8dcd8e7 100644 (file)
@@ -2,7 +2,7 @@
 //
 // GfxFont.cc
 //
-// Copyright 1996 Derek B. Noonburg
+// Copyright 1996-2002 Glyph & Cog, LLC
 //
 //========================================================================
 
 #pragma implementation
 #endif
 
-#include <stdlib.h>
-#include <stddef.h>
+#include <aconf.h>
 #include <stdio.h>
+#include <stdlib.h>
 #include <string.h>
 #include <ctype.h>
-#include "GString.h"
 #include "gmem.h"
-#include "gfile.h"
-#include "config.h"
+#include "Error.h"
 #include "Object.h"
-#include "Array.h"
 #include "Dict.h"
-#include "Error.h"
-#include "Params.h"
+#include "GlobalParams.h"
+#include "CMap.h"
+#include "CharCodeToUnicode.h"
+#include "FontEncodingTables.h"
+#include "BuiltinFontTables.h"
 #include "FontFile.h"
 #include "GfxFont.h"
 
-#include "FontInfo.h"
-#if JAPANESE_SUPPORT
-#include "Japan12CMapInfo.h"
-#endif
-#if CHINESE_GB_SUPPORT
-#include "GB12CMapInfo.h"
-#endif
-#if CHINESE_CNS_SUPPORT
-#include "CNS13CMapInfo.h"
-#endif
-
 //------------------------------------------------------------------------
 
-static int CDECL cmpWidthExcep(const void *w1, const void *w2);
-static int CDECL cmpWidthExcepV(const void *w1, const void *w2);
-
-//------------------------------------------------------------------------
+struct StdFontMapEntry {
+  char *altName;
+  char *properName;
+};
 
-static Gushort *defCharWidths[12] = {
-  courierWidths,
-  courierObliqueWidths,
-  courierBoldWidths,
-  courierBoldObliqueWidths,
-  helveticaWidths,
-  helveticaObliqueWidths,
-  helveticaBoldWidths,
-  helveticaBoldObliqueWidths,
-  timesRomanWidths,
-  timesItalicWidths,
-  timesBoldWidths,
-  timesBoldItalicWidths
+static StdFontMapEntry stdFontMap[] = {
+  { "Arial",                        "Helvetica" },
+  { "Arial,Bold",                   "Helvetica-Bold" },
+  { "Arial,BoldItalic",             "Helvetica-BoldOblique" },
+  { "Arial,Italic",                 "Helvetica-Oblique" },
+  { "Arial-Bold",                   "Helvetica-Bold" },
+  { "Arial-BoldItalic",             "Helvetica-BoldOblique" },
+  { "Arial-BoldItalicMT",           "Helvetica-BoldOblique" },
+  { "Arial-BoldMT",                 "Helvetica-Bold" },
+  { "Arial-Italic",                 "Helvetica-Oblique" },
+  { "Arial-ItalicMT",               "Helvetica-Oblique" },
+  { "ArialMT",                      "Helvetica" },
+  { "Courier,Bold",                 "Courier-Bold" },
+  { "Courier,Italic",               "Courier-Oblique" },
+  { "Courier,BoldItalic",           "Courier-BoldOblique" },
+  { "CourierNew",                   "Courier" },
+  { "CourierNew,Bold",              "Courier-Bold" },
+  { "CourierNew,BoldItalic",        "Courier-BoldOblique" },
+  { "CourierNew,Italic",            "Courier-Oblique" },
+  { "CourierNew-Bold",              "Courier-Bold" },
+  { "CourierNew-BoldItalic",        "Courier-BoldOblique" },
+  { "CourierNew-Italic",            "Courier-Oblique" },
+  { "CourierNewPS-BoldItalicMT",    "Courier-BoldOblique" },
+  { "CourierNewPS-BoldMT",          "Courier-Bold" },
+  { "CourierNewPS-ItalicMT",        "Courier-Oblique" },
+  { "CourierNewPSMT",               "Courier" },
+  { "Helvetica,Bold",               "Helvetica-Bold" },
+  { "Helvetica,BoldItalic",         "Helvetica-BoldOblique" },
+  { "Helvetica,Italic",             "Helvetica-Oblique" },
+  { "Helvetica-BoldItalic",         "Helvetica-BoldOblique" },
+  { "Helvetica-Italic",             "Helvetica-Oblique" },
+  { "TimesNewRoman",                "Times-Roman" },
+  { "TimesNewRoman,Bold",           "Times-Bold" },
+  { "TimesNewRoman,BoldItalic",     "Times-BoldItalic" },
+  { "TimesNewRoman,Italic",         "Times-Italic" },
+  { "TimesNewRoman-Bold",           "Times-Bold" },
+  { "TimesNewRoman-BoldItalic",     "Times-BoldItalic" },
+  { "TimesNewRoman-Italic",         "Times-Italic" },
+  { "TimesNewRomanPS",              "Times-Roman" },
+  { "TimesNewRomanPS-Bold",         "Times-Bold" },
+  { "TimesNewRomanPS-BoldItalic",   "Times-BoldItalic" },
+  { "TimesNewRomanPS-BoldItalicMT", "Times-BoldItalic" },
+  { "TimesNewRomanPS-BoldMT",       "Times-Bold" },
+  { "TimesNewRomanPS-Italic",       "Times-Italic" },
+  { "TimesNewRomanPS-ItalicMT",     "Times-Italic" },
+  { "TimesNewRomanPSMT",            "Times-Roman" }
 };
 
 //------------------------------------------------------------------------
 // GfxFont
 //------------------------------------------------------------------------
 
-GfxFont::GfxFont(char *tag1, Ref id1, Dict *fontDict) {
-  BuiltinFont *builtinFont;
-  Object obj1, obj2, obj3, obj4;
-  int missingWidth;
-  char *name2, *p;
-  int i;
+GfxFont *GfxFont::makeFont(XRef *xref, char *tagA, Ref idA, Dict *fontDict) {
+  GString *nameA;
+  GfxFont *font;
+  Object obj1;
 
-  // get font tag and ID
-  tag = new GString(tag1);
-  id = id1;
+  // get base font name
+  nameA = NULL;
+  fontDict->lookup("BaseFont", &obj1);
+  if (obj1.isName()) {
+    nameA = new GString(obj1.getName());
+  }
+  obj1.free();
 
   // get font type
-  type = fontUnknownType;
+  font = NULL;
   fontDict->lookup("Subtype", &obj1);
-  if (obj1.isName("Type1"))
-    type = fontType1;
-  else if (obj1.isName("Type1C"))
-    type = fontType1C;
-  else if (obj1.isName("Type3"))
-    type = fontType3;
-  else if (obj1.isName("TrueType"))
-    type = fontTrueType;
-  else if (obj1.isName("Type0"))
-    type = fontType0;
+  if (obj1.isName("Type1") || obj1.isName("MMType1")) {
+    font = new Gfx8BitFont(xref, tagA, idA, nameA, fontType1, fontDict);
+  } else if (obj1.isName("Type1C")) {
+    font = new Gfx8BitFont(xref, tagA, idA, nameA, fontType1C, fontDict);
+  } else if (obj1.isName("Type3")) {
+    font = new Gfx8BitFont(xref, tagA, idA, nameA, fontType3, fontDict);
+  } else if (obj1.isName("TrueType")) {
+    font = new Gfx8BitFont(xref, tagA, idA, nameA, fontTrueType, fontDict);
+  } else if (obj1.isName("Type0")) {
+    font = new GfxCIDFont(xref, tagA, idA, nameA, fontDict);
+  } else {
+    error(-1, "Unknown font type: '%s'",
+         obj1.isName() ? obj1.getName() : "???");
+    font = new Gfx8BitFont(xref, tagA, idA, nameA, fontUnknownType, fontDict);
+  }
   obj1.free();
-  is16 = gFalse;
 
-  // get base font name
-  name = NULL;
-  fontDict->lookup("BaseFont", &obj1);
-  if (obj1.isName())
-    name = new GString(obj1.getName());
-  obj1.free();
+  return font;
+}
 
-  // Newer Adobe tools are using Base14-compatible TrueType fonts
-  // without embedding them, so munge the names into the equivalent
-  // PostScript names.  This is a kludge -- it would be nice if Adobe
-  // followed their own spec.
-  if (type == fontTrueType) {
-    p = name->getCString();
-    name2 = NULL;
-    if (!strncmp(p, "Arial", 5)) {
-      if (!strcmp(p+5, ",Bold")) {
-       name2 = "Helvetica-Bold";
-      } else if (!strcmp(p+5, ",Italic")) {
-       name2 = "Helvetica-Oblique";
-      } else if (!strcmp(p+5, ",BoldItalic")) {
-       name2 = "Helvetica-BoldOblique";
-      } else {
-       name2 = "Helvetica";
-      }
-    } else if (!strncmp(p, "TimesNewRoman", 13)) {
-      if (!strcmp(p+13, ",Bold")) {
-       name2 = "Times-Bold";
-      } else if (!strcmp(p+13, ",Italic")) {
-       name2 = "Times-Italic";
-      } else if (!strcmp(p+13, ",BoldItalic")) {
-       name2 = "Times-BoldItalic";
-      } else {
-       name2 = "Times-Roman";
-      }
-    } else if (!strncmp(p, "CourierNew", 10)) {
-      if (!strcmp(p+10, ",Bold")) {
-       name2 = "Courier-Bold";
-      } else if (!strcmp(p+10, ",Italic")) {
-       name2 = "Courier-Oblique";
-      } else if (!strcmp(p+10, ",BoldItalic")) {
-       name2 = "Courier-BoldOblique";
-      } else {
-       name2 = "Courier";
-      }
-    }
-    if (name2) {
-      delete name;
-      name = new GString(name2);
-    }
-  }
+GfxFont::GfxFont(char *tagA, Ref idA, GString *nameA) {
+  ok = gFalse;
+  tag = new GString(tagA);
+  id = idA;
+  name = nameA;
+  embFontName = NULL;
+  extFontFile = NULL;
+}
 
-  // is it a built-in font?
-  builtinFont = NULL;
+GfxFont::~GfxFont() {
+  delete tag;
   if (name) {
-    for (i = 0; i < numBuiltinFonts; ++i) {
-      if (!strcmp(builtinFonts[i].name, name->getCString())) {
-       builtinFont = &builtinFonts[i];
-       break;
-      }
-    }
+    delete name;
+  }
+  if (embFontName) {
+    delete embFontName;
+  }
+  if (extFontFile) {
+    delete extFontFile;
   }
+}
+
+void GfxFont::readFontDescriptor(XRef *xref, Dict *fontDict) {
+  Object obj1, obj2, obj3, obj4;
+  double t;
+  int i;
 
   // assume Times-Roman by default (for substitution purposes)
   flags = fontSerif;
 
-  // get info from font descriptor
-  embFontName = NULL;
   embFontID.num = -1;
   embFontID.gen = -1;
   missingWidth = 0;
-  fontDict->lookup("FontDescriptor", &obj1);
-  if (obj1.isDict()) {
+
+  if (fontDict->lookup("FontDescriptor", &obj1)->isDict()) {
 
     // get flags
-    obj1.dictLookup("Flags", &obj2);
-    if (obj2.isInt())
+    if (obj1.dictLookup("Flags", &obj2)->isInt()) {
       flags = obj2.getInt();
+    }
     obj2.free();
 
     // get name
     obj1.dictLookup("FontName", &obj2);
-    if (obj2.isName())
+    if (obj2.isName()) {
       embFontName = new GString(obj2.getName());
+    }
     obj2.free();
 
     // look for embedded font file
-    if (type == fontType1) {
-      obj1.dictLookupNF("FontFile", &obj2);
-      if (obj2.isRef())
+    if (obj1.dictLookupNF("FontFile", &obj2)->isRef()) {
+      if (type == fontType1) {
        embFontID = obj2.getRef();
-      obj2.free();
+      } else {
+       error(-1, "Mismatch between font type and embedded font file");
+      }
     }
-    if (embFontID.num == -1 && type == fontTrueType) {
-      obj1.dictLookupNF("FontFile2", &obj2);
-      if (obj2.isRef())
+    obj2.free();
+    if (embFontID.num == -1 &&
+       obj1.dictLookupNF("FontFile2", &obj2)->isRef()) {
+      if (type == fontTrueType || type == fontCIDType2) {
        embFontID = obj2.getRef();
-      obj2.free();
+      } else {
+       error(-1, "Mismatch between font type and embedded font file");
+      }
     }
-    if (embFontID.num == -1) {
-      obj1.dictLookupNF("FontFile3", &obj2);
-      if (obj2.isRef()) {
-       embFontID = obj2.getRef();
-       obj2.fetch(&obj3);
-       if (obj3.isStream()) {
-         obj3.streamGetDict()->lookup("Subtype", &obj4);
-         if (obj4.isName("Type1"))
-           type = fontType1;
-         else if (obj4.isName("Type1C"))
+    obj2.free();
+    if (embFontID.num == -1 &&
+       obj1.dictLookupNF("FontFile3", &obj2)->isRef()) {
+      if (obj2.fetch(xref, &obj3)->isStream()) {
+       obj3.streamGetDict()->lookup("Subtype", &obj4);
+       if (obj4.isName("Type1")) {
+         if (type == fontType1) {
+           embFontID = obj2.getRef();
+         } else {
+           error(-1, "Mismatch between font type and embedded font file");
+         }
+       } else if (obj4.isName("Type1C")) {
+         if (type == fontType1) {
            type = fontType1C;
-         else if (obj4.isName("Type3"))
-           type = fontType3;
-         else if (obj4.isName("TrueType"))
-           type = fontTrueType;
-         else if (obj4.isName("Type0"))
-           type = fontType0;
-         obj4.free();
+           embFontID = obj2.getRef();
+         } else if (type == fontType1C) {
+           embFontID = obj2.getRef();
+         } else {
+           error(-1, "Mismatch between font type and embedded font file");
+         }
+       } else if (obj4.isName("TrueType")) {
+         if (type == fontTrueType) {
+           embFontID = obj2.getRef();
+         } else {
+           error(-1, "Mismatch between font type and embedded font file");
+         }
+       } else if (obj4.isName("CIDFontType0C")) {
+         if (type == fontCIDType0) {
+           type = fontCIDType0C;
+           embFontID = obj2.getRef();
+         } else {
+           error(-1, "Mismatch between font type and embedded font file");
+         }
+       } else {
+         error(-1, "Unknown embedded font type '%s'",
+               obj4.isName() ? obj4.getName() : "???");
        }
-       obj3.free();
+       obj4.free();
       }
-      obj2.free();
+      obj3.free();
     }
+    obj2.free();
 
     // look for MissingWidth
     obj1.dictLookup("MissingWidth", &obj2);
-    if (obj2.isInt()) {
-      missingWidth = obj2.getInt();
+    if (obj2.isNum()) {
+      missingWidth = obj2.getNum();
     }
     obj2.free();
-  }
-  obj1.free();
-
-  // get Type3 font definition
-  if (type == fontType3) {
-    fontDict->lookup("CharProcs", &charProcs);
-    if (!charProcs.isDict()) {
-      error(-1, "Missing or invalid CharProcs dictionary in Type 3 font");
-      charProcs.free();
-    }
-  }
-
-  // look for an external font file
-  extFontFile = NULL;
-  if (type == fontType1 && name)
-    findExtFontFile();
-
-  // get font matrix
-  fontMat[0] = fontMat[3] = 1;
-  fontMat[1] = fontMat[2] = fontMat[4] = fontMat[5] = 0;
-  if (fontDict->lookup("FontMatrix", &obj1)->isArray()) {
-    for (i = 0; i < 6 && i < obj1.arrayGetLength(); ++i) {
-      if (obj1.arrayGet(i, &obj2)->isNum())
-       fontMat[i] = obj2.getNum();
-      obj2.free();
-    }
-  }
-  obj1.free();
-
-  // get encoding and character widths
-  if (type == fontType0) {
-    getType0EncAndWidths(fontDict);
-  } else {
-    getEncAndWidths(fontDict, builtinFont, missingWidth);
-  }
-}
-
-GfxFont::~GfxFont() {
-  delete tag;
-  if (name) {
-    delete name;
-  }
-  if (!is16 && encoding) {
-    delete encoding;
-  }
-  if (embFontName) {
-    delete embFontName;
-  }
-  if (extFontFile) {
-    delete extFontFile;
-  }
-  if (charProcs.isDict()) {
-    charProcs.free();
-  }
-  if (is16) {
-    gfree(widths16.exceps);
-    gfree(widths16.excepsV);
-  }
-}
-
-double GfxFont::getWidth(GString *s) {
-  double w;
-  int i;
 
-  w = 0;
-  for (i = 0; i < s->getLength(); ++i)
-    w += widths[s->getChar(i) & 0xff];
-  return w;
-}
-
-double GfxFont::getWidth16(int c) {
-  double w;
-  int a, b, m;
-
-  w = widths16.defWidth;
-  a = -1;
-  b = widths16.numExceps;
-  // invariant: widths16.exceps[a].last < c < widths16.exceps[b].first
-  while (b - a > 1) {
-    m = (a + b) / 2;
-    if (widths16.exceps[m].last < c) {
-      a = m;
-    } else if (c < widths16.exceps[m].first) {
-      b = m;
-    } else {
-      w = widths16.exceps[m].width;
-      break;
-    }
-  }
-  return w;
-}
-
-double GfxFont::getHeight16(int c) {
-  double h;
-  int a, b, m;
-
-  h = widths16.defHeight;
-  a = -1;
-  b = widths16.numExcepsV;
-  // invariant: widths16.excepsV[a].last < c < widths16.excepsV[b].first
-  while (b - a > 1) {
-    m = (a + b) / 2;
-    if (widths16.excepsV[m].last < c) {
-      a = m;
-    } else if (c < widths16.excepsV[m].first) {
-      b = m;
-    } else {
-      h = widths16.excepsV[m].height;
-      break;
-    }
-  }
-  return h;
-}
-
-double GfxFont::getOriginX16(int c) {
-  double vx;
-  int a, b, m;
-
-  vx = widths16.defWidth / 2;
-  a = -1;
-  b = widths16.numExcepsV;
-  // invariant: widths16.excepsV[a].last < c < widths16.excepsV[b].first
-  while (b - a > 1) {
-    m = (a + b) / 2;
-    if (widths16.excepsV[m].last < c) {
-      a = m;
-    } else if (c < widths16.excepsV[m].first) {
-      b = m;
-    } else {
-      vx = widths16.excepsV[m].vx;
-      break;
-    }
-  }
-  return vx;
-}
-
-double GfxFont::getOriginY16(int c) {
-  double vy;
-  int a, b, m;
-
-  vy = widths16.defVY;
-  a = -1;
-  b = widths16.numExcepsV;
-  // invariant: widths16.excepsV[a].last < c < widths16.excepsV[b].first
-  while (b - a > 1) {
-    m = (a + b) / 2;
-    if (widths16.excepsV[m].last < c) {
-      a = m;
-    } else if (c < widths16.excepsV[m].first) {
-      b = m;
-    } else {
-      vy = widths16.excepsV[m].vy;
-      break;
-    }
-  }
-  return vy;
-}
-
-Object *GfxFont::getCharProc(int code, Object *proc) {
-  if (charProcs.isDict()) {
-    charProcs.dictLookup(encoding->getCharName(code), proc);
-  } else {
-    proc->initNull();
-  }
-  return proc;
-}
-
-void GfxFont::getEncAndWidths(Dict *fontDict, BuiltinFont *builtinFont,
-                             int missingWidth) {
-  Object obj1, obj2, obj3;
-  char *buf;
-  int len;
-  FontFile *fontFile;
-  int code, i;
-
-  // Encodings start with a base encoding, which can come from
-  // (in order of priority):
-  //   1. FontDict.Encoding or FontDict.Encoding.BaseEncoding
-  //        - MacRoman / WinAnsi / Standard
-  //   2. embedded font file
-  //   3. default:
-  //        - builtin --> builtin encoding
-  //        - TrueType --> MacRomanEncoding
-  //        - others --> StandardEncoding
-  // and then add a list of differences from
-  // FontDict.Encoding.Differences.
-
-  // check FontDict for base encoding
-  encoding = NULL;
-  fontDict->lookup("Encoding", &obj1);
-  if (obj1.isDict()) {
-    obj1.dictLookup("BaseEncoding", &obj2);
-    if (obj2.isName("MacRomanEncoding")) {
-      encoding = macRomanEncoding.copy();
-    } else if (obj2.isName("WinAnsiEncoding")) {
-      encoding = winAnsiEncoding.copy();
-    } else if (obj2.isName("StandardEncoding")) {
-      encoding = standardEncoding.copy();
+    // get Ascent and Descent
+    obj1.dictLookup("Ascent", &obj2);
+    if (obj2.isNum()) {
+      t = 0.001 * obj2.getNum();
+      // some broken font descriptors set ascent and descent to 0
+      if (t != 0) {
+       ascent = t;
+      }
     }
     obj2.free();
-  } else if (obj1.isName("MacRomanEncoding")) {
-    encoding = macRomanEncoding.copy();
-  } else if (obj1.isName("WinAnsiEncoding")) {
-    encoding = winAnsiEncoding.copy();
-  } else if (obj1.isName("StandardEncoding")) {
-    encoding = standardEncoding.copy();
-  }
-  obj1.free();
-
-  // check embedded or external font file for base encoding
-  if ((type == fontType1 || type == fontType1C) &&
-      (extFontFile || embFontID.num >= 0)) {
-    if (extFontFile)
-      buf = readExtFontFile(&len);
-    else
-      buf = readEmbFontFile(&len);
-    if (buf) {
-      if (type == fontType1)
-       fontFile = new Type1FontFile(buf, len);
-      else
-       fontFile = new Type1CFontFile(buf, len);
-      if (fontFile->getName()) {
-       if (embFontName)
-         delete embFontName;
-       embFontName = new GString(fontFile->getName());
+    obj1.dictLookup("Descent", &obj2);
+    if (obj2.isNum()) {
+      t = 0.001 * obj2.getNum();
+      // some broken font descriptors set ascent and descent to 0
+      if (t != 0) {
+       descent = t;
       }
-      if (!encoding)
-       encoding = fontFile->getEncoding(gTrue);
-      delete fontFile;
-      gfree(buf);
     }
-  }
-
-  // get default base encoding
-  if (!encoding) {
-    if (builtinFont)
-      encoding = builtinFont->encoding->copy();
-    else if (type == fontTrueType)
-      encoding = macRomanEncoding.copy();
-    else
-      encoding = standardEncoding.copy();
-  }
+    obj2.free();
 
-  // merge differences into encoding
-  fontDict->lookup("Encoding", &obj1);
-  if (obj1.isDict()) {
-    obj1.dictLookup("Differences", &obj2);
-    if (obj2.isArray()) {
-      code = 0;
-      for (i = 0; i < obj2.arrayGetLength(); ++i) {
-       obj2.arrayGet(i, &obj3);
-       if (obj3.isInt()) {
-         code = obj3.getInt();
-       } else if (obj3.isName()) {
-         if (code < 256)
-           encoding->addChar(code, copyString(obj3.getName()));
-         ++code;
-       } else {
-         error(-1, "Wrong type in font encoding resource differences (%s)",
-               obj3.getTypeName());
+    // font FontBBox
+    if (obj1.dictLookup("FontBBox", &obj2)->isArray()) {
+      for (i = 0; i < 4 && i < obj2.arrayGetLength(); ++i) {
+       if (obj2.arrayGet(i, &obj3)->isNum()) {
+         fontBBox[i] = 0.001 * obj3.getNum();
        }
        obj3.free();
       }
     }
     obj2.free();
+
   }
   obj1.free();
+}
+
+CharCodeToUnicode *GfxFont::readToUnicodeCMap(Dict *fontDict, int nBits) {
+  CharCodeToUnicode *ctu;
+  GString *buf;
+  Object obj1;
+  int c;
 
-  // get character widths
-  if (builtinFont)
-    makeWidths(fontDict, builtinFont->encoding, builtinFont->widths,
-              missingWidth);
-  else
-    makeWidths(fontDict, NULL, NULL, missingWidth);
+  if (!fontDict->lookup("ToUnicode", &obj1)->isStream()) {
+    obj1.free();
+    return NULL;
+  }
+  buf = new GString();
+  obj1.streamReset();
+  while ((c = obj1.streamGetChar()) != EOF) {
+    buf->append(c);
+  }
+  obj1.streamClose();
+  obj1.free();
+  ctu = CharCodeToUnicode::parseCMap(buf, nBits);
+  delete buf;
+  return ctu;
 }
 
 void GfxFont::findExtFontFile() {
-  char **path;
-  FILE *f;
-
-  for (path = fontPath; *path; ++path) {
-    extFontFile = appendToPath(new GString(*path), name->getCString());
-    f = fopen(extFontFile->getCString(), "rb");
-    if (!f) {
-      extFontFile->append(".pfb");
-      f = fopen(extFontFile->getCString(), "rb");
-    }
-    if (!f) {
-      extFontFile->del(extFontFile->getLength() - 4, 4);
-      extFontFile->append(".pfa");
-      f = fopen(extFontFile->getCString(), "rb");
-    }
-    if (f) {
-      fclose(f);
-      break;
+  if (name) {
+    if (type == fontType1) {
+      extFontFile = globalParams->findFontFile(name, ".pfa", ".pfb");
+    } else if (type == fontTrueType) {
+      extFontFile = globalParams->findFontFile(name, ".ttf", NULL);
     }
-    delete extFontFile;
-    extFontFile = NULL;
   }
 }
 
@@ -525,20 +310,21 @@ char *GfxFont::readExtFontFile(int *len) {
   char *buf;
 
   if (!(f = fopen(extFontFile->getCString(), "rb"))) {
-    error(-1, "Internal: external font file '%s' vanished", extFontFile);
+    error(-1, "External font file '%s' vanished", extFontFile->getCString());
     return NULL;
   }
   fseek(f, 0, SEEK_END);
   *len = (int)ftell(f);
   fseek(f, 0, SEEK_SET);
   buf = (char *)gmalloc(*len);
-  if ((int)fread(buf, 1, *len, f) != *len)
+  if ((int)fread(buf, 1, *len, f) != *len) {
     error(-1, "Error reading external font file '%s'", extFontFile);
+  }
   fclose(f);
   return buf;
 }
 
-char *GfxFont::readEmbFontFile(int *len) {
+char *GfxFont::readEmbFontFile(XRef *xref, int *len) {
   char *buf;
   Object obj1, obj2;
   Stream *str;
@@ -546,7 +332,7 @@ char *GfxFont::readEmbFontFile(int *len) {
   int size, i;
 
   obj1.initRef(embFontID.num, embFontID.gen);
-  obj1.fetch(&obj2);
+  obj1.fetch(xref, &obj2);
   if (!obj2.isStream()) {
     error(-1, "Embedded font file is not a stream");
     obj2.free();
@@ -575,410 +361,883 @@ char *GfxFont::readEmbFontFile(int *len) {
   return buf;
 }
 
-void GfxFont::makeWidths(Dict *fontDict, FontEncoding *builtinEncoding,
-                        Gushort *builtinWidths, int missingWidth) {
-  Object obj1, obj2;
-  int firstChar, lastChar;
+//------------------------------------------------------------------------
+// Gfx8BitFont
+//------------------------------------------------------------------------
+
+Gfx8BitFont::Gfx8BitFont(XRef *xref, char *tagA, Ref idA, GString *nameA,
+                        GfxFontType typeA, Dict *fontDict):
+  GfxFont(tagA, idA, nameA)
+{
+  BuiltinFont *builtinFont;
+  char **baseEnc;
+  GBool baseEncFromFontFile;
+  char *buf;
+  int len;
+  FontFile *fontFile;
   int code, code2;
   char *charName;
-  Gushort *defWidths;
-  int index;
-  double mult;
+  GBool missing, hex;
+  Unicode toUnicode[256];
+  double mul;
+  int firstChar, lastChar;
+  Gushort w;
+  Object obj1, obj2, obj3;
+  int n, i, a, b, m;
 
-  // initialize all widths
-  for (code = 0; code < 256; ++code) {
-    widths[code] = missingWidth * 0.001;
-  }
+  type = typeA;
+  ctu = NULL;
 
-  // use widths from built-in font
-  if (builtinEncoding) {
-    code2 = 0; // to make gcc happy
-    for (code = 0; code < 256; ++code) {
-      if ((charName = encoding->getCharName(code)) &&
-         (code2 = builtinEncoding->getCharCode(charName)) >= 0)
-       widths[code] = builtinWidths[code2] * 0.001;
+  // Acrobat 4.0 and earlier substituted Base14-compatible fonts
+  // without providing Widths and a FontDescriptor, so we munge the
+  // names into the proper Base14 names.  (This table is from
+  // implementation note 44 in the PDF 1.4 spec.)
+  if (name) {
+    a = 0;
+    b = sizeof(stdFontMap) / sizeof(StdFontMapEntry);
+    // invariant: stdFontMap[a].altName <= name < stdFontMap[b].altName
+    while (b - a > 1) {
+      m = (a + b) / 2;
+      if (name->cmp(stdFontMap[m].altName) >= 0) {
+       a = m;
+      } else {
+       b = m;
+      }
+    }
+    if (!name->cmp(stdFontMap[a].altName)) {
+      delete name;
+      name = new GString(stdFontMap[a].properName);
     }
+  }
 
-  // get widths from font dict
+  // is it a built-in font?
+  builtinFont = NULL;
+  if (name) {
+    for (i = 0; i < nBuiltinFonts; ++i) {
+      if (!name->cmp(builtinFonts[i].name)) {
+       builtinFont = &builtinFonts[i];
+       break;
+      }
+    }
+  }
+
+  // default ascent/descent values
+  if (builtinFont) {
+    ascent = 0.001 * builtinFont->ascent;
+    descent = 0.001 * builtinFont->descent;
+    fontBBox[0] = 0.001 * builtinFont->bbox[0];
+    fontBBox[1] = 0.001 * builtinFont->bbox[1];
+    fontBBox[2] = 0.001 * builtinFont->bbox[2];
+    fontBBox[3] = 0.001 * builtinFont->bbox[3];
   } else {
-    fontDict->lookup("FirstChar", &obj1);
-    firstChar = obj1.isInt() ? obj1.getInt() : 0;
-    obj1.free();
-    fontDict->lookup("LastChar", &obj1);
-    lastChar = obj1.isInt() ? obj1.getInt() : 255;
-    obj1.free();
-    if (type == fontType3)
-      mult = fontMat[0];
-    else
-      mult = 0.001;
-    fontDict->lookup("Widths", &obj1);
-    if (obj1.isArray()) {
-      for (code = firstChar; code <= lastChar; ++code) {
-       obj1.arrayGet(code - firstChar, &obj2);
-       if (obj2.isNum())
-         widths[code] = obj2.getNum() * mult;
+    ascent = 0.95;
+    descent = -0.35;
+    fontBBox[0] = fontBBox[1] = fontBBox[2] = fontBBox[3] = 0;
+  }
+
+  // get info from font descriptor
+  readFontDescriptor(xref, fontDict);
+
+  // look for an external font file
+  findExtFontFile();
+
+  // get font matrix
+  fontMat[0] = fontMat[3] = 1;
+  fontMat[1] = fontMat[2] = fontMat[4] = fontMat[5] = 0;
+  if (fontDict->lookup("FontMatrix", &obj1)->isArray()) {
+    for (i = 0; i < 6 && i < obj1.arrayGetLength(); ++i) {
+      if (obj1.arrayGet(i, &obj2)->isNum()) {
+       fontMat[i] = obj2.getNum();
+      }
+      obj2.free();
+    }
+  }
+  obj1.free();
+
+  // get Type 3 bounding box, font definition, and resources
+  if (type == fontType3) {
+    if (fontDict->lookup("FontBBox", &obj1)->isArray()) {
+      for (i = 0; i < 4 && i < obj1.arrayGetLength(); ++i) {
+       if (obj1.arrayGet(i, &obj2)->isNum()) {
+         fontBBox[i] = obj2.getNum();
+       }
        obj2.free();
       }
+    }
+    obj1.free();
+    if (!fontDict->lookup("CharProcs", &charProcs)->isDict()) {
+      error(-1, "Missing or invalid CharProcs dictionary in Type 3 font");
+      charProcs.free();
+    }
+    if (!fontDict->lookup("Resources", &resources)->isDict()) {
+      resources.free();
+    }
+  }
+
+  //----- build the font encoding -----
+
+  // Encodings start with a base encoding, which can come from
+  // (in order of priority):
+  //   1. FontDict.Encoding or FontDict.Encoding.BaseEncoding
+  //        - MacRoman / MacExpert / WinAnsi / Standard
+  //   2. embedded or external font file
+  //   3. default:
+  //        - builtin --> builtin encoding
+  //        - TrueType --> MacRomanEncoding
+  //        - others --> StandardEncoding
+  // and then add a list of differences (if any) from
+  // FontDict.Encoding.Differences.
+
+  // check FontDict for base encoding
+  hasEncoding = gFalse;
+  baseEnc = NULL;
+  baseEncFromFontFile = gFalse;
+  fontDict->lookup("Encoding", &obj1);
+  if (obj1.isDict()) {
+    obj1.dictLookup("BaseEncoding", &obj2);
+    if (obj2.isName("MacRomanEncoding")) {
+      hasEncoding = gTrue;
+      baseEnc = macRomanEncoding;
+    } else if (obj2.isName("MacExpertEncoding")) {
+      hasEncoding = gTrue;
+      baseEnc = macExpertEncoding;
+    } else if (obj2.isName("WinAnsiEncoding")) {
+      hasEncoding = gTrue;
+      baseEnc = winAnsiEncoding;
+    } else if (obj2.isName("StandardEncoding")) {
+      hasEncoding = gTrue;
+      baseEnc = standardEncoding;
+    }
+    obj2.free();
+  } else if (obj1.isName("MacRomanEncoding")) {
+    hasEncoding = gTrue;
+    baseEnc = macRomanEncoding;
+  } else if (obj1.isName("MacExpertEncoding")) {
+    hasEncoding = gTrue;
+    baseEnc = macExpertEncoding;
+  } else if (obj1.isName("WinAnsiEncoding")) {
+    hasEncoding = gTrue;
+    baseEnc = winAnsiEncoding;
+  } else if (obj1.isName("StandardEncoding")) {
+    hasEncoding = gTrue;
+    baseEnc = standardEncoding;
+  }
+
+  // check embedded or external font file for base encoding
+  // (only for Type 1 fonts - trying to get an encoding out of a
+  // TrueType font is a losing proposition)
+  fontFile = NULL;
+  buf = NULL;
+  if ((type == fontType1 || type == fontType1C) &&
+      (extFontFile || embFontID.num >= 0)) {
+    if (extFontFile) {
+      buf = readExtFontFile(&len);
+    } else {
+      buf = readEmbFontFile(xref, &len);
+    }
+    if (buf) {
+      if (type == fontType1C && !strncmp(buf, "%!", 2)) {
+       // various tools (including Adobe's) occasionally embed Type 1
+       // fonts but label them Type 1C
+       type = fontType1;
+      }
+      if (type == fontType1) {
+       fontFile = new Type1FontFile(buf, len);
+      } else {
+       fontFile = new Type1CFontFile(buf, len);
+      }
+      if (fontFile->getName()) {
+       if (embFontName) {
+         delete embFontName;
+       }
+       embFontName = new GString(fontFile->getName());
+      }
+      if (!baseEnc) {
+       baseEnc = fontFile->getEncoding();
+       baseEncFromFontFile = gTrue;
+      }
+      gfree(buf);
+    }
+  }
+
+  // get default base encoding
+  if (!baseEnc) {
+    if (builtinFont) {
+      baseEnc = builtinFont->defaultBaseEnc;
+    } else if (type == fontTrueType) {
+      baseEnc = macRomanEncoding;
     } else {
+      baseEnc = standardEncoding;
+    }
+  }
 
-      // couldn't find widths -- use defaults 
-#if 0 //~
-      //~ certain PDF generators apparently don't include widths
-      //~ for Arial and TimesNewRoman -- and this error message
-      //~ is a nuisance
-      error(-1, "No character widths resource for non-builtin font");
-#endif
-      if (isFixedWidth())
-       index = 0;
-      else if (isSerif())
-       index = 8;
-      else
-       index = 4;
-      if (isBold())
-       index += 2;
-      if (isItalic())
-       index += 1;
-      defWidths = defCharWidths[index];
-      code2 = 0; // to make gcc happy
+  // copy the base encoding
+  for (i = 0; i < 256; ++i) {
+    enc[i] = baseEnc[i];
+    if ((encFree[i] = baseEncFromFontFile) && enc[i]) {
+      enc[i] = copyString(baseEnc[i]);
+    }
+  }
+
+  // merge differences into encoding
+  if (obj1.isDict()) {
+    obj1.dictLookup("Differences", &obj2);
+    if (obj2.isArray()) {
+      hasEncoding = gTrue;
+      code = 0;
+      for (i = 0; i < obj2.arrayGetLength(); ++i) {
+       obj2.arrayGet(i, &obj3);
+       if (obj3.isInt()) {
+         code = obj3.getInt();
+       } else if (obj3.isName()) {
+         if (code < 256) {
+           if (encFree[code]) {
+             gfree(enc[code]);
+           }
+           enc[code] = copyString(obj3.getName());
+           encFree[code] = gTrue;
+         }
+         ++code;
+       } else {
+         error(-1, "Wrong type in font encoding resource differences (%s)",
+               obj3.getTypeName());
+       }
+       obj3.free();
+      }
+    }
+    obj2.free();
+  }
+  obj1.free();
+  if (fontFile) {
+    delete fontFile;
+  }
+
+  //----- build the mapping to Unicode -----
+
+  // look for a ToUnicode CMap
+  if (!(ctu = readToUnicodeCMap(fontDict, 8))) {
+
+    // no ToUnicode CMap, so use the char names
+
+    // pass 1: use the name-to-Unicode mapping table
+    missing = hex = gFalse;
+    for (code = 0; code < 256; ++code) {
+      if ((charName = enc[code])) {
+       if (!(toUnicode[code] = globalParams->mapNameToUnicode(charName)) &&
+           strcmp(charName, ".notdef")) {
+         // if it wasn't in the name-to-Unicode table, check for a
+         // name that looks like 'Axx' or 'xx', where 'A' is any letter
+         // and 'xx' is two hex digits
+         if ((strlen(charName) == 3 &&
+              isalpha(charName[0]) &&
+              isxdigit(charName[1]) && isxdigit(charName[2]) &&
+              ((charName[1] >= 'a' && charName[1] <= 'f') ||
+               (charName[1] >= 'A' && charName[1] <= 'F') ||
+               (charName[2] >= 'a' && charName[2] <= 'f') ||
+               (charName[2] >= 'A' && charName[2] <= 'F'))) ||
+             (strlen(charName) == 2 &&
+              isxdigit(charName[0]) && isxdigit(charName[1]) &&
+              ((charName[0] >= 'a' && charName[0] <= 'f') ||
+               (charName[0] >= 'A' && charName[0] <= 'F') ||
+               (charName[1] >= 'a' && charName[1] <= 'f') ||
+               (charName[1] >= 'A' && charName[1] <= 'F')))) {
+           hex = gTrue;
+         }
+         missing = gTrue;
+       }
+      } else {
+       toUnicode[code] = 0;
+      }
+    }
+
+    // pass 2: try to fill in the missing chars, looking for names of
+    // the form 'Axx', 'xx', 'Ann', 'ABnn', or 'nn', where 'A' and 'B'
+    // are any letters, 'xx' is two hex digits, and 'nn' is 2-4
+    // decimal digits
+    if (missing && globalParams->getMapNumericCharNames()) {
       for (code = 0; code < 256; ++code) {
-       if ((charName = encoding->getCharName(code)) &&
-           (code2 = standardEncoding.getCharCode(charName)) >= 0)
-         widths[code] = defWidths[code2] * 0.001;
+       if ((charName = enc[code]) && !toUnicode[code] &&
+           strcmp(charName, ".notdef")) {
+         n = strlen(charName);
+         code2 = -1;
+         if (hex && n == 3 && isalpha(charName[0]) &&
+             isxdigit(charName[1]) && isxdigit(charName[2])) {
+           sscanf(charName+1, "%x", &code2);
+         } else if (hex && n == 2 &&
+                    isxdigit(charName[0]) && isxdigit(charName[1])) {
+           sscanf(charName, "%x", &code2);
+         } else if (!hex && n >= 2 && n <= 4 &&
+                    isdigit(charName[0]) && isdigit(charName[1])) {
+           code2 = atoi(charName);
+         } else if (n >= 3 && n <= 5 &&
+                    isdigit(charName[1]) && isdigit(charName[2])) {
+           code2 = atoi(charName+1);
+         } else if (n >= 4 && n <= 6 &&
+                    isdigit(charName[2]) && isdigit(charName[3])) {
+           code2 = atoi(charName+2);
+         }
+         if (code2 >= 0 && code2 <= 0xff) {
+           toUnicode[code] = (Unicode)code2;
+         }
+       }
       }
     }
-    obj1.free();
+
+    ctu = CharCodeToUnicode::make8BitToUnicode(toUnicode);
+  }
+
+  //----- get the character widths -----
+
+  // initialize all widths
+  for (code = 0; code < 256; ++code) {
+    widths[code] = missingWidth * 0.001;
+  }
+
+  // use widths from font dict, if present
+  fontDict->lookup("FirstChar", &obj1);
+  firstChar = obj1.isInt() ? obj1.getInt() : 0;
+  obj1.free();
+  fontDict->lookup("LastChar", &obj1);
+  lastChar = obj1.isInt() ? obj1.getInt() : 255;
+  obj1.free();
+  mul = (type == fontType3) ? fontMat[0] : 0.001;
+  fontDict->lookup("Widths", &obj1);
+  if (obj1.isArray()) {
+    flags |= fontFixedWidth;
+    for (code = firstChar; code <= lastChar; ++code) {
+      obj1.arrayGet(code - firstChar, &obj2);
+      if (obj2.isNum()) {
+       widths[code] = obj2.getNum() * mul;
+       if (widths[code] != widths[firstChar]) {
+         flags &= ~fontFixedWidth;
+       }
+      }
+      obj2.free();
+    }
+
+  // use widths from built-in font
+  } else if (builtinFont) {
+    // this is a kludge for broken PDF files that encode char 32
+    // as .notdef
+    if (builtinFont->widths->getWidth("space", &w)) {
+      widths[32] = 0.001 * w;
+    }
+    for (code = 0; code < 256; ++code) {
+      if (enc[code] && builtinFont->widths->getWidth(enc[code], &w)) {
+       widths[code] = 0.001 * w;
+      }
+    }
+
+  // couldn't find widths -- use defaults 
+  } else {
+    // this is technically an error -- the Widths entry is required
+    // for all but the Base-14 fonts -- but certain PDF generators
+    // apparently don't include widths for Arial and TimesNewRoman
+    if (isFixedWidth()) {
+      i = 0;
+    } else if (isSerif()) {
+      i = 8;
+    } else {
+      i = 4;
+    }
+    if (isBold()) {
+      i += 2;
+    }
+    if (isItalic()) {
+      i += 1;
+    }
+    builtinFont = builtinFontSubst[i];
+    // this is a kludge for broken PDF files that encode char 32
+    // as .notdef
+    if (builtinFont->widths->getWidth("space", &w)) {
+      widths[32] = 0.001 * w;
+    }
+    for (code = 0; code < 256; ++code) {
+      if (enc[code] && builtinFont->widths->getWidth(enc[code], &w)) {
+       widths[code] = 0.001 * w;
+      }
+    }
+  }
+  obj1.free();
+
+  ok = gTrue;
+}
+
+Gfx8BitFont::~Gfx8BitFont() {
+  int i;
+
+  for (i = 0; i < 256; ++i) {
+    if (encFree[i] && enc[i]) {
+      gfree(enc[i]);
+    }
+  }
+  ctu->decRefCnt();
+  if (charProcs.isDict()) {
+    charProcs.free();
+  }
+  if (resources.isDict()) {
+    resources.free();
   }
 }
 
-void GfxFont::getType0EncAndWidths(Dict *fontDict) {
-  Object obj1, obj2, obj3, obj4, obj5, obj6, obj7, obj8;
-  int excepsSize;
-  int i, j, k, n;
+int Gfx8BitFont::getNextChar(char *s, int len, CharCode *code,
+                            Unicode *u, int uSize, int *uLen,
+                            double *dx, double *dy, double *ox, double *oy) {
+  CharCode c;
 
-  widths16.exceps = NULL;
-  widths16.excepsV = NULL;
+  *code = c = (CharCode)(*s & 0xff);
+  *uLen = ctu->mapToUnicode(c, u, uSize);
+  *dx = widths[c];
+  *dy = *ox = *oy = 0;
+  return 1;
+}
 
-  // get the CIDFont
-  fontDict->lookup("DescendantFonts", &obj1);
-  if (!obj1.isArray() || obj1.arrayGetLength() != 1) {
-    error(-1, "Bad DescendantFonts entry for Type 0 font");
+CharCodeToUnicode *Gfx8BitFont::getToUnicode() {
+  ctu->incRefCnt();
+  return ctu;
+}
+
+Dict *Gfx8BitFont::getCharProcs() {
+  return charProcs.isDict() ? charProcs.getDict() : (Dict *)NULL;
+}
+
+Object *Gfx8BitFont::getCharProc(int code, Object *proc) {
+  if (charProcs.isDict()) {
+    charProcs.dictLookup(enc[code], proc);
+  } else {
+    proc->initNull();
+  }
+  return proc;
+}
+
+Dict *Gfx8BitFont::getResources() {
+  return resources.isDict() ? resources.getDict() : (Dict *)NULL;
+}
+
+//------------------------------------------------------------------------
+// GfxCIDFont
+//------------------------------------------------------------------------
+
+static int cmpWidthExcep(const void *w1, const void *w2) {
+  return ((GfxFontCIDWidthExcep *)w1)->first -
+         ((GfxFontCIDWidthExcep *)w2)->first;
+}
+
+static int cmpWidthExcepV(const void *w1, const void *w2) {
+  return ((GfxFontCIDWidthExcepV *)w1)->first -
+         ((GfxFontCIDWidthExcepV *)w2)->first;
+}
+
+GfxCIDFont::GfxCIDFont(XRef *xref, char *tagA, Ref idA, GString *nameA,
+                      Dict *fontDict):
+  GfxFont(tagA, idA, nameA)
+{
+  Dict *desFontDict;
+  GString *collection, *cMapName;
+  Object desFontDictObj;
+  Object obj1, obj2, obj3, obj4, obj5, obj6;
+  int c1, c2;
+  int excepsSize, i, j, k;
+
+  ascent = 0.95;
+  descent = -0.35;
+  fontBBox[0] = fontBBox[1] = fontBBox[2] = fontBBox[3] = 0;
+  cMap = NULL;
+  ctu = NULL;
+  widths.defWidth = 1.0;
+  widths.defHeight = -1.0;
+  widths.defVY = 0.880;
+  widths.exceps = NULL;
+  widths.nExceps = 0;
+  widths.excepsV = NULL;
+  widths.nExcepsV = 0;
+  cidToGID = NULL;
+  cidToGIDLen = 0;
+
+  // get the descendant font
+  if (!fontDict->lookup("DescendantFonts", &obj1)->isArray()) {
+    error(-1, "Missing DescendantFonts entry in Type 0 font");
+    obj1.free();
     goto err1;
   }
-  obj1.arrayGet(0, &obj2);
-  if (!obj2.isDict()) {
-    error(-1, "Bad descendant font of Type 0 font");
-    goto err2;
+  if (!obj1.arrayGet(0, &desFontDictObj)->isDict()) {
+    error(-1, "Bad descendant font in Type 0 font");
+    goto err3;
   }
+  obj1.free();
+  desFontDict = desFontDictObj.getDict();
 
-  // get font info
-  obj2.dictLookup("CIDSystemInfo", &obj3);
-  if (!obj3.isDict()) {
-    error(-1, "Bad CIDSystemInfo in Type 0 font descendant");
+  // font type
+  if (!desFontDict->lookup("Subtype", &obj1)) {
+    error(-1, "Missing Subtype entry in Type 0 descendant font");
     goto err3;
   }
-  obj3.dictLookup("Registry", &obj4);
-  obj3.dictLookup("Ordering", &obj5);
-  if (obj4.isString() && obj5.isString()) {
-    if (obj4.getString()->cmp("Adobe") == 0 &&
-       obj5.getString()->cmp("Japan1") == 0) {
-#if JAPANESE_SUPPORT
-      is16 = gTrue;
-      enc16.charSet = font16AdobeJapan12;
-#else
-      error(-1, "Xpdf was compiled without Japanese font support");
-      goto err4;
-#endif
-    } else if (obj4.getString()->cmp("Adobe") == 0 &&
-              obj5.getString()->cmp("GB1") == 0) {
-#if CHINESE_GB_SUPPORT
-      is16 = gTrue;
-      enc16.charSet = font16AdobeGB12;
-#else
-      error(-1, "Xpdf was compiled without Chinese GB font support");
-      goto err4;
-#endif
-    } else if (obj4.getString()->cmp("Adobe") == 0 &&
-              obj5.getString()->cmp("CNS1") == 0) {
-#if CHINESE_CNS_SUPPORT
-      is16 = gTrue;
-      enc16.charSet = font16AdobeCNS13;
-#else
-      error(-1, "Xpdf was compiled without Chinese CNS font support");
-      goto err4;
-#endif
-    } else {
-      error(-1, "Uknown Type 0 character set: %s-%s",
-           obj4.getString()->getCString(), obj5.getString()->getCString());
-      goto err4;
-    }
+  if (obj1.isName("CIDFontType0")) {
+    type = fontCIDType0;
+  } else if (obj1.isName("CIDFontType2")) {
+    type = fontCIDType2;
   } else {
-    error(-1, "Unknown Type 0 character set");
+    error(-1, "Unknown Type 0 descendant font type '%s'",
+         obj1.isName() ? obj1.getName() : "???");
+    goto err3;
+  }
+  obj1.free();
+
+  // get info from font descriptor
+  readFontDescriptor(xref, desFontDict);
+
+  // look for an external font file
+  findExtFontFile();
+
+  //----- encoding info -----
+
+  // char collection
+  if (!desFontDict->lookup("CIDSystemInfo", &obj1)->isDict()) {
+    error(-1, "Missing CIDSystemInfo dictionary in Type 0 descendant font");
+    goto err3;
+  }
+  obj1.dictLookup("Registry", &obj2);
+  obj1.dictLookup("Ordering", &obj3);
+  if (!obj2.isString() || !obj3.isString()) {
+    error(-1, "Invalid CIDSystemInfo dictionary in Type 0 descendant font");
     goto err4;
   }
-  obj5.free();
-  obj4.free();
+  collection = obj2.getString()->copy()->append('-')->append(obj3.getString());
   obj3.free();
+  obj2.free();
+  obj1.free();
 
-  // get default char width
-  obj2.dictLookup("DW", &obj3);
-  if (obj3.isInt())
-    widths16.defWidth = obj3.getInt() * 0.001;
-  else
-    widths16.defWidth = 1.0;
-  obj3.free();
+  // look for a ToUnicode CMap
+  if (!(ctu = readToUnicodeCMap(fontDict, 16))) {
+
+    // the "Adobe-Identity" and "Adobe-UCS" collections don't have
+    // cidToUnicode files
+    if (collection->cmp("Adobe-Identity") &&
+       collection->cmp("Adobe-UCS")) {
 
-  // get default char metrics for vertical font
-  obj2.dictLookup("DW2", &obj3);
-  widths16.defVY = 0.880;
-  widths16.defHeight = -1;
-  if (obj3.isArray() && obj3.arrayGetLength() == 2) {
-    obj3.arrayGet(0, &obj4);
-    if (obj4.isInt()) {
-      widths16.defVY = obj4.getInt() * 0.001;
+      // look for a user-supplied .cidToUnicode file
+      if (!(ctu = globalParams->getCIDToUnicode(collection))) {
+       error(-1, "Unknown character collection '%s'",
+             collection->getCString());
+       delete collection;
+       goto err2;
+      }
     }
-    obj4.free();
-    obj3.arrayGet(1, &obj4);
-    if (obj4.isInt()) {
-      widths16.defHeight = obj4.getInt() * 0.001;
+  }
+
+  // encoding (i.e., CMap)
+  //~ need to handle a CMap stream here
+  //~ also need to deal with the UseCMap entry in the stream dict
+  if (!fontDict->lookup("Encoding", &obj1)->isName()) {
+    error(-1, "Missing or invalid Encoding entry in Type 0 font");
+    delete collection;
+    goto err3;
+  }
+  cMapName = new GString(obj1.getName());
+  obj1.free();
+  if (!(cMap = globalParams->getCMap(collection, cMapName))) {
+    error(-1, "Unknown CMap '%s' for character collection '%s'",
+         cMapName->getCString(), collection->getCString());
+    delete collection;
+    delete cMapName;
+    goto err2;
+  }
+  delete collection;
+  delete cMapName;
+
+  // CIDToGIDMap (for embedded TrueType fonts)
+  if (type == fontCIDType2) {
+    fontDict->lookup("CIDToGIDMap", &obj1);
+    if (obj1.isStream()) {
+      cidToGIDLen = 0;
+      i = 64;
+      cidToGID = (Gushort *)gmalloc(i * sizeof(Gushort));
+      obj1.streamReset();
+      while ((c1 = obj1.streamGetChar()) != EOF &&
+            (c2 = obj1.streamGetChar()) != EOF) {
+       if (cidToGIDLen == i) {
+         i *= 2;
+         cidToGID = (Gushort *)grealloc(cidToGID, i * sizeof(Gushort));
+       }
+       cidToGID[cidToGIDLen++] = (Gushort)((c1 << 8) + c2);
+      }
+    } else if (!obj1.isName("Identity") && !obj1.isNull()) {
+      error(-1, "Invalid CIDToGIDMap entry in CID font");
     }
-    obj4.free();
+    obj1.free();
   }
-  obj3.free();
 
-  // get char width exceptions
-  widths16.exceps = NULL;
-  widths16.numExceps = 0;
-  obj2.dictLookup("W", &obj3);
-  if (obj3.isArray()) {
+  //----- character metrics -----
+
+  // default char width
+  if (desFontDict->lookup("DW", &obj1)->isInt()) {
+    widths.defWidth = obj1.getInt() * 0.001;
+  }
+  obj1.free();
+
+  // char width exceptions
+  if (desFontDict->lookup("W", &obj1)->isArray()) {
     excepsSize = 0;
-    k = 0;
     i = 0;
-    while (i+1 < obj3.arrayGetLength()) {
-      obj3.arrayGet(i, &obj4);
-      obj3.arrayGet(i+1, &obj5);
-      if (obj4.isInt() && obj5.isInt()) {
-       obj3.arrayGet(i+2, &obj6);
-       if (!obj6.isNum()) {
+    while (i + 1 < obj1.arrayGetLength()) {
+      obj1.arrayGet(i, &obj2);
+      obj1.arrayGet(i + 1, &obj3);
+      if (obj2.isInt() && obj3.isInt() && i + 2 < obj1.arrayGetLength()) {
+       if (obj1.arrayGet(i + 2, &obj4)->isNum()) {
+         if (widths.nExceps == excepsSize) {
+           excepsSize += 16;
+           widths.exceps = (GfxFontCIDWidthExcep *)
+             grealloc(widths.exceps,
+                      excepsSize * sizeof(GfxFontCIDWidthExcep));
+         }
+         widths.exceps[widths.nExceps].first = obj2.getInt();
+         widths.exceps[widths.nExceps].last = obj3.getInt();
+         widths.exceps[widths.nExceps].width = obj4.getNum() * 0.001;
+         ++widths.nExceps;
+       } else {
          error(-1, "Bad widths array in Type 0 font");
-         obj6.free();
-         obj5.free();
-         obj4.free();
-         break;
-       }
-       if (k == excepsSize) {
-         excepsSize += 16;
-         widths16.exceps = (GfxFontWidthExcep *)
-                       grealloc(widths16.exceps,
-                                excepsSize * sizeof(GfxFontWidthExcep));
        }
-       widths16.exceps[k].first = obj4.getInt();
-       widths16.exceps[k].last = obj5.getInt();
-       widths16.exceps[k].width = obj6.getNum() * 0.001;
-       obj6.free();
-       ++k;
+       obj4.free();
        i += 3;
-      } else if (obj4.isInt() && obj5.isArray()) {
-       if (k + obj5.arrayGetLength() >= excepsSize) {
-         excepsSize = (k + obj5.arrayGetLength() + 15) & ~15;
-         widths16.exceps = (GfxFontWidthExcep *)
-                       grealloc(widths16.exceps,
-                                excepsSize * sizeof(GfxFontWidthExcep));
+      } else if (obj2.isInt() && obj3.isArray()) {
+       if (widths.nExceps + obj3.arrayGetLength() > excepsSize) {
+         excepsSize = (widths.nExceps + obj3.arrayGetLength() + 15) & ~15;
+         widths.exceps = (GfxFontCIDWidthExcep *)
+           grealloc(widths.exceps,
+                    excepsSize * sizeof(GfxFontCIDWidthExcep));
        }
-       n = obj4.getInt();
-       for (j = 0; j < obj5.arrayGetLength(); ++j) {
-         obj5.arrayGet(j, &obj6);
-         if (!obj6.isNum()) {
+       j = obj2.getInt();
+       for (k = 0; k < obj3.arrayGetLength(); ++k) {
+         if (obj3.arrayGet(k, &obj4)->isNum()) {
+           widths.exceps[widths.nExceps].first = j;
+           widths.exceps[widths.nExceps].last = j;
+           widths.exceps[widths.nExceps].width = obj4.getNum() * 0.001;
+           ++j;
+           ++widths.nExceps;
+         } else {
            error(-1, "Bad widths array in Type 0 font");
-           obj6.free();
-           break;
          }
-         widths16.exceps[k].first = widths16.exceps[k].last = n++;
-         widths16.exceps[k].width = obj6.getNum() * 0.001;
-         obj6.free();
-         ++k;
+         obj4.free();
        }
        i += 2;
       } else {
        error(-1, "Bad widths array in Type 0 font");
-       obj6.free();
-       obj5.free();
-       obj4.free();
-       break;
+       ++i;
       }
-      obj5.free();
-      obj4.free();
+      obj3.free();
+      obj2.free();
     }
-    widths16.numExceps = k;
-    if (k > 0)
-      qsort(widths16.exceps, k, sizeof(GfxFontWidthExcep), &cmpWidthExcep);
+    qsort(widths.exceps, widths.nExceps, sizeof(GfxFontCIDWidthExcep),
+         &cmpWidthExcep);
   }
-  obj3.free();
+  obj1.free();
 
-  // get char metric exceptions for vertical font
-  widths16.excepsV = NULL;
-  widths16.numExcepsV = 0;
-  obj2.dictLookup("W2", &obj3);
-  if (obj3.isArray()) {
+  // default metrics for vertical font
+  if (desFontDict->lookup("DW2", &obj1)->isArray() &&
+      obj1.arrayGetLength() == 2) {
+    if (obj1.arrayGet(0, &obj2)->isNum()) {
+      widths.defVY = obj1.getNum() * 0.001;
+    }
+    obj2.free();
+    if (obj1.arrayGet(1, &obj2)->isNum()) {
+      widths.defHeight = obj1.getNum() * 0.001;
+    }
+    obj2.free();
+  }
+  obj1.free();
+
+  // char metric exceptions for vertical font
+  if (desFontDict->lookup("W2", &obj1)->isArray()) {
     excepsSize = 0;
-    k = 0;
     i = 0;
-    while (i+1 < obj3.arrayGetLength()) {
-      obj3.arrayGet(i, &obj4);
-      obj3.arrayGet(i+1, &obj5);
-      if (obj4.isInt() && obj5.isInt()) {
-       obj3.arrayGet(i+2, &obj6);
-       obj3.arrayGet(i+3, &obj7);
-       obj3.arrayGet(i+4, &obj8);
-       if (!obj6.isNum() || !obj7.isNum() || !obj8.isNum()) {
+    while (i + 1 < obj1.arrayGetLength()) {
+      obj1.arrayGet(0, &obj2);
+      obj2.arrayGet(0, &obj3);
+      if (obj2.isInt() && obj3.isInt() && i + 4 < obj1.arrayGetLength()) {
+       if (obj1.arrayGet(i + 2, &obj4)->isNum() &&
+           obj1.arrayGet(i + 3, &obj5)->isNum() &&
+           obj1.arrayGet(i + 4, &obj6)->isNum()) {
+         if (widths.nExcepsV == excepsSize) {
+           excepsSize += 16;
+           widths.excepsV = (GfxFontCIDWidthExcepV *)
+             grealloc(widths.excepsV,
+                      excepsSize * sizeof(GfxFontCIDWidthExcepV));
+         }
+         widths.excepsV[widths.nExcepsV].first = obj2.getInt();
+         widths.excepsV[widths.nExcepsV].last = obj3.getInt();
+         widths.excepsV[widths.nExcepsV].height = obj4.getNum() * 0.001;
+         widths.excepsV[widths.nExcepsV].vx = obj5.getNum() * 0.001;
+         widths.excepsV[widths.nExcepsV].vy = obj6.getNum() * 0.001;
+         ++widths.nExcepsV;
+       } else {
          error(-1, "Bad widths (W2) array in Type 0 font");
-         obj8.free();
-         obj7.free();
-         obj6.free();
-         obj5.free();
-         obj4.free();
-         break;
-       }
-       if (k == excepsSize) {
-         excepsSize += 16;
-         widths16.excepsV = (GfxFontWidthExcepV *)
-                       grealloc(widths16.excepsV,
-                                excepsSize * sizeof(GfxFontWidthExcepV));
        }
-       widths16.excepsV[k].first = obj4.getInt();
-       widths16.excepsV[k].last = obj5.getInt();
-       widths16.excepsV[k].height = obj6.getNum() * 0.001;
-       widths16.excepsV[k].vx = obj7.getNum() * 0.001;
-       widths16.excepsV[k].vy = obj8.getNum() * 0.001;
-       obj8.free();
-       obj7.free();
        obj6.free();
-       ++k;
+       obj5.free();
+       obj4.free();
        i += 5;
-      } else if (obj4.isInt() && obj5.isArray()) {
-       if (k + obj5.arrayGetLength() / 3 >= excepsSize) {
-         excepsSize = (k + obj5.arrayGetLength() / 3 + 15) & ~15;
-         widths16.excepsV = (GfxFontWidthExcepV *)
-                       grealloc(widths16.excepsV,
-                                excepsSize * sizeof(GfxFontWidthExcepV));
+      } else if (obj2.isInt() && obj3.isArray()) {
+       if (widths.nExcepsV + obj3.arrayGetLength() / 3 > excepsSize) {
+         excepsSize =
+           (widths.nExcepsV + obj3.arrayGetLength() / 3 + 15) & ~15;
+         widths.excepsV = (GfxFontCIDWidthExcepV *)
+           grealloc(widths.excepsV,
+                    excepsSize * sizeof(GfxFontCIDWidthExcepV));
        }
-       n = obj4.getInt();
-       for (j = 0; j < obj5.arrayGetLength(); j += 3) {
-         obj5.arrayGet(j, &obj6);
-         obj5.arrayGet(j+1, &obj7);
-         obj5.arrayGet(j+1, &obj8);
-         if (!obj6.isNum() || !obj7.isNum() || !obj8.isNum()) {
+       j = obj2.getInt();
+       for (k = 0; k < obj3.arrayGetLength(); ++k) {
+         if (obj3.arrayGet(k, &obj4)->isNum() &&
+             obj3.arrayGet(k, &obj5)->isNum() &&
+             obj3.arrayGet(k, &obj6)->isNum()) {
+           widths.excepsV[widths.nExceps].first = j;
+           widths.excepsV[widths.nExceps].last = j;
+           widths.excepsV[widths.nExceps].height = obj4.getNum() * 0.001;
+           widths.excepsV[widths.nExceps].vx = obj5.getNum() * 0.001;
+           widths.excepsV[widths.nExceps].vy = obj6.getNum() * 0.001;
+           ++j;
+           ++widths.nExcepsV;
+         } else {
            error(-1, "Bad widths (W2) array in Type 0 font");
-           obj6.free();
-           break;
          }
-         widths16.excepsV[k].first = widths16.exceps[k].last = n++;
-         widths16.excepsV[k].height = obj6.getNum() * 0.001;
-         widths16.excepsV[k].vx = obj7.getNum() * 0.001;
-         widths16.excepsV[k].vy = obj8.getNum() * 0.001;
-         obj8.free();
-         obj7.free();
          obj6.free();
-         ++k;
+         obj5.free();
+         obj4.free();
        }
        i += 2;
       } else {
-       error(-1, "Bad widths array in Type 0 font");
-       obj5.free();
-       obj4.free();
-       break;
+       error(-1, "Bad widths (W2) array in Type 0 font");
+       ++i;
       }
-      obj5.free();
-      obj4.free();
-    }
-    widths16.numExcepsV = k;
-    if (k > 0) {
-      qsort(widths16.excepsV, k, sizeof(GfxFontWidthExcepV), &cmpWidthExcepV);
+      obj3.free();
+      obj2.free();
     }
+    qsort(widths.excepsV, widths.nExcepsV, sizeof(GfxFontCIDWidthExcepV),
+         &cmpWidthExcepV);
   }
-  obj3.free();
+  obj1.free();
 
+  desFontDictObj.free();
+  ok = gTrue;
+  return;
+
+ err4:
+  obj3.free();
   obj2.free();
+ err3:
   obj1.free();
+ err2:
+  desFontDictObj.free();
+ err1:;
+}
 
-  // get encoding (CMap)
-  fontDict->lookup("Encoding", &obj1);
-  if (!obj1.isName()) {
-    error(-1, "Bad encoding for Type 0 font");
-    goto err1;
+GfxCIDFont::~GfxCIDFont() {
+  if (cMap) {
+    cMap->decRefCnt();
   }
-#if JAPANESE_SUPPORT
-  if (enc16.charSet == font16AdobeJapan12) {
-    for (i = 0; gfxJapan12Tab[i].name; ++i) {
-      if (!strcmp(obj1.getName(), gfxJapan12Tab[i].name))
-       break;
-    }
-    if (!gfxJapan12Tab[i].name) {
-      error(-1, "Unknown encoding '%s' for Adobe-Japan1-2 font",
-           obj1.getName());
-      goto err1;
-    }
-    enc16.enc = gfxJapan12Tab[i].enc;
+  if (ctu) {
+    ctu->decRefCnt();
   }
-#endif
-#if CHINESE_GB_SUPPORT
-  if (enc16.charSet == font16AdobeGB12) {
-    for (i = 0; gfxGB12Tab[i].name; ++i) {
-      if (!strcmp(obj1.getName(), gfxGB12Tab[i].name))
-       break;
-    }
-    if (!gfxGB12Tab[i].name) {
-      error(-1, "Unknown encoding '%s' for Adobe-GB1-2 font",
-           obj1.getName());
-      goto err1;
-    }
-    enc16.enc = gfxGB12Tab[i].enc;
+  gfree(widths.exceps);
+  gfree(widths.excepsV);
+  if (cidToGID) {
+    gfree(cidToGID);
   }
-#endif
-#if CHINESE_CNS_SUPPORT
-  if (enc16.charSet == font16AdobeCNS13) {
-    for (i = 0; gfxCNS13Tab[i].name; ++i) {
-      if (!strcmp(obj1.getName(), gfxCNS13Tab[i].name))
-       break;
+}
+
+int GfxCIDFont::getNextChar(char *s, int len, CharCode *code,
+                           Unicode *u, int uSize, int *uLen,
+                           double *dx, double *dy, double *ox, double *oy) {
+  CID cid;
+  double w, h, vx, vy;
+  int n, a, b, m;
+
+  if (!cMap) {
+    *code = 0;
+    *uLen = 0;
+    *dx = *dy = 0;
+    return 1;
+  }
+
+  *code = (CharCode)(cid = cMap->getCID(s, len, &n));
+  if (ctu) {
+    *uLen = ctu->mapToUnicode(cid, u, uSize);
+  } else {
+    *uLen = 0;
+  }
+
+  // horizontal
+  if (cMap->getWMode() == 0) {
+    w = widths.defWidth;
+    h = vx = vy = 0;
+    if (widths.nExceps > 0 && cid >= widths.exceps[0].first) {
+      a = 0;
+      b = widths.nExceps;
+      // invariant: widths.exceps[a].first <= cid < widths.exceps[b].first
+      while (b - a > 1) {
+       m = (a + b) / 2;
+       if (widths.exceps[m].first <= cid) {
+         a = m;
+       } else {
+         b = m;
+       }
+      }
+      if (cid <= widths.exceps[a].last) {
+       w = widths.exceps[a].width;
+      }
     }
-    if (!gfxCNS13Tab[i].name) {
-      error(-1, "Unknown encoding '%s' for Adobe-CNS1-3 font",
-           obj1.getName());
-      goto err1;
+
+  // vertical
+  } else {
+    w = 0;
+    h = widths.defHeight;
+    vx = widths.defWidth / 2;
+    vy = widths.defVY;
+    if (widths.nExcepsV > 0 && cid >= widths.excepsV[0].first) {
+      a = 0;
+      b = widths.nExcepsV;
+      // invariant: widths.excepsV[a].first <= cid < widths.excepsV[b].first
+      while (b - a > 1) {
+       m = (a + b) / 2;
+       if (widths.excepsV[m].last <= cid) {
+         a = m;
+       } else {
+         b = m;
+       }
+      }
+      if (cid <= widths.excepsV[a].last) {
+       h = widths.excepsV[a].height;
+       vx = widths.excepsV[a].vx;
+       vy = widths.excepsV[a].vy;
+      }
     }
-    enc16.enc = gfxCNS13Tab[i].enc;
   }
-#endif
-  obj1.free();
 
-  return;
+  *dx = w;
+  *dy = h;
+  *ox = vx;
+  *oy = vy;
 
- err4:
-  obj5.free();
-  obj4.free();
- err3:
-  obj3.free();
- err2:
-  obj2.free();
- err1:
-  obj1.free();
-  //~ fix this --> add 16-bit font support to FontFile
-  encoding = new FontEncoding();
-  makeWidths(fontDict, NULL, NULL, 0);
+  return n;
+}
+
+int GfxCIDFont::getWMode() {
+  return cMap ? cMap->getWMode() : 0;
 }
 
-static int CDECL cmpWidthExcep(const void *w1, const void *w2) {
-  return ((GfxFontWidthExcep *)w1)->first - ((GfxFontWidthExcep *)w2)->first;
+CharCodeToUnicode *GfxCIDFont::getToUnicode() {
+  ctu->incRefCnt();
+  return ctu;
 }
 
-static int CDECL cmpWidthExcepV(const void *w1, const void *w2) {
-  return ((GfxFontWidthExcepV *)w1)->first - ((GfxFontWidthExcepV *)w2)->first;
+GString *GfxCIDFont::getCollection() {
+  return cMap ? cMap->getCollection() : (GString *)NULL;
 }
 
 //------------------------------------------------------------------------
 // GfxFontDict
 //------------------------------------------------------------------------
 
-GfxFontDict::GfxFontDict(Dict *fontDict) {
+GfxFontDict::GfxFontDict(XRef *xref, Dict *fontDict) {
   int i;
   Object obj1, obj2;
 
@@ -986,12 +1245,16 @@ GfxFontDict::GfxFontDict(Dict *fontDict) {
   fonts = (GfxFont **)gmalloc(numFonts * sizeof(GfxFont *));
   for (i = 0; i < numFonts; ++i) {
     fontDict->getValNF(i, &obj1);
-    obj1.fetch(&obj2);
+    obj1.fetch(xref, &obj2);
     if (obj1.isRef() && obj2.isDict()) {
-      fonts[i] = new GfxFont(fontDict->getKey(i), obj1.getRef(),
-                            obj2.getDict());
+      fonts[i] = GfxFont::makeFont(xref, fontDict->getKey(i),
+                                  obj1.getRef(), obj2.getDict());
+      if (fonts[i] && !fonts[i]->isOk()) {
+       delete fonts[i];
+       fonts[i] = NULL;
+      }
     } else {
-      error(-1, "font resource is not a dictionary");
+      error(-1, "font resource is not a dictionary reference");
       fonts[i] = NULL;
     }
     obj1.free();
@@ -1002,8 +1265,11 @@ GfxFontDict::GfxFontDict(Dict *fontDict) {
 GfxFontDict::~GfxFontDict() {
   int i;
 
-  for (i = 0; i < numFonts; ++i)
-    delete fonts[i];
+  for (i = 0; i < numFonts; ++i) {
+    if (fonts[i]) {
+      delete fonts[i];
+    }
+  }
   gfree(fonts);
 }
 
@@ -1011,8 +1277,9 @@ GfxFont *GfxFontDict::lookup(char *tag) {
   int i;
 
   for (i = 0; i < numFonts; ++i) {
-    if (fonts[i]->matches(tag))
+    if (fonts[i] && fonts[i]->matches(tag)) {
       return fonts[i];
+    }
   }
   return NULL;
 }
index 0435d90..c67ac29 100644 (file)
@@ -2,7 +2,7 @@
 //
 // GfxFont.h
 //
-// Copyright 1996 Derek B. Noonburg
+// Copyright 1996-2002 Glyph & Cog, LLC
 //
 //========================================================================
 
 #include "gtypes.h"
 #include "GString.h"
 #include "Object.h"
-#include "FontEncoding.h"
+#include "CharTypes.h"
 
 class Dict;
-struct BuiltinFont;
+class CMap;
+class CharCodeToUnicode;
+struct GfxFontCIDWidths;
 
 //------------------------------------------------------------------------
-// GfxFontCharSet16
+// GfxFontType
 //------------------------------------------------------------------------
 
-enum GfxFontCharSet16 {
-  font16AdobeJapan12,          // Adobe-Japan1-2
-  font16AdobeGB12,             // Adobe-GB1-2 (Chinese)
-  font16AdobeCNS13             // Adobe-CNS1-3 (Chinese)
-};
-
-//------------------------------------------------------------------------
-// GfxFontEncoding16
-//------------------------------------------------------------------------
-
-struct GfxFontEncoding16 {
-  int wMode;                   // writing mode (0=horizontal, 1=vertical)
-  Guchar codeLen[256];         // length of codes, in bytes, indexed by
-                               //   first byte of code
-  Gushort map1[256];           // one-byte code mapping:
-                               //   map1[code] --> 16-bit char selector
-  Gushort *map2;               // two-byte code mapping
-                               //   map2[2*i]   --> first code in range
-                               //   map2[2*i+1] --> 16-bit char selector
-                               //                   for map2[2*i]
-  int map2Len;                 // length of map2 array (divided by 2)
+enum GfxFontType {
+  //----- Gfx8BitFont
+  fontUnknownType,
+  fontType1,
+  fontType1C,
+  fontType3,
+  fontTrueType,
+  //----- GfxCIDFont
+  fontCIDType0,
+  fontCIDType0C,
+  fontCIDType2
 };
 
 //------------------------------------------------------------------------
-// GfxFontWidths16
+// GfxFontCIDWidths
 //------------------------------------------------------------------------
 
-struct GfxFontWidthExcep {
-  int first;                   // this record applies to
-  int last;                    //   chars <first>..<last>
+struct GfxFontCIDWidthExcep {
+  CID first;                   // this record applies to
+  CID last;                    //   CIDs <first>..<last>
   double width;                        // char width
 };
 
-struct GfxFontWidthExcepV {
-  int first;                   // this record applies to
-  int last;                    //   chars <first>..<last>
+struct GfxFontCIDWidthExcepV {
+  CID first;                   // this record applies to
+  CID last;                    //   CIDs <first>..<last>
   double height;               // char height
   double vx, vy;               // origin position
 };
 
-struct GfxFontWidths16 {
+struct GfxFontCIDWidths {
   double defWidth;             // default char width
   double defHeight;            // default char height
   double defVY;                        // default origin position
-  GfxFontWidthExcep *exceps;   // exceptions
-  int numExceps;               // number of valid entries in exceps
-  GfxFontWidthExcepV *excepsV; // exceptions for vertical font
-  int numExcepsV;              // number of valid entries in excepsV
+  GfxFontCIDWidthExcep *exceps;        // exceptions
+  int nExceps;                 // number of valid entries in exceps
+  GfxFontCIDWidthExcepV *      // exceptions for vertical font
+    excepsV;
+  int nExcepsV;                        // number of valid entries in excepsV
 };
 
 //------------------------------------------------------------------------
@@ -85,41 +78,33 @@ struct GfxFontWidths16 {
 #define fontItalic     (1 << 6)
 #define fontBold       (1 << 18)
 
-enum GfxFontType {
-  fontUnknownType,
-  fontType1,
-  fontType1C,
-  fontType3,
-  fontTrueType,
-  fontType0
-};
-
 class GfxFont {
 public:
 
-  // Constructor.
-  GfxFont(char *tag1, Ref id1, Dict *fontDict);
+  // Build a GfxFont object.
+  static GfxFont *makeFont(XRef *xref, char *tagA, Ref idA, Dict *fontDict);
 
-  // Destructor.
-  ~GfxFont();
+  GfxFont(char *tagA, Ref idA, GString *nameA);
+
+  virtual ~GfxFont();
+
+  GBool isOk() { return ok; }
 
   // Get font tag.
   GString *getTag() { return tag; }
 
   // Get font dictionary ID.
-  Ref getID() { return id; }
+  Ref *getID() { return &id; }
 
   // Does this font match the tag?
-  GBool matches(char *tag1) { return !tag->cmp(tag1); }
+  GBool matches(char *tagA) { return !tag->cmp(tagA); }
 
   // Get base font name.
   GString *getName() { return name; }
 
   // Get font type.
   GfxFontType getType() { return type; }
-
-  // Does this font use 16-bit characters?
-  GBool is16Bit() { return is16; }
+  virtual GBool isCIDFont() { return gFalse; }
 
   // Get embedded font ID, i.e., a ref for the font file stream.
   // Returns false if there is no embedded font.
@@ -128,8 +113,7 @@ public:
 
   // Get the PostScript font name for the embedded font.  Returns
   // NULL if there is no embedded font.
-  char *getEmbeddedFontName()
-    { return embFontName ? embFontName->getCString() : (char *)NULL; }
+  GString *getEmbeddedFontName() { return embFontName; }
 
   // Get the name of the external font file.  Returns NULL if there
   // is no external font file.
@@ -142,73 +126,147 @@ public:
   GBool isItalic() { return flags & fontItalic; }
   GBool isBold() { return flags & fontBold; }
 
-  // Get width of a character or string.
-  double getWidth(Guchar c) { return widths[c]; }
-  double getWidth(GString *s);
+  // Return the font matrix.
+  double *getFontMatrix() { return fontMat; }
+
+  // Return the font bounding box.
+  double *getFontBBox() { return fontBBox; }
+
+  // Return the ascent and descent values.
+  double getAscent() { return ascent; }
+  double getDescent() { return descent; }
+
+  // Return the writing mode (0=horizontal, 1=vertical).
+  virtual int getWMode() { return 0; }
+
+  // Read an external or embedded font file into a buffer.
+  char *readExtFontFile(int *len);
+  char *readEmbFontFile(XRef *xref, int *len);
+
+  // Get the next char from a string <s> of <len> bytes, returning the
+  // char <code>, its Unicode mapping <u>, its displacement vector
+  // (<dx>, <dy>), and its origin offset vector (<ox>, <oy>).  <uSize>
+  // is the number of entries available in <u>, and <uLen> is set to
+  // the number actually used.  Returns the number of bytes used by
+  // the char code.
+  virtual int getNextChar(char *s, int len, CharCode *code,
+                         Unicode *u, int uSize, int *uLen,
+                         double *dx, double *dy, double *ox, double *oy) = 0;
+
+protected:
+
+  void readFontDescriptor(XRef *xref, Dict *fontDict);
+  CharCodeToUnicode *readToUnicodeCMap(Dict *fontDict, int nBits);
+  void findExtFontFile();
+
+  GString *tag;                        // PDF font tag
+  Ref id;                      // reference (used as unique ID)
+  GString *name;               // font name
+  GfxFontType type;            // type of font
+  int flags;                   // font descriptor flags
+  GString *embFontName;                // name of embedded font
+  Ref embFontID;               // ref to embedded font file stream
+  GString *extFontFile;                // external font file name
+  double fontMat[6];           // font matrix (Type 3 only)
+  double fontBBox[4];          // font bounding box (Type 3 only)
+  double missingWidth;         // "default" width
+  double ascent;               // max height above baseline
+  double descent;              // max depth below baseline
+  GBool ok;
+};
+
+//------------------------------------------------------------------------
+// Gfx8BitFont
+//------------------------------------------------------------------------
 
-  // Get character metrics for 16-bit font.
-  double getWidth16(int c);
-  double getHeight16(int c);
-  double getOriginX16(int c);
-  double getOriginY16(int c);
+class Gfx8BitFont: public GfxFont {
+public:
+
+  Gfx8BitFont(XRef *xref, char *tagA, Ref idA, GString *nameA,
+             GfxFontType typeA, Dict *fontDict);
+
+  virtual ~Gfx8BitFont();
+
+  virtual int getNextChar(char *s, int len, CharCode *code,
+                         Unicode *u, int uSize, int *uLen,
+                         double *dx, double *dy, double *ox, double *oy);
 
   // Return the encoding.
-  FontEncoding *getEncoding() { return encoding; }
+  char **getEncoding() { return enc; }
+
+  // Return the Unicode map.
+  CharCodeToUnicode *getToUnicode();
 
   // Return the character name associated with <code>.
-  char *getCharName(int code) { return encoding->getCharName(code); }
+  char *getCharName(int code) { return enc[code]; }
 
-  // Return the code associated with <name>.
-  int getCharCode(char *charName) { return encoding->getCharCode(charName); }
+  // Returns true if the PDF font specified an encoding.
+  GBool getHasEncoding() { return hasEncoding; }
+
+  // Get width of a character or string.
+  double getWidth(Guchar c) { return widths[c]; }
+
+  // Return the Type 3 CharProc dictionary, or NULL if none.
+  Dict *getCharProcs();
 
   // Return the Type 3 CharProc for the character associated with <code>.
   Object *getCharProc(int code, Object *proc);
 
-  // Return the 16-bit character set and encoding.
-  GfxFontCharSet16 getCharSet16() { return enc16.charSet; }
-  GfxFontEncoding16 *getEncoding16() { return enc16.enc; }
+  // Return the Type 3 Resources dictionary, or NULL if none.
+  Dict *getResources();
 
-  // Get the writing mode (0=horizontal, 1=vertical).
-  int getWMode16() { return enc16.enc->wMode; }
+private:
 
-  // Return the font matrix.
-  double *getFontMatrix() { return fontMat; }
+  char *enc[256];              // char code --> char name
+  char encFree[256];           // boolean for each char name: if set,
+                               //   the string is malloc'ed
+  CharCodeToUnicode *ctu;      // char code --> Unicode
+  GBool hasEncoding;
+  double widths[256];          // character widths
+  Object charProcs;            // Type 3 CharProcs dictionary
+  Object resources;            // Type 3 Resources dictionary
+};
 
-  // Read an external or embedded font file into a buffer.
-  char *readExtFontFile(int *len);
-  char *readEmbFontFile(int *len);
+//------------------------------------------------------------------------
+// GfxCIDFont
+//------------------------------------------------------------------------
 
-private:
+class GfxCIDFont: public GfxFont {
+public:
 
-  void getEncAndWidths(Dict *fontDict, BuiltinFont *builtinFont,
-                      int missingWidth);
-  void findExtFontFile();
-  void makeWidths(Dict *fontDict, FontEncoding *builtinEncoding,
-                 Gushort *builtinWidths, int missingWidth);
-  void getType0EncAndWidths(Dict *fontDict);
+  GfxCIDFont(XRef *xref, char *tagA, Ref idA, GString *nameA,
+            Dict *fontDict);
 
-  GString *tag;                        // PDF font tag
-  Ref id;                      // reference (used as unique ID)
-  GString *name;               // font name
-  int flags;                   // font descriptor flags
-  GfxFontType type;            // type of font
-  GBool is16;                  // set if font uses 16-bit chars
-  GString *embFontName;                // name of embedded font
-  Ref embFontID;               // ref to embedded font file stream
-  GString *extFontFile;                // external font file name
-  Object charProcs;            // Type3 CharProcs dictionary
-  double fontMat[6];           // font matrix
-  union {
-    FontEncoding *encoding;    // 8-bit font encoding
-    struct {
-      GfxFontCharSet16 charSet;        // 16-bit character set
-      GfxFontEncoding16 *enc;  // 16-bit encoding (CMap)
-    } enc16;
-  };
-  union {
-    double widths[256];                // width of each char for 8-bit font
-    GfxFontWidths16 widths16;  // char widths for 16-bit font
-  };
+  virtual ~GfxCIDFont();
+
+  virtual GBool isCIDFont() { return gTrue; }
+
+  virtual int getNextChar(char *s, int len, CharCode *code,
+                         Unicode *u, int uSize, int *uLen,
+                         double *dx, double *dy, double *ox, double *oy);
+
+  // Return the writing mode (0=horizontal, 1=vertical).
+  virtual int getWMode();
+
+  // Return the Unicode map.
+  CharCodeToUnicode *getToUnicode();
+
+  // Get the collection name (<registry>-<ordering>).
+  GString *getCollection();
+
+  // Return the CID-to-GID mapping table.  These should only be called
+  // if type is fontCIDType2.
+  Gushort *getCIDToGID() { return cidToGID; }
+  int getCIDToGIDLen() { return cidToGIDLen; }
+
+private:
+
+  CMap *cMap;                  // char code --> CID
+  CharCodeToUnicode *ctu;      // CID --> Unicode
+  GfxFontCIDWidths widths;     // character widths
+  Gushort *cidToGID;           // CID --> GID mapping (for embedded
+                               //   TrueType fonts)
+  int cidToGIDLen;
 };
 
 //------------------------------------------------------------------------
@@ -219,7 +277,7 @@ class GfxFontDict {
 public:
 
   // Build the font dictionary, given the PDF font dictionary.
-  GfxFontDict(Dict *fontDict);
+  GfxFontDict(XRef *xref, Dict *fontDict);
 
   // Destructor.
   ~GfxFontDict();
index b7afdae..1668c24 100644 (file)
@@ -2,7 +2,7 @@
 //
 // GfxState.cc
 //
-// Copyright 1996 Derek B. Noonburg
+// Copyright 1996-2002 Glyph & Cog, LLC
 //
 //========================================================================
 
@@ -10,6 +10,7 @@
 #pragma implementation
 #endif
 
+#include <aconf.h>
 #include <stddef.h>
 #include <math.h>
 #include <string.h> // for memcpy()
@@ -17,6 +18,7 @@
 #include "Error.h"
 #include "Object.h"
 #include "Array.h"
+#include "Page.h"
 #include "GfxState.h"
 
 //------------------------------------------------------------------------
@@ -261,9 +263,9 @@ GfxCalRGBColorSpace::GfxCalRGBColorSpace() {
   whiteX = whiteY = whiteZ = 1;
   blackX = blackY = blackZ = 0;
   gammaR = gammaG = gammaB = 1;
-  m[0] = 1; m[1] = 0; m[2] = 0;
-  m[3] = 0; m[4] = 1; m[5] = 0;
-  m[6] = 0; m[7] = 0; m[8] = 1;
+  mat[0] = 1; mat[1] = 0; mat[2] = 0;
+  mat[3] = 0; mat[4] = 1; mat[5] = 0;
+  mat[6] = 0; mat[7] = 0; mat[8] = 1;
 }
 
 GfxCalRGBColorSpace::~GfxCalRGBColorSpace() {
@@ -284,7 +286,7 @@ GfxColorSpace *GfxCalRGBColorSpace::copy() {
   cs->gammaG = gammaG;
   cs->gammaB = gammaB;
   for (i = 0; i < 9; ++i) {
-    cs->m[i] = m[i];
+    cs->mat[i] = mat[i];
   }
   return cs;
 }
@@ -344,7 +346,7 @@ GfxColorSpace *GfxCalRGBColorSpace::parse(Array *arr) {
       obj2.arrayGetLength() == 9) {
     for (i = 0; i < 9; ++i) {
       obj2.arrayGet(i, &obj3);
-      cs->m[i] = obj3.getNum();
+      cs->mat[i] = obj3.getNum();
       obj3.free();
     }
   }
@@ -406,9 +408,22 @@ void GfxDeviceCMYKColorSpace::getGray(GfxColor *color, double *gray) {
 }
 
 void GfxDeviceCMYKColorSpace::getRGB(GfxColor *color, GfxRGB *rgb) {
-  rgb->r = clip01(1 - (color->c[0] + color->c[3]));
-  rgb->g = clip01(1 - (color->c[1] + color->c[3]));
-  rgb->b = clip01(1 - (color->c[2] + color->c[3]));
+  double c, m, y, aw, ac, am, ay, ar, ag, ab;
+
+  c = clip01(color->c[0] + color->c[3]);
+  m = clip01(color->c[1] + color->c[3]);
+  y = clip01(color->c[2] + color->c[3]);
+  aw = (1-c) * (1-m) * (1-y);
+  ac = c * (1-m) * (1-y);
+  am = (1-c) * m * (1-y);
+  ay = (1-c) * (1-m) * y;
+  ar = (1-c) * m * y;
+  ag = c * (1-m) * y;
+  ab = c * m * (1-y);
+  rgb->r = clip01(aw + 0.9137*am + 0.9961*ay + 0.9882*ar);
+  rgb->g = clip01(aw + 0.6196*ac + ay + 0.5176*ag);
+  rgb->b = clip01(aw + 0.7804*ac + 0.5412*am + 0.0667*ar + 0.2118*ag +
+                 0.4863*ab);
 }
 
 void GfxDeviceCMYKColorSpace::getCMYK(GfxColor *color, GfxCMYK *cmyk) {
@@ -609,11 +624,11 @@ void GfxLabColorSpace::getDefaultRanges(double *decodeLow, double *decodeRange,
 // GfxICCBasedColorSpace
 //------------------------------------------------------------------------
 
-GfxICCBasedColorSpace::GfxICCBasedColorSpace(int nComps, GfxColorSpace *alt,
-                                            Ref *iccProfileStream) {
-  this->nComps = nComps;
-  this->alt = alt;
-  this->iccProfileStream = *iccProfileStream;
+GfxICCBasedColorSpace::GfxICCBasedColorSpace(int nCompsA, GfxColorSpace *altA,
+                                            Ref *iccProfileStreamA) {
+  nComps = nCompsA;
+  alt = altA;
+  iccProfileStream = *iccProfileStreamA;
   rangeMin[0] = rangeMin[1] = rangeMin[2] = rangeMin[3] = 0;
   rangeMax[0] = rangeMax[1] = rangeMax[2] = rangeMax[3] = 1;
 }
@@ -636,19 +651,19 @@ GfxColorSpace *GfxICCBasedColorSpace::copy() {
 
 GfxColorSpace *GfxICCBasedColorSpace::parse(Array *arr) {
   GfxICCBasedColorSpace *cs;
-  Ref iccProfileStream;
-  int nComps;
-  GfxColorSpace *alt;
+  Ref iccProfileStreamA;
+  int nCompsA;
+  GfxColorSpace *altA;
   Dict *dict;
   Object obj1, obj2, obj3;
   int i;
 
   arr->getNF(1, &obj1);
   if (obj1.isRef()) {
-    iccProfileStream = obj1.getRef();
+    iccProfileStreamA = obj1.getRef();
   } else {
-    iccProfileStream.num = 0;
-    iccProfileStream.gen = 0;
+    iccProfileStreamA.num = 0;
+    iccProfileStreamA.gen = 0;
   }
   obj1.free();
   arr->get(1, &obj1);
@@ -664,19 +679,19 @@ GfxColorSpace *GfxICCBasedColorSpace::parse(Array *arr) {
     obj1.free();
     return NULL;
   }
-  nComps = obj2.getInt();
+  nCompsA = obj2.getInt();
   obj2.free();
   if (dict->lookup("Alternate", &obj2)->isNull() ||
-      !(alt = GfxColorSpace::parse(&obj2))) {
-    switch (nComps) {
+      !(altA = GfxColorSpace::parse(&obj2))) {
+    switch (nCompsA) {
     case 1:
-      alt = new GfxDeviceGrayColorSpace();
+      altA = new GfxDeviceGrayColorSpace();
       break;
     case 3:
-      alt = new GfxDeviceRGBColorSpace();
+      altA = new GfxDeviceRGBColorSpace();
       break;
     case 4:
-      alt = new GfxDeviceCMYKColorSpace();
+      altA = new GfxDeviceCMYKColorSpace();
       break;
     default:
       error(-1, "Bad ICCBased color space - invalid N");
@@ -686,10 +701,10 @@ GfxColorSpace *GfxICCBasedColorSpace::parse(Array *arr) {
     }
   }
   obj2.free();
-  cs = new GfxICCBasedColorSpace(nComps, alt, &iccProfileStream);
+  cs = new GfxICCBasedColorSpace(nCompsA, altA, &iccProfileStreamA);
   if (dict->lookup("Range", &obj2)->isArray() &&
-      obj2.arrayGetLength() == 2 * nComps) {
-    for (i = 0; i < nComps; ++i) {
+      obj2.arrayGetLength() == 2 * nCompsA) {
+    for (i = 0; i < nCompsA; ++i) {
       obj2.arrayGet(2*i, &obj3);
       cs->rangeMin[i] = obj3.getNum();
       obj3.free();
@@ -730,12 +745,12 @@ void GfxICCBasedColorSpace::getDefaultRanges(double *decodeLow,
 // GfxIndexedColorSpace
 //------------------------------------------------------------------------
 
-GfxIndexedColorSpace::GfxIndexedColorSpace(GfxColorSpace *base,
-                                          int indexHigh) {
-  this->base = base;
-  this->indexHigh = indexHigh;
-  this->lookup = (Guchar *)gmalloc((indexHigh + 1) * base->getNComps() *
-                                  sizeof(Guchar));
+GfxIndexedColorSpace::GfxIndexedColorSpace(GfxColorSpace *baseA,
+                                          int indexHighA) {
+  base = baseA;
+  indexHigh = indexHighA;
+  lookup = (Guchar *)gmalloc((indexHigh + 1) * base->getNComps() *
+                            sizeof(Guchar));
 }
 
 GfxIndexedColorSpace::~GfxIndexedColorSpace() {
@@ -754,8 +769,8 @@ GfxColorSpace *GfxIndexedColorSpace::copy() {
 
 GfxColorSpace *GfxIndexedColorSpace::parse(Array *arr) {
   GfxIndexedColorSpace *cs;
-  GfxColorSpace *base;
-  int indexHigh;
+  GfxColorSpace *baseA;
+  int indexHighA;
   Object obj1;
   int x;
   char *s;
@@ -766,7 +781,7 @@ GfxColorSpace *GfxIndexedColorSpace::parse(Array *arr) {
     goto err1;
   }
   arr->get(1, &obj1);
-  if (!(base = GfxColorSpace::parse(&obj1))) {
+  if (!(baseA = GfxColorSpace::parse(&obj1))) {
     error(-1, "Bad Indexed color space (base color space)");
     goto err2;
   }
@@ -775,14 +790,14 @@ GfxColorSpace *GfxIndexedColorSpace::parse(Array *arr) {
     error(-1, "Bad Indexed color space (hival)");
     goto err2;
   }
-  indexHigh = obj1.getInt();
+  indexHighA = obj1.getInt();
   obj1.free();
-  cs = new GfxIndexedColorSpace(base, indexHigh);
+  cs = new GfxIndexedColorSpace(baseA, indexHighA);
   arr->get(3, &obj1);
-  n = base->getNComps();
+  n = baseA->getNComps();
   if (obj1.isStream()) {
     obj1.streamReset();
-    for (i = 0; i <= indexHigh; ++i) {
+    for (i = 0; i <= indexHighA; ++i) {
       for (j = 0; j < n; ++j) {
        if ((x = obj1.streamGetChar()) == EOF) {
          error(-1, "Bad Indexed color space (lookup table stream too short)");
@@ -793,12 +808,12 @@ GfxColorSpace *GfxIndexedColorSpace::parse(Array *arr) {
     }
     obj1.streamClose();
   } else if (obj1.isString()) {
-    if (obj1.getString()->getLength() < (indexHigh + 1) * n) {
+    if (obj1.getString()->getLength() < (indexHighA + 1) * n) {
       error(-1, "Bad Indexed color space (lookup table string too short)");
       goto err3;
     }
     s = obj1.getString()->getCString();
-    for (i = 0; i <= indexHigh; ++i) {
+    for (i = 0; i <= indexHighA; ++i) {
       for (j = 0; j < n; ++j) {
        cs->lookup[i*n + j] = (Guchar)*s++;
       }
@@ -868,12 +883,12 @@ void GfxIndexedColorSpace::getDefaultRanges(double *decodeLow,
 // GfxSeparationColorSpace
 //------------------------------------------------------------------------
 
-GfxSeparationColorSpace::GfxSeparationColorSpace(GString *name,
-                                                GfxColorSpace *alt,
-                                                Function *func) {
-  this->name = name;
-  this->alt = alt;
-  this->func = func;
+GfxSeparationColorSpace::GfxSeparationColorSpace(GString *nameA,
+                                                GfxColorSpace *altA,
+                                                Function *funcA) {
+  name = nameA;
+  alt = altA;
+  func = funcA;
 }
 
 GfxSeparationColorSpace::~GfxSeparationColorSpace() {
@@ -889,9 +904,9 @@ GfxColorSpace *GfxSeparationColorSpace::copy() {
 //~ handle the 'All' and 'None' colorants
 GfxColorSpace *GfxSeparationColorSpace::parse(Array *arr) {
   GfxSeparationColorSpace *cs;
-  GString *name;
-  GfxColorSpace *alt;
-  Function *func;
+  GString *nameA;
+  GfxColorSpace *altA;
+  Function *funcA;
   Object obj1;
 
   if (arr->getLength() != 4) {
@@ -902,28 +917,26 @@ GfxColorSpace *GfxSeparationColorSpace::parse(Array *arr) {
     error(-1, "Bad Separation color space (name)");
     goto err2;
   }
-  name = new GString(obj1.getName());
+  nameA = new GString(obj1.getName());
   obj1.free();
   arr->get(2, &obj1);
-  if (!(alt = GfxColorSpace::parse(&obj1))) {
+  if (!(altA = GfxColorSpace::parse(&obj1))) {
     error(-1, "Bad Separation color space (alternate color space)");
     goto err3;
   }
   obj1.free();
-  func = Function::parse(arr->get(3, &obj1));
-  obj1.free();
-  if (!func || !func->isOk()) {
+  arr->get(3, &obj1);
+  if (!(funcA = Function::parse(&obj1))) {
     goto err4;
   }
-  cs = new GfxSeparationColorSpace(name, alt, func);
+  obj1.free();
+  cs = new GfxSeparationColorSpace(nameA, altA, funcA);
   return cs;
 
  err4:
-  if(func)
-    delete func;
-  delete alt;
+  delete altA;
  err3:
-  delete name;
+  delete nameA;
  err2:
   obj1.free();
  err1:
@@ -955,12 +968,12 @@ void GfxSeparationColorSpace::getCMYK(GfxColor *color, GfxCMYK *cmyk) {
 // GfxDeviceNColorSpace
 //------------------------------------------------------------------------
 
-GfxDeviceNColorSpace::GfxDeviceNColorSpace(int nComps,
-                                          GfxColorSpace *alt,
-                                          Function *func) {
-  this->nComps = nComps;
-  this->alt = alt;
-  this->func = func;
+GfxDeviceNColorSpace::GfxDeviceNColorSpace(int nCompsA,
+                                          GfxColorSpace *altA,
+                                          Function *funcA) {
+  nComps = nCompsA;
+  alt = altA;
+  func = funcA;
 }
 
 GfxDeviceNColorSpace::~GfxDeviceNColorSpace() {
@@ -987,10 +1000,10 @@ GfxColorSpace *GfxDeviceNColorSpace::copy() {
 //~ handle the 'None' colorant
 GfxColorSpace *GfxDeviceNColorSpace::parse(Array *arr) {
   GfxDeviceNColorSpace *cs;
-  int nComps;
-  GString *names[gfxColorMaxComps];
-  GfxColorSpace *alt;
-  Function *func;
+  int nCompsA;
+  GString *namesA[gfxColorMaxComps];
+  GfxColorSpace *altA;
+  Function *funcA;
   Object obj1, obj2;
   int i;
 
@@ -1002,40 +1015,39 @@ GfxColorSpace *GfxDeviceNColorSpace::parse(Array *arr) {
     error(-1, "Bad DeviceN color space (names)");
     goto err2;
   }
-  nComps = obj1.arrayGetLength();
-  for (i = 0; i < nComps; ++i) {
+  nCompsA = obj1.arrayGetLength();
+  for (i = 0; i < nCompsA; ++i) {
     if (!obj1.arrayGet(i, &obj2)->isName()) {
       error(-1, "Bad DeviceN color space (names)");
       obj2.free();
       goto err2;
     }
-    names[i] = new GString(obj2.getName());
+    namesA[i] = new GString(obj2.getName());
     obj2.free();
   }
   obj1.free();
   arr->get(2, &obj1);
-  if (!(alt = GfxColorSpace::parse(&obj1))) {
+  if (!(altA = GfxColorSpace::parse(&obj1))) {
     error(-1, "Bad DeviceN color space (alternate color space)");
     goto err3;
   }
   obj1.free();
-  func = Function::parse(arr->get(3, &obj1));
-  obj1.free();
-  if (!func->isOk()) {
+  arr->get(3, &obj1);
+  if (!(funcA = Function::parse(&obj1))) {
     goto err4;
   }
-  cs = new GfxDeviceNColorSpace(nComps, alt, func);
-  for (i = 0; i < nComps; ++i) {
-    cs->names[i] = names[i];
+  obj1.free();
+  cs = new GfxDeviceNColorSpace(nCompsA, altA, funcA);
+  for (i = 0; i < nCompsA; ++i) {
+    cs->names[i] = namesA[i];
   }
   return cs;
 
  err4:
-  delete func;
-  delete alt;
+  delete altA;
  err3:
-  for (i = 0; i < nComps; ++i) {
-    delete names[i];
+  for (i = 0; i < nCompsA; ++i) {
+    delete namesA[i];
   }
  err2:
   obj1.free();
@@ -1068,8 +1080,8 @@ void GfxDeviceNColorSpace::getCMYK(GfxColor *color, GfxCMYK *cmyk) {
 // GfxPatternColorSpace
 //------------------------------------------------------------------------
 
-GfxPatternColorSpace::GfxPatternColorSpace(GfxColorSpace *under) {
-  this->under = under;
+GfxPatternColorSpace::GfxPatternColorSpace(GfxColorSpace *underA) {
+  under = underA;
 }
 
 GfxPatternColorSpace::~GfxPatternColorSpace() {
@@ -1085,24 +1097,24 @@ GfxColorSpace *GfxPatternColorSpace::copy() {
 
 GfxColorSpace *GfxPatternColorSpace::parse(Array *arr) {
   GfxPatternColorSpace *cs;
-  GfxColorSpace *under;
+  GfxColorSpace *underA;
   Object obj1;
 
   if (arr->getLength() != 1 && arr->getLength() != 2) {
     error(-1, "Bad Pattern color space");
     return NULL;
   }
-  under = NULL;
+  underA = NULL;
   if (arr->getLength() == 2) {
     arr->get(1, &obj1);
-    if (!(under = GfxColorSpace::parse(&obj1))) {
+    if (!(underA = GfxColorSpace::parse(&obj1))) {
       error(-1, "Bad Pattern color space (underlying color space)");
       obj1.free();
       return NULL;
     }
     obj1.free();
   }
-  cs = new GfxPatternColorSpace(under);
+  cs = new GfxPatternColorSpace(underA);
   return cs;
 }
 
@@ -1123,8 +1135,8 @@ void GfxPatternColorSpace::getCMYK(GfxColor *color, GfxCMYK *cmyk) {
 // Pattern
 //------------------------------------------------------------------------
 
-GfxPattern::GfxPattern(int type) {
-  this->type = type;
+GfxPattern::GfxPattern(int typeA) {
+  type = typeA;
 }
 
 GfxPattern::~GfxPattern() {
@@ -1238,480 +1250,375 @@ GfxTilingPattern::GfxTilingPattern(GfxTilingPattern *pat):
 }
 
 //------------------------------------------------------------------------
-// Function
+// GfxShading
 //------------------------------------------------------------------------
 
-Function::Function() {
-}
-
-Function::~Function() {
+GfxShading::GfxShading() {
 }
 
-Function *Function::parse(Object *funcObj) {
-  Function *func;
-  Dict *dict;
-  int funcType;
-  Object obj1;
-
-  if (funcObj->isStream()) {
-    dict = funcObj->streamGetDict();
-  } else if (funcObj->isDict()) {
-    dict = funcObj->getDict();
-  } else {
-    error(-1, "Expected function dictionary or stream");
-    return NULL;
-  }
-
-  if (!dict->lookup("FunctionType", &obj1)->isInt()) {
-    error(-1, "Function type is missing or wrong type");
-    obj1.free();
-    return NULL;
-  }
-  funcType = obj1.getInt();
-  obj1.free();
-
-  if (funcType == 0) {
-    func = new SampledFunction(funcObj, dict);
-  } else if (funcType == 2) {
-    func = new ExponentialFunction(funcObj, dict);
-  } else {
-    error(-1, "Unimplemented function type");
-    return NULL;
-  }
-  if (!func->isOk()) {
-    delete func;
-    return NULL;
-  }
-
-  return func;
+GfxShading::~GfxShading() {
+  delete colorSpace;
 }
 
-GBool Function::init(Dict *dict) {
+GfxShading *GfxShading::parse(Object *obj) {
+  GfxShading *shading;
+  int typeA;
+  GfxColorSpace *colorSpaceA;
+  GfxColor backgroundA;
+  GBool hasBackgroundA;
+  double xMinA, yMinA, xMaxA, yMaxA;
+  GBool hasBBoxA;
   Object obj1, obj2;
   int i;
 
-  //----- Domain
-  if (!dict->lookup("Domain", &obj1)->isArray()) {
-    error(-1, "Function is missing domain");
-    goto err2;
-  }
-  m = obj1.arrayGetLength() / 2;
-  if (m > funcMaxInputs) {
-    error(-1, "Functions with more than %d inputs are unsupported",
-         funcMaxInputs);
-    goto err2;
-  }
-  for (i = 0; i < m; ++i) {
-    obj1.arrayGet(2*i, &obj2);
-    if (!obj2.isNum()) {
-      error(-1, "Illegal value in function domain array");
+  shading = NULL;
+  if (obj->isDict()) {
+
+    if (!obj->dictLookup("ShadingType", &obj1)->isInt()) {
+      error(-1, "Invalid ShadingType in shading dictionary");
+      obj1.free();
       goto err1;
     }
-    domain[i][0] = obj2.getNum();
-    obj2.free();
-    obj1.arrayGet(2*i+1, &obj2);
-    if (!obj2.isNum()) {
-      error(-1, "Illegal value in function domain array");
+    typeA = obj1.getInt();
+    obj1.free();
+
+    obj->dictLookup("ColorSpace", &obj1);
+    if (!(colorSpaceA = GfxColorSpace::parse(&obj1))) {
+      error(-1, "Bad color space in shading dictionary");
+      obj1.free();
       goto err1;
     }
-    domain[i][1] = obj2.getNum();
-    obj2.free();
-  }
-  obj1.free();
+    obj1.free();
 
-  //----- Range
-  hasRange = gFalse;
-  n = 0;
-  if (dict->lookup("Range", &obj1)->isArray()) {
-    hasRange = gTrue;
-    n = obj1.arrayGetLength() / 2;
-    if (n > funcMaxOutputs) {
-      error(-1, "Functions with more than %d outputs are unsupported",
-           funcMaxOutputs);
-      goto err2;
+    for (i = 0; i < gfxColorMaxComps; ++i) {
+      backgroundA.c[i] = 0;
     }
-    for (i = 0; i < n; ++i) {
-      obj1.arrayGet(2*i, &obj2);
-      if (!obj2.isNum()) {
-       error(-1, "Illegal value in function range array");
-       goto err1;
+    hasBackgroundA = gFalse;
+    if (obj->dictLookup("Background", &obj1)->isArray()) {
+      if (obj1.arrayGetLength() == colorSpaceA->getNComps()) {
+       hasBackgroundA = gTrue;
+       for (i = 0; i < colorSpaceA->getNComps(); ++i) {
+         backgroundA.c[i] = obj1.arrayGet(i, &obj2)->getNum();
+         obj2.free();
+       }
+      } else {
+       error(-1, "Bad Background in shading dictionary");
       }
-      range[i][0] = obj2.getNum();
-      obj2.free();
-      obj1.arrayGet(2*i+1, &obj2);
-      if (!obj2.isNum()) {
-       error(-1, "Illegal value in function range array");
-       goto err1;
+    }
+    obj1.free();
+
+    xMinA = yMinA = xMaxA = yMaxA = 0;
+    hasBBoxA = gFalse;
+    if (obj->dictLookup("BBox", &obj1)->isArray()) {
+      if (obj1.arrayGetLength() == 4) {
+       hasBBoxA = gTrue;
+       xMinA = obj1.arrayGet(0, &obj2)->getNum();
+       obj2.free();
+       yMinA = obj1.arrayGet(1, &obj2)->getNum();
+       obj2.free();
+       xMaxA = obj1.arrayGet(2, &obj2)->getNum();
+       obj2.free();
+       yMaxA = obj1.arrayGet(3, &obj2)->getNum();
+       obj2.free();
+      } else {
+       error(-1, "Bad BBox in shading dictionary");
       }
-      range[i][1] = obj2.getNum();
-      obj2.free();
     }
     obj1.free();
+
+    switch (typeA) {
+    case 2:
+      shading = GfxAxialShading::parse(obj->getDict());
+      break;
+    case 3:
+      shading = GfxRadialShading::parse(obj->getDict());
+      break;
+    default:
+      error(-1, "Unimplemented shading type %d", typeA);
+      goto err1;
+    }
+
+    if (shading) {
+      shading->type = typeA;
+      shading->colorSpace = colorSpaceA;
+      shading->background = backgroundA;
+      shading->hasBackground = hasBackgroundA;
+      shading->xMin = xMinA;
+      shading->yMin = yMinA;
+      shading->xMax = xMaxA;
+      shading->yMax = yMaxA;
+      shading->hasBBox = hasBBoxA;
+    } else {
+      delete colorSpaceA;
+    }
   }
 
-  return gTrue;
+  return shading;
 
  err1:
-  obj2.free();
- err2:
-  obj1.free();
-  return gFalse;
+  return NULL;
 }
 
 //------------------------------------------------------------------------
-// SampledFunction
+// GfxAxialShading
 //------------------------------------------------------------------------
 
-SampledFunction::SampledFunction(Object *funcObj, Dict *dict) {
-  Stream *str;
-  int nSamples, sampleBits;
-  double sampleMul;
-  Object obj1, obj2;
-  Guint buf, bitMask;
-  int bits;
-  int s;
+GfxAxialShading::GfxAxialShading(double x0A, double y0A,
+                                double x1A, double y1A,
+                                double t0A, double t1A,
+                                Function **funcsA, int nFuncsA,
+                                GBool extend0A, GBool extend1A) {
   int i;
 
-  samples = NULL;
-  ok = gFalse;
-
-  //----- initialize the generic stuff
-  if (!init(dict)) {
-    goto err1;
-  }
-  if (!hasRange) {
-    error(-1, "Type 0 function is missing range");
-    goto err1;
+  x0 = x0A;
+  y0 = y0A;
+  x1 = x1A;
+  y1 = y1A;
+  t0 = t0A;
+  t1 = t1A;
+  nFuncs = nFuncsA;
+  for (i = 0; i < nFuncs; ++i) {
+    funcs[i] = funcsA[i];
   }
+  extend0 = extend0A;
+  extend1 = extend1A;
+}
 
-  //----- get the stream
-  if (!funcObj->isStream()) {
-    error(-1, "Type 0 function isn't a stream");
-    goto err1;
-  }
-  str = funcObj->getStream();
+GfxAxialShading::~GfxAxialShading() {
+  int i;
 
-  //----- Size
-  if (!dict->lookup("Size", &obj1)->isArray() ||
-      obj1.arrayGetLength() != m) {
-    error(-1, "Function has missing or invalid size array");
-    goto err2;
+  for (i = 0; i < nFuncs; ++i) {
+    delete funcs[i];
   }
-  for (i = 0; i < m; ++i) {
-    obj1.arrayGet(i, &obj2);
-    if (!obj2.isInt()) {
-      error(-1, "Illegal value in function size array");
-      goto err3;
-    }
-    sampleSize[i] = obj2.getInt();
+}
+
+GfxAxialShading *GfxAxialShading::parse(Dict *dict) {
+  double x0A, y0A, x1A, y1A;
+  double t0A, t1A;
+  Function *funcsA[gfxColorMaxComps];
+  int nFuncsA;
+  GBool extend0A, extend1A;
+  Object obj1, obj2;
+  int i;
+
+  x0A = y0A = x1A = y1A = 0;
+  if (dict->lookup("Coords", &obj1)->isArray() &&
+      obj1.arrayGetLength() == 4) {
+    x0A = obj1.arrayGet(0, &obj2)->getNum();
+    obj2.free();
+    y0A = obj1.arrayGet(1, &obj2)->getNum();
+    obj2.free();
+    x1A = obj1.arrayGet(2, &obj2)->getNum();
     obj2.free();
+    y1A = obj1.arrayGet(3, &obj2)->getNum();
+    obj2.free();
+  } else {
+    error(-1, "Missing or invalid Coords in shading dictionary");
+    goto err1;
   }
   obj1.free();
 
-  //----- BitsPerSample
-  if (!dict->lookup("BitsPerSample", &obj1)->isInt()) {
-    error(-1, "Function has missing or invalid BitsPerSample");
-    goto err2;
+  t0A = 0;
+  t1A = 1;
+  if (dict->lookup("Domain", &obj1)->isArray() &&
+      obj1.arrayGetLength() == 2) {
+    t0A = obj1.arrayGet(0, &obj2)->getNum();
+    obj2.free();
+    t1A = obj1.arrayGet(1, &obj2)->getNum();
+    obj2.free();
   }
-  sampleBits = obj1.getInt();
-  sampleMul = 1.0 / (double)((1 << sampleBits) - 1);
   obj1.free();
 
-  //----- Encode
-  if (dict->lookup("Encode", &obj1)->isArray() &&
-      obj1.arrayGetLength() == 2*m) {
-    for (i = 0; i < m; ++i) {
-      obj1.arrayGet(2*i, &obj2);
-      if (!obj2.isNum()) {
-       error(-1, "Illegal value in function encode array");
-       goto err3;
-      }
-      encode[i][0] = obj2.getNum();
-      obj2.free();
-      obj1.arrayGet(2*i+1, &obj2);
-      if (!obj2.isNum()) {
-       error(-1, "Illegal value in function encode array");
-       goto err3;
+  dict->lookup("Function", &obj1);
+  if (obj1.isArray()) {
+    nFuncsA = obj1.arrayGetLength();
+    for (i = 0; i < nFuncsA; ++i) {
+      obj1.arrayGet(i, &obj2);
+      if (!(funcsA[i] = Function::parse(&obj2))) {
+       obj1.free();
+       obj2.free();
+       goto err1;
       }
-      encode[i][1] = obj2.getNum();
       obj2.free();
     }
   } else {
-    for (i = 0; i < m; ++i) {
-      encode[i][0] = 0;
-      encode[i][1] = sampleSize[i] - 1;
+    nFuncsA = 1;
+    if (!(funcsA[0] = Function::parse(&obj1))) {
+      obj1.free();
+      goto err1;
     }
   }
   obj1.free();
 
-  //----- Decode
-  if (dict->lookup("Decode", &obj1)->isArray() &&
-      obj1.arrayGetLength() == 2*n) {
-    for (i = 0; i < n; ++i) {
-      obj1.arrayGet(2*i, &obj2);
-      if (!obj2.isNum()) {
-       error(-1, "Illegal value in function decode array");
-       goto err3;
-      }
-      decode[i][0] = obj2.getNum();
-      obj2.free();
-      obj1.arrayGet(2*i+1, &obj2);
-      if (!obj2.isNum()) {
-       error(-1, "Illegal value in function decode array");
-       goto err3;
-      }
-      decode[i][1] = obj2.getNum();
-      obj2.free();
-    }
-  } else {
-    for (i = 0; i < n; ++i) {
-      decode[i][0] = range[i][0];
-      decode[i][1] = range[i][1];
-    }
+  extend0A = extend1A = gFalse;
+  if (dict->lookup("Extend", &obj1)->isArray() &&
+      obj1.arrayGetLength() == 2) {
+    extend0A = obj1.arrayGet(0, &obj2)->getBool();
+    obj2.free();
+    extend1A = obj1.arrayGet(1, &obj2)->getBool();
+    obj2.free();
   }
   obj1.free();
 
-  //----- samples
-  nSamples = n;
-  for (i = 0; i < m; ++i)
-    nSamples *= sampleSize[i];
-  samples = (double *)gmalloc(nSamples * sizeof(double));
-  buf = 0;
-  bits = 0;
-  bitMask = (1 << sampleBits) - 1;
-  str->reset();
-  for (i = 0; i < nSamples; ++i) {
-    if (sampleBits == 8) {
-      s = str->getChar();
-    } else if (sampleBits == 16) {
-      s = str->getChar();
-      s = (s << 8) + str->getChar();
-    } else if (sampleBits == 32) {
-      s = str->getChar();
-      s = (s << 8) + str->getChar();
-      s = (s << 8) + str->getChar();
-      s = (s << 8) + str->getChar();
-    } else {
-      while (bits < sampleBits) {
-       buf = (buf << 8) | (str->getChar() & 0xff);
-       bits += 8;
-      }
-      s = (buf >> (bits - sampleBits)) & bitMask;
-      bits -= sampleBits;
-    }
-    samples[i] = (double)s * sampleMul;
-  }
-  str->close();
-
-  ok = gTrue;
-  return;
+  return new GfxAxialShading(x0A, y0A, x1A, y1A, t0A, t1A,
+                            funcsA, nFuncsA, extend0A, extend1A);
 
- err3:
-  obj2.free();
- err2:
-  obj1.free();
  err1:
-  return;
+  return NULL;
 }
 
-SampledFunction::~SampledFunction() {
-  if (samples) {
-    gfree(samples);
+void GfxAxialShading::getColor(double t, GfxColor *color) {
+  int i;
+
+  for (i = 0; i < nFuncs; ++i) {
+    funcs[i]->transform(&t, &color->c[i]);
   }
 }
 
-SampledFunction::SampledFunction(SampledFunction *func) {
-  int nSamples, i;
+//------------------------------------------------------------------------
+// GfxRadialShading
+//------------------------------------------------------------------------
 
-  memcpy(this, func, sizeof(SampledFunction));
+GfxRadialShading::GfxRadialShading(double x0A, double y0A, double r0A,
+                                  double x1A, double y1A, double r1A,
+                                  double t0A, double t1A,
+                                  Function **funcsA, int nFuncsA,
+                                  GBool extend0A, GBool extend1A) {
+  int i;
 
-  nSamples = n;
-  for (i = 0; i < m; ++i) {
-    nSamples *= sampleSize[i];
+  x0 = x0A;
+  y0 = y0A;
+  r0 = r0A;
+  x1 = x1A;
+  y1 = y1A;
+  r1 = r1A;
+  t0 = t0A;
+  t1 = t1A;
+  nFuncs = nFuncsA;
+  for (i = 0; i < nFuncs; ++i) {
+    funcs[i] = funcsA[i];
   }
-  samples = (double *)gmalloc(nSamples * sizeof(double));
-  memcpy(samples, func->samples, nSamples * sizeof(double));
+  extend0 = extend0A;
+  extend1 = extend1A;
 }
 
-void SampledFunction::transform(double *in, double *out) {
-  double e[4];
-  double s;
-  double x0, x1;
-  int e0, e1;
-  double efrac;
+GfxRadialShading::~GfxRadialShading() {
   int i;
 
-  // map input values into sample array
-  for (i = 0; i < m; ++i) {
-    e[i] = ((in[i] - domain[i][0]) / (domain[i][1] - domain[i][0])) *
-           (encode[i][1] - encode[i][0]) + encode[i][0];
-    if (e[i] < 0) {
-      e[i] = 0;
-    } else if (e[i] > sampleSize[i] - 1) {
-      e[i] = sampleSize[i] - 1;
-    }
-  }
-
-  for (i = 0; i < n; ++i) {
-
-    // m-linear interpolation
-    // (only m=1 is currently supported)
-    e0 = (int)floor(e[0]);
-    e1 = (int)ceil(e[0]);
-    efrac = e[0] - e0;
-    x0 = samples[e0 * n + i];
-    x1 = samples[e1 * n + i];
-    s = (1 - efrac) * x0 + efrac * x1;
-
-    // map output values to range
-    out[i] = s * (decode[i][1] - decode[i][0]) + decode[i][0];
-    if (out[i] < range[i][0]) {
-      out[i] = range[i][0];
-    } else if (out[i] > range[i][1]) {
-      out[i] = range[i][1];
-    }
+  for (i = 0; i < nFuncs; ++i) {
+    delete funcs[i];
   }
 }
 
-//------------------------------------------------------------------------
-// ExponentialFunction
-//------------------------------------------------------------------------
-
-ExponentialFunction::ExponentialFunction(Object *funcObj, Dict *dict) {
+GfxRadialShading *GfxRadialShading::parse(Dict *dict) {
+  double x0A, y0A, r0A, x1A, y1A, r1A;
+  double t0A, t1A;
+  Function *funcsA[gfxColorMaxComps];
+  int nFuncsA;
+  GBool extend0A, extend1A;
   Object obj1, obj2;
-  GBool hasN;
   int i;
 
-  ok = gFalse;
-  hasN = gFalse;
-
-  //----- initialize the generic stuff
-  if (!init(dict)) {
-    goto err1;
-  }
-  if (m != 1) {
-    error(-1, "Exponential function with more than one input");
+  x0A = y0A = r0A = x1A = y1A = r1A = 0;
+  if (dict->lookup("Coords", &obj1)->isArray() &&
+      obj1.arrayGetLength() == 6) {
+    x0A = obj1.arrayGet(0, &obj2)->getNum();
+    obj2.free();
+    y0A = obj1.arrayGet(1, &obj2)->getNum();
+    obj2.free();
+    r0A = obj1.arrayGet(2, &obj2)->getNum();
+    obj2.free();
+    x1A = obj1.arrayGet(3, &obj2)->getNum();
+    obj2.free();
+    y1A = obj1.arrayGet(4, &obj2)->getNum();
+    obj2.free();
+    r1A = obj1.arrayGet(5, &obj2)->getNum();
+    obj2.free();
+  } else {
+    error(-1, "Missing or invalid Coords in shading dictionary");
     goto err1;
   }
+  obj1.free();
 
-  //----- default values
-  for (i = 0; i < funcMaxOutputs; ++i) {
-    c0[i] = 0;
-    c1[i] = 1;
+  t0A = 0;
+  t1A = 1;
+  if (dict->lookup("Domain", &obj1)->isArray() &&
+      obj1.arrayGetLength() == 2) {
+    t0A = obj1.arrayGet(0, &obj2)->getNum();
+    obj2.free();
+    t1A = obj1.arrayGet(1, &obj2)->getNum();
+    obj2.free();
   }
+  obj1.free();
 
-  //----- C0
-  if (dict->lookup("C0", &obj1)->isArray()) {
-    if (!hasN) {
-      n = obj1.arrayGetLength();
-    } else if (obj1.arrayGetLength() != n) {
-      error(-1, "Function's C0 array is wrong length");
-      goto err2;
-    }
-    for (i = 0; i < n; ++i) {
+  dict->lookup("Function", &obj1);
+  if (obj1.isArray()) {
+    nFuncsA = obj1.arrayGetLength();
+    for (i = 0; i < nFuncsA; ++i) {
       obj1.arrayGet(i, &obj2);
-      if (!obj2.isNum()) {
-       error(-1, "Illegal value in function C0 array");
-       goto err3;
+      if (!(funcsA[i] = Function::parse(&obj2))) {
+       obj1.free();
+       obj2.free();
+       goto err1;
       }
-      c0[i] = obj2.getNum();
       obj2.free();
     }
-    obj1.free();
-  }
-
-  //----- C1
-  if (dict->lookup("C1", &obj1)->isArray()) {
-    if (!hasN) {
-      n = obj1.arrayGetLength();
-    } else if (obj1.arrayGetLength() != n) {
-      error(-1, "Function's C1 array is wrong length");
-      goto err2;
-    }
-    for (i = 0; i < n; ++i) {
-      obj1.arrayGet(i, &obj2);
-      if (!obj2.isNum()) {
-       error(-1, "Illegal value in function C1 array");
-       goto err3;
-      }
-      c1[i] = obj2.getNum();
-      obj2.free();
+  } else {
+    nFuncsA = 1;
+    if (!(funcsA[0] = Function::parse(&obj1))) {
+      obj1.free();
+      goto err1;
     }
-    obj1.free();
   }
+  obj1.free();
 
-  //----- N (exponent)
-  if (!dict->lookup("N", &obj1)->isNum()) {
-    error(-1, "Function has missing or invalid N");
-    goto err2;
+  extend0A = extend1A = gFalse;
+  if (dict->lookup("Extend", &obj1)->isArray() &&
+      obj1.arrayGetLength() == 2) {
+    extend0A = obj1.arrayGet(0, &obj2)->getBool();
+    obj2.free();
+    extend1A = obj1.arrayGet(1, &obj2)->getBool();
+    obj2.free();
   }
-  e = obj1.getNum();
   obj1.free();
 
-  ok = gTrue;
-  return;
+  return new GfxRadialShading(x0A, y0A, r0A, x1A, y1A, r1A, t0A, t1A,
+                             funcsA, nFuncsA, extend0A, extend1A);
 
- err3:
-  obj2.free();
- err2:
-  obj1.free();
  err1:
-  return;
-}
-
-ExponentialFunction::~ExponentialFunction() {
-}
-
-ExponentialFunction::ExponentialFunction(ExponentialFunction *func) {
-  memcpy(this, func, sizeof(ExponentialFunction));
+  return NULL;
 }
 
-void ExponentialFunction::transform(double *in, double *out) {
-  double x;
+void GfxRadialShading::getColor(double t, GfxColor *color) {
   int i;
 
-  if (in[0] < domain[0][0]) {
-    x = domain[0][0];
-  } else if (in[0] > domain[0][1]) {
-    x = domain[0][1];
-  } else {
-    x = in[0];
-  }
-  for (i = 0; i < n; ++i) {
-    out[i] = c0[i] + pow(x, e) * (c1[i] - c0[i]);
-    if (hasRange) {
-      if (out[i] < range[i][0]) {
-       out[i] = range[i][0];
-      } else if (out[i] > range[i][1]) {
-       out[i] = range[i][1];
-      }
-    }
+  for (i = 0; i < nFuncs; ++i) {
+    funcs[i]->transform(&t, &color->c[i]);
   }
-  return;
 }
 
 //------------------------------------------------------------------------
 // GfxImageColorMap
 //------------------------------------------------------------------------
 
-GfxImageColorMap::GfxImageColorMap(int bits, Object *decode,
-                                  GfxColorSpace *colorSpace) {
+GfxImageColorMap::GfxImageColorMap(int bitsA, Object *decode,
+                                  GfxColorSpace *colorSpaceA) {
   GfxIndexedColorSpace *indexedCS;
   GfxSeparationColorSpace *sepCS;
   int maxPixel, indexHigh;
   Guchar *lookup2;
   Function *sepFunc;
   Object obj;
-  double x;
+  double x[gfxColorMaxComps];
   double y[gfxColorMaxComps];
   int i, j, k;
 
   ok = gTrue;
 
   // bits per component and color space
-  this->bits = bits;
+  bits = bitsA;
   maxPixel = (1 << bits) - 1;
-  this->colorSpace = colorSpace;
+  colorSpace = colorSpaceA;
 
   // get decode map
   if (decode->isNull()) {
@@ -1740,18 +1647,6 @@ GfxImageColorMap::GfxImageColorMap(int bits, Object *decode,
     goto err1;
   }
 
-#if 0 //~
-  // handle the case where fewer than 2^n palette entries of an n-bit
-  // indexed color space are populated (this happens, e.g., in files
-  // optimized by Distiller)
-  if (colorSpace->getMode() == csIndexed) {
-    i = ((GfxIndexedColorSpace *)colorSpace)->getIndexHigh();
-    if (i < maxPixel) {
-      maxPixel = i;
-    }
-  }
-#endif
-
   // Construct a lookup table -- this stores pre-computed decoded
   // values for each component, i.e., the result of applying the
   // decode mapping to each possible image pixel component value.
@@ -1784,8 +1679,8 @@ GfxImageColorMap::GfxImageColorMap(int bits, Object *decode,
     lookup = (double *)gmalloc((maxPixel + 1) * nComps2 * sizeof(double));
     sepFunc = sepCS->getFunc();
     for (i = 0; i <= maxPixel; ++i) {
-      x = decodeLow[0] + (i * decodeRange[0]) / maxPixel;
-      sepFunc->transform(&x, y);
+      x[0] = decodeLow[0] + (i * decodeRange[0]) / maxPixel;
+      sepFunc->transform(x, y);
       for (k = 0; k < nComps2; ++k) {
        lookup[i*nComps2 + k] = y[k];
       }
@@ -2010,19 +1905,34 @@ void GfxPath::curveTo(double x1, double y1, double x2, double y2,
   subpaths[n-1]->curveTo(x1, y1, x2, y2, x3, y3);
 }
 
+void GfxPath::close() {
+  // this is necessary to handle the pathological case of
+  // moveto/closepath/clip, which defines an empty clipping region
+  if (justMoved) {
+    if (n >= size) {
+      size += 16;
+      subpaths = (GfxSubpath **)
+                  grealloc(subpaths, size * sizeof(GfxSubpath *));
+    }
+    subpaths[n] = new GfxSubpath(firstX, firstY);
+    ++n;
+    justMoved = gFalse;
+  }
+  subpaths[n-1]->close();
+}
 
 //------------------------------------------------------------------------
 // GfxState
 //------------------------------------------------------------------------
 
-GfxState::GfxState(double dpi, double px1a, double py1a,
-                  double px2a, double py2a, int rotate, GBool upsideDown) {
+GfxState::GfxState(double dpi, PDFRectangle *pageBox, int rotate,
+                  GBool upsideDown) {
   double k;
 
-  px1 = px1a;
-  py1 = py1a;
-  px2 = px2a;
-  py2 = py2a;
+  px1 = pageBox->x1;
+  py1 = pageBox->y1;
+  px2 = pageBox->x2;
+  py2 = pageBox->y2;
   k = dpi / 72.0;
   if (rotate == 90) {
     ctm[0] = 0;
@@ -2096,6 +2006,11 @@ GfxState::GfxState(double dpi, double px1a, double py1a,
   curX = curY = 0;
   lineX = lineY = 0;
 
+  clipXMin = 0;
+  clipYMin = 0;
+  clipXMax = pageWidth;
+  clipYMax = pageHeight;
+
   saved = NULL;
 }
 
@@ -2113,7 +2028,10 @@ GfxState::~GfxState() {
     delete strokePattern;
   }
   gfree(lineDash);
-  delete path;
+  if (path) {
+    // this gets set to NULL by restore()
+    delete path;
+  }
   if (saved) {
     delete saved;
   }
@@ -2138,10 +2056,70 @@ GfxState::GfxState(GfxState *state) {
     lineDash = (double *)gmalloc(lineDashLength * sizeof(double));
     memcpy(lineDash, state->lineDash, lineDashLength * sizeof(double));
   }
-  path = state->path->copy();
   saved = NULL;
 }
 
+void GfxState::getUserClipBBox(double *xMin, double *yMin,
+                              double *xMax, double *yMax) {
+  double ictm[6];
+  double xMin1, yMin1, xMax1, yMax1, det, tx, ty;
+
+  // invert the CTM
+  det = 1 / (ctm[0] * ctm[3] - ctm[1] * ctm[2]);
+  ictm[0] = ctm[3] * det;
+  ictm[1] = -ctm[1] * det;
+  ictm[2] = -ctm[2] * det;
+  ictm[3] = ctm[0] * det;
+  ictm[4] = (ctm[2] * ctm[5] - ctm[3] * ctm[4]) * det;
+  ictm[5] = (ctm[1] * ctm[4] - ctm[0] * ctm[5]) * det;
+
+  // transform all four corners of the clip bbox; find the min and max
+  // x and y values
+  xMin1 = xMax1 = clipXMin * ictm[0] + clipYMin * ictm[2] + ictm[4];
+  yMin1 = yMax1 = clipXMin * ictm[1] + clipYMin * ictm[3] + ictm[5];
+  tx = clipXMin * ictm[0] + clipYMax * ictm[2] + ictm[4];
+  ty = clipXMin * ictm[1] + clipYMax * ictm[3] + ictm[5];
+  if (tx < xMin1) {
+    xMin1 = tx;
+  } else if (tx > xMax1) {
+    xMax1 = tx;
+  }
+  if (ty < yMin1) {
+    yMin1 = ty;
+  } else if (ty > yMax1) {
+    yMax1 = ty;
+  }
+  tx = clipXMax * ictm[0] + clipYMin * ictm[2] + ictm[4];
+  ty = clipXMax * ictm[1] + clipYMin * ictm[3] + ictm[5];
+  if (tx < xMin1) {
+    xMin1 = tx;
+  } else if (tx > xMax1) {
+    xMax1 = tx;
+  }
+  if (ty < yMin1) {
+    yMin1 = ty;
+  } else if (ty > yMax1) {
+    yMax1 = ty;
+  }
+  tx = clipXMax * ictm[0] + clipYMax * ictm[2] + ictm[4];
+  ty = clipXMax * ictm[1] + clipYMax * ictm[3] + ictm[5];
+  if (tx < xMin1) {
+    xMin1 = tx;
+  } else if (tx > xMax1) {
+    xMax1 = tx;
+  }
+  if (ty < yMin1) {
+    yMin1 = ty;
+  } else if (ty > yMax1) {
+    yMax1 = ty;
+  }
+
+  *xMin = xMin1;
+  *yMin = yMin1;
+  *xMax = xMax1;
+  *yMax = yMax1;
+}
+
 double GfxState::transformWidth(double w) {
   double x, y;
 
@@ -2170,12 +2148,23 @@ void GfxState::getFontTransMat(double *m11, double *m12,
 
 void GfxState::setCTM(double a, double b, double c,
                      double d, double e, double f) {
+  int i;
+
   ctm[0] = a;
   ctm[1] = b;
   ctm[2] = c;
   ctm[3] = d;
   ctm[4] = e;
   ctm[5] = f;
+
+  // avoid FP exceptions on badly messed up PDF files
+  for (i = 0; i < 6; ++i) {
+    if (ctm[i] > 1e10) {
+      ctm[i] = 1e10;
+    } else if (ctm[i] < -1e10) {
+      ctm[i] = -1e10;
+    }
+  }
 }
 
 void GfxState::concatCTM(double a, double b, double c,
@@ -2184,6 +2173,7 @@ void GfxState::concatCTM(double a, double b, double c,
   double b1 = ctm[1];
   double c1 = ctm[2];
   double d1 = ctm[3];
+  int i;
 
   ctm[0] = a * a1 + b * c1;
   ctm[1] = a * b1 + b * d1;
@@ -2191,6 +2181,15 @@ void GfxState::concatCTM(double a, double b, double c,
   ctm[3] = c * b1 + d * d1;
   ctm[4] = e * a1 + f * c1 + ctm[4];
   ctm[5] = e * b1 + f * d1 + ctm[5];
+
+  // avoid FP exceptions on badly messed up PDF files
+  for (i = 0; i < 6; ++i) {
+    if (ctm[i] > 1e10) {
+      ctm[i] = 1e10;
+    } else if (ctm[i] < -1e10) {
+      ctm[i] = -1e10;
+    }
+  }
 }
 
 void GfxState::setFillColorSpace(GfxColorSpace *colorSpace) {
@@ -2234,12 +2233,45 @@ void GfxState::clearPath() {
   path = new GfxPath();
 }
 
-void GfxState::textShift(double tx) {
-  double dx, dy;
-
-  textTransformDelta(tx, 0, &dx, &dy);
-  curX += dx;
-  curY += dy;
+void GfxState::clip() {
+  double xMin, yMin, xMax, yMax, x, y;
+  GfxSubpath *subpath;
+  int i, j;
+
+  xMin = xMax = yMin = yMax = 0; // make gcc happy
+  for (i = 0; i < path->getNumSubpaths(); ++i) {
+    subpath = path->getSubpath(i);
+    for (j = 0; j < subpath->getNumPoints(); ++j) {
+      transform(subpath->getX(j), subpath->getY(j), &x, &y);
+      if (i == 0 && j == 0) {
+       xMin = xMax = x;
+       yMin = yMax = y;
+      } else {
+       if (x < xMin) {
+         xMin = x;
+       } else if (x > xMax) {
+         xMax = x;
+       }
+       if (y < yMin) {
+         yMin = y;
+       } else if (y > yMax) {
+         yMax = y;
+       }
+      }
+    }
+  }
+  if (xMin > clipXMin) {
+    clipXMin = xMin;
+  }
+  if (yMin > clipYMin) {
+    clipYMin = yMin;
+  }
+  if (xMax < clipXMax) {
+    clipXMax = xMax;
+  }
+  if (yMax < clipYMax) {
+    clipYMax = yMax;
+  }
 }
 
 void GfxState::textShift(double tx, double ty) {
@@ -2250,6 +2282,11 @@ void GfxState::textShift(double tx, double ty) {
   curY += dy;
 }
 
+void GfxState::shift(double dx, double dy) {
+  curX += dx;
+  curY += dy;
+}
+
 GfxState *GfxState::save() {
   GfxState *newState;
 
@@ -2263,10 +2300,21 @@ GfxState *GfxState::restore() {
 
   if (saved) {
     oldState = saved;
+
+    // these attributes aren't saved/restored by the q/Q operators
+    oldState->path = path;
+    oldState->curX = curX;
+    oldState->curY = curY;
+    oldState->lineX = lineX;
+    oldState->lineY = lineY;
+
+    path = NULL;
     saved = NULL;
     delete this;
+
   } else {
     oldState = this;
   }
+
   return oldState;
 }
index 2056c4d..b1f6f28 100644 (file)
@@ -2,7 +2,7 @@
 //
 // GfxState.h
 //
-// Copyright 1996 Derek B. Noonburg
+// Copyright 1996-2002 Glyph & Cog, LLC
 //
 //========================================================================
 
 
 #include "gtypes.h"
 #include "Object.h"
+#include "Function.h"
 
 class Array;
-class Function;
 class GfxFont;
+struct PDFRectangle;
 
 //------------------------------------------------------------------------
 // GfxColor
 //------------------------------------------------------------------------
 
-#define gfxColorMaxComps 8
+#define gfxColorMaxComps funcMaxOutputs
 
 struct GfxColor {
   double c[gfxColorMaxComps];
@@ -201,14 +202,14 @@ public:
   double getGammaR() { return gammaR; }
   double getGammaG() { return gammaG; }
   double getGammaB() { return gammaB; }
-  double *getMatrix() { return m; }
+  double *getMatrix() { return mat; }
 
 private:
 
   double whiteX, whiteY, whiteZ;    // white point
   double blackX, blackY, blackZ;    // black point
   double gammaR, gammaG, gammaB;    // gamma values
-  double m[9];                     // ABC -> XYZ transform matrix
+  double mat[9];               // ABC -> XYZ transform matrix
 };
 
 //------------------------------------------------------------------------
@@ -283,8 +284,8 @@ private:
 class GfxICCBasedColorSpace: public GfxColorSpace {
 public:
 
-  GfxICCBasedColorSpace(int nComps, GfxColorSpace *alt,
-                       Ref *iccProfileStream);
+  GfxICCBasedColorSpace(int nCompsA, GfxColorSpace *altA,
+                       Ref *iccProfileStreamA);
   virtual ~GfxICCBasedColorSpace();
   virtual GfxColorSpace *copy();
   virtual GfxColorSpaceMode getMode() { return csICCBased; }
@@ -320,7 +321,7 @@ private:
 class GfxIndexedColorSpace: public GfxColorSpace {
 public:
 
-  GfxIndexedColorSpace(GfxColorSpace *base, int indexHigh);
+  GfxIndexedColorSpace(GfxColorSpace *baseA, int indexHighA);
   virtual ~GfxIndexedColorSpace();
   virtual GfxColorSpace *copy();
   virtual GfxColorSpaceMode getMode() { return csIndexed; }
@@ -356,8 +357,8 @@ private:
 class GfxSeparationColorSpace: public GfxColorSpace {
 public:
 
-  GfxSeparationColorSpace(GString *name, GfxColorSpace *alt,
-                         Function *func);
+  GfxSeparationColorSpace(GString *nameA, GfxColorSpace *altA,
+                         Function *funcA);
   virtual ~GfxSeparationColorSpace();
   virtual GfxColorSpace *copy();
   virtual GfxColorSpaceMode getMode() { return csSeparation; }
@@ -424,7 +425,7 @@ private:
 class GfxPatternColorSpace: public GfxColorSpace {
 public:
 
-  GfxPatternColorSpace(GfxColorSpace *under);
+  GfxPatternColorSpace(GfxColorSpace *underA);
   virtual ~GfxPatternColorSpace();
   virtual GfxColorSpace *copy();
   virtual GfxColorSpaceMode getMode() { return csPattern; }
@@ -454,7 +455,7 @@ private:
 class GfxPattern {
 public:
 
-  GfxPattern(int type);
+  GfxPattern(int typeA);
   virtual ~GfxPattern();
 
   static GfxPattern *parse(Object *obj);
@@ -504,94 +505,100 @@ private:
 };
 
 //------------------------------------------------------------------------
-// Function
+// GfxShading
 //------------------------------------------------------------------------
 
-#define funcMaxInputs  1
-#define funcMaxOutputs 8
-
-class Function {
+class GfxShading {
 public:
 
-  Function();
-
-  virtual ~Function();
-
-  // Construct a function.  Returns NULL if unsuccessful.
-  static Function *parse(Object *funcObj);
-
-  // Initialize the entries common to all function types.
-  GBool init(Dict *dict);
-
-  virtual Function *copy() = 0;
-
-  // Return size of input and output tuples.
-  int getInputSize() { return m; }
-  int getOutputSize() { return n; }
+  GfxShading();
+  virtual ~GfxShading();
 
-  // Transform an input tuple into an output tuple.
-  virtual void transform(double *in, double *out) = 0;
+  static GfxShading *parse(Object *obj);
 
-  virtual GBool isOk() = 0;
+  int getType() { return type; }
+  GfxColorSpace *getColorSpace() { return colorSpace; }
+  GfxColor *getBackground() { return &background; }
+  GBool getHasBackground() { return hasBackground; }
+  void getBBox(double *xMinA, double *yMinA, double *xMaxA, double *yMaxA)
+    { *xMinA = xMin; *yMinA = yMin; *xMaxA = xMax; *yMaxA = yMax; }
+  GBool getHasBBox() { return hasBBox; }
 
-protected:
+private:
 
-  int m, n;                    // size of input and output tuples
-  double                       // min and max values for function domain
-    domain[funcMaxInputs][2];
-  double                       // min and max values for function range
-    range[funcMaxOutputs][2];
-  GBool hasRange;              // set if range is defined
+  int type;
+  GfxColorSpace *colorSpace;
+  GfxColor background;
+  GBool hasBackground;
+  double xMin, yMin, xMax, yMax;
+  GBool hasBBox;
 };
 
 //------------------------------------------------------------------------
-// SampledFunction
+// GfxAxialShading
 //------------------------------------------------------------------------
 
-class SampledFunction: public Function {
+class GfxAxialShading: public GfxShading {
 public:
 
-  SampledFunction(Object *funcObj, Dict *dict);
-  virtual ~SampledFunction();
-  virtual Function *copy() { return new SampledFunction(this); }
-  virtual void transform(double *in, double *out);
-  virtual GBool isOk() { return ok; }
+  GfxAxialShading(double x0A, double y0A,
+                 double x1A, double y1A,
+                 double t0A, double t1A,
+                 Function **funcsA, int nFuncsA,
+                 GBool extend0A, GBool extend1A);
+  virtual ~GfxAxialShading();
 
-private:
+  static GfxAxialShading *parse(Dict *dict);
 
-  SampledFunction(SampledFunction *func);
+  void getCoords(double *x0A, double *y0A, double *x1A, double *y1A)
+    { *x0A = x0; *y0A = y0; *x1A = x1; *y1A = y1; }
+  double getDomain0() { return t0; }
+  double getDomain1() { return t1; }
+  void getColor(double t, GfxColor *color);
+  GBool getExtend0() { return extend0; }
+  GBool getExtend1() { return extend1; }
 
-  int                          // number of samples for each domain element
-    sampleSize[funcMaxInputs];
-  double                       // min and max values for domain encoder
-    encode[funcMaxInputs][2];
-  double                       // min and max values for range decoder
-    decode[funcMaxOutputs][2];
-  double *samples;             // the samples
-  GBool ok;
+private:
+
+  double x0, y0, x1, y1;
+  double t0, t1;
+  Function *funcs[gfxColorMaxComps];
+  int nFuncs;
+  GBool extend0, extend1;
 };
 
 //------------------------------------------------------------------------
-// ExponentialFunction
+// GfxRadialShading
 //------------------------------------------------------------------------
 
-class ExponentialFunction: public Function {
+class GfxRadialShading: public GfxShading {
 public:
 
-  ExponentialFunction(Object *funcObj, Dict *dict);
-  virtual ~ExponentialFunction();
-  virtual Function *copy() { return new ExponentialFunction(this); }
-  virtual void transform(double *in, double *out);
-  virtual GBool isOk() { return ok; }
+  GfxRadialShading(double x0A, double y0A, double r0A,
+                  double x1A, double y1A, double r1A,
+                  double t0A, double t1A,
+                  Function **funcsA, int nFuncsA,
+                  GBool extend0A, GBool extend1A);
+  virtual ~GfxRadialShading();
 
-private:
+  static GfxRadialShading *parse(Dict *dict);
 
-  ExponentialFunction(ExponentialFunction *func);
+  void getCoords(double *x0A, double *y0A, double *r0A,
+                double *x1A, double *y1A, double *r1A)
+    { *x0A = x0; *y0A = y0; *r0A = r0; *x1A = x1; *y1A = y1; *r1A = r1; }
+  double getDomain0() { return t0; }
+  double getDomain1() { return t1; }
+  void getColor(double t, GfxColor *color);
+  GBool getExtend0() { return extend0; }
+  GBool getExtend1() { return extend1; }
 
-  double c0[funcMaxOutputs];
-  double c1[funcMaxOutputs];
-  double e;
-  GBool ok;
+private:
+
+  double x0, y0, r0, x1, y1, r1;
+  double t0, t1;
+  Function *funcs[gfxColorMaxComps];
+  int nFuncs;
+  GBool extend0, extend1;
 };
 
 //------------------------------------------------------------------------
@@ -602,7 +609,7 @@ class GfxImageColorMap {
 public:
 
   // Constructor.
-  GfxImageColorMap(int bits, Object *decode, GfxColorSpace *colorSpace);
+  GfxImageColorMap(int bitsA, Object *decode, GfxColorSpace *colorSpaceA);
 
   // Destructor.
   ~GfxImageColorMap();
@@ -728,7 +735,7 @@ public:
               double x3, double y3);
 
   // Close the last subpath.
-  void close() { subpaths[n-1]->close(); }
+  void close();
 
 private:
 
@@ -750,10 +757,10 @@ class GfxState {
 public:
 
   // Construct a default GfxState, for a device with resolution <dpi>,
-  // page box (<x1>,<y1>)-(<x2>,<y2>), page rotation <rotate>, and
-  // coordinate system specified by <upsideDown>.
-  GfxState(double dpi, double px1a, double py1a,
-          double px2a, double py2a, int rotate, GBool upsideDown);
+  // page box <pageBox>, page rotation <rotate>, and coordinate system
+  // specified by <upsideDown>.
+  GfxState(double dpi, PDFRectangle *pageBox, int rotate,
+          GBool upsideDown);
 
   // Destructor.
   ~GfxState();
@@ -771,6 +778,10 @@ public:
   double getPageHeight() { return pageHeight; }
   GfxColor *getFillColor() { return &fillColor; }
   GfxColor *getStrokeColor() { return &strokeColor; }
+  void getFillGray(double *gray)
+    { fillColorSpace->getGray(&fillColor, gray); }
+  void getStrokeGray(double *gray)
+    { strokeColorSpace->getGray(&fillColor, gray); }
   void getFillRGB(GfxRGB *rgb)
     { fillColorSpace->getRGB(&fillColor, rgb); }
   void getStrokeRGB(GfxRGB *rgb)
@@ -804,6 +815,9 @@ public:
   GfxPath *getPath() { return path; }
   double getCurX() { return curX; }
   double getCurY() { return curY; }
+  void getClipBBox(double *xMin, double *yMin, double *xMax, double *yMax)
+    { *xMin = clipXMin; *yMin = clipYMin; *xMax = clipXMax; *yMax = clipYMax; }
+  void getUserClipBBox(double *xMin, double *yMin, double *xMax, double *yMax);
   double getLineX() { return lineX; }
   double getLineY() { return lineY; }
 
@@ -848,9 +862,9 @@ public:
   void setFlatness(int flatness1) { flatness = flatness1; }
   void setLineJoin(int lineJoin1) { lineJoin = lineJoin1; }
   void setLineCap(int lineCap1) { lineCap = lineCap1; }
-  void setMiterLimit(double miterLimit1) { miterLimit = miterLimit1; }
-  void setFont(GfxFont *font1, double fontSize1)
-    { font = font1; fontSize = fontSize1; }
+  void setMiterLimit(double limit) { miterLimit = limit; }
+  void setFont(GfxFont *fontA, double fontSizeA)
+    { font = fontA; fontSize = fontSizeA; }
   void setTextMat(double a, double b, double c,
                  double d, double e, double f)
     { textMat[0] = a; textMat[1] = b; textMat[2] = c;
@@ -861,12 +875,12 @@ public:
     { wordSpace = space; }
   void setHorizScaling(double scale)
     { horizScaling = 0.01 * scale; }
-  void setLeading(double leading1)
-    { leading = leading1; }
-  void setRise(double rise1)
-    { rise = rise1; }
-  void setRender(int render1)
-    { render = render1; }
+  void setLeading(double leadingA)
+    { leading = leadingA; }
+  void setRise(double riseA)
+    { rise = riseA; }
+  void setRender(int renderA)
+    { render = renderA; }
 
   // Add to path.
   void moveTo(double x, double y)
@@ -880,11 +894,14 @@ public:
     { path->close(); curX = path->getLastX(); curY = path->getLastY(); }
   void clearPath();
 
+  // Update clip region.
+  void clip();
+
   // Text position.
   void textMoveTo(double tx, double ty)
     { lineX = tx; lineY = ty; textTransform(tx, ty, &curX, &curY); }
-  void textShift(double tx);
   void textShift(double tx, double ty);
+  void shift(double dx, double dy);
 
   // Push/pop GfxState on/off stack.
   GfxState *save();
@@ -929,6 +946,9 @@ private:
   double curX, curY;           // current point (user coords)
   double lineX, lineY;         // start of current text line (text coords)
 
+  double clipXMin, clipYMin,   // bounding box for clip region
+         clipXMax, clipYMax;
+
   GfxState *saved;             // next GfxState on stack
 
   GfxState(GfxState *state);
index 4ca8cfe..d037469 100644 (file)
@@ -2,7 +2,7 @@
 //
 // Lexer.cc
 //
-// Copyright 1996 Derek B. Noonburg
+// Copyright 1996-2002 Glyph & Cog, LLC
 //
 //========================================================================
 
@@ -10,6 +10,7 @@
 #pragma implementation
 #endif
 
+#include <aconf.h>
 #include <stdlib.h>
 #include <stddef.h>
 #include <string.h>
@@ -44,22 +45,22 @@ static char specialChars[256] = {
 // Lexer
 //------------------------------------------------------------------------
 
-Lexer::Lexer(Stream *str) {
+Lexer::Lexer(XRef *xref, Stream *str) {
   Object obj;
 
   curStr.initStream(str);
-  streams = new Array();
+  streams = new Array(xref);
   streams->add(curStr.copy(&obj));
   strPtr = 0;
   freeArray = gTrue;
   curStr.streamReset();
 }
 
-Lexer::Lexer(Object *obj) {
+Lexer::Lexer(XRef *xref, Object *obj) {
   Object obj2;
 
   if (obj->isStream()) {
-    streams = new Array();
+    streams = new Array(xref);
     freeArray = gTrue;
     streams->add(obj->copy(&obj2));
   } else {
@@ -204,11 +205,15 @@ Object *Lexer::getObj(Object *obj) {
 
       case '(':
        ++numParen;
+       c2 = c;
        break;
 
       case ')':
-       if (--numParen == 0)
+       if (--numParen == 0) {
          done = gTrue;
+       } else {
+         c2 = c;
+       }
        break;
 
       case '\\':
index 70144b8..8a01ab2 100644 (file)
@@ -2,7 +2,7 @@
 //
 // Lexer.h
 //
-// Copyright 1996 Derek B. Noonburg
+// Copyright 1996-2002 Glyph & Cog, LLC
 //
 //========================================================================
 
@@ -16,6 +16,8 @@
 #include "Object.h"
 #include "Stream.h"
 
+class XRef;
+
 #define tokBufSize 128         // size of token buffer
 
 //------------------------------------------------------------------------
@@ -27,11 +29,11 @@ public:
 
   // Construct a lexer for a single stream.  Deletes the stream when
   // lexer is deleted.
-  Lexer(Stream *str);
+  Lexer(XRef *xref, Stream *str);
 
   // Construct a lexer for a stream or array of streams (assumes obj
   // is either a stream or array of streams).
-  Lexer(Object *obj);
+  Lexer(XRef *xref, Object *obj);
 
   // Destructor.
   ~Lexer();
@@ -49,13 +51,14 @@ public:
   Stream *getStream()
     { return curStr.isNone() ? (Stream *)NULL : curStr.getStream(); }
 
-  // Get current position in file.
+  // Get current position in file.  This is only used for error
+  // messages, so it returns an int instead of a Guint.
   int getPos()
-    { return curStr.isNone() ? -1 : curStr.streamGetPos(); }
+    { return curStr.isNone() ? -1 : (int)curStr.streamGetPos(); }
 
   // Set position in file.
-  void setPos(int pos)
-    { if (!curStr.isNone()) curStr.streamSetPos(pos); }
+  void setPos(Guint pos, int dir = 0)
+    { if (!curStr.isNone()) curStr.streamSetPos(pos, dir); }
 
 private:
 
index 154a577..af64c8b 100644 (file)
@@ -2,7 +2,7 @@
 //
 // Link.cc
 //
-// Copyright 1996 Derek B. Noonburg
+// Copyright 1996-2002 Glyph & Cog, LLC
 //
 //========================================================================
 
@@ -10,6 +10,7 @@
 #pragma implementation
 #endif
 
+#include <aconf.h>
 #include <stddef.h>
 #include <string.h>
 #include "gmem.h"
@@ -28,32 +29,27 @@ static GString *getFileSpecName(Object *fileSpecObj);
 // LinkDest
 //------------------------------------------------------------------------
 
-LinkDest::LinkDest(Array *a, GBool pageIsRef1) {
+LinkDest::LinkDest(Array *a) {
   Object obj1, obj2;
 
   // initialize fields
-  pageIsRef = pageIsRef1;
   left = bottom = right = top = zoom = 0;
   ok = gFalse;
 
   // get page
-  if (pageIsRef) {
-    if (!a->getNF(0, &obj1)->isRef()) {
-      if(obj1.getType()!=objInt)
-        error(-1, "Bad annotation destination (1) type=%d", obj1.getType());
-      goto err2;
-    }
+  a->getNF(0, &obj1);
+  if (obj1.isInt()) {
+    pageNum = obj1.getInt() + 1;
+    pageIsRef = gFalse;
+  } else if (obj1.isRef()) {
     pageRef.num = obj1.getRefNum();
     pageRef.gen = obj1.getRefGen();
-    obj1.free();
+    pageIsRef = gTrue;
   } else {
-    if (!a->get(0, &obj1)->isInt()) {
-      error(-1, "Bad annotation destination (2)");
-      goto err2;
-    }
-    pageNum = obj1.getInt() + 1;
-    obj1.free();
+    error(-1, "Bad annotation destination");
+    goto err2;
   }
+  obj1.free();
 
   // get destination type
   a->get(1, &obj1);
@@ -221,7 +217,7 @@ LinkGoTo::LinkGoTo(Object *destObj) {
 
   // destination dictionary
   } else if (destObj->isArray()) {
-    dest = new LinkDest(destObj->getArray(), gTrue);
+    dest = new LinkDest(destObj->getArray());
     if (!dest->isOk()) {
       delete dest;
       dest = NULL;
@@ -259,7 +255,7 @@ LinkGoToR::LinkGoToR(Object *fileSpecObj, Object *destObj) {
 
   // destination dictionary
   } else if (destObj->isArray()) {
-    dest = new LinkDest(destObj->getArray(), gFalse);
+    dest = new LinkDest(destObj->getArray());
     if (!dest->isOk()) {
       delete dest;
       dest = NULL;
@@ -385,8 +381,8 @@ LinkNamed::~LinkNamed() {
 // LinkUnknown
 //------------------------------------------------------------------------
 
-LinkUnknown::LinkUnknown(char *action1) {
-  action = new GString(action1);
+LinkUnknown::LinkUnknown(char *actionA) {
+  action = new GString(actionA);
 }
 
 LinkUnknown::~LinkUnknown() {
@@ -446,13 +442,16 @@ Link::Link(Dict *dict, GString *baseURI) {
   }
 
   // get border
-  borderW = 0;
+  borderW = 1;
   if (!dict->lookup("Border", &obj1)->isNull()) {
-    if (obj1.isArray() && obj1.arrayGet(2, &obj2)->isNum())
-      borderW = obj2.getNum();
-    else
-      error(-1, "Bad annotation border");
-    obj2.free();
+    if (obj1.isArray() && obj1.arrayGetLength() >= 3) {
+      if (obj1.arrayGet(2, &obj2)->isNum()) {
+       borderW = obj2.getNum();
+      } else {
+       error(-1, "Bad annotation border");
+      }
+      obj2.free();
+    }
   }
   obj1.free();
 
@@ -579,7 +578,7 @@ Links::~Links() {
 LinkAction *Links::find(double x, double y) {
   int i;
 
-  for (i = 0; i < numLinks; ++i) {
+  for (i = numLinks - 1; i >= 0; --i) {
     if (links[i]->inRect(x, y)) {
       return links[i]->getAction();
     }
index 7e00510..4c644b8 100644 (file)
@@ -2,7 +2,7 @@
 //
 // Link.h
 //
-// Copyright 1996 Derek B. Noonburg
+// Copyright 1996-2002 Glyph & Cog, LLC
 //
 //========================================================================
 
@@ -63,10 +63,8 @@ enum LinkDestKind {
 class LinkDest {
 public:
 
-  // Build a LinkDest from the array.  If <pageIsRef> is true, the
-  // page is specified by an object reference; otherwise the page is
-  // specified by a (zero-relative) page number.
-  LinkDest(Array *a, GBool pageIsRef1);
+  // Build a LinkDest from the array.
+  LinkDest(Array *a);
 
   // Copy a LinkDest.
   LinkDest *copy() { return new LinkDest(this); }
@@ -249,7 +247,7 @@ class LinkUnknown: public LinkAction {
 public:
 
   // Build a LinkUnknown with the specified action type.
-  LinkUnknown(char *action1);
+  LinkUnknown(char *actionA);
 
   // Destructor.
   virtual ~LinkUnknown();
index f9c1067..6d92c6a 100644 (file)
@@ -2,7 +2,7 @@
 //
 // Object.cc
 //
-// Copyright 1996 Derek B. Noonburg
+// Copyright 1996-2002 Glyph & Cog, LLC
 //
 //========================================================================
 
@@ -10,6 +10,7 @@
 #pragma implementation
 #endif
 
+#include <aconf.h>
 #include <stddef.h>
 #include "Object.h"
 #include "Array.h"
@@ -44,21 +45,21 @@ int Object::numAlloc[numObjTypes] =
   {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
 #endif
 
-Object *Object::initArray() {
+Object *Object::initArray(XRef *xref) {
   initObj(objArray);
-  array = new Array();
+  array = new Array(xref);
   return this;
 }
 
-Object *Object::initDict() {
+Object *Object::initDict(XRef *xref) {
   initObj(objDict);
-  dict = new Dict();
+  dict = new Dict(xref);
   return this;
 }
 
-Object *Object::initStream(Stream *stream1) {
+Object *Object::initStream(Stream *streamA) {
   initObj(objStream);
-  stream = stream1;
+  stream = streamA;
   return this;
 }
 
@@ -92,7 +93,7 @@ Object *Object::copy(Object *obj) {
   return obj;
 }
 
-Object *Object::fetch(Object *obj) {
+Object *Object::fetch(XRef *xref, Object *obj) {
   return (type == objRef && xref) ?
          xref->fetch(ref.num, ref.gen, obj) : copy(obj);
 }
@@ -151,7 +152,9 @@ void Object::print(FILE *f) {
     fprintf(f, "%g", real);
     break;
   case objString:
-    fprintf(f, "(%s)", string->getCString());
+    fprintf(f, "(");
+    fwrite(string->getCString(), 1, string->getLength(), stdout);
+    fprintf(f, ")");
     break;
   case objName:
     fprintf(f, "/%s", name);
index fa4f740..65d0be0 100644 (file)
@@ -2,7 +2,7 @@
 //
 // Object.h
 //
-// Copyright 1996 Derek B. Noonburg
+// Copyright 1996-2002 Glyph & Cog, LLC
 //
 //========================================================================
 
@@ -19,6 +19,7 @@
 #include "gmem.h"
 #include "GString.h"
 
+class XRef;
 class Array;
 class Dict;
 class Stream;
@@ -78,27 +79,25 @@ public:
     type(objNone) {}
 
   // Initialize an object.
-  Object *initBool(GBool booln1)
-    { initObj(objBool); booln = booln1; return this; }
-  Object *initInt(int intg1)
-    { initObj(objInt); intg = intg1; return this; }
-  Object *initReal(double real1)
-    { initObj(objReal); real = real1; return this; }
-  Object *initString(GString *string1)
-    { initObj(objString); string = string1; return this; }
-  Object *initName(char *name1)
-    { initObj(objName); name = copyString(name1); return this; }
+  Object *initBool(GBool boolnA)
+    { initObj(objBool); booln = boolnA; return this; }
+  Object *initInt(int intgA)
+    { initObj(objInt); intg = intgA; return this; }
+  Object *initReal(double realA)
+    { initObj(objReal); real = realA; return this; }
+  Object *initString(GString *stringA)
+    { initObj(objString); string = stringA; return this; }
+  Object *initName(char *nameA)
+    { initObj(objName); name = copyString(nameA); return this; }
   Object *initNull()
     { initObj(objNull); return this; }
-  Object *initArray();
-  Object *initDict();
-  Object *initDict(Dict *dict1)
-    { initObj(objDict); dict = dict1; return this; }
-  Object *initStream(Stream *stream1);
-  Object *initRef(int num1, int gen1)
-    { initObj(objRef); ref.num = num1; ref.gen = gen1; return this; }
-  Object *initCmd(char *cmd1)
-    { initObj(objCmd); cmd = copyString(cmd1); return this; }
+  Object *initArray(XRef *xref);
+  Object *initDict(XRef *xref);
+  Object *initStream(Stream *streamA);
+  Object *initRef(int numA, int genA)
+    { initObj(objRef); ref.num = numA; ref.gen = genA; return this; }
+  Object *initCmd(char *cmdA)
+    { initObj(objCmd); cmd = copyString(cmdA); return this; }
   Object *initError()
     { initObj(objError); return this; }
   Object *initEOF()
@@ -109,7 +108,7 @@ public:
 
   // If object is a Ref, fetch and return the referenced object.
   // Otherwise, return a copy of the object.
-  Object *fetch(Object *obj);
+  Object *fetch(XRef *xref, Object *obj);
 
   // Free object contents.
   void free();
@@ -133,12 +132,12 @@ public:
   GBool isNone() { return type == objNone; }
 
   // Special type checking.
-  GBool isName(char *name1)
-    { return type == objName && !strcmp(name, name1); }
+  GBool isName(char *nameA)
+    { return type == objName && !strcmp(name, nameA); }
   GBool isDict(char *dictType);
   GBool isStream(char *dictType);
-  GBool isCmd(char *cmd1)
-    { return type == objCmd && !strcmp(cmd, cmd1); }
+  GBool isCmd(char *cmdA)
+    { return type == objCmd && !strcmp(cmd, cmdA); }
 
   // Accessors.  NB: these assume object is of correct type.
   GBool getBool() { return booln; }
@@ -177,8 +176,8 @@ public:
   int streamGetChar();
   int streamLookChar();
   char *streamGetLine(char *buf, int size);
-  int streamGetPos();
-  void streamSetPos(int pos);
+  Guint streamGetPos();
+  void streamSetPos(Guint pos, int dir = 0);
   Dict *streamGetDict();
 
   // Output.
@@ -288,11 +287,11 @@ inline int Object::streamLookChar()
 inline char *Object::streamGetLine(char *buf, int size)
   { return stream->getLine(buf, size); }
 
-inline int Object::streamGetPos()
+inline Guint Object::streamGetPos()
   { return stream->getPos(); }
 
-inline void Object::streamSetPos(int pos)
-  { stream->setPos(pos); }
+inline void Object::streamSetPos(Guint pos, int dir)
+  { stream->setPos(pos, dir); }
 
 inline Dict *Object::streamGetDict()
   { return stream->getDict(); }
index eebf460..6d46542 100644 (file)
@@ -2,7 +2,7 @@
 //
 // OutputDev.cc
 //
-// Copyright 1996 Derek B. Noonburg
+// Copyright 1996-2002 Glyph & Cog, LLC
 //
 //========================================================================
 
@@ -10,6 +10,7 @@
 #pragma implementation
 #endif
 
+#include <aconf.h>
 #include <stddef.h>
 #include "Object.h"
 #include "Stream.h"
 // OutputDev
 //------------------------------------------------------------------------
 
-void OutputDev::setDefaultCTM(double *ctm1) {
+void OutputDev::setDefaultCTM(double *ctm) {
   int i;
   double det;
 
-  for (i = 0; i < 6; ++i)
-    ctm[i] = ctm1[i];
-  det = 1 / (ctm[0] * ctm[3] - ctm[1] * ctm[2]);
-  ictm[0] = ctm[3] * det;
-  ictm[1] = -ctm[1] * det;
-  ictm[2] = -ctm[2] * det;
-  ictm[3] = ctm[0] * det;
-  ictm[4] = (ctm[2] * ctm[5] - ctm[3] * ctm[4]) * det;
-  ictm[5] = (ctm[1] * ctm[4] - ctm[0] * ctm[5]) * det;
+  for (i = 0; i < 6; ++i) {
+    defCTM[i] = ctm[i];
+  }
+  det = 1 / (defCTM[0] * defCTM[3] - defCTM[1] * defCTM[2]);
+  defICTM[0] = defCTM[3] * det;
+  defICTM[1] = -defCTM[1] * det;
+  defICTM[2] = -defCTM[2] * det;
+  defICTM[3] = defCTM[0] * det;
+  defICTM[4] = (defCTM[2] * defCTM[5] - defCTM[3] * defCTM[4]) * det;
+  defICTM[5] = (defCTM[1] * defCTM[4] - defCTM[0] * defCTM[5]) * det;
 }
 
 void OutputDev::cvtDevToUser(int dx, int dy, double *ux, double *uy) {
-  *ux = ictm[0] * dx + ictm[2] * dy + ictm[4];
-  *uy = ictm[1] * dx + ictm[3] * dy + ictm[5];
+  *ux = defICTM[0] * dx + defICTM[2] * dy + defICTM[4];
+  *uy = defICTM[1] * dx + defICTM[3] * dy + defICTM[5];
 }
 
 void OutputDev::cvtUserToDev(double ux, double uy, int *dx, int *dy) {
-  *dx = (int)(ctm[0] * ux + ctm[2] * uy + ctm[4] + 0.5);
-  *dy = (int)(ctm[1] * ux + ctm[3] * uy + ctm[5] + 0.5);
+  *dx = (int)(defCTM[0] * ux + defCTM[2] * uy + defCTM[4] + 0.5);
+  *dy = (int)(defCTM[1] * ux + defCTM[3] * uy + defCTM[5] + 0.5);
 }
 
 void OutputDev::updateAll(GfxState *state) {
@@ -57,6 +59,11 @@ void OutputDev::updateAll(GfxState *state) {
   updateFont(state);
 }
 
+GBool OutputDev::beginType3Char(GfxState *state,
+                               CharCode code, Unicode *u, int uLen) {
+  return gFalse;
+}
+
 void OutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
                              int width, int height, GBool invert,
                              GBool inlineImg) {
@@ -67,12 +74,13 @@ void OutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
     j = height * ((width + 7) / 8);
     for (i = 0; i < j; ++i)
       str->getChar();
+    str->close();
   }
 }
 
 void OutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
                          int width, int height, GfxImageColorMap *colorMap,
-                         GBool inlineImg) {
+                         int *maskColors, GBool inlineImg) {
   int i, j;
 
   if (inlineImg) {
@@ -81,6 +89,7 @@ void OutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
                   colorMap->getBits() + 7) / 8);
     for (i = 0; i < j; ++i)
       str->getChar();
+    str->close();
   }
 }
 
index d7de97a..cb825e0 100644 (file)
@@ -2,7 +2,7 @@
 //
 // OutputDev.h
 //
-// Copyright 1996 Derek B. Noonburg
+// Copyright 1996-2002 Glyph & Cog, LLC
 //
 //========================================================================
 
@@ -14,6 +14,7 @@
 #endif
 
 #include "gtypes.h"
+#include "CharTypes.h"
 
 class GString;
 class GfxState;
@@ -45,10 +46,17 @@ public:
   // Does this device use drawChar() or drawString()?
   virtual GBool useDrawChar() = 0;
 
+  // Does this device use beginType3Char/endType3Char?  Otherwise,
+  // text in Type 3 fonts will be drawn with drawChar/drawString.
+  virtual GBool interpretType3Chars() = 0;
+
+  // Does this device need non-text content?
+  virtual GBool needNonText() { return gTrue; }
+
   //----- initialization and control
 
   // Set default transform matrix.
-  virtual void setDefaultCTM(double *ctm1);
+  virtual void setDefaultCTM(double *ctm);
 
   // Start a page.
   virtual void startPage(int pageNum, GfxState *state) {}
@@ -111,11 +119,13 @@ public:
   virtual void beginString(GfxState *state, GString *s) {}
   virtual void endString(GfxState *state) {}
   virtual void drawChar(GfxState *state, double x, double y,
-                       double dx, double dy, Guchar c) {}
-  virtual void drawChar16(GfxState *state, double x, double y,
-                         double dx, double dy, int c) {}
+                       double dx, double dy,
+                       double originX, double originY,
+                       CharCode code, Unicode *u, int uLen) {}
   virtual void drawString(GfxState *state, GString *s) {}
-  virtual void drawString16(GfxState *state, GString *s) {}
+  virtual GBool beginType3Char(GfxState *state,
+                              CharCode code, Unicode *u, int uLen);
+  virtual void endType3Char(GfxState *state) {}
 
   //----- image drawing
   virtual void drawImageMask(GfxState *state, Object *ref, Stream *str,
@@ -123,7 +133,7 @@ public:
                             GBool inlineImg);
   virtual void drawImage(GfxState *state, Object *ref, Stream *str,
                         int width, int height, GfxImageColorMap *colorMap,
-                        GBool inlineImg);
+                        int *maskColors, GBool inlineImg);
 
 #if OPI_SUPPORT
   //----- OPI functions
@@ -131,10 +141,18 @@ public:
   virtual void opiEnd(GfxState *state, Dict *opiDict);
 #endif
 
+  //----- Type 3 font operators
+  virtual void type3D0(GfxState *state, double wx, double wy) {}
+  virtual void type3D1(GfxState *state, double wx, double wy,
+                      double llx, double lly, double urx, double ury) {}
+
+  //----- PostScript XObjects
+  virtual void psXObject(Stream *psStream, Stream *level1Stream) {}
+
 private:
 
-  double ctm[6];               // coordinate transform matrix
-  double ictm[6];              // inverse CTM
+  double defCTM[6];            // default coordinate transform matrix
+  double defICTM[6];           // inverse of default CTM
 };
 
 #endif
index ae55d23..29abba0 100644 (file)
@@ -2,7 +2,7 @@
 //
 // PDFDoc.cc
 //
-// Copyright 1996 Derek B. Noonburg
+// Copyright 1996-2002 Glyph & Cog, LLC
 //
 //========================================================================
 
@@ -10,6 +10,7 @@
 #pragma implementation
 #endif
 
+#include <aconf.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <stddef.h>
@@ -22,8 +23,8 @@
 #include "XRef.h"
 #include "Link.h"
 #include "OutputDev.h"
-#include "Params.h"
 #include "Error.h"
+#include "ErrorCodes.h"
 #include "Lexer.h"
 #include "Parser.h"
 #include "PDFDoc.h"
 // PDFDoc
 //------------------------------------------------------------------------
 
-PDFDoc::PDFDoc(GString *fileName1, GString *userPassword) {
+PDFDoc::PDFDoc(GString *fileNameA, GString *ownerPassword,
+              GString *userPassword, GBool printCommandsA) {
   Object obj;
   GString *fileName2;
 
   ok = gFalse;
+  errCode = errNone;
 
   file = NULL;
   str = NULL;
   xref = NULL;
   catalog = NULL;
   links = NULL;
+  printCommands = printCommandsA;
 
   // try to open file
-  fileName = fileName1;
+  fileName = fileNameA;
   fileName2 = NULL;
 #ifdef VMS
   if (!(file = fopen(fileName->getCString(), "rb", "ctx=stm"))) {
     error(-1, "Couldn't open file '%s'", fileName->getCString());
+    errCode = errOpenFile;
     return;
   }
 #else
@@ -66,6 +71,7 @@ PDFDoc::PDFDoc(GString *fileName1, GString *userPassword) {
       if (!(file = fopen(fileName2->getCString(), "rb"))) {
        error(-1, "Couldn't open file '%s'", fileName->getCString());
        delete fileName2;
+       errCode = errOpenFile;
        return;
       }
     }
@@ -75,40 +81,42 @@ PDFDoc::PDFDoc(GString *fileName1, GString *userPassword) {
 
   // create stream
   obj.initNull();
-  str = new FileStream(file, 0, -1, &obj);
+  str = new FileStream(file, 0, gFalse, 0, &obj);
 
-  ok = setup(userPassword);
+  ok = setup(ownerPassword, userPassword);
 }
 
-PDFDoc::PDFDoc(BaseStream *str, GString *userPassword) {
+PDFDoc::PDFDoc(BaseStream *strA, GString *ownerPassword,
+              GString *userPassword, GBool printCommandsA) {
   ok = gFalse;
+  errCode = errNone;
   fileName = NULL;
   file = NULL;
-  this->str = str;
+  str = strA;
   xref = NULL;
   catalog = NULL;
   links = NULL;
-  ok = setup(userPassword);
+  printCommands = printCommandsA;
+  ok = setup(ownerPassword, userPassword);
 }
 
-GBool PDFDoc::setup(GString *userPassword) {
-  Object catObj;
-
+GBool PDFDoc::setup(GString *ownerPassword, GString *userPassword) {
   // check header
   checkHeader();
 
   // read xref table
-  xref = new XRef(str, userPassword);
+  xref = new XRef(str, ownerPassword, userPassword);
   if (!xref->isOk()) {
     error(-1, "Couldn't read xref table");
+    errCode = xref->getErrorCode();
     return gFalse;
   }
 
   // read catalog
-  catalog = new Catalog(xref->getCatalog(&catObj));
-  catObj.free();
+  catalog = new Catalog(xref, printCommands);
   if (!catalog->isOk()) {
     error(-1, "Couldn't read page catalog");
+    errCode = errBadCatalog;
     return gFalse;
   }
 
@@ -203,8 +211,9 @@ GBool PDFDoc::isLinearized() {
 
   lin = gFalse;
   obj1.initNull();
-  parser = new Parser(new Lexer(str->makeSubStream(str->getStart(),
-                                                  -1, &obj1)));
+  parser = new Parser(xref,
+            new Lexer(xref,
+              str->makeSubStream(str->getStart(), gFalse, 0, &obj1)));
   parser->getObj(&obj1);
   parser->getObj(&obj2);
   parser->getObj(&obj3);
@@ -248,4 +257,3 @@ void PDFDoc::getLinks(Page *page) {
   links = new Links(page->getAnnots(&obj), catalog->getBaseURI());
   obj.free();
 }
-
index e679db9..c12531e 100644 (file)
@@ -2,7 +2,7 @@
 //
 // PDFDoc.h
 //
-// Copyright 1996 Derek B. Noonburg
+// Copyright 1996-2002 Glyph & Cog, LLC
 //
 //========================================================================
 
 #endif
 
 #include <stdio.h>
+#include "XRef.h"
 #include "Link.h"
 #include "Catalog.h"
 #include "Page.h"
 
 class GString;
 class BaseStream;
-class XRef;
 class OutputDev;
 class Links;
 class LinkAction;
@@ -33,16 +33,24 @@ class LinkDest;
 class PDFDoc {
 public:
 
-  PDFDoc(GString *fileName1, GString *userPassword = NULL);
-  PDFDoc(BaseStream *str, GString *userPassword = NULL);
+  PDFDoc(GString *fileNameA, GString *ownerPassword = NULL,
+        GString *userPassword = NULL, GBool printCommandsA = gFalse);
+  PDFDoc(BaseStream *strA, GString *ownerPassword = NULL,
+        GString *userPassword = NULL, GBool printCommandsA = gFalse);
   ~PDFDoc();
 
   // Was PDF document successfully opened?
   GBool isOk() { return ok; }
 
+  // Get the error code (if isOk() returns false).
+  int getErrorCode() { return errCode; }
+
   // Get file name.
   GString *getFileName() { return fileName; }
 
+  // Get the xref table.
+  XRef *getXRef() { return xref; }
+
   // Get catalog.
   Catalog *getCatalog() { return catalog; }
 
@@ -60,6 +68,13 @@ public:
   // Get number of pages.
   int getNumPages() { return catalog->getNumPages(); }
 
+  // Return the contents of the metadata stream, or NULL if there is
+  // no metadata.
+  GString *readMetadata() { return catalog->readMetadata(); }
+
+  // Return the structure tree root object.
+  Object *getStructTreeRoot() { return catalog->getStructTreeRoot(); }
+
   // Display a page.
   void displayPage(OutputDev *out, int page, double zoom,
                   int rotate, GBool doLinks);
@@ -88,10 +103,14 @@ public:
   GBool isEncrypted() { return xref->isEncrypted(); }
 
   // Check various permissions.
-  GBool okToPrint() { return xref->okToPrint(); }
-  GBool okToChange() { return xref->okToChange(); }
-  GBool okToCopy() { return xref->okToCopy(); }
-  GBool okToAddNotes() { return xref->okToAddNotes(); }
+  GBool okToPrint(GBool ignoreOwnerPW = gFalse)
+    { return xref->okToPrint(ignoreOwnerPW); }
+  GBool okToChange(GBool ignoreOwnerPW = gFalse)
+    { return xref->okToChange(ignoreOwnerPW); }
+  GBool okToCopy(GBool ignoreOwnerPW = gFalse)
+    { return xref->okToCopy(ignoreOwnerPW); }
+  GBool okToAddNotes(GBool ignoreOwnerPW = gFalse)
+    { return xref->okToAddNotes(ignoreOwnerPW); }
 
   // Is this document linearized?
   GBool isLinearized();
@@ -107,7 +126,7 @@ public:
 
 private:
 
-  GBool setup(GString *userPassword);
+  GBool setup(GString *ownerPassword, GString *userPassword);
   void checkHeader();
   void getLinks(Page *page);
 
@@ -118,8 +137,10 @@ private:
   XRef *xref;
   Catalog *catalog;
   Links *links;
+  GBool printCommands;
 
   GBool ok;
+  int errCode;
 };
 
 #endif
index c2ac6b1..c601857 100644 (file)
@@ -2,7 +2,7 @@
 //
 // Page.cc
 //
-// Copyright 1996 Derek B. Noonburg
+// Copyright 1996-2002 Glyph & Cog, LLC
 //
 //========================================================================
 
@@ -10,6 +10,7 @@
 #pragma implementation
 #endif
 
+#include <aconf.h>
 #include <stddef.h>
 #include "Object.h"
 #include "Array.h"
 #include "OutputDev.h"
 #ifndef PDF_PARSER_ONLY
 #include "Gfx.h"
-#include "FormWidget.h"
+#include "Annot.h"
 #endif
 #include "Error.h"
-
-#include "Params.h"
 #include "Page.h"
 
 //------------------------------------------------------------------------
 //------------------------------------------------------------------------
 
 PageAttrs::PageAttrs(PageAttrs *attrs, Dict *dict) {
-  Object obj1, obj2;
+  Object obj1;
   double w, h;
 
   // get old/default values
   if (attrs) {
-    x1 = attrs->x1;
-    y1 = attrs->y1;
-    x2 = attrs->x2;
-    y2 = attrs->y2;
-    cropX1 = attrs->cropX1;
-    cropY1 = attrs->cropY1;
-    cropX2 = attrs->cropX2;
-    cropY2 = attrs->cropY2;
+    mediaBox = attrs->mediaBox;
+    cropBox = attrs->cropBox;
+    haveCropBox = attrs->haveCropBox;
     rotate = attrs->rotate;
     attrs->resources.copy(&resources);
   } else {
     // set default MediaBox to 8.5" x 11" -- this shouldn't be necessary
     // but some (non-compliant) PDF files don't specify a MediaBox
-    x1 = 0;
-    y1 = 0;
-    x2 = 612;
-    y2 = 792;
-    cropX1 = cropY1 = cropX2 = cropY2 = 0;
+    mediaBox.x1 = 0;
+    mediaBox.y1 = 0;
+    mediaBox.x2 = 612;
+    mediaBox.y2 = 792;
+    cropBox.x1 = cropBox.y1 = cropBox.x2 = cropBox.y2 = 0;
+    haveCropBox = gFalse;
     rotate = 0;
     resources.initNull();
   }
 
   // media box
-  dict->lookup("MediaBox", &obj1);
-  if (obj1.isArray() && obj1.arrayGetLength() == 4) {
-    obj1.arrayGet(0, &obj2);
-    if (obj2.isNum())
-      x1 = obj2.getNum();
-    obj2.free();
-    obj1.arrayGet(1, &obj2);
-    if (obj2.isNum())
-      y1 = obj2.getNum();
-    obj2.free();
-    obj1.arrayGet(2, &obj2);
-    if (obj2.isNum())
-      x2 = obj2.getNum();
-    obj2.free();
-    obj1.arrayGet(3, &obj2);
-    if (obj2.isNum())
-      y2 = obj2.getNum();
-    obj2.free();
-  }
-  obj1.free();
+  readBox(dict, "MediaBox", &mediaBox);
 
   // crop box
-  dict->lookup("CropBox", &obj1);
-  if (obj1.isArray() && obj1.arrayGetLength() == 4) {
-    obj1.arrayGet(0, &obj2);
-    if (obj2.isNum())
-      cropX1 = obj2.getNum();
-    obj2.free();
-    obj1.arrayGet(1, &obj2);
-    if (obj2.isNum())
-      cropY1 = obj2.getNum();
-    obj2.free();
-    obj1.arrayGet(2, &obj2);
-    if (obj2.isNum())
-      cropX2 = obj2.getNum();
-    obj2.free();
-    obj1.arrayGet(3, &obj2);
-    if (obj2.isNum())
-      cropY2 = obj2.getNum();
-    obj2.free();
-  }
-  obj1.free();
+  cropBox = mediaBox;
+  haveCropBox = readBox(dict, "CropBox", &cropBox);
 
   // if the MediaBox is excessively larger than the CropBox,
   // just use the CropBox
   limitToCropBox = gFalse;
-  w = 0.25 * (cropX2 - cropX1);
-  h = 0.25 * (cropY2 - cropY1);
-  if (cropX2 > cropX1 &&
-      ((cropX1 - x1) + (x2 - cropX2) > w ||
-       (cropY1 - y1) + (y2 - cropY2) > h)) {
-    limitToCropBox = gTrue;
+  if (haveCropBox) {
+    w = 0.25 * (cropBox.x2 - cropBox.x1);
+    h = 0.25 * (cropBox.y2 - cropBox.y1);
+    if ((cropBox.x1 - mediaBox.x1) + (mediaBox.x2 - cropBox.x2) > w ||
+       (cropBox.y1 - mediaBox.y1) + (mediaBox.y2 - cropBox.y2) > h) {
+      limitToCropBox = gTrue;
+    }
   }
 
+  // other boxes
+  bleedBox = cropBox;
+  readBox(dict, "BleedBox", &bleedBox);
+  trimBox = cropBox;
+  readBox(dict, "TrimBox", &trimBox);
+  artBox = cropBox;
+  readBox(dict, "ArtBox", &artBox);
+
   // rotate
   dict->lookup("Rotate", &obj1);
-  if (obj1.isInt())
+  if (obj1.isInt()) {
     rotate = obj1.getInt();
+  }
   obj1.free();
-  while (rotate < 0)
+  while (rotate < 0) {
     rotate += 360;
-  while (rotate >= 360)
+  }
+  while (rotate >= 360) {
     rotate -= 360;
+  }
+
+  // misc attributes
+  dict->lookup("LastModified", &lastModified);
+  dict->lookup("BoxColorInfo", &boxColorInfo);
+  dict->lookup("Group", &group);
+  dict->lookup("Metadata", &metadata);
+  dict->lookup("PieceInfo", &pieceInfo);
+  dict->lookup("SeparationInfo", &separationInfo);
 
   // resource dictionary
   dict->lookup("Resources", &obj1);
@@ -133,20 +111,75 @@ PageAttrs::PageAttrs(PageAttrs *attrs, Dict *dict) {
 }
 
 PageAttrs::~PageAttrs() {
+  lastModified.free();
+  boxColorInfo.free();
+  group.free();
+  metadata.free();
+  pieceInfo.free();
+  separationInfo.free();
   resources.free();
 }
 
+GBool PageAttrs::readBox(Dict *dict, char *key, PDFRectangle *box) {
+  PDFRectangle tmp;
+  Object obj1, obj2;
+  GBool ok;
+
+  dict->lookup(key, &obj1);
+  if (obj1.isArray() && obj1.arrayGetLength() == 4) {
+    ok = gTrue;
+    obj1.arrayGet(0, &obj2);
+    if (obj2.isNum()) {
+      tmp.x1 = obj2.getNum();
+    } else {
+      ok = gFalse;
+    }
+    obj2.free();
+    obj1.arrayGet(1, &obj2);
+    if (obj2.isNum()) {
+      tmp.y1 = obj2.getNum();
+    } else {
+      ok = gFalse;
+    }
+    obj2.free();
+    obj1.arrayGet(2, &obj2);
+    if (obj2.isNum()) {
+      tmp.x2 = obj2.getNum();
+    } else {
+      ok = gFalse;
+    }
+    obj2.free();
+    obj1.arrayGet(3, &obj2);
+    if (obj2.isNum()) {
+      tmp.y2 = obj2.getNum();
+    } else {
+      ok = gFalse;
+    }
+    obj2.free();
+    if (ok) {
+      *box = tmp;
+    }
+  } else {
+    ok = gFalse;
+  }
+  obj1.free();
+  return ok;
+}
+
 //------------------------------------------------------------------------
 // Page
 //------------------------------------------------------------------------
 
-Page::Page(int num1, Dict *pageDict, PageAttrs *attrs1) {
+Page::Page(XRef *xrefA, int numA, Dict *pageDict, PageAttrs *attrsA,
+          GBool printCommandsA) {
 
   ok = gTrue;
-  num = num1;
+  xref = xrefA;
+  num = numA;
+  printCommands = printCommandsA;
 
   // get attributes
-  attrs = attrs1;
+  attrs = attrsA;
 
   // annotations
   pageDict->lookupNF("Annots", &annots);
@@ -185,18 +218,22 @@ Page::~Page() {
 void Page::display(OutputDev *out, double dpi, int rotate,
                   Links *links, Catalog *catalog) {
 #ifndef PDF_PARSER_ONLY
+  PDFRectangle *box, *cropBox;
   Gfx *gfx;
   Object obj;
   Link *link;
   int i;
-  FormWidgets *formWidgets;
+  Annots *annotList;
+
+  box = getBox();
+  cropBox = getCropBox();
 
   if (printCommands) {
     printf("***** MediaBox = ll:%g,%g ur:%g,%g\n",
-          getX1(), getY1(), getX2(), getY2());
+          box->x1, box->y1, box->x2, box->y2);
     if (isCropped()) {
       printf("***** CropBox = ll:%g,%g ur:%g,%g\n",
-            getCropX1(), getCropY1(), getCropX2(), getCropY2());
+            cropBox->x1, cropBox->y1, cropBox->x2, cropBox->y2);
     }
     printf("***** Rotate = %d\n", attrs->getRotate());
   }
@@ -207,10 +244,9 @@ void Page::display(OutputDev *out, double dpi, int rotate,
   } else if (rotate < 0) {
     rotate += 360;
   }
-  gfx = new Gfx(out, num, attrs->getResourceDict(),
-               dpi, getX1(), getY1(), getX2(), getY2(), isCropped(),
-               getCropX1(), getCropY1(), getCropX2(), getCropY2(), rotate);
-  contents.fetch(&obj);
+  gfx = new Gfx(xref, out, num, attrs->getResourceDict(),
+               dpi, box, isCropped(), cropBox, rotate, printCommands);
+  contents.fetch(xref, &obj);
   if (!obj.isNull()) {
     gfx->display(&obj);
   }
@@ -225,20 +261,20 @@ void Page::display(OutputDev *out, double dpi, int rotate,
     out->dump();
   }
 
-  // draw AcroForm widgets
+  // draw non-link annotations
   //~ need to reset CTM ???
-  formWidgets = new FormWidgets(annots.fetch(&obj));
+  annotList = new Annots(xref, annots.fetch(xref, &obj));
   obj.free();
-  if (printCommands && formWidgets->getNumWidgets() > 0) {
-    printf("***** AcroForm widgets\n");
-  }
-  for (i = 0; i < formWidgets->getNumWidgets(); ++i) {
-    formWidgets->getWidget(i)->draw(gfx);
-  }
-  if (formWidgets->getNumWidgets() > 0) {
+  if (annotList->getNumAnnots() > 0) {
+    if (printCommands) {
+      printf("***** Annotations\n");
+    }
+    for (i = 0; i < annotList->getNumAnnots(); ++i) {
+      annotList->getAnnot(i)->draw(gfx);
+    }
     out->dump();
   }
-  delete formWidgets;
+  delete annotList;
 
   delete gfx;
 #endif
index a144d2c..7207b20 100644 (file)
@@ -2,7 +2,7 @@
 //
 // Page.h
 //
-// Copyright 1996 Derek B. Noonburg
+// Copyright 1996-2002 Glyph & Cog, LLC
 //
 //========================================================================
 
@@ -22,6 +22,12 @@ class Links;
 class Catalog;
 
 //------------------------------------------------------------------------
+
+struct PDFRectangle {
+  double x1, y1, x2, y2;
+};
+
+//------------------------------------------------------------------------
 // PageAttrs
 //------------------------------------------------------------------------
 
@@ -37,25 +43,49 @@ public:
   ~PageAttrs();
 
   // Accessors.
-  double getX1() { return limitToCropBox ? cropX1 : x1; }
-  double getY1() { return limitToCropBox ? cropY1 : y1; }
-  double getX2() { return limitToCropBox ? cropX2 : x2; }
-  double getY2() { return limitToCropBox ? cropY2 : y2; }
-  GBool isCropped() { return cropX2 > cropX1; }
-  double getCropX1() { return cropX1; }
-  double getCropY1() { return cropY1; }
-  double getCropX2() { return cropX2; }
-  double getCropY2() { return cropY2; }
+  PDFRectangle *getBox() { return limitToCropBox ? &cropBox : &mediaBox; }
+  PDFRectangle *getMediaBox() { return &mediaBox; }
+  PDFRectangle *getCropBox() { return &cropBox; }
+  GBool isCropped() { return haveCropBox; }
+  PDFRectangle *getBleedBox() { return &bleedBox; }
+  PDFRectangle *getTrimBox() { return &trimBox; }
+  PDFRectangle *getArtBox() { return &artBox; }
   int getRotate() { return rotate; }
+  GString *getLastModified()
+    { return lastModified.isString()
+       ? lastModified.getString() : (GString *)NULL; }
+  Dict *getBoxColorInfo()
+    { return boxColorInfo.isDict() ? boxColorInfo.getDict() : (Dict *)NULL; }
+  Dict *getGroup()
+    { return group.isDict() ? group.getDict() : (Dict *)NULL; }
+  Stream *getMetadata()
+    { return metadata.isStream() ? metadata.getStream() : (Stream *)NULL; }
+  Dict *getPieceInfo()
+    { return pieceInfo.isDict() ? pieceInfo.getDict() : (Dict *)NULL; }
+  Dict *getSeparationInfo()
+    { return separationInfo.isDict()
+       ? separationInfo.getDict() : (Dict *)NULL; }
   Dict *getResourceDict()
     { return resources.isDict() ? resources.getDict() : (Dict *)NULL; }
 
 private:
 
-  double x1, y1, x2, y2;
-  double cropX1, cropY1, cropX2, cropY2;
+  GBool readBox(Dict *dict, char *key, PDFRectangle *box);
+
+  PDFRectangle mediaBox;
+  PDFRectangle cropBox;
+  GBool haveCropBox;
   GBool limitToCropBox;
+  PDFRectangle bleedBox;
+  PDFRectangle trimBox;
+  PDFRectangle artBox;
   int rotate;
+  Object lastModified;
+  Object boxColorInfo;
+  Object group;
+  Object metadata;
+  Object pieceInfo;
+  Object separationInfo;
   Object resources;
 };
 
@@ -67,7 +97,8 @@ class Page {
 public:
 
   // Constructor.
-  Page(int num1, Dict *pageDict, PageAttrs *attrs1);
+  Page(XRef *xrefA, int numA, Dict *pageDict, PageAttrs *attrsA,
+       GBool printCommandsA);
 
   // Destructor.
   ~Page();
@@ -76,27 +107,31 @@ public:
   GBool isOk() { return ok; }
 
   // Get page parameters.
-  double getX1() { return attrs->getX1(); }
-  double getY1() { return attrs->getY1(); }
-  double getX2() { return attrs->getX2(); }
-  double getY2() { return attrs->getY2(); }
+  PDFRectangle *getBox() { return attrs->getBox(); }
+  PDFRectangle *getMediaBox() { return attrs->getMediaBox(); }
+  PDFRectangle *getCropBox() { return attrs->getCropBox(); }
   GBool isCropped() { return attrs->isCropped(); }
-  double getCropX1() { return attrs->getCropX1(); }
-  double getCropY1() { return attrs->getCropY1(); }
-  double getCropX2() { return attrs->getCropX2(); }
-  double getCropY2() { return attrs->getCropY2(); }
-  double getWidth() { return attrs->getX2() - attrs->getX1(); }
-  double getHeight() { return attrs->getY2() - attrs->getY1(); }
+  double getWidth() { return attrs->getBox()->x2 - attrs->getBox()->x1; }
+  double getHeight() { return attrs->getBox()->y2 - attrs->getBox()->y1; }
+  PDFRectangle *getBleedBox() { return attrs->getBleedBox(); }
+  PDFRectangle *getTrimBox() { return attrs->getTrimBox(); }
+  PDFRectangle *getArtBox() { return attrs->getArtBox(); }
   int getRotate() { return attrs->getRotate(); }
+  GString *getLastModified() { return attrs->getLastModified(); }
+  Dict *getBoxColorInfo() { return attrs->getBoxColorInfo(); }
+  Dict *getGroup() { return attrs->getGroup(); }
+  Stream *getMetadata() { return attrs->getMetadata(); }
+  Dict *getPieceInfo() { return attrs->getPieceInfo(); }
+  Dict *getSeparationInfo() { return attrs->getSeparationInfo(); }
 
   // Get resource dictionary.
   Dict *getResourceDict() { return attrs->getResourceDict(); }
 
   // Get annotations array.
-  Object *getAnnots(Object *obj) { return annots.fetch(obj); }
+  Object *getAnnots(Object *obj) { return annots.fetch(xref, obj); }
 
   // Get contents.
-  Object *getContents(Object *obj) { return contents.fetch(obj); }
+  Object *getContents(Object *obj) { return contents.fetch(xref, obj); }
 
   // Display a page.
   void display(OutputDev *out, double dpi, int rotate,
@@ -104,10 +139,12 @@ public:
 
 private:
 
+  XRef *xref;                  // the xref table for this PDF file
   int num;                     // page number
   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
 };
 
diff --git a/pdf2swf/xpdf/Params.cc b/pdf2swf/xpdf/Params.cc
deleted file mode 100644 (file)
index 35adfdf..0000000
+++ /dev/null
@@ -1,87 +0,0 @@
-//========================================================================
-//
-// Params.cc
-//
-// Copyright 1996 Derek B. Noonburg
-//
-//========================================================================
-
-#include <stdlib.h>
-#include <stddef.h>
-#include <stdio.h>
-#include <string.h>
-#include "gtypes.h"
-#include "gmem.h"
-#include "GString.h"
-#include "gfile.h"
-#include "Params.h"
-
-char **fontPath = NULL;
-static int fontPathLen, fontPathSize;
-
-DevFontMapEntry *devFontMap = NULL;
-static int devFontMapLen, devFontMapSize;
-
-void initParams(char *configFile) {
-  GString *fileName;
-  FILE *f;
-  char buf[256];
-  char *p, *q;
-
-  // initialize font path and font map
-  fontPath = (char **)gmalloc((fontPathSize = 8) * sizeof(char *));
-  fontPath[fontPathLen = 0] = NULL;
-  devFontMap = (DevFontMapEntry *)gmalloc((devFontMapSize = 8) *
-                                         sizeof(DevFontMapEntry));
-  devFontMap[devFontMapLen = 0].pdfFont = NULL;
-
-  // read config file
-  fileName = appendToPath(getHomeDir(), configFile);
-  if ((f = fopen(fileName->getCString(), "r"))) {
-    while (fgets(buf, sizeof(buf)-1, f)) {
-      buf[sizeof(buf)-1] = '\0';
-      p = strtok(buf, " \t\n\r");
-      if (p && !strcmp(p, "fontpath")) {
-       if (fontPathLen+1 >= fontPathSize)
-         fontPath = (char **)
-             grealloc(fontPath, (fontPathSize += 8) * sizeof(char *));
-       p = strtok(NULL, " \t\n\r");
-       fontPath[fontPathLen++] = copyString(p);
-      } else if (p && !strcmp(p, "fontmap")) {
-       if (devFontMapLen+1 >= devFontMapSize)
-         devFontMap = (DevFontMapEntry *)
-             grealloc(devFontMap,
-                      (devFontMapSize += 8) * sizeof(DevFontMapEntry));
-       p = strtok(NULL, " \t\n\r");
-       devFontMap[devFontMapLen].pdfFont = copyString(p);
-       p = strtok(NULL, "\t\n\r");
-       while (*p == ' ')
-         ++p;
-       for (q = p + strlen(p) - 1; q >= p && *q == ' '; --q) ;
-       q[1] = '\0';
-       devFontMap[devFontMapLen++].devFont = copyString(p);
-      }
-    }
-    fclose(f);
-    fontPath[fontPathLen] = NULL;
-    devFontMap[devFontMapLen].pdfFont = NULL;
-  }
-  delete fileName;
-}
-
-void freeParams() {
-  int i;
-
-  if (fontPath) {
-    for (i = 0; i < fontPathLen; ++i)
-      gfree(fontPath[i]);
-    gfree(fontPath);
-  }
-  if (devFontMap) {
-    for (i = 0; i < devFontMapLen; ++i) {
-      gfree(devFontMap[i].pdfFont);
-      gfree(devFontMap[i].devFont);
-    }
-    gfree(devFontMap);
-  }
-}
diff --git a/pdf2swf/xpdf/Params.h b/pdf2swf/xpdf/Params.h
deleted file mode 100644 (file)
index ea8536f..0000000
+++ /dev/null
@@ -1,37 +0,0 @@
-//========================================================================
-//
-// Params.h
-//
-// Copyright 1996 Derek B. Noonburg
-//
-//========================================================================
-
-#ifndef PARAMS_H
-#define PARAMS_H
-
-// Print commands as they're executed.
-extern GBool printCommands;
-
-// If this is set, error messages will be silently discarded.
-extern GBool errQuiet;
-
-// Font search path.
-extern char **fontPath;
-
-// Mapping from PDF font name to device font name.
-struct DevFontMapEntry {
-  char *pdfFont;
-  char *devFont;
-};
-extern DevFontMapEntry *devFontMap;
-
-//------------------------------------------------------------------------
-
-// Initialize font path and font map, and read configuration file,
-// if present.
-extern void initParams(char *configFile);
-
-// Free memory used for font path and font map.
-extern void freeParams();
-
-#endif
index 6ad0c5a..4df53c9 100644 (file)
@@ -2,7 +2,7 @@
 //
 // Parser.cc
 //
-// Copyright 1996 Derek B. Noonburg
+// Copyright 1996-2002 Glyph & Cog, LLC
 //
 //========================================================================
 
@@ -10,6 +10,7 @@
 #pragma implementation
 #endif
 
+#include <aconf.h>
 #include <stddef.h>
 #include "Object.h"
 #include "Array.h"
@@ -21,8 +22,9 @@
 #include "Decrypt.h"
 #endif
 
-Parser::Parser(Lexer *lexer1) {
-  lexer = lexer1;
+Parser::Parser(XRef *xrefA, Lexer *lexerA) {
+  xref = xrefA;
+  lexer = lexerA;
   inlineImg = 0;
   lexer->getObj(&buf1);
   lexer->getObj(&buf2);
@@ -36,7 +38,8 @@ Parser::~Parser() {
 
 #ifndef NO_DECRYPTION
 Object *Parser::getObj(Object *obj,
-                      Guchar *fileKey, int objNum, int objGen) {
+                      Guchar *fileKey, int keyLength,
+                      int objNum, int objGen) {
 #else
 Object *Parser::getObj(Object *obj) {
 #endif
@@ -63,10 +66,10 @@ Object *Parser::getObj(Object *obj) {
   // array
   if (buf1.isCmd("[")) {
     shift();
-    obj->initArray();
+    obj->initArray(xref);
     while (!buf1.isCmd("]") && !buf1.isEOF())
 #ifndef NO_DECRYPTION
-      obj->arrayAdd(getObj(&obj2, fileKey, objNum, objGen));
+      obj->arrayAdd(getObj(&obj2, fileKey, keyLength, objNum, objGen));
 #else
       obj->arrayAdd(getObj(&obj2));
 #endif
@@ -77,7 +80,7 @@ Object *Parser::getObj(Object *obj) {
   // dictionary or stream
   } else if (buf1.isCmd("<<")) {
     shift();
-    obj->initDict();
+    obj->initDict(xref);
     while (!buf1.isCmd(">>") && !buf1.isEOF()) {
       if (!buf1.isName()) {
        error(getPos(), "Dictionary key must be a name object");
@@ -88,7 +91,7 @@ Object *Parser::getObj(Object *obj) {
        if (buf1.isEOF() || buf1.isError())
          break;
 #ifndef NO_DECRYPTION
-       obj->dictAdd(key, getObj(&obj2, fileKey, objNum, objGen));
+       obj->dictAdd(key, getObj(&obj2, fileKey, keyLength, objNum, objGen));
 #else
        obj->dictAdd(key, getObj(&obj2));
 #endif
@@ -101,7 +104,8 @@ Object *Parser::getObj(Object *obj) {
        obj->initStream(str);
 #ifndef NO_DECRYPTION
        if (fileKey) {
-         str->getBaseStream()->doDecryption(fileKey, objNum, objGen);
+         str->getBaseStream()->doDecryption(fileKey, keyLength,
+                                            objNum, objGen);
        }
 #endif
       } else {
@@ -129,7 +133,7 @@ Object *Parser::getObj(Object *obj) {
   } else if (buf1.isString() && fileKey) {
     buf1.copy(obj);
     s = obj->getString();
-    decrypt = new Decrypt(fileKey, objNum, objGen);
+    decrypt = new Decrypt(fileKey, keyLength, objNum, objGen);
     for (i = 0, p = obj->getString()->getCString();
         i < s->getLength();
         ++i, ++p) {
@@ -151,7 +155,7 @@ Object *Parser::getObj(Object *obj) {
 Stream *Parser::makeStream(Object *dict) {
   Object obj;
   Stream *str;
-  int pos, endPos, length;
+  Guint pos, endPos, length;
 
   // get stream start position
   lexer->skipToNextLine();
@@ -160,7 +164,7 @@ Stream *Parser::makeStream(Object *dict) {
   // get length
   dict->dictLookup("Length", &obj);
   if (obj.isInt()) {
-    length = obj.getInt();
+    length = (Guint)obj.getInt();
     obj.free();
   } else {
     error(getPos(), "Bad 'Length' attribute in stream");
@@ -169,12 +173,13 @@ Stream *Parser::makeStream(Object *dict) {
   }
 
   // check for length in damaged file
-  if ((endPos = xref->getStreamEnd(pos)) >= 0) {
+  if (xref->getStreamEnd(pos, &endPos)) {
     length = endPos - pos;
   }
 
   // make base stream
-  str = lexer->getStream()->getBaseStream()->makeSubStream(pos, length, dict);
+  str = lexer->getStream()->getBaseStream()->makeSubStream(pos, gTrue,
+                                                          length, dict);
 
   // get filters
   str = str->addFilters(dict);
index 6e61844..c11475b 100644 (file)
@@ -2,7 +2,7 @@
 //
 // Parser.h
 //
-// Copyright 1996 Derek B. Noonburg
+// Copyright 1996-2002 Glyph & Cog, LLC
 //
 //========================================================================
 
@@ -23,7 +23,7 @@ class Parser {
 public:
 
   // Constructor.
-  Parser(Lexer *lexer1);
+  Parser(XRef *xrefA, Lexer *lexerA);
 
   // Destructor.
   ~Parser();
@@ -31,7 +31,8 @@ public:
   // Get the next object from the input stream.
 #ifndef NO_DECRYPTION
   Object *getObj(Object *obj,
-                Guchar *fileKey = NULL, int objNum = 0, int objGen = 0);
+                Guchar *fileKey = NULL, int keyLength = 0,
+                int objNum = 0, int objGen = 0);
 #else
   Object *getObj(Object *obj);
 #endif
@@ -44,6 +45,7 @@ public:
 
 private:
 
+  XRef *xref;                  // the xref table for this PDF file
   Lexer *lexer;                        // input stream
   Object buf1, buf2;           // next two tokens
   int inlineImg;               // set when inline image data is encountered
diff --git a/pdf2swf/xpdf/StdFontInfo.h b/pdf2swf/xpdf/StdFontInfo.h
deleted file mode 100644 (file)
index 0db033f..0000000
+++ /dev/null
@@ -1,546 +0,0 @@
-//========================================================================
-//
-// StdFontInfo.h
-//
-// This file was automatically generated by makeFontInfo.
-//
-// Copyright 1999 Derek B. Noonburg
-//
-//========================================================================
-
-#ifndef STDFONTINFO_H
-#define STDFONTINFO_H
-
-//------------------------------------------------------------------------
-// type1StdEncoding -- Adobe Type 1 StandardEncoding
-//------------------------------------------------------------------------
-
-#define type1StdEncodingSize 256
-static char *type1StdEncodingNames[type1StdEncodingSize] = {
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  "space",
-  "exclam",
-  "quotedbl",
-  "numbersign",
-  "dollar",
-  "percent",
-  "ampersand",
-  "quoteright",
-  "parenleft",
-  "parenright",
-  "asterisk",
-  "plus",
-  "comma",
-  "hyphen",
-  "period",
-  "slash",
-  "zero",
-  "one",
-  "two",
-  "three",
-  "four",
-  "five",
-  "six",
-  "seven",
-  "eight",
-  "nine",
-  "colon",
-  "semicolon",
-  "less",
-  "equal",
-  "greater",
-  "question",
-  "at",
-  "A",
-  "B",
-  "C",
-  "D",
-  "E",
-  "F",
-  "G",
-  "H",
-  "I",
-  "J",
-  "K",
-  "L",
-  "M",
-  "N",
-  "O",
-  "P",
-  "Q",
-  "R",
-  "S",
-  "T",
-  "U",
-  "V",
-  "W",
-  "X",
-  "Y",
-  "Z",
-  "bracketleft",
-  "backslash",
-  "bracketright",
-  "asciicircum",
-  "underscore",
-  "quoteleft",
-  "a",
-  "b",
-  "c",
-  "d",
-  "e",
-  "f",
-  "g",
-  "h",
-  "i",
-  "j",
-  "k",
-  "l",
-  "m",
-  "n",
-  "o",
-  "p",
-  "q",
-  "r",
-  "s",
-  "t",
-  "u",
-  "v",
-  "w",
-  "x",
-  "y",
-  "z",
-  "braceleft",
-  "bar",
-  "braceright",
-  "asciitilde",
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  "exclamdown",
-  "cent",
-  "sterling",
-  "fraction",
-  "yen",
-  "florin",
-  "section",
-  "currency",
-  "quotesingle",
-  "quotedblleft",
-  "guillemotleft",
-  "guilsinglleft",
-  "guilsinglright",
-  "fi",
-  "fl",
-  NULL,
-  "endash",
-  "dagger",
-  "daggerdbl",
-  "periodcentered",
-  NULL,
-  "paragraph",
-  "bullet",
-  "quotesinglbase",
-  "quotedblbase",
-  "quotedblright",
-  "guillemotright",
-  "ellipsis",
-  "perthousand",
-  NULL,
-  "questiondown",
-  NULL,
-  "grave",
-  "acute",
-  "circumflex",
-  "tilde",
-  "macron",
-  "breve",
-  "dotaccent",
-  "dieresis",
-  NULL,
-  "ring",
-  "cedilla",
-  NULL,
-  "hungarumlaut",
-  "ogonek",
-  "caron",
-  "emdash",
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  "AE",
-  NULL,
-  "ordfeminine",
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  "Lslash",
-  "Oslash",
-  "OE",
-  "ordmasculine",
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  "ae",
-  NULL,
-  NULL,
-  NULL,
-  "dotlessi",
-  NULL,
-  NULL,
-  "lslash",
-  "oslash",
-  "oe",
-  "germandbls",
-  NULL,
-  NULL,
-  NULL,
-  NULL
-};
-static FontEncoding type1StdEncoding(type1StdEncodingNames,
-                                     type1StdEncodingSize);
-
-//------------------------------------------------------------------------
-// type1ExpertEncoding -- Adobe Type 1 ExpertEncoding
-//------------------------------------------------------------------------
-
-#define type1ExpertEncodingSize 256
-static char *type1ExpertEncodingNames[type1ExpertEncodingSize] = {
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  "space",
-  "exclamsmall",
-  "Hungarumlautsmall",
-  NULL,
-  "dollaroldstyle",
-  "dollarsuperior",
-  "ampersandsmall",
-  "Acutesmall",
-  "parenleftsuperior",
-  "parenrightsuperior",
-  "twodotenleader",
-  "onedotenleader",
-  "comma",
-  "hyphen",
-  "period",
-  "fraction",
-  "zerooldstyle",
-  "oneoldstyle",
-  "twooldstyle",
-  "threeoldstyle",
-  "fouroldstyle",
-  "fiveoldstyle",
-  "sixoldstyle",
-  "sevenoldstyle",
-  "eightoldstyle",
-  "nineoldstyle",
-  "colon",
-  "semicolon",
-  "commasuperior",
-  "threequartersemdash",
-  "periodsuperior",
-  "questionsmall",
-  NULL,
-  "asuperior",
-  "bsuperior",
-  "centsuperior",
-  "dsuperior",
-  "esuperior",
-  NULL,
-  NULL,
-  NULL,
-  "isuperior",
-  NULL,
-  NULL,
-  "lsuperior",
-  "msuperior",
-  "nsuperior",
-  "osuperior",
-  NULL,
-  NULL,
-  "rsuperior",
-  "ssuperior",
-  "tsuperior",
-  NULL,
-  "ff",
-  "fi",
-  "fl",
-  "ffi",
-  "ffl",
-  "parenleftinferior",
-  NULL,
-  "parenrightinferior",
-  "Circumflexsmall",
-  "hyphensuperior",
-  "Gravesmall",
-  "Asmall",
-  "Bsmall",
-  "Csmall",
-  "Dsmall",
-  "Esmall",
-  "Fsmall",
-  "Gsmall",
-  "Hsmall",
-  "Ismall",
-  "Jsmall",
-  "Ksmall",
-  "Lsmall",
-  "Msmall",
-  "Nsmall",
-  "Osmall",
-  "Psmall",
-  "Qsmall",
-  "Rsmall",
-  "Ssmall",
-  "Tsmall",
-  "Usmall",
-  "Vsmall",
-  "Wsmall",
-  "Xsmall",
-  "Ysmall",
-  "Zsmall",
-  "colonmonetary",
-  "onefitted",
-  "rupiah",
-  "Tildesmall",
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  "exclamdownsmall",
-  "centoldstyle",
-  "Lslashsmall",
-  NULL,
-  NULL,
-  "Scaronsmall",
-  "Zcaronsmall",
-  "Dieresissmall",
-  "Brevesmall",
-  "Caronsmall",
-  NULL,
-  "Dotaccentsmall",
-  NULL,
-  NULL,
-  "Macronsmall",
-  NULL,
-  NULL,
-  "figuredash",
-  "hypheninferior",
-  NULL,
-  NULL,
-  "Ogoneksmall",
-  "Ringsmall",
-  "Cedillasmall",
-  NULL,
-  NULL,
-  NULL,
-  "onequarter",
-  "onehalf",
-  "threequarters",
-  "questiondownsmall",
-  "oneeighth",
-  "threeeighths",
-  "fiveeighths",
-  "seveneighths",
-  "onethird",
-  "twothirds",
-  NULL,
-  NULL,
-  "zerosuperior",
-  "onesuperior",
-  "twosuperior",
-  "threesuperior",
-  "foursuperior",
-  "fivesuperior",
-  "sixsuperior",
-  "sevensuperior",
-  "eightsuperior",
-  "ninesuperior",
-  "zeroinferior",
-  "oneinferior",
-  "twoinferior",
-  "threeinferior",
-  "fourinferior",
-  "fiveinferior",
-  "sixinferior",
-  "seveninferior",
-  "eightinferior",
-  "nineinferior",
-  "centinferior",
-  "dollarinferior",
-  "periodinferior",
-  "commainferior",
-  "Agravesmall",
-  "Aacutesmall",
-  "Acircumflexsmall",
-  "Atildesmall",
-  "Adieresissmall",
-  "Aringsmall",
-  "AEsmall",
-  "Ccedillasmall",
-  "Egravesmall",
-  "Eacutesmall",
-  "Ecircumflexsmall",
-  "Edieresissmall",
-  "Igravesmall",
-  "Iacutesmall",
-  "Icircumflexsmall",
-  "Idieresissmall",
-  "Ethsmall",
-  "Ntildesmall",
-  "Ogravesmall",
-  "Oacutesmall",
-  "Ocircumflexsmall",
-  "Otildesmall",
-  "Odieresissmall",
-  "OEsmall",
-  "Oslashsmall",
-  "Ugravesmall",
-  "Uacutesmall",
-  "Ucircumflexsmall",
-  "Udieresissmall",
-  "Yacutesmall",
-  "Thornsmall",
-  "Ydieresissmall"
-};
-static FontEncoding type1ExpertEncoding(type1ExpertEncodingNames,
-                                        type1ExpertEncodingSize);
-
-#endif
index 1af8742..f5a77b0 100644 (file)
@@ -4,7 +4,7 @@
 //
 // Tables for CCITT Fax decoding.
 //
-// Copyright 1996 Derek B. Noonburg
+// Copyright 1996-2002 Glyph & Cog, LLC
 //
 //========================================================================
 
index 0afcc94..9777940 100644 (file)
@@ -2,7 +2,7 @@
 //
 // Stream.cc
 //
-// Copyright 1996 Derek B. Noonburg
+// Copyright 1996-2002 Glyph & Cog, LLC
 //
 //========================================================================
 
@@ -10,6 +10,7 @@
 #pragma implementation
 #endif
 
+#include <aconf.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <stddef.h>
@@ -265,8 +266,8 @@ Stream *Stream::makeFilter(char *name, Stream *str, Object *params) {
 // BaseStream
 //------------------------------------------------------------------------
 
-BaseStream::BaseStream(Object *dict) {
-  this->dict = *dict;
+BaseStream::BaseStream(Object *dictA) {
+  dict = *dictA;
 #ifndef NO_DECRYPTION
   decrypt = NULL;
 #endif
@@ -281,8 +282,9 @@ BaseStream::~BaseStream() {
 }
 
 #ifndef NO_DECRYPTION
-void BaseStream::doDecryption(Guchar *fileKey, int objNum, int objGen) {
-  decrypt = new Decrypt(fileKey, objNum, objGen);
+void BaseStream::doDecryption(Guchar *fileKey, int keyLength,
+                             int objNum, int objGen) {
+  decrypt = new Decrypt(fileKey, keyLength, objNum, objGen);
 }
 #endif
 
@@ -290,8 +292,8 @@ void BaseStream::doDecryption(Guchar *fileKey, int objNum, int objGen) {
 // FilterStream
 //------------------------------------------------------------------------
 
-FilterStream::FilterStream(Stream *str) {
-  this->str = str;
+FilterStream::FilterStream(Stream *strA) {
+  str = strA;
 }
 
 FilterStream::~FilterStream() {
@@ -301,7 +303,7 @@ void FilterStream::close() {
   str->close();
 }
 
-void FilterStream::setPos(int pos) {
+void FilterStream::setPos(Guint pos, int dir) {
   error(-1, "Internal: called setPos() on FilterStream");
 }
 
@@ -309,13 +311,13 @@ void FilterStream::setPos(int pos) {
 // ImageStream
 //------------------------------------------------------------------------
 
-ImageStream::ImageStream(Stream *str, int width, int nComps, int nBits) {
+ImageStream::ImageStream(Stream *strA, int widthA, int nCompsA, int nBitsA) {
   int imgLineSize;
 
-  this->str = str;
-  this->width = width;
-  this->nComps = nComps;
-  this->nBits = nBits;
+  str = strA;
+  width = widthA;
+  nComps = nCompsA;
+  nBits = nBitsA;
 
   nVals = width * nComps;
   if (nBits == 1) {
@@ -396,13 +398,13 @@ void ImageStream::skipLine() {
 // StreamPredictor
 //------------------------------------------------------------------------
 
-StreamPredictor::StreamPredictor(Stream *str, int predictor,
-                                int width, int nComps, int nBits) {
-  this->str = str;
-  this->predictor = predictor;
-  this->width = width;
-  this->nComps = nComps;
-  this->nBits = nBits;
+StreamPredictor::StreamPredictor(Stream *strA, int predictorA,
+                                int widthA, int nCompsA, int nBitsA) {
+  str = strA;
+  predictor = predictorA;
+  width = widthA;
+  nComps = nCompsA;
+  nBits = nBitsA;
 
   nVals = width * nComps;
   pixBytes = (nComps * nBits + 7) >> 3;
@@ -486,11 +488,11 @@ GBool StreamPredictor::getNextLine() {
       if ((pc = p - upLeft) < 0)
        pc = -pc;
       if (pa <= pb && pa <= pc)
-       predLine[i] = pa + (Guchar)c;
+       predLine[i] = left + (Guchar)c;
       else if (pb <= pc)
-       predLine[i] = pb + (Guchar)c;
+       predLine[i] = up + (Guchar)c;
       else
-       predLine[i] = pc + (Guchar)c;
+       predLine[i] = upLeft + (Guchar)c;
       break;
     case 10:                   // PNG none
     default:                   // no predictor or TIFF predictor
@@ -552,27 +554,37 @@ GBool StreamPredictor::getNextLine() {
 // FileStream
 //------------------------------------------------------------------------
 
-FileStream::FileStream(FILE *f, int start, int length, Object *dict):
-    BaseStream(dict) {
-  this->f = f;
-  this->start = start;
-  this->length = length;
+FileStream::FileStream(FILE *fA, Guint startA, GBool limitedA,
+                      Guint lengthA, Object *dictA):
+    BaseStream(dictA) {
+  f = fA;
+  start = startA;
+  limited = limitedA;
+  length = lengthA;
   bufPtr = bufEnd = buf;
   bufPos = start;
-  savePos = -1;
+  savePos = 0;
+  saved = gFalse;
 }
 
 FileStream::~FileStream() {
   close();
 }
 
-Stream *FileStream::makeSubStream(int start, int length, Object *dict) {
-  return new FileStream(f, start, length, dict);
+Stream *FileStream::makeSubStream(Guint startA, GBool limitedA,
+                                 Guint lengthA, Object *dictA) {
+  return new FileStream(f, startA, limitedA, lengthA, dictA);
 }
 
 void FileStream::reset() {
-  savePos = (int)ftell(f);
+#if HAVE_FSEEK64
+  savePos = (Guint)ftell64(f);
+  fseek64(f, start, SEEK_SET);
+#else
+  savePos = (Guint)ftell(f);
   fseek(f, start, SEEK_SET);
+#endif
+  saved = gTrue;
   bufPtr = bufEnd = buf;
   bufPos = start;
 #ifndef NO_DECRYPTION
@@ -582,9 +594,13 @@ void FileStream::reset() {
 }
 
 void FileStream::close() {
-  if (savePos >= 0) {
+  if (saved) {
+#if HAVE_FSEEK64
+    fseek64(f, savePos, SEEK_SET);
+#else
     fseek(f, savePos, SEEK_SET);
-    savePos = -1;
+#endif
+    saved = gFalse;
   }
 }
 
@@ -596,10 +612,10 @@ GBool FileStream::fillBuf() {
 
   bufPos += bufEnd - buf;
   bufPtr = bufEnd = buf;
-  if (length >= 0 && bufPos >= start + length) {
+  if (limited && bufPos >= start + length) {
     return gFalse;
   }
-  if (length >= 0 && bufPos + fileStreamBufSize > start + length) {
+  if (limited && bufPos + fileStreamBufSize > start + length) {
     n = start + length - bufPos;
   } else {
     n = fileStreamBufSize;
@@ -619,56 +635,159 @@ GBool FileStream::fillBuf() {
   return gTrue;
 }
 
-void FileStream::setPos(int pos1) {
-  long size;
+void FileStream::setPos(Guint pos, int dir) {
+  Guint size;
 
-  if (pos1 >= 0) {
-    fseek(f, pos1, SEEK_SET);
-    bufPos = pos1;
+  if (dir >= 0) {
+#if HAVE_FSEEK64
+    fseek64(f, pos, SEEK_SET);
+#else
+    fseek(f, pos, SEEK_SET);
+#endif
+    bufPos = pos;
   } else {
+#if HAVE_FSEEK64
+    fseek64(f, 0, SEEK_END);
+    size = (Guint)ftell64(f);
+#else
     fseek(f, 0, SEEK_END);
-    size = ftell(f);
-    if (pos1 < -size)
-      pos1 = (int)(-size);
-    fseek(f, pos1, SEEK_END);
-    bufPos = (int)ftell(f);
+    size = (Guint)ftell(f);
+#endif
+    if (pos > size)
+      pos = (Guint)size;
+#ifdef __CYGWIN32__
+    //~ work around a bug in cygwin's implementation of fseek
+    rewind(f);
+#endif
+#if HAVE_FSEEK64
+    fseek64(f, -(int)pos, SEEK_END);
+    bufPos = (Guint)ftell64(f);
+#else
+    fseek(f, -(int)pos, SEEK_END);
+    bufPos = (Guint)ftell(f);
+#endif
   }
   bufPtr = bufEnd = buf;
 }
 
 void FileStream::moveStart(int delta) {
-  this->start += delta;
+  start += delta;
   bufPtr = bufEnd = buf;
   bufPos = start;
 }
 
 //------------------------------------------------------------------------
+// MemStream
+//------------------------------------------------------------------------
+
+MemStream::MemStream(char *bufA, Guint lengthA, Object *dictA):
+    BaseStream(dictA) {
+  buf = bufA;
+  needFree = gFalse;
+  length = lengthA;
+  bufEnd = buf + length;
+  bufPtr = buf;
+}
+
+MemStream::~MemStream() {
+  if (needFree) {
+    gfree(buf);
+  }
+}
+
+Stream *MemStream::makeSubStream(Guint start, GBool limited,
+                                Guint lengthA, Object *dictA) {
+  Guint newLength;
+
+  if (!limited || start + lengthA > length) {
+    newLength = length - start;
+  } else {
+    newLength = lengthA;
+  }
+  return new MemStream(buf + start, newLength, dictA);
+}
+
+void MemStream::reset() {
+  bufPtr = buf;
+#ifndef NO_DECRYPTION
+  if (decrypt) {
+    decrypt->reset();
+  }
+#endif
+}
+
+void MemStream::close() {
+}
+
+void MemStream::setPos(Guint pos, int dir) {
+  if (dir >= 0) {
+    if (pos > length) {
+      bufPtr = bufEnd;
+    } else {
+      bufPtr = buf + pos;
+    }
+  } else {
+    if (pos > length) {
+      bufPtr = buf;
+    } else {
+      bufPtr = bufEnd - pos;
+    }
+  }
+}
+
+void MemStream::moveStart(int delta) {
+  buf += delta;
+  bufPtr = buf;
+}
+
+#ifndef NO_DECRYPTION
+void MemStream::doDecryption(Guchar *fileKey, int keyLength,
+                            int objNum, int objGen) {
+  char *newBuf;
+  char *p, *q;
+
+  this->BaseStream::doDecryption(fileKey, keyLength, objNum, objGen);
+  if (decrypt) {
+    newBuf = (char *)gmalloc(bufEnd - buf);
+    for (p = buf, q = newBuf; p < bufEnd; ++p, ++q) {
+      *q = (char)decrypt->decryptByte((Guchar)*p);
+    }
+    bufEnd = newBuf + (bufEnd - buf);
+    bufPtr = newBuf + (bufPtr - buf);
+    buf = newBuf;
+    needFree = gTrue;
+  }
+}
+#endif
+
+//------------------------------------------------------------------------
 // EmbedStream
 //------------------------------------------------------------------------
 
-EmbedStream::EmbedStream(Stream *str, Object *dict):
-    BaseStream(dict) {
-  this->str = str;
+EmbedStream::EmbedStream(Stream *strA, Object *dictA):
+    BaseStream(dictA) {
+  str = strA;
 }
 
 EmbedStream::~EmbedStream() {
 }
 
-Stream *EmbedStream::makeSubStream(int start, int length, Object *dict) {
+Stream *EmbedStream::makeSubStream(Guint start, GBool limited,
+                                  Guint length, Object *dictA) {
   error(-1, "Internal: called makeSubStream() on EmbedStream");
   return NULL;
 }
 
-void EmbedStream::setPos(int pos) {
+void EmbedStream::setPos(Guint pos, int dir) {
   error(-1, "Internal: called setPos() on EmbedStream");
 }
 
-int EmbedStream::getStart() {
+Guint EmbedStream::getStart() {
   error(-1, "Internal: called getStart() on EmbedStream");
   return 0;
 }
 
-void EmbedStream::moveStart(int start) {
+void EmbedStream::moveStart(int delta) {
   error(-1, "Internal: called moveStart() on EmbedStream");
 }
 
@@ -676,8 +795,8 @@ void EmbedStream::moveStart(int start) {
 // ASCIIHexStream
 //------------------------------------------------------------------------
 
-ASCIIHexStream::ASCIIHexStream(Stream *str):
-    FilterStream(str) {
+ASCIIHexStream::ASCIIHexStream(Stream *strA):
+    FilterStream(strA) {
   buf = EOF;
   eof = gFalse;
 }
@@ -748,7 +867,9 @@ int ASCIIHexStream::lookChar() {
 GString *ASCIIHexStream::getPSFilter(char *indent) {
   GString *s;
 
-  s = str->getPSFilter(indent);
+  if (!(s = str->getPSFilter(indent))) {
+    return NULL;
+  }
   s->append(indent)->append("/ASCIIHexDecode filter\n");
   return s;
 }
@@ -761,8 +882,8 @@ GBool ASCIIHexStream::isBinary(GBool last) {
 // ASCII85Stream
 //------------------------------------------------------------------------
 
-ASCII85Stream::ASCII85Stream(Stream *str):
-    FilterStream(str) {
+ASCII85Stream::ASCII85Stream(Stream *strA):
+    FilterStream(strA) {
   index = n = 0;
   eof = gFalse;
 }
@@ -824,7 +945,9 @@ int ASCII85Stream::lookChar() {
 GString *ASCII85Stream::getPSFilter(char *indent) {
   GString *s;
 
-  s = str->getPSFilter(indent);
+  if (!(s = str->getPSFilter(indent))) {
+    return NULL;
+  }
   s->append(indent)->append("/ASCII85Decode filter\n");
   return s;
 }
@@ -837,15 +960,15 @@ GBool ASCII85Stream::isBinary(GBool last) {
 // LZWStream
 //------------------------------------------------------------------------
 
-LZWStream::LZWStream(Stream *str, int predictor1, int columns1, int colors1,
-                    int bits1, int early1):
-    FilterStream(str) {
-  if (predictor1 != 1) {
-    pred = new StreamPredictor(this, predictor1, columns1, colors1, bits1);
+LZWStream::LZWStream(Stream *strA, int predictor, int columns, int colors,
+                    int bits, int earlyA):
+    FilterStream(strA) {
+  if (predictor != 1) {
+    pred = new StreamPredictor(this, predictor, columns, colors, bits);
   } else {
     pred = NULL;
   }
-  early = early1;
+  early = earlyA;
   zPipe = NULL;
   bufPtr = bufEnd = buf;
 }
@@ -949,11 +1072,7 @@ void LZWStream::reset() {
     return;
   }
 #else // HAVE_POPEN
-#ifdef VMS
-  if (!system(zCmd->getCString())) {
-#else
-  if (system(zCmd->getCString())) {
-#endif
+  if (!executeCommand(zCmd->getCString())) {
     error(getPos(), "Couldn't execute '%s'", zCmd->getCString());
     unlink(zName->getCString());
     delete zName;
@@ -1139,7 +1258,9 @@ GString *LZWStream::getPSFilter(char *indent) {
   if (pred) {
     return NULL;
   }
-  s = str->getPSFilter(indent);
+  if (!(s = str->getPSFilter(indent))) {
+    return NULL;
+  }
   s->append(indent)->append("/LZWDecode filter\n");
   return s;
 }
@@ -1152,8 +1273,8 @@ GBool LZWStream::isBinary(GBool last) {
 // RunLengthStream
 //------------------------------------------------------------------------
 
-RunLengthStream::RunLengthStream(Stream *str):
-    FilterStream(str) {
+RunLengthStream::RunLengthStream(Stream *strA):
+    FilterStream(strA) {
   bufPtr = bufEnd = buf;
   eof = gFalse;
 }
@@ -1171,7 +1292,9 @@ void RunLengthStream::reset() {
 GString *RunLengthStream::getPSFilter(char *indent) {
   GString *s;
 
-  s = str->getPSFilter(indent);
+  if (!(s = str->getPSFilter(indent))) {
+    return NULL;
+  }
   s->append(indent)->append("/RunLengthDecode filter\n");
   return s;
 }
@@ -1210,17 +1333,17 @@ GBool RunLengthStream::fillBuf() {
 // CCITTFaxStream
 //------------------------------------------------------------------------
 
-CCITTFaxStream::CCITTFaxStream(Stream *str, int encoding, GBool endOfLine,
-                              GBool byteAlign, int columns, int rows,
-                              GBool endOfBlock, GBool black):
-    FilterStream(str) {
-  this->encoding = encoding;
-  this->endOfLine = endOfLine;
-  this->byteAlign = byteAlign;
-  this->columns = columns;
-  this->rows = rows;
-  this->endOfBlock = endOfBlock;
-  this->black = black;
+CCITTFaxStream::CCITTFaxStream(Stream *strA, int encodingA, GBool endOfLineA,
+                              GBool byteAlignA, int columnsA, int rowsA,
+                              GBool endOfBlockA, GBool blackA):
+    FilterStream(strA) {
+  encoding = encodingA;
+  endOfLine = endOfLineA;
+  byteAlign = byteAlignA;
+  columns = columnsA;
+  rows = rowsA;
+  endOfBlock = endOfBlockA;
+  black = blackA;
   refLine = (short *)gmalloc((columns + 3) * sizeof(short));
   codingLine = (short *)gmalloc((columns + 2) * sizeof(short));
 
@@ -1274,7 +1397,7 @@ void CCITTFaxStream::reset() {
 int CCITTFaxStream::lookChar() {
   short code1, code2, code3;
   int a0New;
-#if 0 //~
+#if 0
   GBool err;
 #endif
   GBool gotEOL;
@@ -1287,7 +1410,7 @@ int CCITTFaxStream::lookChar() {
   }
 
   // read the next row
-#if 0 //~
+#if 0
   err = gFalse;
 #endif
   if (codingLine[a0] >= columns) {
@@ -1389,7 +1512,7 @@ int CCITTFaxStream::lookChar() {
          return EOF;
        default:
          error(getPos(), "Bad 2D code %04x in CCITTFax stream", code1);
-#if 0 //~
+#if 0
          err = gTrue;
          break;
 #else
@@ -1424,7 +1547,7 @@ int CCITTFaxStream::lookChar() {
 
     if (codingLine[a0] != columns) {
       error(getPos(), "CCITTFax row is wrong length (%d)", codingLine[a0]);
-#if 0 //~
+#if 0
       err = gTrue;
 #endif
     }
@@ -1484,7 +1607,7 @@ int CCITTFaxStream::lookChar() {
       }
     }
 
-#if 0 //~
+#if 0
     // This looks for an end-of-line marker after an error, however
     // some (most?) CCITT streams in PDF files don't use end-of-line
     // markers, and the just-plow-on technique works better in those
@@ -1720,7 +1843,9 @@ GString *CCITTFaxStream::getPSFilter(char *indent) {
   GString *s;
   char s1[50];
 
-  s = str->getPSFilter(indent);
+  if (!(s = str->getPSFilter(indent))) {
+    return NULL;
+  }
   s->append(indent)->append("<< ");
   if (encoding != 0) {
     sprintf(s1, "/K %d ", encoding);
@@ -1810,8 +1935,8 @@ static int dctZigZag[64] = {
   63
 };
 
-DCTStream::DCTStream(Stream *str):
-    FilterStream(str) {
+DCTStream::DCTStream(Stream *strA):
+    FilterStream(strA) {
   int i, j;
 
   width = height = 0;
@@ -2001,7 +2126,7 @@ GBool DCTStream::readMCURow() {
            pCr = rowBuf[2][y2][x1+x2] - 128;
            pR = ((pY << 16) + dctCrToR * pCr + 32768) >> 16;
            rowBuf[0][y2][x1+x2] = 255 - dctClip[dctClipOffset + pR];
-           pG = ((pY << 16) + dctCbToG * pCb + dctCrToG * pCr + 32678) >> 16;
+           pG = ((pY << 16) + dctCbToG * pCb + dctCrToG * pCr + 32768) >> 16;
            rowBuf[1][y2][x1+x2] = 255 - dctClip[dctClipOffset + pG];
            pB = ((pY << 16) + dctCbToB * pCb + 32768) >> 16;
            rowBuf[2][y2][x1+x2] = 255 - dctClip[dctClipOffset + pB];
@@ -2710,7 +2835,9 @@ int DCTStream::read16() {
 GString *DCTStream::getPSFilter(char *indent) {
   GString *s;
 
-  s = str->getPSFilter(indent);
+  if (!(s = str->getPSFilter(indent))) {
+    return NULL;
+  }
   s->append(indent)->append("<< >> /DCTDecode filter\n");
   return s;
 }
@@ -2792,11 +2919,11 @@ FlateDecode FlateStream::distDecode[flateMaxDistCodes] = {
   {13, 24577}
 };
 
-FlateStream::FlateStream(Stream *str, int predictor1, int columns1,
-                        int colors1, int bits1):
-    FilterStream(str) {
-  if (predictor1 != 1) {
-    pred = new StreamPredictor(this, predictor1, columns1, colors1, bits1);
+FlateStream::FlateStream(Stream *strA, int predictor, int columns,
+                        int colors, int bits):
+    FilterStream(strA) {
+  if (predictor != 1) {
+    pred = new StreamPredictor(this, predictor, columns, colors, bits);
   } else {
     pred = NULL;
   }
@@ -3224,8 +3351,8 @@ int FlateStream::getCodeWord(int bits) {
 // EOFStream
 //------------------------------------------------------------------------
 
-EOFStream::EOFStream(Stream *str):
-    FilterStream(str) {
+EOFStream::EOFStream(Stream *strA):
+    FilterStream(strA) {
 }
 
 EOFStream::~EOFStream() {
@@ -3236,9 +3363,9 @@ EOFStream::~EOFStream() {
 // FixedLengthEncoder
 //------------------------------------------------------------------------
 
-FixedLengthEncoder::FixedLengthEncoder(Stream *str, int length1):
-    FilterStream(str) {
-  length = length1;
+FixedLengthEncoder::FixedLengthEncoder(Stream *strA, int lengthA):
+    FilterStream(strA) {
+  length = lengthA;
   count = 0;
 }
 
@@ -3269,11 +3396,61 @@ int FixedLengthEncoder::lookChar() {
 }
 
 //------------------------------------------------------------------------
+// ASCIIHexEncoder
+//------------------------------------------------------------------------
+
+ASCIIHexEncoder::ASCIIHexEncoder(Stream *strA):
+    FilterStream(strA) {
+  bufPtr = bufEnd = buf;
+  lineLen = 0;
+  eof = gFalse;
+}
+
+ASCIIHexEncoder::~ASCIIHexEncoder() {
+  if (str->isEncoder()) {
+    delete str;
+  }
+}
+
+void ASCIIHexEncoder::reset() {
+  str->reset();
+  bufPtr = bufEnd = buf;
+  lineLen = 0;
+  eof = gFalse;
+}
+
+void ASCIIHexEncoder::close() {
+}
+
+GBool ASCIIHexEncoder::fillBuf() {
+  static char *hex = "0123456789abcdef";
+  int c;
+
+  if (eof) {
+    return gFalse;
+  }
+  bufPtr = bufEnd = buf;
+  if ((c = str->getChar()) == EOF) {
+    *bufEnd++ = '>';
+    eof = gTrue;
+  } else {
+    if (lineLen >= 64) {
+      *bufEnd++ = '\n';
+      lineLen = 0;
+    }
+    *bufEnd++ = hex[(c >> 4) & 0x0f];
+    *bufEnd++ = hex[c & 0x0f];
+    lineLen += 2;
+  }
+  return gTrue;
+}
+
+//------------------------------------------------------------------------
 // ASCII85Encoder
 //------------------------------------------------------------------------
 
-ASCII85Encoder::ASCII85Encoder(Stream *str):
-    FilterStream(str) {
+ASCII85Encoder::ASCII85Encoder(Stream *strA):
+    FilterStream(strA) {
   bufPtr = bufEnd = buf;
   lineLen = 0;
   eof = gFalse;
@@ -3344,8 +3521,8 @@ GBool ASCII85Encoder::fillBuf() {
 // RunLengthEncoder
 //------------------------------------------------------------------------
 
-RunLengthEncoder::RunLengthEncoder(Stream *str):
-    FilterStream(str) {
+RunLengthEncoder::RunLengthEncoder(Stream *strA):
+    FilterStream(strA) {
   bufPtr = bufEnd = nextEnd = buf;
   eof = gFalse;
 }
index 50345bb..3319dcc 100644 (file)
@@ -2,7 +2,7 @@
 //
 // Stream.h
 //
-// Copyright 1996 Derek B. Noonburg
+// Copyright 1996-2002 Glyph & Cog, LLC
 //
 //========================================================================
 
@@ -78,8 +78,10 @@ public:
   // Get current position in file.
   virtual int getPos() = 0;
 
-  // Go to a position in the stream.
-  virtual void setPos(int pos1) = 0;
+  // Go to a position in the stream.  If <dir> is negative, the
+  // position is from the end of the file; otherwise the position is
+  // from the start of the file.
+  virtual void setPos(Guint pos, int dir = 0) = 0;
 
   // Get PostScript command for the filter(s).
   virtual GString *getPSFilter(char *indent);
@@ -116,20 +118,22 @@ private:
 class BaseStream: public Stream {
 public:
 
-  BaseStream(Object *dict);
+  BaseStream(Object *dictA);
   virtual ~BaseStream();
-  virtual Stream *makeSubStream(int start, int length, Object *dict) = 0;
-  virtual void setPos(int pos1) = 0;
+  virtual Stream *makeSubStream(Guint start, GBool limited,
+                               Guint length, Object *dict) = 0;
+  virtual void setPos(Guint pos, int dir = 0) = 0;
   virtual BaseStream *getBaseStream() { return this; }
   virtual Dict *getDict() { return dict.getDict(); }
 
   // Get/set position of first byte of stream within the file.
-  virtual int getStart() = 0;
+  virtual Guint getStart() = 0;
   virtual void moveStart(int delta) = 0;
 
 #ifndef NO_DECRYPTION
   // Set decryption for this stream.
-  void doDecryption(Guchar *fileKey, int objNum, int objGen);
+  virtual void doDecryption(Guchar *fileKey, int keyLength,
+                           int objNum, int objGen);
 #endif
 
 #ifndef NO_DECRYPTION
@@ -152,11 +156,11 @@ private:
 class FilterStream: public Stream {
 public:
 
-  FilterStream(Stream *str);
+  FilterStream(Stream *strA);
   virtual ~FilterStream();
   virtual void close();
   virtual int getPos() { return str->getPos(); }
-  virtual void setPos(int pos);
+  virtual void setPos(Guint pos, int dir = 0);
   virtual BaseStream *getBaseStream() { return str->getBaseStream(); }
   virtual Dict *getDict() { return str->getDict(); }
 
@@ -175,7 +179,7 @@ public:
   // Create an image stream object for an image with the specified
   // parameters.  Note that these are the actual image parameters,
   // which may be different from the predictor parameters.
-  ImageStream(Stream *str, int width, int nComps, int nBits);
+  ImageStream(Stream *strA, int widthA, int nCompsA, int nBitsA);
 
   ~ImageStream();
 
@@ -209,8 +213,8 @@ public:
 
   // Create a predictor object.  Note that the parameters are for the
   // predictor, and may not match the actual image parameters.
-  StreamPredictor(Stream *str, int predictor,
-                 int width, int nComps, int nBits);
+  StreamPredictor(Stream *strA, int predictorA,
+                 int widthA, int nCompsA, int nBitsA);
 
   ~StreamPredictor();
 
@@ -242,9 +246,11 @@ private:
 class FileStream: public BaseStream {
 public:
 
-  FileStream(FILE *f, int start, int length, Object *dict);
+  FileStream(FILE *fA, Guint startA, GBool limitedA,
+            Guint lengthA, Object *dictA);
   virtual ~FileStream();
-  virtual Stream *makeSubStream(int start, int length, Object *dict);
+  virtual Stream *makeSubStream(Guint startA, GBool limitedA,
+                               Guint lengthA, Object *dictA);
   virtual StreamKind getKind() { return strFile; }
   virtual void reset();
   virtual void close();
@@ -253,9 +259,9 @@ public:
   virtual int lookChar()
     { return (bufPtr >= bufEnd && !fillBuf()) ? EOF : (*bufPtr & 0xff); }
   virtual int getPos() { return bufPos + (bufPtr - buf); }
-  virtual void setPos(int pos1);
+  virtual void setPos(Guint pos, int dir = 0);
   virtual GBool isBinary(GBool last = gTrue) { return last; }
-  virtual int getStart() { return start; }
+  virtual Guint getStart() { return start; }
   virtual void moveStart(int delta);
 
 private:
@@ -263,13 +269,52 @@ private:
   GBool fillBuf();
 
   FILE *f;
-  int start;
-  int length;
+  Guint start;
+  GBool limited;
+  Guint length;
   char buf[fileStreamBufSize];
   char *bufPtr;
   char *bufEnd;
-  int bufPos;
+  Guint bufPos;
   int savePos;
+  GBool saved;
+};
+
+//------------------------------------------------------------------------
+// MemStream
+//------------------------------------------------------------------------
+
+class MemStream: public BaseStream {
+public:
+
+  MemStream(char *bufA, Guint lengthA, Object *dictA);
+  virtual ~MemStream();
+  virtual Stream *makeSubStream(Guint start, GBool limited,
+                               Guint lengthA, Object *dictA);
+  virtual StreamKind getKind() { return strWeird; }
+  virtual void reset();
+  virtual void close();
+  virtual int getChar()
+    { return (bufPtr < bufEnd) ? (*bufPtr++ & 0xff) : EOF; }
+  virtual int lookChar()
+    { return (bufPtr < bufEnd) ? (*bufPtr & 0xff) : EOF; }
+  virtual int getPos() { return bufPtr - buf; }
+  virtual void setPos(Guint pos, int dir = 0);
+  virtual GBool isBinary(GBool last = gTrue) { return last; }
+  virtual Guint getStart() { return 0; }
+  virtual void moveStart(int delta);
+#ifndef NO_DECRYPTION
+  virtual void doDecryption(Guchar *fileKey, int keyLength,
+                           int objNum, int objGen);
+#endif
+
+private:
+
+  char *buf;
+  Guint length;
+  GBool needFree;
+  char *bufEnd;
+  char *bufPtr;
 };
 
 //------------------------------------------------------------------------
@@ -285,17 +330,18 @@ private:
 class EmbedStream: public BaseStream {
 public:
 
-  EmbedStream(Stream *str, Object *dict);
+  EmbedStream(Stream *strA, Object *dictA);
   virtual ~EmbedStream();
-  virtual Stream *makeSubStream(int start, int length, Object *dict);
+  virtual Stream *makeSubStream(Guint start, GBool limited,
+                               Guint length, Object *dictA);
   virtual StreamKind getKind() { return str->getKind(); }
   virtual void reset() {}
   virtual int getChar() { return str->getChar(); }
   virtual int lookChar() { return str->lookChar(); }
   virtual int getPos() { return str->getPos(); }
-  virtual void setPos(int pos);
+  virtual void setPos(Guint pos, int dir = 0);
   virtual GBool isBinary(GBool last = gTrue) { return last; }
-  virtual int getStart();
+  virtual Guint getStart();
   virtual void moveStart(int delta);
 
 private:
@@ -310,7 +356,7 @@ private:
 class ASCIIHexStream: public FilterStream {
 public:
 
-  ASCIIHexStream(Stream *str);
+  ASCIIHexStream(Stream *strA);
   virtual ~ASCIIHexStream();
   virtual StreamKind getKind() { return strASCIIHex; }
   virtual void reset();
@@ -333,7 +379,7 @@ private:
 class ASCII85Stream: public FilterStream {
 public:
 
-  ASCII85Stream(Stream *str);
+  ASCII85Stream(Stream *strA);
   virtual ~ASCII85Stream();
   virtual StreamKind getKind() { return strASCII85; }
   virtual void reset();
@@ -358,8 +404,8 @@ private:
 class LZWStream: public FilterStream {
 public:
 
-  LZWStream(Stream *str, int predictor1, int columns1, int colors1,
-           int bits1, int early1);
+  LZWStream(Stream *strA, int predictor, int columns, int colors,
+           int bits, int earlyA);
   virtual ~LZWStream();
   virtual StreamKind getKind() { return strLZW; }
   virtual void reset();
@@ -394,7 +440,7 @@ private:
 class RunLengthStream: public FilterStream {
 public:
 
-  RunLengthStream(Stream *str);
+  RunLengthStream(Stream *strA);
   virtual ~RunLengthStream();
   virtual StreamKind getKind() { return strRunLength; }
   virtual void reset();
@@ -424,9 +470,9 @@ struct CCITTCodeTable;
 class CCITTFaxStream: public FilterStream {
 public:
 
-  CCITTFaxStream(Stream *str, int encoding, GBool endOfLine,
-                GBool byteAlign, int columns, int rows,
-                GBool endOfBlock, GBool black);
+  CCITTFaxStream(Stream *strA, int encodingA, GBool endOfLineA,
+                GBool byteAlignA, int columnsA, int rowsA,
+                GBool endOfBlockA, GBool blackA);
   virtual ~CCITTFaxStream();
   virtual StreamKind getKind() { return strCCITTFax; }
   virtual void reset();
@@ -489,7 +535,7 @@ struct DCTHuffTable {
 class DCTStream: public FilterStream {
 public:
 
-  DCTStream(Stream *str);
+  DCTStream(Stream *strA);
   virtual ~DCTStream();
   virtual StreamKind getKind() { return strDCT; }
   virtual void reset();
@@ -573,8 +619,8 @@ struct FlateDecode {
 class FlateStream: public FilterStream {
 public:
 
-  FlateStream(Stream *str, int predictor1, int columns1,
-             int colors1, int bits1);
+  FlateStream(Stream *strA, int predictor, int columns,
+             int colors, int bits);
   virtual ~FlateStream();
   virtual StreamKind getKind() { return strFlate; }
   virtual void reset();
@@ -624,7 +670,7 @@ private:
 class EOFStream: public FilterStream {
 public:
 
-  EOFStream(Stream *str);
+  EOFStream(Stream *strA);
   virtual ~EOFStream();
   virtual StreamKind getKind() { return strWeird; }
   virtual void reset() {}
@@ -641,7 +687,7 @@ public:
 class FixedLengthEncoder: public FilterStream {
 public:
 
-  FixedLengthEncoder(Stream *str, int length1);
+  FixedLengthEncoder(Stream *strA, int lengthA);
   ~FixedLengthEncoder();
   virtual StreamKind getKind() { return strWeird; }
   virtual void reset();
@@ -659,13 +705,44 @@ private:
 };
 
 //------------------------------------------------------------------------
+// ASCIIHexEncoder
+//------------------------------------------------------------------------
+
+class ASCIIHexEncoder: public FilterStream {
+public:
+
+  ASCIIHexEncoder(Stream *strA);
+  virtual ~ASCIIHexEncoder();
+  virtual StreamKind getKind() { return strWeird; }
+  virtual void reset();
+  virtual void close();
+  virtual int getChar()
+    { return (bufPtr >= bufEnd && !fillBuf()) ? EOF : (*bufPtr++ & 0xff); }
+  virtual int lookChar()
+    { return (bufPtr >= bufEnd && !fillBuf()) ? EOF : (*bufPtr & 0xff); }
+  virtual GString *getPSFilter(char *indent) { return NULL; }
+  virtual GBool isBinary(GBool last = gTrue) { return gFalse; }
+  virtual GBool isEncoder() { return gTrue; }
+
+private:
+
+  char buf[4];
+  char *bufPtr;
+  char *bufEnd;
+  int lineLen;
+  GBool eof;
+
+  GBool fillBuf();
+};
+
+//------------------------------------------------------------------------
 // ASCII85Encoder
 //------------------------------------------------------------------------
 
 class ASCII85Encoder: public FilterStream {
 public:
 
-  ASCII85Encoder(Stream *str);
+  ASCII85Encoder(Stream *strA);
   virtual ~ASCII85Encoder();
   virtual StreamKind getKind() { return strWeird; }
   virtual void reset();
@@ -696,7 +773,7 @@ private:
 class RunLengthEncoder: public FilterStream {
 public:
 
-  RunLengthEncoder(Stream *str);
+  RunLengthEncoder(Stream *strA);
   virtual ~RunLengthEncoder();
   virtual StreamKind getKind() { return strWeird; }
   virtual void reset();
index d70b9f2..0e1bbc9 100644 (file)
@@ -2,7 +2,7 @@
 //
 // XRef.cc
 //
-// Copyright 1996 Derek B. Noonburg
+// Copyright 1996-2002 Glyph & Cog, LLC
 //
 //========================================================================
 
@@ -10,6 +10,7 @@
 #pragma implementation
 #endif
 
+#include <aconf.h>
 #include <stdlib.h>
 #include <stddef.h>
 #include <string.h>
@@ -24,6 +25,7 @@
 #include "Decrypt.h"
 #endif
 #include "Error.h"
+#include "ErrorCodes.h"
 #include "XRef.h"
 
 //------------------------------------------------------------------------
 #endif
 
 //------------------------------------------------------------------------
-// The global xref table
-//------------------------------------------------------------------------
-
-XRef *xref = NULL;
-
-//------------------------------------------------------------------------
 // XRef
 //------------------------------------------------------------------------
 
-XRef::XRef(BaseStream *str, GString *userPassword) {
-  XRef *oldXref;
-  int pos;
+XRef::XRef(BaseStream *strA, GString *ownerPassword, GString *userPassword) {
+  Guint pos;
   int i;
 
   ok = gTrue;
+  errCode = errNone;
   size = 0;
   entries = NULL;
   streamEnds = NULL;
   streamEndsLen = 0;
 
-  // get rid of old xref (otherwise it will try to fetch the Root object
-  // in the new document, using the old xref)
-  oldXref = xref;
-  xref = NULL;
-
   // read the trailer
-  this->str = str;
+  str = strA;
   start = str->getStart();
   pos = readTrailer();
 
@@ -78,7 +69,7 @@ XRef::XRef(BaseStream *str, GString *userPassword) {
   // try to reconstruct the xref table
   if (pos == 0) {
     if (!(ok = constructXRef())) {
-      xref = oldXref;
+      errCode = errDamaged;
       return;
     }
 
@@ -86,7 +77,7 @@ XRef::XRef(BaseStream *str, GString *userPassword) {
   } else {
     entries = (XRefEntry *)gmalloc(size * sizeof(XRefEntry));
     for (i = 0; i < size; ++i) {
-      entries[i].offset = -1;
+      entries[i].offset = 0xffffffff;
       entries[i].used = gFalse;
     }
     while (readXRef(&pos)) ;
@@ -98,22 +89,23 @@ XRef::XRef(BaseStream *str, GString *userPassword) {
       size = 0;
       entries = NULL;
       if (!(ok = constructXRef())) {
-       xref = oldXref;
+       errCode = errDamaged;
        return;
       }
     }
   }
 
-  // set up new xref table
-  xref = this;
+  // now set the trailer dictionary's xref pointer so we can fetch
+  // indirect objects from it
+  trailerDict.getDict()->setXRef(this);
 
   // check for encryption
 #ifndef NO_DECRYPTION
   encrypted = gFalse;
 #endif
-  if (checkEncrypted(userPassword)) {
+  if (checkEncrypted(ownerPassword, userPassword)) {
     ok = gFalse;
-    xref = oldXref;
+    errCode = errEncrypted;
     return;
   }
 }
@@ -128,17 +120,18 @@ XRef::~XRef() {
 
 // Read startxref position, xref table size, and root.  Returns
 // first xref position.
-int XRef::readTrailer() {
+Guint XRef::readTrailer() {
   Parser *parser;
   Object obj;
   char buf[xrefSearchSize+1];
-  int n, pos, pos1;
+  int n;
+  Guint pos, pos1;
   char *p;
   int c;
   int i;
 
   // read last xrefSearchSize bytes
-  str->setPos(-xrefSearchSize);
+  str->setPos(xrefSearchSize, -1);
   for (n = 0; n < xrefSearchSize; ++n) {
     if ((c = str->getChar()) == EOF)
       break;
@@ -154,7 +147,7 @@ int XRef::readTrailer() {
   if (i < 0)
     return 0;
   for (p = &buf[i+9]; isspace(*p); ++p) ;
-  pos = lastXRefPos = atoi(p);
+  pos = lastXRefPos = strToUnsigned(p);
 
   // find trailer dict by looking after first xref table
   // (NB: we can't just use the trailer dict at the end of the file --
@@ -189,7 +182,9 @@ int XRef::readTrailer() {
 
   // read trailer dict
   obj.initNull();
-  parser = new Parser(new Lexer(str->makeSubStream(start + pos1, -1, &obj)));
+  parser = new Parser(NULL,
+            new Lexer(NULL,
+              str->makeSubStream(start + pos1, gFalse, 0, &obj)));
   parser->getObj(&trailerDict);
   if (trailerDict.isDict()) {
     trailerDict.dictLookupNF("Size", &obj);
@@ -216,12 +211,12 @@ int XRef::readTrailer() {
 }
 
 // Read an xref table and the prev pointer from the trailer.
-GBool XRef::readXRef(int *pos) {
+GBool XRef::readXRef(Guint *pos) {
   Parser *parser;
   Object obj, obj2;
   char s[20];
   GBool more;
-  int first, n, i, j;
+  int first, newSize, n, i, j;
   int c;
 
   // seek to xref in stream
@@ -233,77 +228,101 @@ GBool XRef::readXRef(int *pos) {
   s[1] = (char)str->getChar();
   s[2] = (char)str->getChar();
   s[3] = (char)str->getChar();
-  if (!(s[0] == 'x' && s[1] == 'r' && s[2] == 'e' && s[3] == 'f'))
+  if (!(s[0] == 'x' && s[1] == 'r' && s[2] == 'e' && s[3] == 'f')) {
     goto err2;
+  }
 
   // read xref
   while (1) {
-    while ((c = str->lookChar()) != EOF && isspace(c))
+    while ((c = str->lookChar()) != EOF && isspace(c)) {
       str->getChar();
-    if (c == 't')
+    }
+    if (c == 't') {
       break;
-    for (i = 0; (c = str->getChar()) != EOF && isdigit(c) && i < 20; ++i)
+    }
+    for (i = 0; (c = str->getChar()) != EOF && isdigit(c) && i < 20; ++i) {
       s[i] = (char)c;
-    if (i == 0)
+    }
+    if (i == 0) {
       goto err2;
+    }
     s[i] = '\0';
     first = atoi(s);
-    while ((c = str->lookChar()) != EOF && isspace(c))
+    while ((c = str->lookChar()) != EOF && isspace(c)) {
       str->getChar();
-    for (i = 0; (c = str->getChar()) != EOF && isdigit(c) && i < 20; ++i)
+    }
+    for (i = 0; (c = str->getChar()) != EOF && isdigit(c) && i < 20; ++i) {
       s[i] = (char)c;
-    if (i == 0)
+    }
+    if (i == 0) {
       goto err2;
+    }
     s[i] = '\0';
     n = atoi(s);
-    while ((c = str->lookChar()) != EOF && isspace(c))
+    while ((c = str->lookChar()) != EOF && isspace(c)) {
       str->getChar();
+    }
+    // check for buggy PDF files with an incorrect (too small) xref
+    // table size
+    if (first + n > size) {
+      newSize = size + 256;
+      entries = (XRefEntry *)grealloc(entries, newSize * sizeof(XRefEntry));
+      for (i = size; i < newSize; ++i) {
+       entries[i].offset = 0xffffffff;
+       entries[i].used = gFalse;
+      }
+      size = newSize;
+    }
     for (i = first; i < first + n; ++i) {
       for (j = 0; j < 20; ++j) {
-       if ((c = str->getChar()) == EOF)
+       if ((c = str->getChar()) == EOF) {
          goto err2;
+       }
        s[j] = (char)c;
       }
-      if (entries[i].offset < 0) {
+      if (entries[i].offset == 0xffffffff) {
        s[10] = '\0';
-       entries[i].offset = atoi(s);
+       entries[i].offset = strToUnsigned(s);
        s[16] = '\0';
        entries[i].gen = atoi(&s[11]);
-       if (s[17] == 'n')
+       if (s[17] == 'n') {
          entries[i].used = gTrue;
-       else if (s[17] == 'f')
+       } else if (s[17] == 'f') {
          entries[i].used = gFalse;
-       else
+       } else {
          goto err2;
-#if 1 //~
-       //~ PDF files of patents from the IBM Intellectual Property
-       //~ Network have a bug: the xref table claims to start at 1
-       //~ instead of 0.
+       }
+       // PDF files of patents from the IBM Intellectual Property
+       // Network have a bug: the xref table claims to start at 1
+       // instead of 0.
        if (i == 1 && first == 1 &&
            entries[1].offset == 0 && entries[1].gen == 65535 &&
            !entries[1].used) {
          i = first = 0;
          entries[0] = entries[1];
-         entries[1].offset = -1;
+         entries[1].offset = 0xffffffff;
        }
-#endif
       }
     }
   }
 
   // read prev pointer from trailer dictionary
   obj.initNull();
-  parser = new Parser(new Lexer(str->makeSubStream(str->getPos(), -1, &obj)));
+  parser = new Parser(NULL,
+            new Lexer(NULL,
+              str->makeSubStream(str->getPos(), gFalse, 0, &obj)));
   parser->getObj(&obj);
-  if (!obj.isCmd("trailer"))
+  if (!obj.isCmd("trailer")) {
     goto err1;
+  }
   obj.free();
   parser->getObj(&obj);
-  if (!obj.isDict())
+  if (!obj.isDict()) {
     goto err1;
+  }
   obj.getDict()->lookupNF("Prev", &obj2);
   if (obj2.isInt()) {
-    *pos = obj2.getInt();
+    *pos = (Guint)obj2.getInt();
     more = gTrue;
   } else {
     more = gFalse;
@@ -326,7 +345,7 @@ GBool XRef::constructXRef() {
   Parser *parser;
   Object obj;
   char buf[256];
-  int pos;
+  Guint pos;
   int num, gen;
   int newSize;
   int streamEndsSize;
@@ -349,8 +368,9 @@ GBool XRef::constructXRef() {
     // got trailer dictionary
     if (!strncmp(p, "trailer", 7)) {
       obj.initNull();
-      parser = new Parser(new Lexer(
-                     str->makeSubStream(start + pos + 7, -1, &obj)));
+      parser = new Parser(NULL,
+                new Lexer(NULL,
+                  str->makeSubStream(start + pos + 7, gFalse, 0, &obj)));
       if (!trailerDict.isNone())
        trailerDict.free();
       parser->getObj(&trailerDict);
@@ -392,7 +412,7 @@ GBool XRef::constructXRef() {
                entries = (XRefEntry *)
                            grealloc(entries, newSize * sizeof(XRefEntry));
                for (i = size; i < newSize; ++i) {
-                 entries[i].offset = -1;
+                 entries[i].offset = 0xffffffff;
                  entries[i].used = gFalse;
                }
                size = newSize;
@@ -410,7 +430,8 @@ GBool XRef::constructXRef() {
     } else if (!strncmp(p, "endstream", 9)) {
       if (streamEndsLen == streamEndsSize) {
        streamEndsSize += 64;
-       streamEnds = (int *)grealloc(streamEnds, streamEndsSize * sizeof(int));
+       streamEnds = (Guint *)grealloc(streamEnds,
+                                      streamEndsSize * sizeof(int));
       }
       streamEnds[streamEndsLen++] = pos;
     }
@@ -424,8 +445,9 @@ GBool XRef::constructXRef() {
 }
 
 #ifndef NO_DECRYPTION
-GBool XRef::checkEncrypted(GString *userPassword) {
-  Object encrypt, ownerKey, userKey, permissions, fileID, fileID1;
+GBool XRef::checkEncrypted(GString *ownerPassword, GString *userPassword) {
+  Object encrypt, filterObj, versionObj, revisionObj, lengthObj;
+  Object ownerKey, userKey, permissions, fileID, fileID1;
   GBool encrypted1;
   GBool ret;
 
@@ -435,35 +457,68 @@ GBool XRef::checkEncrypted(GString *userPassword) {
   trailerDict.dictLookup("Encrypt", &encrypt);
   if ((encrypted1 = encrypt.isDict())) {
     ret = gTrue;
-    encrypt.dictLookup("O", &ownerKey);
-    encrypt.dictLookup("U", &userKey);
-    encrypt.dictLookup("P", &permissions);
-    trailerDict.dictLookup("ID", &fileID);
-    if (ownerKey.isString() && ownerKey.getString()->getLength() == 32 &&
-       userKey.isString() && userKey.getString()->getLength() == 32 &&
-       permissions.isInt() &&
-       fileID.isArray()) {
-      permFlags = permissions.getInt();
-      fileID.arrayGet(0, &fileID1);
-      if (fileID1.isString()) {
-       if (Decrypt::makeFileKey(ownerKey.getString(), userKey.getString(),
-                                permFlags, fileID1.getString(),
-                                userPassword, fileKey)) {
-         ret = gFalse;
+    encrypt.dictLookup("Filter", &filterObj);
+    if (filterObj.isName("Standard")) {
+      encrypt.dictLookup("V", &versionObj);
+      encrypt.dictLookup("R", &revisionObj);
+      encrypt.dictLookup("Length", &lengthObj);
+      encrypt.dictLookup("O", &ownerKey);
+      encrypt.dictLookup("U", &userKey);
+      encrypt.dictLookup("P", &permissions);
+      trailerDict.dictLookup("ID", &fileID);
+      if (versionObj.isInt() &&
+         revisionObj.isInt() &&
+         ownerKey.isString() && ownerKey.getString()->getLength() == 32 &&
+         userKey.isString() && userKey.getString()->getLength() == 32 &&
+         permissions.isInt() &&
+         fileID.isArray()) {
+       encVersion = versionObj.getInt();
+       encRevision = revisionObj.getInt();
+       if (lengthObj.isInt()) {
+         keyLength = lengthObj.getInt() / 8;
        } else {
-         error(-1, "Incorrect user password");
+         keyLength = 5;
+       }
+       permFlags = permissions.getInt();
+       if (encVersion >= 1 && encVersion <= 2 &&
+           encRevision >= 2 && encRevision <= 3) {
+         fileID.arrayGet(0, &fileID1);
+         if (fileID1.isString()) {
+           if (Decrypt::makeFileKey(encVersion, encRevision, keyLength,
+                                    ownerKey.getString(), userKey.getString(),
+                                    permFlags, fileID1.getString(),
+                                    ownerPassword, userPassword, fileKey,
+                                    &ownerPasswordOk)) {
+             if (ownerPassword && !ownerPasswordOk) {
+               error(-1, "Incorrect owner password");
+             }
+             ret = gFalse;
+           } else {
+             error(-1, "Incorrect password");
+           }
+         } else {
+           error(-1, "Weird encryption info");
+         }
+         fileID1.free();
+       } else {
+         error(-1, "Unsupported version/revision (%d/%d) of Standard security handler",
+               encVersion, encRevision);
        }
       } else {
        error(-1, "Weird encryption info");
       }
-      fileID1.free();
+      fileID.free();
+      permissions.free();
+      userKey.free();
+      ownerKey.free();
+      lengthObj.free();
+      revisionObj.free();
+      versionObj.free();
     } else {
-      error(-1, "Weird encryption info");
+      error(-1, "Unknown security handler '%s'",
+           filterObj.isName() ? filterObj.getName() : "???");
     }
-    ownerKey.free();
-    userKey.free();
-    permissions.free();
-    fileID.free();
+    filterObj.free();
   }
   encrypt.free();
 
@@ -473,52 +528,50 @@ GBool XRef::checkEncrypted(GString *userPassword) {
   return ret;
 }
 #else
-GBool XRef::checkEncrypted(GString *userPassword) {
+GBool XRef::checkEncrypted(GString *ownerPassword, GString *userPassword) {
   Object obj;
   GBool encrypted;
 
   trailerDict.dictLookup("Encrypt", &obj);
   if ((encrypted = !obj.isNull())) {
-    error(-1, "PDF file is encrypted and cannot be displayed");
-    error(-1, "* Decryption support is currently not included in xpdf");
-    error(-1, "* due to legal restrictions: the U.S.A. still has bogus");
-    error(-1, "* export controls on cryptography software.");
+    error(-1, "PDF file is encrypted and this version of the Xpdf tools");
+    error(-1, "was built without decryption support.");
   }
   obj.free();
   return encrypted;
 }
 #endif
 
-GBool XRef::okToPrint() {
+GBool XRef::okToPrint(GBool ignoreOwnerPW) {
 #ifndef NO_DECRYPTION
-  if (!(permFlags & permPrint)) {
+  if ((ignoreOwnerPW || !ownerPasswordOk) && !(permFlags & permPrint)) {
     return gFalse;
   }
 #endif
   return gTrue;
 }
 
-GBool XRef::okToChange() {
+GBool XRef::okToChange(GBool ignoreOwnerPW) {
 #ifndef NO_DECRYPTION
-  if (!(permFlags & permChange)) {
+  if ((ignoreOwnerPW || !ownerPasswordOk) && !(permFlags & permChange)) {
     return gFalse;
   }
 #endif
   return gTrue;
 }
 
-GBool XRef::okToCopy() {
+GBool XRef::okToCopy(GBool ignoreOwnerPW) {
 #ifndef NO_DECRYPTION
-  if (!(permFlags & permCopy)) {
+  if ((ignoreOwnerPW || !ownerPasswordOk) && !(permFlags & permCopy)) {
     return gFalse;
   }
 #endif
   return gTrue;
 }
 
-GBool XRef::okToAddNotes() {
+GBool XRef::okToAddNotes(GBool ignoreOwnerPW) {
 #ifndef NO_DECRYPTION
-  if (!(permFlags & permNotes)) {
+  if ((ignoreOwnerPW || !ownerPasswordOk) && !(permFlags & permNotes)) {
     return gFalse;
   }
 #endif
@@ -537,10 +590,11 @@ Object *XRef::fetch(int num, int gen, Object *obj) {
   }
 
   e = &entries[num];
-  if (e->gen == gen && e->offset >= 0) {
+  if (e->gen == gen && e->offset != 0xffffffff) {
     obj1.initNull();
-    parser = new Parser(new Lexer(
-                  str->makeSubStream(start + e->offset, -1, &obj1)));
+    parser = new Parser(this,
+              new Lexer(this,
+                str->makeSubStream(start + e->offset, gFalse, 0, &obj1)));
     parser->getObj(&obj1);
     parser->getObj(&obj2);
     parser->getObj(&obj3);
@@ -548,7 +602,8 @@ Object *XRef::fetch(int num, int gen, Object *obj) {
        obj2.isInt() && obj2.getInt() == gen &&
        obj3.isCmd("obj")) {
 #ifndef NO_DECRYPTION
-      parser->getObj(obj, encrypted ? fileKey : (Guchar *)NULL, num, gen);
+      parser->getObj(obj, encrypted ? fileKey : (Guchar *)NULL, keyLength,
+                    num, gen);
 #else
       parser->getObj(obj);
 #endif
@@ -569,24 +624,42 @@ Object *XRef::getDocInfo(Object *obj) {
   return trailerDict.dictLookup("Info", obj);
 }
 
-int XRef::getStreamEnd(int start) {
+// Added for the pdftex project.
+Object *XRef::getDocInfoNF(Object *obj) {
+  return trailerDict.dictLookupNF("Info", obj);
+}
+
+GBool XRef::getStreamEnd(Guint streamStart, Guint *streamEnd) {
   int a, b, m;
 
   if (streamEndsLen == 0 ||
-      start > streamEnds[streamEndsLen - 1]) {
-    return -1;
+      streamStart > streamEnds[streamEndsLen - 1]) {
+    return gFalse;
   }
 
   a = -1;
   b = streamEndsLen - 1;
-  // invariant: streamEnds[a] < start <= streamEnds[b]
+  // invariant: streamEnds[a] < streamStart <= streamEnds[b]
   while (b - a > 1) {
     m = (a + b) / 2;
-    if (start <= streamEnds[m]) {
+    if (streamStart <= streamEnds[m]) {
       b = m;
     } else {
       a = m;
     }
   }
-  return streamEnds[b];
+  *streamEnd = streamEnds[b];
+  return gTrue;
+}
+
+Guint XRef::strToUnsigned(char *s) {
+  Guint x;
+  char *p;
+  int i;
+
+  x = 0;
+  for (p = s, i = 0; *p && isdigit(*p) && i < 10; ++p, ++i) {
+    x = 10 * x + (*p - '0');
+  }
+  return x;
 }
index e2260d0..7876fa6 100644 (file)
@@ -2,7 +2,7 @@
 //
 // XRef.h
 //
-// Copyright 1996 Derek B. Noonburg
+// Copyright 1996-2002 Glyph & Cog, LLC
 //
 //========================================================================
 
@@ -24,7 +24,7 @@ class Stream;
 //------------------------------------------------------------------------
 
 struct XRefEntry {
-  int offset;
+  Guint offset;
   int gen;
   GBool used;
 };
@@ -33,7 +33,7 @@ class XRef {
 public:
 
   // Constructor.  Read xref table from stream.
-  XRef(BaseStream *str, GString *userPassword);
+  XRef(BaseStream *strA, GString *ownerPassword, GString *userPassword);
 
   // Destructor.
   ~XRef();
@@ -41,6 +41,9 @@ public:
   // Is xref table valid?
   GBool isOk() { return ok; }
 
+  // Get the error code (if isOk() returns false).
+  int getErrorCode() { return errCode; }
+
   // Is the file encrypted?
 #ifndef NO_DECRYPTION
   GBool isEncrypted() { return encrypted; }
@@ -49,10 +52,10 @@ public:
 #endif
 
   // Check various permissions.
-  GBool okToPrint();
-  GBool okToChange();
-  GBool okToCopy();
-  GBool okToAddNotes();
+  GBool okToPrint(GBool ignoreOwnerPW = gFalse);
+  GBool okToChange(GBool ignoreOwnerPW = gFalse);
+  GBool okToCopy(GBool ignoreOwnerPW = gFalse);
+  GBool okToAddNotes(GBool ignoreOwnerPW = gFalse);
 
   // Get catalog object.
   Object *getCatalog(Object *obj) { return fetch(rootNum, rootGen, obj); }
@@ -62,51 +65,52 @@ public:
 
   // Return the document's Info dictionary (if any).
   Object *getDocInfo(Object *obj);
+  Object *getDocInfoNF(Object *obj);
 
   // Return the number of objects in the xref table.
   int getNumObjects() { return size; }
 
   // Return the offset of the last xref table.
-  int getLastXRefPos() { return lastXRefPos; }
+  Guint getLastXRefPos() { return lastXRefPos; }
 
   // Return the catalog object reference.
   int getRootNum() { return rootNum; }
   int getRootGen() { return rootGen; }
 
   // Get end position for a stream in a damaged file.
-  // Returns -1 if unknown or file is not damaged.
-  int getStreamEnd(int start);
+  // Returns false if unknown or file is not damaged.
+  GBool getStreamEnd(Guint streamStart, Guint *streamEnd);
 
 private:
 
   BaseStream *str;             // input stream
-  int start;                   // offset in file (to allow for garbage
+  Guint start;                 // offset in file (to allow for garbage
                                //   at beginning of file)
   XRefEntry *entries;          // xref entries
   int size;                    // size of <entries> array
   int rootNum, rootGen;                // catalog dict
   GBool ok;                    // true if xref table is valid
+  int errCode;                 // error code (if <ok> is false)
   Object trailerDict;          // trailer dictionary
-  int lastXRefPos;             // offset of last xref table
-  int *streamEnds;             // 'endstream' positions - only used in
+  Guint lastXRefPos;           // offset of last xref table
+  Guint *streamEnds;           // 'endstream' positions - only used in
                                //   damaged files
   int streamEndsLen;           // number of valid entries in streamEnds
 #ifndef NO_DECRYPTION
   GBool encrypted;             // true if file is encrypted
+  int encVersion;              // encryption algorithm
+  int encRevision;             // security handler revision
+  int keyLength;               // length of key, in bytes
   int permFlags;               // permission bits
   Guchar fileKey[16];          // file decryption key
+  GBool ownerPasswordOk;       // true if owner password is correct
 #endif
 
-  int readTrailer();
-  GBool readXRef(int *pos);
+  Guint readTrailer();
+  GBool readXRef(Guint *pos);
   GBool constructXRef();
-  GBool checkEncrypted(GString *userPassword);
+  GBool checkEncrypted(GString *ownerPassword, GString *userPassword);
+  Guint strToUnsigned(char *s);
 };
 
-//------------------------------------------------------------------------
-// The global xref table
-//------------------------------------------------------------------------
-
-extern XRef *xref;
-
 #endif
index c09d8f5..39cb291 100644 (file)
@@ -2,7 +2,7 @@
 //
 // config.h
 //
-// Copyright 1996 Derek B. Noonburg
+// Copyright 1996-2002 Glyph & Cog, LLC
 //
 //========================================================================
 
 #define CONFIG_H
 
 //------------------------------------------------------------------------
-// general constants
+// version
 //------------------------------------------------------------------------
 
 // xpdf version
-#define xpdfVersion "0.92"
+
+#define xpdfVersion "1.01"
 
 // supported PDF version
-#define supportedPDFVersionStr "1.3"
-#define supportedPDFVersionNum 1.3
+#define supportedPDFVersionStr "1.4"
+#define supportedPDFVersionNum 1.4
 
 // copyright notice
-#define xpdfCopyright "Copyright 1996-2000 Derek B. Noonburg"
+#define xpdfCopyright "Copyright 1996-2002 Glyph & Cog, LLC"
+
+//------------------------------------------------------------------------
+// paper size
+//------------------------------------------------------------------------
 
 // default paper size (in points) for PostScript output
 #ifdef A4_PAPER
 #define defPaperHeight 792
 #endif
 
-// config file name
-#if defined(VMS)
-#define xpdfConfigFile "xpdfrc"
+//------------------------------------------------------------------------
+// config file (xpdfrc) path
+//------------------------------------------------------------------------
+
+// user config file name, relative to the user's home directory
+#if defined(VMS) || (defined(WIN32) && !defined(__CYGWIN32__))
+#define xpdfUserConfigFile "xpdfrc"
 #else
-#define xpdfConfigFile ".xpdfrc"
+#define xpdfUserConfigFile ".xpdfrc"
+#endif
+
+// system config file name (set via the configure script)
+#ifdef SYSTEM_XPDFRC
+#define xpdfSysConfigFile SYSTEM_XPDFRC
+#else
+// under Windows, we get the directory with the executable and then
+// append this file name
+#define xpdfSysConfigFile "xpdfrc"
 #endif
 
 //------------------------------------------------------------------------
 // default maximum size of color cube to allocate
 #define defaultRGBCube 5
 
-// number of X server fonts to cache
-#define serverFontCacheSize 16
-
-// number of Type 1 (t1lib) fonts to cache
-#define t1FontCacheSize 32
+// number of fonts (combined t1lib, FreeType, X server) to cache
+#define xOutFontCacheSize 64
 
-// number of TrueType (FreeType) fonts to cache
-#define ttFontCacheSize 32
+// number of Type 3 fonts to cache
+#define xOutT3FontCacheSize 8
 
 //------------------------------------------------------------------------
 // popen
@@ -64,7 +79,7 @@
 #define pclose _pclose
 #endif
 
-#if defined(VMS) || defined(VMCMS) || defined(DOS) || defined(OS2) || defined(WIN32) || defined(__DJGPP__) || defined(__CYGWIN32) || defined(MACOS)
+#if defined(VMS) || defined(VMCMS) || defined(DOS) || defined(OS2) || defined(__EMX__) || defined(WIN32) || defined(__DJGPP__) || defined(__CYGWIN32__) || defined(MACOS)
 #define POPEN_READ_MODE "rb"
 #else
 #define POPEN_READ_MODE "r"
index 2350a90..d6d2363 100644 (file)
@@ -4,11 +4,12 @@
 //
 // Miscellaneous file and directory name manipulation.
 //
-// Copyright 1996 Derek B. Noonburg
+// Copyright 1996-2002 Glyph & Cog, LLC
 //
 //========================================================================
 
-#include "../../config.h"
+#include <aconf.h>
+
 #ifdef WIN32
    extern "C" {
 #  ifndef _MSC_VER
@@ -441,23 +442,28 @@ time_t getModTime(char *fileName) {
   return statBuf.st_mtime;
 #endif
 }
-static char tmpbuf[128];
-char* mktmpname(char*ptr) {
-//   used to be mktemp. This does remove the warnings, but
-//   It's not exactly an improvement.
-#ifdef HAVE_LRAND48
-    sprintf(tmpbuf, "/tmp/%08x%08x",lrand48(),lrand48());
-#else
-#   ifdef HAVE_RAND
-       sprintf(tmpbuf, "/tmp/%08x%08x",rand(),rand());
-#   else
-       sprintf(tmpbuf, "/tmp/%08x%08x",time(0),(unsigned int)tmpbuf);
-#   endif
-#endif
-    return tmpbuf;
-}
+
 GBool openTempFile(GString **name, FILE **f, char *mode, char *ext) {
-#if defined(VMS) || defined(__EMX__) || defined(WIN32) || defined(ACORN) || defined(MACOS)
+#if defined(WIN32)
+  //---------- Win32 ----------
+  char *s;
+  char buf[_MAX_PATH];
+  char *fp;
+
+  if (!(s = _tempnam(getenv("TEMP"), NULL))) {
+    return gFalse;
+  }
+  *name = new GString(s);
+  free(s);
+  if (ext) {
+    (*name)->append(ext);
+  }
+  if (!(*f = fopen((*name)->getCString(), mode))) {
+    delete (*name);
+    return gFalse;
+  }
+  return gTrue;
+#elif defined(VMS) || defined(__EMX__) || defined(ACORN) || defined(MACOS)
   //---------- non-Unix ----------
   char *s;
 
@@ -465,7 +471,7 @@ GBool openTempFile(GString **name, FILE **f, char *mode, char *ext) {
   // with this file name after the tmpnam call and before the fopen
   // call.  I will happily accept fixes to this function for non-Unix
   // OSs.
-  if (!(s = mktmpname(NULL))) {
+  if (!(s = tmpnam(NULL))) {
     return gFalse;
   }
   *name = new GString(s);
@@ -479,20 +485,26 @@ GBool openTempFile(GString **name, FILE **f, char *mode, char *ext) {
   return gTrue;
 #else
   //---------- Unix ----------
-  char *s, *p;
+  char *s;
   int fd;
 
   if (ext) {
-    if (!(s = mktmpname(NULL))) {
+#if HAVE_MKSTEMPS
+    if ((s = getenv("TMPDIR"))) {
+      *name = new GString(s);
+    } else {
+      *name = new GString("/tmp");
+    }
+    (*name)->append("/XXXXXX")->append(ext);
+    fd = mkstemps((*name)->getCString(), strlen(ext));
+#else
+    if (!(s = tmpnam(NULL))) {
       return gFalse;
     }
     *name = new GString(s);
-    s = (*name)->getCString();
-    if ((p = strrchr(s, '.'))) {
-      (*name)->del(p - s, (*name)->getLength() - (p - s));
-    }
     (*name)->append(ext);
     fd = open((*name)->getCString(), O_WRONLY | O_CREAT | O_EXCL, 0600);
+#endif
   } else {
 #if HAVE_MKSTEMP
     if ((s = getenv("TMPDIR"))) {
@@ -503,7 +515,7 @@ GBool openTempFile(GString **name, FILE **f, char *mode, char *ext) {
     (*name)->append("/XXXXXX");
     fd = mkstemp((*name)->getCString());
 #else // HAVE_MKSTEMP
-    if (!(s = mktmpname(NULL))) {
+    if (!(s = tmpnam(NULL))) {
       return gFalse;
     }
     *name = new GString(s);
@@ -518,11 +530,48 @@ GBool openTempFile(GString **name, FILE **f, char *mode, char *ext) {
 #endif
 }
 
+GBool executeCommand(char *cmd) {
+#ifdef VMS
+  return system(cmd) ? gTrue : gFalse;
+#else
+  return system(cmd) ? gFalse : gTrue;
+#endif
+}
+
+char *getLine(char *buf, int size, FILE *f) {
+  int c, i;
+
+  i = 0;
+  while (i < size - 1) {
+    if ((c = fgetc(f)) == EOF) {
+      break;
+    }
+    buf[i++] = (char)c;
+    if (c == '\x0a') {
+      break;
+    }
+    if (c == '\x0d') {
+      c = fgetc(f);
+      if (c == '\x0a' && i < size - 1) {
+       buf[i++] = (char)c;
+      } else if (c != EOF) {
+       ungetc(c, f);
+      }
+      break;
+    }
+  }
+  buf[i] = '\0';
+  if (i == 0) {
+    return NULL;
+  }
+  return buf;
+}
+
 //------------------------------------------------------------------------
 // GDir and GDirEntry
 //------------------------------------------------------------------------
 
-GDirEntry::GDirEntry(char *dirPath, char *name1, GBool doStat) {
+GDirEntry::GDirEntry(char *dirPath, char *nameA, GBool doStat) {
 #ifdef VMS
   char *p;
 #elif defined(WIN32)
@@ -534,17 +583,17 @@ GDirEntry::GDirEntry(char *dirPath, char *name1, GBool doStat) {
   GString *s;
 #endif
 
-  name = new GString(name1);
+  name = new GString(nameA);
   dir = gFalse;
   if (doStat) {
 #ifdef VMS
-    if (!strcmp(name1, "-") ||
-       ((p = strrchr(name1, '.')) && !strncmp(p, ".DIR;", 5)))
+    if (!strcmp(nameA, "-") ||
+       ((p = strrchr(nameA, '.')) && !strncmp(p, ".DIR;", 5)))
       dir = gTrue;
 #elif defined(ACORN)
 #else
     s = new GString(dirPath);
-    appendToPath(s, name1);
+    appendToPath(s, nameA);
 #ifdef WIN32
     fa = GetFileAttributes(s->getCString());
     dir = (fa != 0xFFFFFFFF && (fa & FILE_ATTRIBUTE_DIRECTORY));
@@ -561,9 +610,9 @@ GDirEntry::~GDirEntry() {
   delete name;
 }
 
-GDir::GDir(char *name, GBool doStat1) {
+GDir::GDir(char *name, GBool doStatA) {
   path = new GString(name);
-  doStat = doStat1;
+  doStat = doStatA;
 #if defined(WIN32)
   GString *tmp;
 
index f3041b9..bb6131d 100644 (file)
@@ -4,7 +4,7 @@
 //
 // Miscellaneous file and directory name manipulation.
 //
-// Copyright 1996 Derek B. Noonburg
+// Copyright 1996-2002 Glyph & Cog, LLC
 //
 //========================================================================
 
@@ -14,7 +14,6 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <stddef.h>
-#include "../../config.h"
 #if defined(WIN32)
 #  include <sys/stat.h>
 #  ifdef FPTEX
@@ -72,11 +71,11 @@ extern GBool isAbsolutePath(char *path);
 
 // Make this path absolute by prepending current directory (if path is
 // relative) or prepending user's directory (if path starts with '~').
-GString *makePathAbsolute(GString *path);
+extern GString *makePathAbsolute(GString *path);
 
 // Get the modification time for <fileName>.  Returns 0 if there is an
 // error.
-time_t getModTime(char *fileName);
+extern time_t getModTime(char *fileName);
 
 // Create a temporary file and open it for writing.  If <ext> is not
 // NULL, it will be used as the file name extension.  Returns both the
@@ -84,10 +83,14 @@ time_t getModTime(char *fileName);
 // should be done to the returned file pointer; the file may be
 // reopened later for reading, but not for writing.  The <mode> string
 // should be "w" or "wb".  Returns true on success.
-GBool openTempFile(GString **name, FILE **f, char *mode, char *ext);
+extern GBool openTempFile(GString **name, FILE **f, char *mode, char *ext);
 
-/* create a temporary filename */
-char* mktmpname(char*ptr);
+// Execute <command>.  Returns true on success.
+extern GBool executeCommand(char *cmd);
+
+// Just like fgets, but handles Unix, Mac, and/or DOS end-of-line
+// conventions.
+extern char *getLine(char *buf, int size, FILE *f);
 
 //------------------------------------------------------------------------
 // GDir and GDirEntry
@@ -96,7 +99,7 @@ char* mktmpname(char*ptr);
 class GDirEntry {
 public:
 
-  GDirEntry(char *dirPath, char *name1, GBool doStat);
+  GDirEntry(char *dirPath, char *nameA, GBool doStat);
   ~GDirEntry();
   GString *getName() { return name; }
   GBool isDir() { return dir; }
@@ -110,7 +113,7 @@ private:
 class GDir {
 public:
 
-  GDir(char *name, GBool doStat1 = gTrue);
+  GDir(char *name, GBool doStatA = gTrue);
   ~GDir();
   GDirEntry *getNextEntry();
   void rewind();
index cac386b..27ebb2b 100644 (file)
@@ -3,9 +3,10 @@
  *
  * Memory routines with out-of-memory checking.
  *
- * Copyright 1996 Derek B. Noonburg
+ * Copyright 1996-2002 Glyph & Cog, LLC
  */
 
+#include <aconf.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <stddef.h>
@@ -24,9 +25,9 @@ typedef struct _GMemHdr {
 #define gMemTrlSize (sizeof(long))
 
 #if gmemTrlSize==8
-#define gMemDeadVal 0xdeadbeefdeadbeef
+#define gMemDeadVal 0xdeadbeefdeadbeefUL
 #else
-#define gMemDeadVal 0xdeadbeef
+#define gMemDeadVal 0xdeadbeefUL
 #endif
 
 /* round data size so trailer will be aligned */
@@ -59,7 +60,7 @@ void *gmalloc(int size) {
   GMemHdr *hdr;
   void *data;
   int lst;
-  long *trl, *p;
+  unsigned long *trl, *p;
 
   if (size == 0)
     return NULL;
@@ -70,14 +71,14 @@ void *gmalloc(int size) {
   }
   hdr = (GMemHdr *)mem;
   data = (void *)(mem + gMemHdrSize);
-  trl = (long *)(mem + gMemHdrSize + size1);
+  trl = (unsigned long *)(mem + gMemHdrSize + size1);
   hdr->size = size;
   hdr->index = gMemIndex++;
   lst = ((int)hdr >> gMemListShift) & gMemListMask;
   hdr->next = gMemList[lst];
   gMemList[lst] = hdr;
   ++gMemAlloc;
-  for (p = (long *)data; p <= trl; ++p)
+  for (p = (unsigned long *)data; p <= trl; ++p)
     *p = gMemDeadVal;
   return data;
 #else
@@ -140,7 +141,7 @@ void gfree(void *p) {
   GMemHdr *hdr;
   GMemHdr *prevHdr, *q;
   int lst;
-  long *trl, *clr;
+  unsigned long *trl, *clr;
 
   if (p) {
     hdr = (GMemHdr *)((char *)p - gMemHdrSize);
@@ -156,12 +157,12 @@ void gfree(void *p) {
        gMemList[lst] = hdr->next;
       --gMemAlloc;
       size = gMemDataSize(hdr->size);
-      trl = (long *)((char *)hdr + gMemHdrSize + size);
+      trl = (unsigned long *)((char *)hdr + gMemHdrSize + size);
       if (*trl != gMemDeadVal) {
        fprintf(stderr, "Overwrite past end of block %d at address %p\n",
                hdr->index, p);
       }
-      for (clr = (long *)hdr; clr <= trl; ++clr)
+      for (clr = (unsigned long *)hdr; clr <= trl; ++clr)
        *clr = gMemDeadVal;
       free(hdr);
     } else {
index 732726d..93ccb94 100644 (file)
@@ -3,14 +3,13 @@
  *
  * Memory routines with out-of-memory checking.
  *
- * Copyright 1996 Derek B. Noonburg
+ * Copyright 1996-2002 Glyph & Cog, LLC
  */
 
 #ifndef GMEM_H
 #define GMEM_H
 
 #include <stdio.h>
-#include "../../config.h"
 
 #ifdef __cplusplus
 extern "C" {
index 6593267..1879b88 100644 (file)
@@ -3,7 +3,7 @@
  *
  * Some useful simple types.
  *
- * Copyright 1996 Derek B. Noonburg
+ * Copyright 1996-2002 Glyph & Cog, LLC
  */
 
 #ifndef GTYPES_H