moved ../../pdf2swf/xpdf to .
authorkramm <kramm>
Mon, 5 Jun 2006 07:16:09 +0000 (07:16 +0000)
committerkramm <kramm>
Mon, 5 Jun 2006 07:16:09 +0000 (07:16 +0000)
92 files changed:
lib/xpdf/Annot.cc [new file with mode: 0644]
lib/xpdf/Annot.h [new file with mode: 0644]
lib/xpdf/Array.cc [new file with mode: 0644]
lib/xpdf/Array.h [new file with mode: 0644]
lib/xpdf/BuiltinFont.cc [new file with mode: 0644]
lib/xpdf/BuiltinFont.h [new file with mode: 0644]
lib/xpdf/BuiltinFontTables.cc [new file with mode: 0644]
lib/xpdf/BuiltinFontTables.h [new file with mode: 0644]
lib/xpdf/CMap.cc [new file with mode: 0644]
lib/xpdf/CMap.h [new file with mode: 0644]
lib/xpdf/Catalog.cc [new file with mode: 0644]
lib/xpdf/Catalog.h [new file with mode: 0644]
lib/xpdf/CharCodeToUnicode.cc [new file with mode: 0644]
lib/xpdf/CharCodeToUnicode.h [new file with mode: 0644]
lib/xpdf/CharTypes.h [new file with mode: 0644]
lib/xpdf/Decrypt.cc [new file with mode: 0644]
lib/xpdf/Decrypt.h [new file with mode: 0644]
lib/xpdf/Dict.cc [new file with mode: 0644]
lib/xpdf/Dict.h [new file with mode: 0644]
lib/xpdf/Error.cc [new file with mode: 0644]
lib/xpdf/Error.h [new file with mode: 0644]
lib/xpdf/ErrorCodes.h [new file with mode: 0644]
lib/xpdf/FoFiBase.cc [new file with mode: 0644]
lib/xpdf/FoFiBase.h [new file with mode: 0644]
lib/xpdf/FoFiEncodings.cc [new file with mode: 0644]
lib/xpdf/FoFiEncodings.h [new file with mode: 0644]
lib/xpdf/FoFiTrueType.cc [new file with mode: 0644]
lib/xpdf/FoFiTrueType.h [new file with mode: 0644]
lib/xpdf/FoFiType1.cc [new file with mode: 0644]
lib/xpdf/FoFiType1.h [new file with mode: 0644]
lib/xpdf/FoFiType1C.cc [new file with mode: 0644]
lib/xpdf/FoFiType1C.h [new file with mode: 0644]
lib/xpdf/FontEncodingTables.cc [new file with mode: 0644]
lib/xpdf/FontEncodingTables.h [new file with mode: 0644]
lib/xpdf/Function.cc [new file with mode: 0644]
lib/xpdf/Function.h [new file with mode: 0644]
lib/xpdf/GFXOutputDev.cc [new file with mode: 0644]
lib/xpdf/GFXOutputDev.h [new file with mode: 0644]
lib/xpdf/GHash.cc [new file with mode: 0644]
lib/xpdf/GHash.h [new file with mode: 0644]
lib/xpdf/GList.cc [new file with mode: 0644]
lib/xpdf/GList.h [new file with mode: 0644]
lib/xpdf/GMutex.h [new file with mode: 0644]
lib/xpdf/GString.cc [new file with mode: 0644]
lib/xpdf/GString.h [new file with mode: 0644]
lib/xpdf/Gfx.cc [new file with mode: 0644]
lib/xpdf/Gfx.h [new file with mode: 0644]
lib/xpdf/GfxFont.cc [new file with mode: 0644]
lib/xpdf/GfxFont.h [new file with mode: 0644]
lib/xpdf/GfxState.cc [new file with mode: 0644]
lib/xpdf/GfxState.h [new file with mode: 0644]
lib/xpdf/GlobalParams.cc [new file with mode: 0644]
lib/xpdf/GlobalParams.h [new file with mode: 0644]
lib/xpdf/InfoOutputDev.cc [new file with mode: 0644]
lib/xpdf/InfoOutputDev.h [new file with mode: 0644]
lib/xpdf/JArithmeticDecoder.cc [new file with mode: 0644]
lib/xpdf/JArithmeticDecoder.h [new file with mode: 0644]
lib/xpdf/JBIG2Stream.cc [new file with mode: 0644]
lib/xpdf/JBIG2Stream.h [new file with mode: 0644]
lib/xpdf/JPXStream.cc [new file with mode: 0644]
lib/xpdf/JPXStream.h [new file with mode: 0644]
lib/xpdf/Lexer.cc [new file with mode: 0644]
lib/xpdf/Lexer.h [new file with mode: 0644]
lib/xpdf/Link.cc [new file with mode: 0644]
lib/xpdf/Link.h [new file with mode: 0644]
lib/xpdf/Makefile [new file with mode: 0644]
lib/xpdf/NameToCharCode.cc [new file with mode: 0644]
lib/xpdf/NameToCharCode.h [new file with mode: 0644]
lib/xpdf/NameToUnicodeTable.h [new file with mode: 0644]
lib/xpdf/Object.cc [new file with mode: 0644]
lib/xpdf/Object.h [new file with mode: 0644]
lib/xpdf/Outline.cc [new file with mode: 0644]
lib/xpdf/Outline.h [new file with mode: 0644]
lib/xpdf/OutputDev.cc [new file with mode: 0644]
lib/xpdf/OutputDev.h [new file with mode: 0644]
lib/xpdf/PDFDoc.cc [new file with mode: 0644]
lib/xpdf/PDFDoc.h [new file with mode: 0644]
lib/xpdf/PDFDocEncoding.cc [new file with mode: 0644]
lib/xpdf/PDFDocEncoding.h [new file with mode: 0644]
lib/xpdf/PSTokenizer.cc [new file with mode: 0644]
lib/xpdf/PSTokenizer.h [new file with mode: 0644]
lib/xpdf/Page.cc [new file with mode: 0644]
lib/xpdf/Page.h [new file with mode: 0644]
lib/xpdf/Parser.cc [new file with mode: 0644]
lib/xpdf/Parser.h [new file with mode: 0644]
lib/xpdf/SWFOutputDev.h [new file with mode: 0644]
lib/xpdf/SecurityHandler.cc [new file with mode: 0644]
lib/xpdf/SecurityHandler.h [new file with mode: 0644]
lib/xpdf/Stream-CCITT.h [new file with mode: 0644]
lib/xpdf/Stream.h [new file with mode: 0644]
lib/xpdf/UTF8.h [new file with mode: 0644]
lib/xpdf/UnicodeMap.cc [new file with mode: 0644]

diff --git a/lib/xpdf/Annot.cc b/lib/xpdf/Annot.cc
new file mode 100644 (file)
index 0000000..68bfb6d
--- /dev/null
@@ -0,0 +1,315 @@
+//========================================================================
+//
+// Annot.cc
+//
+// Copyright 2000-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <stdlib.h>
+#include "gmem.h"
+#include "Object.h"
+#include "Catalog.h"
+#include "Gfx.h"
+#include "Lexer.h"
+#include "Annot.h"
+
+//------------------------------------------------------------------------
+// Annot
+//------------------------------------------------------------------------
+
+Annot::Annot(XRef *xrefA, Dict *acroForm, Dict *dict) {
+  Object apObj, asObj, obj1, obj2;
+  GBool regen, isTextField;
+  double t;
+
+  ok = gFalse;
+  xref = xrefA;
+  appearBuf = NULL;
+
+  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();
+
+  // check if field apperances need to be regenerated
+  regen = gFalse;
+  if (acroForm) {
+    acroForm->lookup("NeedAppearances", &obj1);
+    if (obj1.isBool() && obj1.getBool()) {
+      regen = gTrue;
+    }
+    obj1.free();
+  }
+
+  // check for a text-type field
+  isTextField = dict->lookup("FT", &obj1)->isName("Tx");
+  obj1.free();
+
+#if 0 //~ appearance stream generation is not finished yet
+  if (regen && isTextField) {
+    generateAppearance(acroForm, dict);
+  } else {
+#endif
+    if (dict->lookup("AP", &apObj)->isDict()) {
+      if (dict->lookup("AS", &asObj)->isName()) {
+       if (apObj.dictLookup("N", &obj1)->isDict()) {
+         if (obj1.dictLookupNF(asObj.getName(), &obj2)->isRef()) {
+           obj2.copy(&appearance);
+           ok = gTrue;
+         } else {
+           obj2.free();
+           if (obj1.dictLookupNF("Off", &obj2)->isRef()) {
+             obj2.copy(&appearance);
+             ok = gTrue;
+           }
+         }
+         obj2.free();
+       }
+       obj1.free();
+      } else {
+       if (apObj.dictLookupNF("N", &obj1)->isRef()) {
+         obj1.copy(&appearance);
+         ok = gTrue;
+       }
+       obj1.free();
+      }
+      asObj.free();
+    }
+    apObj.free();
+#if 0 //~ appearance stream generation is not finished yet
+  }
+#endif
+}
+
+Annot::~Annot() {
+  appearance.free();
+  if (appearBuf) {
+    delete appearBuf;
+  }
+}
+
+void Annot::generateAppearance(Dict *acroForm, Dict *dict) {
+  MemStream *appearStream;
+  Object daObj, vObj, drObj, appearDict, obj1, obj2;
+  GString *daStr, *daStr1, *vStr, *s;
+  char buf[256];
+  double fontSize;
+  int c;
+  int i0, i1;
+
+  //~ DA can be inherited
+  if (dict->lookup("DA", &daObj)->isString()) {
+    daStr = daObj.getString();
+
+    // look for a font size
+    //~ may want to parse the DS entry in place of this (if it exists)
+    daStr1 = NULL;
+    fontSize = 10;
+    for (i1 = daStr->getLength() - 2; i1 >= 0; --i1) {
+      if (daStr->getChar(i1) == 'T' && daStr->getChar(i1+1) == 'f') {
+       for (--i1; i1 >= 0 && Lexer::isSpace(daStr->getChar(i1)); --i1) ;
+       for (i0 = i1; i0 >= 0 && !Lexer::isSpace(daStr->getChar(i0)); --i0) ;
+       if (i0 >= 0) {
+         ++i0;
+         ++i1;
+         s = new GString(daStr, i0, i1 - i0);
+         fontSize = atof(s->getCString());
+         delete s;
+
+         // autosize the font
+         if (fontSize == 0) {
+           fontSize = 0.67 * (yMax - yMin);
+           daStr1 = new GString(daStr, 0, i0);
+           sprintf(buf, "%.2f", fontSize);
+           daStr1->append(buf);
+           daStr1->append(daStr->getCString() + i1,
+                          daStr->getLength() - i1);
+         }
+       }
+       break;
+      }
+    }
+
+    // build the appearance stream contents
+    appearBuf = new GString();
+    appearBuf->append("/Tx BMC\n");
+    appearBuf->append("q BT\n");
+    appearBuf->append(daStr1 ? daStr1 : daStr)->append("\n");
+    if (dict->lookup("V", &vObj)->isString()) {
+      //~ handle quadding -- this requires finding the font and using
+      //~   the encoding and char widths
+      sprintf(buf, "1 0 0 1 %.2f %.2f Tm\n", 2.0, yMax - yMin - fontSize);
+      appearBuf->append(buf);
+      sprintf(buf, "%g TL\n", fontSize);
+      appearBuf->append(buf);
+      vStr = vObj.getString();
+      i0 = 0;
+      while (i0 < vStr->getLength()) {
+       for (i1 = i0;
+            i1 < vStr->getLength() &&
+              vStr->getChar(i1) != '\n' && vStr->getChar(i1) != '\r';
+            ++i1) ;
+       if (i0 > 0) {
+         appearBuf->append("T*\n");
+       }
+       appearBuf->append('(');
+       for (; i0 < i1; ++i0) {
+         c = vStr->getChar(i0);
+         if (c == '(' || c == ')' || c == '\\') {
+           appearBuf->append('\\');
+           appearBuf->append(c);
+         } else if (c < 0x20 || c >= 0x80) {
+           sprintf(buf, "\\%03o", c);
+           appearBuf->append(buf);
+         } else {
+           appearBuf->append(c);
+         }
+       }
+       appearBuf->append(") Tj\n");
+       if (i1 + 1 < vStr->getLength() &&
+           vStr->getChar(i1) == '\r' && vStr->getChar(i1 + 1) == '\n') {
+         i0 = i1 + 2;
+       } else {
+         i0 = i1 + 1;
+       }
+      }
+    }
+    vObj.free();
+    appearBuf->append("ET Q\n");
+    appearBuf->append("EMC\n");
+
+    // build the appearance stream dictionary
+    appearDict.initDict(xref);
+    appearDict.dictAdd(copyString("Length"),
+                      obj1.initInt(appearBuf->getLength()));
+    appearDict.dictAdd(copyString("Subtype"), obj1.initName("Form"));
+    obj1.initArray(xref);
+    obj1.arrayAdd(obj2.initReal(0));
+    obj1.arrayAdd(obj2.initReal(0));
+    obj1.arrayAdd(obj2.initReal(xMax - xMin));
+    obj1.arrayAdd(obj2.initReal(yMax - yMin));
+    appearDict.dictAdd(copyString("BBox"), &obj1);
+
+    // find the resource dictionary
+    dict->lookup("DR", &drObj);
+    if (!drObj.isDict()) {
+      dict->lookup("Parent", &obj1);
+      while (obj1.isDict()) {
+       drObj.free();
+       obj1.dictLookup("DR", &drObj);
+       if (drObj.isDict()) {
+         break;
+       }
+       obj1.dictLookup("Parent", &obj2);
+       obj1.free();
+       obj1 = obj2;
+      }
+      obj1.free();
+      if (!drObj.isDict()) {
+       if (acroForm) {
+         drObj.free();
+         acroForm->lookup("DR", &drObj);
+       }
+      }
+    }
+    if (drObj.isDict()) {
+      appearDict.dictAdd(copyString("Resources"), drObj.copy(&obj1));
+    }
+    drObj.free();
+
+    // build the appearance stream
+    appearStream = new MemStream(appearBuf->getCString(), 0,
+                                appearBuf->getLength(), &appearDict);
+    appearance.initStream(appearStream);
+    ok = gTrue;
+
+    if (daStr1) {
+      delete daStr1;
+    }
+  }
+  daObj.free();
+}
+
+void Annot::draw(Gfx *gfx) {
+  Object obj;
+
+  if (appearance.fetch(xref, &obj)->isStream()) {
+    gfx->doAnnot(&obj, xMin, yMin, xMax, yMax);
+  }
+  obj.free();
+}
+
+//------------------------------------------------------------------------
+// Annots
+//------------------------------------------------------------------------
+
+Annots::Annots(XRef *xref, Catalog *catalog, Object *annotsObj) {
+  Dict *acroForm;
+  Annot *annot;
+  Object obj1;
+  int size;
+  int i;
+
+  annots = NULL;
+  size = 0;
+  nAnnots = 0;
+
+  acroForm = catalog->getAcroForm()->isDict() ?
+               catalog->getAcroForm()->getDict() : NULL;
+  if (annotsObj->isArray()) {
+    for (i = 0; i < annotsObj->arrayGetLength(); ++i) {
+      if (annotsObj->arrayGet(i, &obj1)->isDict()) {
+       annot = new Annot(xref, acroForm, obj1.getDict());
+       if (annot->isOk()) {
+         if (nAnnots >= size) {
+           size += 16;
+           annots = (Annot **)greallocn(annots, size, sizeof(Annot *));
+         }
+         annots[nAnnots++] = annot;
+       } else {
+         delete annot;
+       }
+      }
+      obj1.free();
+    }
+  }
+}
+
+Annots::~Annots() {
+  int i;
+
+  for (i = 0; i < nAnnots; ++i) {
+    delete annots[i];
+  }
+  gfree(annots);
+}
diff --git a/lib/xpdf/Annot.h b/lib/xpdf/Annot.h
new file mode 100644 (file)
index 0000000..e5fd841
--- /dev/null
@@ -0,0 +1,73 @@
+//========================================================================
+//
+// Annot.h
+//
+// Copyright 2000-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef ANNOT_H
+#define ANNOT_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+class XRef;
+class Catalog;
+class Gfx;
+
+//------------------------------------------------------------------------
+// Annot
+//------------------------------------------------------------------------
+
+class Annot {
+public:
+
+  Annot(XRef *xrefA, Dict *acroForm, Dict *dict);
+  ~Annot();
+  GBool isOk() { return ok; }
+
+  void draw(Gfx *gfx);
+
+  // Get appearance object.
+  Object *getAppearance(Object *obj) { return appearance.fetch(xref, obj); }
+
+private:
+  void generateAppearance(Dict *acroForm, Dict *dict);
+
+  XRef *xref;                  // the xref table for this PDF file
+  Object appearance;           // a reference to the Form XObject stream
+                               //   for the normal appearance
+  GString *appearBuf;
+  double xMin, yMin,           // annotation rectangle
+         xMax, yMax;
+  GBool ok;
+};
+
+//------------------------------------------------------------------------
+// Annots
+//------------------------------------------------------------------------
+
+class Annots {
+public:
+
+  // Extract non-link annotations from array of annotations.
+  Annots(XRef *xref, Catalog *catalog, Object *annotsObj);
+
+  ~Annots();
+
+  // Iterate through list of annotations.
+  int getNumAnnots() { return nAnnots; }
+  Annot *getAnnot(int i) { return annots[i]; }
+
+private:
+
+  Annot **annots;
+  int nAnnots;
+};
+
+#endif
diff --git a/lib/xpdf/Array.cc b/lib/xpdf/Array.cc
new file mode 100644 (file)
index 0000000..10ded14
--- /dev/null
@@ -0,0 +1,73 @@
+//========================================================================
+//
+// Array.cc
+//
+// Copyright 1996-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <stdlib.h>
+#include <stddef.h>
+#include "gmem.h"
+#include "Object.h"
+#include "Array.h"
+
+//------------------------------------------------------------------------
+// Array
+//------------------------------------------------------------------------
+
+Array::Array(XRef *xrefA) {
+  xref = xrefA;
+  elems = NULL;
+  size = length = 0;
+  ref = 1;
+}
+
+Array::~Array() {
+  int i;
+
+  for (i = 0; i < length; ++i)
+    elems[i].free();
+  gfree(elems);
+}
+
+void Array::add(Object *elem) {
+  if (length == size) {
+    if (length == 0) {
+      size = 8;
+    } else {
+      size *= 2;
+    }
+    elems = (Object *)greallocn(elems, size, sizeof(Object));
+  }
+  elems[length] = *elem;
+  ++length;
+}
+
+Object *Array::get(int i, Object *obj) {
+  if (i < 0 || i >= length) {
+#ifdef DEBUG_MEM
+    abort();
+#else
+    return obj->initNull();
+#endif
+  }
+  return elems[i].fetch(xref, obj);
+}
+
+Object *Array::getNF(int i, Object *obj) {
+  if (i < 0 || i >= length) {
+#ifdef DEBUG_MEM
+    abort();
+#else
+    return obj->initNull();
+#endif
+  }
+  return elems[i].copy(obj);
+}
diff --git a/lib/xpdf/Array.h b/lib/xpdf/Array.h
new file mode 100644 (file)
index 0000000..20ae05f
--- /dev/null
@@ -0,0 +1,58 @@
+//========================================================================
+//
+// Array.h
+//
+// Copyright 1996-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef ARRAY_H
+#define ARRAY_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#include "Object.h"
+
+class XRef;
+
+//------------------------------------------------------------------------
+// Array
+//------------------------------------------------------------------------
+
+class Array {
+public:
+
+  // Constructor.
+  Array(XRef *xrefA);
+
+  // Destructor.
+  ~Array();
+
+  // Reference counting.
+  int incRef() { return ++ref; }
+  int decRef() { return --ref; }
+
+  // Get number of elements.
+  int getLength() { return length; }
+
+  // Add an element.
+  void add(Object *elem);
+
+  // Accessors.
+  Object *get(int i, Object *obj);
+  Object *getNF(int i, Object *obj);
+
+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
+  int ref;                     // reference count
+};
+
+#endif
diff --git a/lib/xpdf/BuiltinFont.cc b/lib/xpdf/BuiltinFont.cc
new file mode 100644 (file)
index 0000000..ce98957
--- /dev/null
@@ -0,0 +1,65 @@
+//========================================================================
+//
+// BuiltinFont.cc
+//
+// Copyright 2001-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+#include "gmem.h"
+#include "FontEncodingTables.h"
+#include "BuiltinFont.h"
+
+//------------------------------------------------------------------------
+
+BuiltinFontWidths::BuiltinFontWidths(BuiltinFontWidth *widths, int sizeA) {
+  int i, h;
+
+  size = sizeA;
+  tab = (BuiltinFontWidth **)gmallocn(size, sizeof(BuiltinFontWidth *));
+  for (i = 0; i < size; ++i) {
+    tab[i] = NULL;
+  }
+  for (i = 0; i < sizeA; ++i) {
+    h = hash(widths[i].name);
+    widths[i].next = tab[h];
+    tab[h] = &widths[i];
+  }
+}
+
+BuiltinFontWidths::~BuiltinFontWidths() {
+  gfree(tab);
+}
+
+GBool BuiltinFontWidths::getWidth(char *name, Gushort *width) {
+  int h;
+  BuiltinFontWidth *p;
+
+  h = hash(name);
+  for (p = tab[h]; p; p = p->next) {
+    if (!strcmp(p->name, name)) {
+      *width = p->width;
+      return gTrue;
+    }
+  }
+  return gFalse;
+}
+
+int BuiltinFontWidths::hash(char *name) {
+  char *p;
+  unsigned int h;
+
+  h = 0;
+  for (p = name; *p; ++p) {
+    h = 17 * h + (int)(*p & 0xff);
+  }
+  return (int)(h % size);
+}
diff --git a/lib/xpdf/BuiltinFont.h b/lib/xpdf/BuiltinFont.h
new file mode 100644 (file)
index 0000000..903ed19
--- /dev/null
@@ -0,0 +1,57 @@
+//========================================================================
+//
+// BuiltinFont.h
+//
+// Copyright 2001-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef BUILTINFONT_H
+#define BUILTINFONT_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#include "gtypes.h"
+
+struct BuiltinFont;
+class BuiltinFontWidths;
+
+//------------------------------------------------------------------------
+
+struct BuiltinFont {
+  char *name;
+  char **defaultBaseEnc;
+  short ascent;
+  short descent;
+  short bbox[4];
+  BuiltinFontWidths *widths;
+};
+
+//------------------------------------------------------------------------
+
+struct BuiltinFontWidth {
+  char *name;
+  Gushort width;
+  BuiltinFontWidth *next;
+};
+
+class BuiltinFontWidths {
+public:
+
+  BuiltinFontWidths(BuiltinFontWidth *widths, int sizeA);
+  ~BuiltinFontWidths();
+  GBool getWidth(char *name, Gushort *width);
+
+private:
+
+  int hash(char *name);
+
+  BuiltinFontWidth **tab;
+  int size;
+};
+
+#endif
diff --git a/lib/xpdf/BuiltinFontTables.cc b/lib/xpdf/BuiltinFontTables.cc
new file mode 100644 (file)
index 0000000..9c36238
--- /dev/null
@@ -0,0 +1,4284 @@
+//========================================================================
+//
+// BuiltinFontTables.cc
+//
+// Copyright 2001-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+#include <stdlib.h>
+#include "FontEncodingTables.h"
+#include "BuiltinFontTables.h"
+
+static BuiltinFontWidth courierWidthsTab[] = {
+  { "Ntilde",                            600, NULL },
+  { "rcaron",                            600, NULL },
+  { "kcommaaccent",                      600, NULL },
+  { "Ncommaaccent",                      600, NULL },
+  { "Zacute",                            600, NULL },
+  { "comma",                             600, NULL },
+  { "cedilla",                           600, NULL },
+  { "plusminus",                         600, NULL },
+  { "circumflex",                        600, NULL },
+  { "dotaccent",                         600, NULL },
+  { "edotaccent",                        600, NULL },
+  { "asciitilde",                        600, NULL },
+  { "colon",                             600, NULL },
+  { "onehalf",                           600, NULL },
+  { "dollar",                            600, NULL },
+  { "Lcaron",                            600, NULL },
+  { "ntilde",                            600, NULL },
+  { "Aogonek",                           600, NULL },
+  { "ncommaaccent",                      600, NULL },
+  { "minus",                             600, NULL },
+  { "Iogonek",                           600, NULL },
+  { "zacute",                            600, NULL },
+  { "yen",                               600, NULL },
+  { "space",                             600, NULL },
+  { "Omacron",                           600, NULL },
+  { "questiondown",                      600, NULL },
+  { "emdash",                            600, NULL },
+  { "Agrave",                            600, NULL },
+  { "three",                             600, NULL },
+  { "numbersign",                        600, NULL },
+  { "lcaron",                            600, NULL },
+  { "A",                                 600, NULL },
+  { "B",                                 600, NULL },
+  { "C",                                 600, NULL },
+  { "aogonek",                           600, NULL },
+  { "D",                                 600, NULL },
+  { "E",                                 600, NULL },
+  { "onequarter",                        600, NULL },
+  { "F",                                 600, NULL },
+  { "G",                                 600, NULL },
+  { "H",                                 600, NULL },
+  { "I",                                 600, NULL },
+  { "J",                                 600, NULL },
+  { "K",                                 600, NULL },
+  { "iogonek",                           600, NULL },
+  { "L",                                 600, NULL },
+  { "backslash",                         600, NULL },
+  { "periodcentered",                    600, NULL },
+  { "M",                                 600, NULL },
+  { "N",                                 600, NULL },
+  { "omacron",                           600, NULL },
+  { "Tcommaaccent",                      600, NULL },
+  { "O",                                 600, NULL },
+  { "P",                                 600, NULL },
+  { "Q",                                 600, NULL },
+  { "Uhungarumlaut",                     600, NULL },
+  { "R",                                 600, NULL },
+  { "Aacute",                            600, NULL },
+  { "caron",                             600, NULL },
+  { "S",                                 600, NULL },
+  { "T",                                 600, NULL },
+  { "U",                                 600, NULL },
+  { "agrave",                            600, NULL },
+  { "V",                                 600, NULL },
+  { "W",                                 600, NULL },
+  { "equal",                             600, NULL },
+  { "question",                          600, NULL },
+  { "X",                                 600, NULL },
+  { "Y",                                 600, NULL },
+  { "Z",                                 600, NULL },
+  { "four",                              600, NULL },
+  { "a",                                 600, NULL },
+  { "Gcommaaccent",                      600, NULL },
+  { "b",                                 600, NULL },
+  { "c",                                 600, NULL },
+  { "d",                                 600, NULL },
+  { "e",                                 600, NULL },
+  { "f",                                 600, NULL },
+  { "g",                                 600, NULL },
+  { "bullet",                            600, NULL },
+  { "h",                                 600, NULL },
+  { "i",                                 600, NULL },
+  { "Oslash",                            600, NULL },
+  { "dagger",                            600, NULL },
+  { "j",                                 600, NULL },
+  { "k",                                 600, NULL },
+  { "l",                                 600, NULL },
+  { "m",                                 600, NULL },
+  { "n",                                 600, NULL },
+  { "tcommaaccent",                      600, NULL },
+  { "o",                                 600, NULL },
+  { "ordfeminine",                       600, NULL },
+  { "ring",                              600, NULL },
+  { "p",                                 600, NULL },
+  { "q",                                 600, NULL },
+  { "uhungarumlaut",                     600, NULL },
+  { "r",                                 600, NULL },
+  { "twosuperior",                       600, NULL },
+  { "aacute",                            600, NULL },
+  { "s",                                 600, NULL },
+  { "OE",                                600, NULL },
+  { "t",                                 600, NULL },
+  { "divide",                            600, NULL },
+  { "u",                                 600, NULL },
+  { "Ccaron",                            600, NULL },
+  { "v",                                 600, NULL },
+  { "w",                                 600, NULL },
+  { "x",                                 600, NULL },
+  { "y",                                 600, NULL },
+  { "z",                                 600, NULL },
+  { "Gbreve",                            600, NULL },
+  { "commaaccent",                       600, NULL },
+  { "hungarumlaut",                      600, NULL },
+  { "Idotaccent",                        600, NULL },
+  { "Nacute",                            600, NULL },
+  { "quotedbl",                          600, NULL },
+  { "gcommaaccent",                      600, NULL },
+  { "mu",                                600, NULL },
+  { "greaterequal",                      600, NULL },
+  { "Scaron",                            600, NULL },
+  { "Lslash",                            600, NULL },
+  { "semicolon",                         600, NULL },
+  { "oslash",                            600, NULL },
+  { "lessequal",                         600, NULL },
+  { "lozenge",                           600, NULL },
+  { "parenright",                        600, NULL },
+  { "ccaron",                            600, NULL },
+  { "Ecircumflex",                       600, NULL },
+  { "gbreve",                            600, NULL },
+  { "trademark",                         600, NULL },
+  { "daggerdbl",                         600, NULL },
+  { "nacute",                            600, NULL },
+  { "macron",                            600, NULL },
+  { "Otilde",                            600, NULL },
+  { "Emacron",                           600, NULL },
+  { "ellipsis",                          600, NULL },
+  { "scaron",                            600, NULL },
+  { "AE",                                600, NULL },
+  { "Ucircumflex",                       600, NULL },
+  { "lslash",                            600, NULL },
+  { "quotedblleft",                      600, NULL },
+  { "hyphen",                            600, NULL },
+  { "guilsinglright",                    600, NULL },
+  { "quotesingle",                       600, NULL },
+  { "eight",                             600, NULL },
+  { "exclamdown",                        600, NULL },
+  { "endash",                            600, NULL },
+  { "oe",                                600, NULL },
+  { "Abreve",                            600, NULL },
+  { "Umacron",                           600, NULL },
+  { "ecircumflex",                       600, NULL },
+  { "Adieresis",                         600, NULL },
+  { "copyright",                         600, NULL },
+  { "Egrave",                            600, NULL },
+  { "slash",                             600, NULL },
+  { "Edieresis",                         600, NULL },
+  { "otilde",                            600, NULL },
+  { "Idieresis",                         600, NULL },
+  { "parenleft",                         600, NULL },
+  { "one",                               600, NULL },
+  { "emacron",                           600, NULL },
+  { "Odieresis",                         600, NULL },
+  { "ucircumflex",                       600, NULL },
+  { "bracketleft",                       600, NULL },
+  { "Ugrave",                            600, NULL },
+  { "quoteright",                        600, NULL },
+  { "Udieresis",                         600, NULL },
+  { "perthousand",                       600, NULL },
+  { "Ydieresis",                         600, NULL },
+  { "umacron",                           600, NULL },
+  { "abreve",                            600, NULL },
+  { "Eacute",                            600, NULL },
+  { "adieresis",                         600, NULL },
+  { "egrave",                            600, NULL },
+  { "edieresis",                         600, NULL },
+  { "idieresis",                         600, NULL },
+  { "Eth",                               600, NULL },
+  { "ae",                                600, NULL },
+  { "asterisk",                          600, NULL },
+  { "odieresis",                         600, NULL },
+  { "Uacute",                            600, NULL },
+  { "ugrave",                            600, NULL },
+  { "five",                              600, NULL },
+  { "nine",                              600, NULL },
+  { "udieresis",                         600, NULL },
+  { "Zcaron",                            600, NULL },
+  { "Scommaaccent",                      600, NULL },
+  { "threequarters",                     600, NULL },
+  { "guillemotright",                    600, NULL },
+  { "Ccedilla",                          600, NULL },
+  { "ydieresis",                         600, NULL },
+  { "tilde",                             600, NULL },
+  { "at",                                600, NULL },
+  { "eacute",                            600, NULL },
+  { "underscore",                        600, NULL },
+  { "Euro",                              600, NULL },
+  { "Dcroat",                            600, NULL },
+  { "zero",                              600, NULL },
+  { "multiply",                          600, NULL },
+  { "eth",                               600, NULL },
+  { "Scedilla",                          600, NULL },
+  { "Racute",                            600, NULL },
+  { "Ograve",                            600, NULL },
+  { "partialdiff",                       600, NULL },
+  { "uacute",                            600, NULL },
+  { "braceleft",                         600, NULL },
+  { "Thorn",                             600, NULL },
+  { "zcaron",                            600, NULL },
+  { "scommaaccent",                      600, NULL },
+  { "ccedilla",                          600, NULL },
+  { "Dcaron",                            600, NULL },
+  { "dcroat",                            600, NULL },
+  { "scedilla",                          600, NULL },
+  { "Oacute",                            600, NULL },
+  { "Ocircumflex",                       600, NULL },
+  { "ogonek",                            600, NULL },
+  { "ograve",                            600, NULL },
+  { "racute",                            600, NULL },
+  { "Tcaron",                            600, NULL },
+  { "Eogonek",                           600, NULL },
+  { "thorn",                             600, NULL },
+  { "degree",                            600, NULL },
+  { "registered",                        600, NULL },
+  { "radical",                           600, NULL },
+  { "Aring",                             600, NULL },
+  { "percent",                           600, NULL },
+  { "six",                               600, NULL },
+  { "paragraph",                         600, NULL },
+  { "dcaron",                            600, NULL },
+  { "Uogonek",                           600, NULL },
+  { "two",                               600, NULL },
+  { "summation",                         600, NULL },
+  { "Igrave",                            600, NULL },
+  { "Lacute",                            600, NULL },
+  { "ocircumflex",                       600, NULL },
+  { "oacute",                            600, NULL },
+  { "Uring",                             600, NULL },
+  { "Lcommaaccent",                      600, NULL },
+  { "tcaron",                            600, NULL },
+  { "eogonek",                           600, NULL },
+  { "Delta",                             600, NULL },
+  { "Ohungarumlaut",                     600, NULL },
+  { "asciicircum",                       600, NULL },
+  { "aring",                             600, NULL },
+  { "grave",                             600, NULL },
+  { "uogonek",                           600, NULL },
+  { "bracketright",                      600, NULL },
+  { "ampersand",                         600, NULL },
+  { "Iacute",                            600, NULL },
+  { "lacute",                            600, NULL },
+  { "igrave",                            600, NULL },
+  { "Ncaron",                            600, NULL },
+  { "plus",                              600, NULL },
+  { "uring",                             600, NULL },
+  { "quotesinglbase",                    600, NULL },
+  { "lcommaaccent",                      600, NULL },
+  { "Yacute",                            600, NULL },
+  { "ohungarumlaut",                     600, NULL },
+  { "threesuperior",                     600, NULL },
+  { "acute",                             600, NULL },
+  { "section",                           600, NULL },
+  { "dieresis",                          600, NULL },
+  { "quotedblbase",                      600, NULL },
+  { "iacute",                            600, NULL },
+  { "ncaron",                            600, NULL },
+  { "florin",                            600, NULL },
+  { "yacute",                            600, NULL },
+  { "Rcommaaccent",                      600, NULL },
+  { "fi",                                600, NULL },
+  { "fl",                                600, NULL },
+  { "Acircumflex",                       600, NULL },
+  { "Cacute",                            600, NULL },
+  { "Icircumflex",                       600, NULL },
+  { "guillemotleft",                     600, NULL },
+  { "germandbls",                        600, NULL },
+  { "seven",                             600, NULL },
+  { "Amacron",                           600, NULL },
+  { "Sacute",                            600, NULL },
+  { "ordmasculine",                      600, NULL },
+  { "dotlessi",                          600, NULL },
+  { "sterling",                          600, NULL },
+  { "notequal",                          600, NULL },
+  { "Imacron",                           600, NULL },
+  { "rcommaaccent",                      600, NULL },
+  { "Zdotaccent",                        600, NULL },
+  { "acircumflex",                       600, NULL },
+  { "cacute",                            600, NULL },
+  { "Ecaron",                            600, NULL },
+  { "braceright",                        600, NULL },
+  { "icircumflex",                       600, NULL },
+  { "quotedblright",                     600, NULL },
+  { "amacron",                           600, NULL },
+  { "sacute",                            600, NULL },
+  { "imacron",                           600, NULL },
+  { "cent",                              600, NULL },
+  { "currency",                          600, NULL },
+  { "logicalnot",                        600, NULL },
+  { "zdotaccent",                        600, NULL },
+  { "Atilde",                            600, NULL },
+  { "breve",                             600, NULL },
+  { "bar",                               600, NULL },
+  { "fraction",                          600, NULL },
+  { "less",                              600, NULL },
+  { "ecaron",                            600, NULL },
+  { "guilsinglleft",                     600, NULL },
+  { "exclam",                            600, NULL },
+  { "period",                            600, NULL },
+  { "Rcaron",                            600, NULL },
+  { "Kcommaaccent",                      600, NULL },
+  { "greater",                           600, NULL },
+  { "atilde",                            600, NULL },
+  { "brokenbar",                         600, NULL },
+  { "quoteleft",                         600, NULL },
+  { "Edotaccent",                        600, NULL },
+  { "onesuperior",                       600, NULL }
+};
+
+static BuiltinFontWidth courierBoldWidthsTab[] = {
+  { "Ntilde",                            600, NULL },
+  { "rcaron",                            600, NULL },
+  { "kcommaaccent",                      600, NULL },
+  { "Ncommaaccent",                      600, NULL },
+  { "Zacute",                            600, NULL },
+  { "comma",                             600, NULL },
+  { "cedilla",                           600, NULL },
+  { "plusminus",                         600, NULL },
+  { "circumflex",                        600, NULL },
+  { "dotaccent",                         600, NULL },
+  { "edotaccent",                        600, NULL },
+  { "asciitilde",                        600, NULL },
+  { "colon",                             600, NULL },
+  { "onehalf",                           600, NULL },
+  { "dollar",                            600, NULL },
+  { "Lcaron",                            600, NULL },
+  { "ntilde",                            600, NULL },
+  { "Aogonek",                           600, NULL },
+  { "ncommaaccent",                      600, NULL },
+  { "minus",                             600, NULL },
+  { "Iogonek",                           600, NULL },
+  { "zacute",                            600, NULL },
+  { "yen",                               600, NULL },
+  { "space",                             600, NULL },
+  { "Omacron",                           600, NULL },
+  { "questiondown",                      600, NULL },
+  { "emdash",                            600, NULL },
+  { "Agrave",                            600, NULL },
+  { "three",                             600, NULL },
+  { "numbersign",                        600, NULL },
+  { "lcaron",                            600, NULL },
+  { "A",                                 600, NULL },
+  { "B",                                 600, NULL },
+  { "C",                                 600, NULL },
+  { "aogonek",                           600, NULL },
+  { "D",                                 600, NULL },
+  { "E",                                 600, NULL },
+  { "onequarter",                        600, NULL },
+  { "F",                                 600, NULL },
+  { "G",                                 600, NULL },
+  { "H",                                 600, NULL },
+  { "I",                                 600, NULL },
+  { "J",                                 600, NULL },
+  { "K",                                 600, NULL },
+  { "iogonek",                           600, NULL },
+  { "backslash",                         600, NULL },
+  { "L",                                 600, NULL },
+  { "periodcentered",                    600, NULL },
+  { "M",                                 600, NULL },
+  { "N",                                 600, NULL },
+  { "omacron",                           600, NULL },
+  { "Tcommaaccent",                      600, NULL },
+  { "O",                                 600, NULL },
+  { "P",                                 600, NULL },
+  { "Q",                                 600, NULL },
+  { "Uhungarumlaut",                     600, NULL },
+  { "R",                                 600, NULL },
+  { "Aacute",                            600, NULL },
+  { "caron",                             600, NULL },
+  { "S",                                 600, NULL },
+  { "T",                                 600, NULL },
+  { "U",                                 600, NULL },
+  { "agrave",                            600, NULL },
+  { "V",                                 600, NULL },
+  { "W",                                 600, NULL },
+  { "X",                                 600, NULL },
+  { "question",                          600, NULL },
+  { "equal",                             600, NULL },
+  { "Y",                                 600, NULL },
+  { "Z",                                 600, NULL },
+  { "four",                              600, NULL },
+  { "a",                                 600, NULL },
+  { "Gcommaaccent",                      600, NULL },
+  { "b",                                 600, NULL },
+  { "c",                                 600, NULL },
+  { "d",                                 600, NULL },
+  { "e",                                 600, NULL },
+  { "f",                                 600, NULL },
+  { "g",                                 600, NULL },
+  { "bullet",                            600, NULL },
+  { "h",                                 600, NULL },
+  { "i",                                 600, NULL },
+  { "Oslash",                            600, NULL },
+  { "dagger",                            600, NULL },
+  { "j",                                 600, NULL },
+  { "k",                                 600, NULL },
+  { "l",                                 600, NULL },
+  { "m",                                 600, NULL },
+  { "n",                                 600, NULL },
+  { "tcommaaccent",                      600, NULL },
+  { "o",                                 600, NULL },
+  { "ordfeminine",                       600, NULL },
+  { "ring",                              600, NULL },
+  { "p",                                 600, NULL },
+  { "q",                                 600, NULL },
+  { "uhungarumlaut",                     600, NULL },
+  { "r",                                 600, NULL },
+  { "twosuperior",                       600, NULL },
+  { "aacute",                            600, NULL },
+  { "s",                                 600, NULL },
+  { "OE",                                600, NULL },
+  { "t",                                 600, NULL },
+  { "divide",                            600, NULL },
+  { "u",                                 600, NULL },
+  { "Ccaron",                            600, NULL },
+  { "v",                                 600, NULL },
+  { "w",                                 600, NULL },
+  { "x",                                 600, NULL },
+  { "y",                                 600, NULL },
+  { "z",                                 600, NULL },
+  { "Gbreve",                            600, NULL },
+  { "commaaccent",                       600, NULL },
+  { "hungarumlaut",                      600, NULL },
+  { "Idotaccent",                        600, NULL },
+  { "Nacute",                            600, NULL },
+  { "quotedbl",                          600, NULL },
+  { "gcommaaccent",                      600, NULL },
+  { "mu",                                600, NULL },
+  { "greaterequal",                      600, NULL },
+  { "Scaron",                            600, NULL },
+  { "Lslash",                            600, NULL },
+  { "semicolon",                         600, NULL },
+  { "oslash",                            600, NULL },
+  { "lessequal",                         600, NULL },
+  { "lozenge",                           600, NULL },
+  { "parenright",                        600, NULL },
+  { "ccaron",                            600, NULL },
+  { "Ecircumflex",                       600, NULL },
+  { "gbreve",                            600, NULL },
+  { "trademark",                         600, NULL },
+  { "daggerdbl",                         600, NULL },
+  { "nacute",                            600, NULL },
+  { "macron",                            600, NULL },
+  { "Otilde",                            600, NULL },
+  { "Emacron",                           600, NULL },
+  { "ellipsis",                          600, NULL },
+  { "scaron",                            600, NULL },
+  { "AE",                                600, NULL },
+  { "Ucircumflex",                       600, NULL },
+  { "lslash",                            600, NULL },
+  { "quotedblleft",                      600, NULL },
+  { "guilsinglright",                    600, NULL },
+  { "hyphen",                            600, NULL },
+  { "quotesingle",                       600, NULL },
+  { "eight",                             600, NULL },
+  { "exclamdown",                        600, NULL },
+  { "endash",                            600, NULL },
+  { "oe",                                600, NULL },
+  { "Abreve",                            600, NULL },
+  { "Umacron",                           600, NULL },
+  { "ecircumflex",                       600, NULL },
+  { "Adieresis",                         600, NULL },
+  { "copyright",                         600, NULL },
+  { "Egrave",                            600, NULL },
+  { "slash",                             600, NULL },
+  { "Edieresis",                         600, NULL },
+  { "otilde",                            600, NULL },
+  { "Idieresis",                         600, NULL },
+  { "parenleft",                         600, NULL },
+  { "one",                               600, NULL },
+  { "emacron",                           600, NULL },
+  { "Odieresis",                         600, NULL },
+  { "ucircumflex",                       600, NULL },
+  { "bracketleft",                       600, NULL },
+  { "Ugrave",                            600, NULL },
+  { "quoteright",                        600, NULL },
+  { "Udieresis",                         600, NULL },
+  { "perthousand",                       600, NULL },
+  { "Ydieresis",                         600, NULL },
+  { "umacron",                           600, NULL },
+  { "abreve",                            600, NULL },
+  { "Eacute",                            600, NULL },
+  { "adieresis",                         600, NULL },
+  { "egrave",                            600, NULL },
+  { "edieresis",                         600, NULL },
+  { "idieresis",                         600, NULL },
+  { "Eth",                               600, NULL },
+  { "ae",                                600, NULL },
+  { "asterisk",                          600, NULL },
+  { "odieresis",                         600, NULL },
+  { "Uacute",                            600, NULL },
+  { "ugrave",                            600, NULL },
+  { "nine",                              600, NULL },
+  { "five",                              600, NULL },
+  { "udieresis",                         600, NULL },
+  { "Zcaron",                            600, NULL },
+  { "Scommaaccent",                      600, NULL },
+  { "threequarters",                     600, NULL },
+  { "guillemotright",                    600, NULL },
+  { "Ccedilla",                          600, NULL },
+  { "ydieresis",                         600, NULL },
+  { "tilde",                             600, NULL },
+  { "at",                                600, NULL },
+  { "eacute",                            600, NULL },
+  { "underscore",                        600, NULL },
+  { "Euro",                              600, NULL },
+  { "Dcroat",                            600, NULL },
+  { "multiply",                          600, NULL },
+  { "zero",                              600, NULL },
+  { "eth",                               600, NULL },
+  { "Scedilla",                          600, NULL },
+  { "Ograve",                            600, NULL },
+  { "Racute",                            600, NULL },
+  { "partialdiff",                       600, NULL },
+  { "uacute",                            600, NULL },
+  { "braceleft",                         600, NULL },
+  { "Thorn",                             600, NULL },
+  { "zcaron",                            600, NULL },
+  { "scommaaccent",                      600, NULL },
+  { "ccedilla",                          600, NULL },
+  { "Dcaron",                            600, NULL },
+  { "dcroat",                            600, NULL },
+  { "Ocircumflex",                       600, NULL },
+  { "Oacute",                            600, NULL },
+  { "scedilla",                          600, NULL },
+  { "ogonek",                            600, NULL },
+  { "ograve",                            600, NULL },
+  { "racute",                            600, NULL },
+  { "Tcaron",                            600, NULL },
+  { "Eogonek",                           600, NULL },
+  { "thorn",                             600, NULL },
+  { "degree",                            600, NULL },
+  { "registered",                        600, NULL },
+  { "radical",                           600, NULL },
+  { "Aring",                             600, NULL },
+  { "percent",                           600, NULL },
+  { "six",                               600, NULL },
+  { "paragraph",                         600, NULL },
+  { "dcaron",                            600, NULL },
+  { "Uogonek",                           600, NULL },
+  { "two",                               600, NULL },
+  { "summation",                         600, NULL },
+  { "Igrave",                            600, NULL },
+  { "Lacute",                            600, NULL },
+  { "ocircumflex",                       600, NULL },
+  { "oacute",                            600, NULL },
+  { "Uring",                             600, NULL },
+  { "Lcommaaccent",                      600, NULL },
+  { "tcaron",                            600, NULL },
+  { "eogonek",                           600, NULL },
+  { "Delta",                             600, NULL },
+  { "Ohungarumlaut",                     600, NULL },
+  { "asciicircum",                       600, NULL },
+  { "aring",                             600, NULL },
+  { "grave",                             600, NULL },
+  { "uogonek",                           600, NULL },
+  { "bracketright",                      600, NULL },
+  { "Iacute",                            600, NULL },
+  { "ampersand",                         600, NULL },
+  { "igrave",                            600, NULL },
+  { "lacute",                            600, NULL },
+  { "Ncaron",                            600, NULL },
+  { "plus",                              600, NULL },
+  { "uring",                             600, NULL },
+  { "quotesinglbase",                    600, NULL },
+  { "lcommaaccent",                      600, NULL },
+  { "Yacute",                            600, NULL },
+  { "ohungarumlaut",                     600, NULL },
+  { "threesuperior",                     600, NULL },
+  { "acute",                             600, NULL },
+  { "section",                           600, NULL },
+  { "dieresis",                          600, NULL },
+  { "iacute",                            600, NULL },
+  { "quotedblbase",                      600, NULL },
+  { "ncaron",                            600, NULL },
+  { "florin",                            600, NULL },
+  { "yacute",                            600, NULL },
+  { "Rcommaaccent",                      600, NULL },
+  { "fi",                                600, NULL },
+  { "fl",                                600, NULL },
+  { "Acircumflex",                       600, NULL },
+  { "Cacute",                            600, NULL },
+  { "Icircumflex",                       600, NULL },
+  { "guillemotleft",                     600, NULL },
+  { "germandbls",                        600, NULL },
+  { "Amacron",                           600, NULL },
+  { "seven",                             600, NULL },
+  { "Sacute",                            600, NULL },
+  { "ordmasculine",                      600, NULL },
+  { "dotlessi",                          600, NULL },
+  { "sterling",                          600, NULL },
+  { "notequal",                          600, NULL },
+  { "Imacron",                           600, NULL },
+  { "rcommaaccent",                      600, NULL },
+  { "Zdotaccent",                        600, NULL },
+  { "acircumflex",                       600, NULL },
+  { "cacute",                            600, NULL },
+  { "Ecaron",                            600, NULL },
+  { "icircumflex",                       600, NULL },
+  { "braceright",                        600, NULL },
+  { "quotedblright",                     600, NULL },
+  { "amacron",                           600, NULL },
+  { "sacute",                            600, NULL },
+  { "imacron",                           600, NULL },
+  { "cent",                              600, NULL },
+  { "currency",                          600, NULL },
+  { "logicalnot",                        600, NULL },
+  { "zdotaccent",                        600, NULL },
+  { "Atilde",                            600, NULL },
+  { "breve",                             600, NULL },
+  { "bar",                               600, NULL },
+  { "fraction",                          600, NULL },
+  { "less",                              600, NULL },
+  { "ecaron",                            600, NULL },
+  { "guilsinglleft",                     600, NULL },
+  { "exclam",                            600, NULL },
+  { "period",                            600, NULL },
+  { "Rcaron",                            600, NULL },
+  { "Kcommaaccent",                      600, NULL },
+  { "greater",                           600, NULL },
+  { "atilde",                            600, NULL },
+  { "brokenbar",                         600, NULL },
+  { "quoteleft",                         600, NULL },
+  { "Edotaccent",                        600, NULL },
+  { "onesuperior",                       600, NULL }
+};
+
+static BuiltinFontWidth courierBoldObliqueWidthsTab[] = {
+  { "Ntilde",                            600, NULL },
+  { "rcaron",                            600, NULL },
+  { "kcommaaccent",                      600, NULL },
+  { "Ncommaaccent",                      600, NULL },
+  { "Zacute",                            600, NULL },
+  { "comma",                             600, NULL },
+  { "cedilla",                           600, NULL },
+  { "plusminus",                         600, NULL },
+  { "circumflex",                        600, NULL },
+  { "dotaccent",                         600, NULL },
+  { "edotaccent",                        600, NULL },
+  { "asciitilde",                        600, NULL },
+  { "colon",                             600, NULL },
+  { "onehalf",                           600, NULL },
+  { "dollar",                            600, NULL },
+  { "Lcaron",                            600, NULL },
+  { "ntilde",                            600, NULL },
+  { "Aogonek",                           600, NULL },
+  { "ncommaaccent",                      600, NULL },
+  { "minus",                             600, NULL },
+  { "Iogonek",                           600, NULL },
+  { "zacute",                            600, NULL },
+  { "yen",                               600, NULL },
+  { "space",                             600, NULL },
+  { "Omacron",                           600, NULL },
+  { "questiondown",                      600, NULL },
+  { "emdash",                            600, NULL },
+  { "Agrave",                            600, NULL },
+  { "three",                             600, NULL },
+  { "numbersign",                        600, NULL },
+  { "lcaron",                            600, NULL },
+  { "A",                                 600, NULL },
+  { "B",                                 600, NULL },
+  { "C",                                 600, NULL },
+  { "aogonek",                           600, NULL },
+  { "D",                                 600, NULL },
+  { "E",                                 600, NULL },
+  { "onequarter",                        600, NULL },
+  { "F",                                 600, NULL },
+  { "G",                                 600, NULL },
+  { "H",                                 600, NULL },
+  { "I",                                 600, NULL },
+  { "J",                                 600, NULL },
+  { "K",                                 600, NULL },
+  { "iogonek",                           600, NULL },
+  { "backslash",                         600, NULL },
+  { "L",                                 600, NULL },
+  { "periodcentered",                    600, NULL },
+  { "M",                                 600, NULL },
+  { "N",                                 600, NULL },
+  { "omacron",                           600, NULL },
+  { "Tcommaaccent",                      600, NULL },
+  { "O",                                 600, NULL },
+  { "P",                                 600, NULL },
+  { "Q",                                 600, NULL },
+  { "Uhungarumlaut",                     600, NULL },
+  { "R",                                 600, NULL },
+  { "Aacute",                            600, NULL },
+  { "caron",                             600, NULL },
+  { "S",                                 600, NULL },
+  { "T",                                 600, NULL },
+  { "U",                                 600, NULL },
+  { "agrave",                            600, NULL },
+  { "V",                                 600, NULL },
+  { "W",                                 600, NULL },
+  { "X",                                 600, NULL },
+  { "question",                          600, NULL },
+  { "equal",                             600, NULL },
+  { "Y",                                 600, NULL },
+  { "Z",                                 600, NULL },
+  { "four",                              600, NULL },
+  { "a",                                 600, NULL },
+  { "Gcommaaccent",                      600, NULL },
+  { "b",                                 600, NULL },
+  { "c",                                 600, NULL },
+  { "d",                                 600, NULL },
+  { "e",                                 600, NULL },
+  { "f",                                 600, NULL },
+  { "g",                                 600, NULL },
+  { "bullet",                            600, NULL },
+  { "h",                                 600, NULL },
+  { "i",                                 600, NULL },
+  { "Oslash",                            600, NULL },
+  { "dagger",                            600, NULL },
+  { "j",                                 600, NULL },
+  { "k",                                 600, NULL },
+  { "l",                                 600, NULL },
+  { "m",                                 600, NULL },
+  { "n",                                 600, NULL },
+  { "tcommaaccent",                      600, NULL },
+  { "o",                                 600, NULL },
+  { "ordfeminine",                       600, NULL },
+  { "ring",                              600, NULL },
+  { "p",                                 600, NULL },
+  { "q",                                 600, NULL },
+  { "uhungarumlaut",                     600, NULL },
+  { "r",                                 600, NULL },
+  { "twosuperior",                       600, NULL },
+  { "aacute",                            600, NULL },
+  { "s",                                 600, NULL },
+  { "OE",                                600, NULL },
+  { "t",                                 600, NULL },
+  { "divide",                            600, NULL },
+  { "u",                                 600, NULL },
+  { "Ccaron",                            600, NULL },
+  { "v",                                 600, NULL },
+  { "w",                                 600, NULL },
+  { "x",                                 600, NULL },
+  { "y",                                 600, NULL },
+  { "z",                                 600, NULL },
+  { "Gbreve",                            600, NULL },
+  { "commaaccent",                       600, NULL },
+  { "hungarumlaut",                      600, NULL },
+  { "Idotaccent",                        600, NULL },
+  { "Nacute",                            600, NULL },
+  { "quotedbl",                          600, NULL },
+  { "gcommaaccent",                      600, NULL },
+  { "mu",                                600, NULL },
+  { "greaterequal",                      600, NULL },
+  { "Scaron",                            600, NULL },
+  { "Lslash",                            600, NULL },
+  { "semicolon",                         600, NULL },
+  { "oslash",                            600, NULL },
+  { "lessequal",                         600, NULL },
+  { "lozenge",                           600, NULL },
+  { "parenright",                        600, NULL },
+  { "ccaron",                            600, NULL },
+  { "Ecircumflex",                       600, NULL },
+  { "gbreve",                            600, NULL },
+  { "trademark",                         600, NULL },
+  { "daggerdbl",                         600, NULL },
+  { "nacute",                            600, NULL },
+  { "macron",                            600, NULL },
+  { "Otilde",                            600, NULL },
+  { "Emacron",                           600, NULL },
+  { "ellipsis",                          600, NULL },
+  { "scaron",                            600, NULL },
+  { "AE",                                600, NULL },
+  { "Ucircumflex",                       600, NULL },
+  { "lslash",                            600, NULL },
+  { "quotedblleft",                      600, NULL },
+  { "guilsinglright",                    600, NULL },
+  { "hyphen",                            600, NULL },
+  { "quotesingle",                       600, NULL },
+  { "eight",                             600, NULL },
+  { "exclamdown",                        600, NULL },
+  { "endash",                            600, NULL },
+  { "oe",                                600, NULL },
+  { "Abreve",                            600, NULL },
+  { "Umacron",                           600, NULL },
+  { "ecircumflex",                       600, NULL },
+  { "Adieresis",                         600, NULL },
+  { "copyright",                         600, NULL },
+  { "Egrave",                            600, NULL },
+  { "slash",                             600, NULL },
+  { "Edieresis",                         600, NULL },
+  { "otilde",                            600, NULL },
+  { "Idieresis",                         600, NULL },
+  { "parenleft",                         600, NULL },
+  { "one",                               600, NULL },
+  { "emacron",                           600, NULL },
+  { "Odieresis",                         600, NULL },
+  { "ucircumflex",                       600, NULL },
+  { "bracketleft",                       600, NULL },
+  { "Ugrave",                            600, NULL },
+  { "quoteright",                        600, NULL },
+  { "Udieresis",                         600, NULL },
+  { "perthousand",                       600, NULL },
+  { "Ydieresis",                         600, NULL },
+  { "umacron",                           600, NULL },
+  { "abreve",                            600, NULL },
+  { "Eacute",                            600, NULL },
+  { "adieresis",                         600, NULL },
+  { "egrave",                            600, NULL },
+  { "edieresis",                         600, NULL },
+  { "idieresis",                         600, NULL },
+  { "Eth",                               600, NULL },
+  { "ae",                                600, NULL },
+  { "asterisk",                          600, NULL },
+  { "odieresis",                         600, NULL },
+  { "Uacute",                            600, NULL },
+  { "ugrave",                            600, NULL },
+  { "nine",                              600, NULL },
+  { "five",                              600, NULL },
+  { "udieresis",                         600, NULL },
+  { "Zcaron",                            600, NULL },
+  { "Scommaaccent",                      600, NULL },
+  { "threequarters",                     600, NULL },
+  { "guillemotright",                    600, NULL },
+  { "Ccedilla",                          600, NULL },
+  { "ydieresis",                         600, NULL },
+  { "tilde",                             600, NULL },
+  { "at",                                600, NULL },
+  { "eacute",                            600, NULL },
+  { "underscore",                        600, NULL },
+  { "Euro",                              600, NULL },
+  { "Dcroat",                            600, NULL },
+  { "multiply",                          600, NULL },
+  { "zero",                              600, NULL },
+  { "eth",                               600, NULL },
+  { "Scedilla",                          600, NULL },
+  { "Ograve",                            600, NULL },
+  { "Racute",                            600, NULL },
+  { "partialdiff",                       600, NULL },
+  { "uacute",                            600, NULL },
+  { "braceleft",                         600, NULL },
+  { "Thorn",                             600, NULL },
+  { "zcaron",                            600, NULL },
+  { "scommaaccent",                      600, NULL },
+  { "ccedilla",                          600, NULL },
+  { "Dcaron",                            600, NULL },
+  { "dcroat",                            600, NULL },
+  { "Ocircumflex",                       600, NULL },
+  { "Oacute",                            600, NULL },
+  { "scedilla",                          600, NULL },
+  { "ogonek",                            600, NULL },
+  { "ograve",                            600, NULL },
+  { "racute",                            600, NULL },
+  { "Tcaron",                            600, NULL },
+  { "Eogonek",                           600, NULL },
+  { "thorn",                             600, NULL },
+  { "degree",                            600, NULL },
+  { "registered",                        600, NULL },
+  { "radical",                           600, NULL },
+  { "Aring",                             600, NULL },
+  { "percent",                           600, NULL },
+  { "six",                               600, NULL },
+  { "paragraph",                         600, NULL },
+  { "dcaron",                            600, NULL },
+  { "Uogonek",                           600, NULL },
+  { "two",                               600, NULL },
+  { "summation",                         600, NULL },
+  { "Igrave",                            600, NULL },
+  { "Lacute",                            600, NULL },
+  { "ocircumflex",                       600, NULL },
+  { "oacute",                            600, NULL },
+  { "Uring",                             600, NULL },
+  { "Lcommaaccent",                      600, NULL },
+  { "tcaron",                            600, NULL },
+  { "eogonek",                           600, NULL },
+  { "Delta",                             600, NULL },
+  { "Ohungarumlaut",                     600, NULL },
+  { "asciicircum",                       600, NULL },
+  { "aring",                             600, NULL },
+  { "grave",                             600, NULL },
+  { "uogonek",                           600, NULL },
+  { "bracketright",                      600, NULL },
+  { "Iacute",                            600, NULL },
+  { "ampersand",                         600, NULL },
+  { "igrave",                            600, NULL },
+  { "lacute",                            600, NULL },
+  { "Ncaron",                            600, NULL },
+  { "plus",                              600, NULL },
+  { "uring",                             600, NULL },
+  { "quotesinglbase",                    600, NULL },
+  { "lcommaaccent",                      600, NULL },
+  { "Yacute",                            600, NULL },
+  { "ohungarumlaut",                     600, NULL },
+  { "threesuperior",                     600, NULL },
+  { "acute",                             600, NULL },
+  { "section",                           600, NULL },
+  { "dieresis",                          600, NULL },
+  { "iacute",                            600, NULL },
+  { "quotedblbase",                      600, NULL },
+  { "ncaron",                            600, NULL },
+  { "florin",                            600, NULL },
+  { "yacute",                            600, NULL },
+  { "Rcommaaccent",                      600, NULL },
+  { "fi",                                600, NULL },
+  { "fl",                                600, NULL },
+  { "Acircumflex",                       600, NULL },
+  { "Cacute",                            600, NULL },
+  { "Icircumflex",                       600, NULL },
+  { "guillemotleft",                     600, NULL },
+  { "germandbls",                        600, NULL },
+  { "Amacron",                           600, NULL },
+  { "seven",                             600, NULL },
+  { "Sacute",                            600, NULL },
+  { "ordmasculine",                      600, NULL },
+  { "dotlessi",                          600, NULL },
+  { "sterling",                          600, NULL },
+  { "notequal",                          600, NULL },
+  { "Imacron",                           600, NULL },
+  { "rcommaaccent",                      600, NULL },
+  { "Zdotaccent",                        600, NULL },
+  { "acircumflex",                       600, NULL },
+  { "cacute",                            600, NULL },
+  { "Ecaron",                            600, NULL },
+  { "icircumflex",                       600, NULL },
+  { "braceright",                        600, NULL },
+  { "quotedblright",                     600, NULL },
+  { "amacron",                           600, NULL },
+  { "sacute",                            600, NULL },
+  { "imacron",                           600, NULL },
+  { "cent",                              600, NULL },
+  { "currency",                          600, NULL },
+  { "logicalnot",                        600, NULL },
+  { "zdotaccent",                        600, NULL },
+  { "Atilde",                            600, NULL },
+  { "breve",                             600, NULL },
+  { "bar",                               600, NULL },
+  { "fraction",                          600, NULL },
+  { "less",                              600, NULL },
+  { "ecaron",                            600, NULL },
+  { "guilsinglleft",                     600, NULL },
+  { "exclam",                            600, NULL },
+  { "period",                            600, NULL },
+  { "Rcaron",                            600, NULL },
+  { "Kcommaaccent",                      600, NULL },
+  { "greater",                           600, NULL },
+  { "atilde",                            600, NULL },
+  { "brokenbar",                         600, NULL },
+  { "quoteleft",                         600, NULL },
+  { "Edotaccent",                        600, NULL },
+  { "onesuperior",                       600, NULL }
+};
+
+static BuiltinFontWidth courierObliqueWidthsTab[] = {
+  { "Ntilde",                            600, NULL },
+  { "rcaron",                            600, NULL },
+  { "kcommaaccent",                      600, NULL },
+  { "Ncommaaccent",                      600, NULL },
+  { "Zacute",                            600, NULL },
+  { "comma",                             600, NULL },
+  { "cedilla",                           600, NULL },
+  { "plusminus",                         600, NULL },
+  { "circumflex",                        600, NULL },
+  { "dotaccent",                         600, NULL },
+  { "edotaccent",                        600, NULL },
+  { "asciitilde",                        600, NULL },
+  { "colon",                             600, NULL },
+  { "onehalf",                           600, NULL },
+  { "dollar",                            600, NULL },
+  { "Lcaron",                            600, NULL },
+  { "ntilde",                            600, NULL },
+  { "Aogonek",                           600, NULL },
+  { "ncommaaccent",                      600, NULL },
+  { "minus",                             600, NULL },
+  { "Iogonek",                           600, NULL },
+  { "zacute",                            600, NULL },
+  { "yen",                               600, NULL },
+  { "space",                             600, NULL },
+  { "Omacron",                           600, NULL },
+  { "questiondown",                      600, NULL },
+  { "emdash",                            600, NULL },
+  { "Agrave",                            600, NULL },
+  { "three",                             600, NULL },
+  { "numbersign",                        600, NULL },
+  { "lcaron",                            600, NULL },
+  { "A",                                 600, NULL },
+  { "B",                                 600, NULL },
+  { "C",                                 600, NULL },
+  { "aogonek",                           600, NULL },
+  { "D",                                 600, NULL },
+  { "E",                                 600, NULL },
+  { "onequarter",                        600, NULL },
+  { "F",                                 600, NULL },
+  { "G",                                 600, NULL },
+  { "H",                                 600, NULL },
+  { "I",                                 600, NULL },
+  { "J",                                 600, NULL },
+  { "K",                                 600, NULL },
+  { "iogonek",                           600, NULL },
+  { "backslash",                         600, NULL },
+  { "L",                                 600, NULL },
+  { "periodcentered",                    600, NULL },
+  { "M",                                 600, NULL },
+  { "N",                                 600, NULL },
+  { "omacron",                           600, NULL },
+  { "Tcommaaccent",                      600, NULL },
+  { "O",                                 600, NULL },
+  { "P",                                 600, NULL },
+  { "Q",                                 600, NULL },
+  { "Uhungarumlaut",                     600, NULL },
+  { "R",                                 600, NULL },
+  { "Aacute",                            600, NULL },
+  { "caron",                             600, NULL },
+  { "S",                                 600, NULL },
+  { "T",                                 600, NULL },
+  { "U",                                 600, NULL },
+  { "agrave",                            600, NULL },
+  { "V",                                 600, NULL },
+  { "W",                                 600, NULL },
+  { "X",                                 600, NULL },
+  { "question",                          600, NULL },
+  { "equal",                             600, NULL },
+  { "Y",                                 600, NULL },
+  { "Z",                                 600, NULL },
+  { "four",                              600, NULL },
+  { "a",                                 600, NULL },
+  { "Gcommaaccent",                      600, NULL },
+  { "b",                                 600, NULL },
+  { "c",                                 600, NULL },
+  { "d",                                 600, NULL },
+  { "e",                                 600, NULL },
+  { "f",                                 600, NULL },
+  { "g",                                 600, NULL },
+  { "bullet",                            600, NULL },
+  { "h",                                 600, NULL },
+  { "i",                                 600, NULL },
+  { "Oslash",                            600, NULL },
+  { "dagger",                            600, NULL },
+  { "j",                                 600, NULL },
+  { "k",                                 600, NULL },
+  { "l",                                 600, NULL },
+  { "m",                                 600, NULL },
+  { "n",                                 600, NULL },
+  { "tcommaaccent",                      600, NULL },
+  { "o",                                 600, NULL },
+  { "ordfeminine",                       600, NULL },
+  { "ring",                              600, NULL },
+  { "p",                                 600, NULL },
+  { "q",                                 600, NULL },
+  { "uhungarumlaut",                     600, NULL },
+  { "r",                                 600, NULL },
+  { "twosuperior",                       600, NULL },
+  { "aacute",                            600, NULL },
+  { "s",                                 600, NULL },
+  { "OE",                                600, NULL },
+  { "t",                                 600, NULL },
+  { "divide",                            600, NULL },
+  { "u",                                 600, NULL },
+  { "Ccaron",                            600, NULL },
+  { "v",                                 600, NULL },
+  { "w",                                 600, NULL },
+  { "x",                                 600, NULL },
+  { "y",                                 600, NULL },
+  { "z",                                 600, NULL },
+  { "Gbreve",                            600, NULL },
+  { "commaaccent",                       600, NULL },
+  { "hungarumlaut",                      600, NULL },
+  { "Idotaccent",                        600, NULL },
+  { "Nacute",                            600, NULL },
+  { "quotedbl",                          600, NULL },
+  { "gcommaaccent",                      600, NULL },
+  { "mu",                                600, NULL },
+  { "greaterequal",                      600, NULL },
+  { "Scaron",                            600, NULL },
+  { "Lslash",                            600, NULL },
+  { "semicolon",                         600, NULL },
+  { "oslash",                            600, NULL },
+  { "lessequal",                         600, NULL },
+  { "lozenge",                           600, NULL },
+  { "parenright",                        600, NULL },
+  { "ccaron",                            600, NULL },
+  { "Ecircumflex",                       600, NULL },
+  { "gbreve",                            600, NULL },
+  { "trademark",                         600, NULL },
+  { "daggerdbl",                         600, NULL },
+  { "nacute",                            600, NULL },
+  { "macron",                            600, NULL },
+  { "Otilde",                            600, NULL },
+  { "Emacron",                           600, NULL },
+  { "ellipsis",                          600, NULL },
+  { "scaron",                            600, NULL },
+  { "AE",                                600, NULL },
+  { "Ucircumflex",                       600, NULL },
+  { "lslash",                            600, NULL },
+  { "quotedblleft",                      600, NULL },
+  { "guilsinglright",                    600, NULL },
+  { "hyphen",                            600, NULL },
+  { "quotesingle",                       600, NULL },
+  { "eight",                             600, NULL },
+  { "exclamdown",                        600, NULL },
+  { "endash",                            600, NULL },
+  { "oe",                                600, NULL },
+  { "Abreve",                            600, NULL },
+  { "Umacron",                           600, NULL },
+  { "ecircumflex",                       600, NULL },
+  { "Adieresis",                         600, NULL },
+  { "copyright",                         600, NULL },
+  { "Egrave",                            600, NULL },
+  { "slash",                             600, NULL },
+  { "Edieresis",                         600, NULL },
+  { "otilde",                            600, NULL },
+  { "Idieresis",                         600, NULL },
+  { "parenleft",                         600, NULL },
+  { "one",                               600, NULL },
+  { "emacron",                           600, NULL },
+  { "Odieresis",                         600, NULL },
+  { "ucircumflex",                       600, NULL },
+  { "bracketleft",                       600, NULL },
+  { "Ugrave",                            600, NULL },
+  { "quoteright",                        600, NULL },
+  { "Udieresis",                         600, NULL },
+  { "perthousand",                       600, NULL },
+  { "Ydieresis",                         600, NULL },
+  { "umacron",                           600, NULL },
+  { "abreve",                            600, NULL },
+  { "Eacute",                            600, NULL },
+  { "adieresis",                         600, NULL },
+  { "egrave",                            600, NULL },
+  { "edieresis",                         600, NULL },
+  { "idieresis",                         600, NULL },
+  { "Eth",                               600, NULL },
+  { "ae",                                600, NULL },
+  { "asterisk",                          600, NULL },
+  { "odieresis",                         600, NULL },
+  { "Uacute",                            600, NULL },
+  { "ugrave",                            600, NULL },
+  { "nine",                              600, NULL },
+  { "five",                              600, NULL },
+  { "udieresis",                         600, NULL },
+  { "Zcaron",                            600, NULL },
+  { "Scommaaccent",                      600, NULL },
+  { "threequarters",                     600, NULL },
+  { "guillemotright",                    600, NULL },
+  { "Ccedilla",                          600, NULL },
+  { "ydieresis",                         600, NULL },
+  { "tilde",                             600, NULL },
+  { "at",                                600, NULL },
+  { "eacute",                            600, NULL },
+  { "underscore",                        600, NULL },
+  { "Euro",                              600, NULL },
+  { "Dcroat",                            600, NULL },
+  { "multiply",                          600, NULL },
+  { "zero",                              600, NULL },
+  { "eth",                               600, NULL },
+  { "Scedilla",                          600, NULL },
+  { "Ograve",                            600, NULL },
+  { "Racute",                            600, NULL },
+  { "partialdiff",                       600, NULL },
+  { "uacute",                            600, NULL },
+  { "braceleft",                         600, NULL },
+  { "Thorn",                             600, NULL },
+  { "zcaron",                            600, NULL },
+  { "scommaaccent",                      600, NULL },
+  { "ccedilla",                          600, NULL },
+  { "Dcaron",                            600, NULL },
+  { "dcroat",                            600, NULL },
+  { "Ocircumflex",                       600, NULL },
+  { "Oacute",                            600, NULL },
+  { "scedilla",                          600, NULL },
+  { "ogonek",                            600, NULL },
+  { "ograve",                            600, NULL },
+  { "racute",                            600, NULL },
+  { "Tcaron",                            600, NULL },
+  { "Eogonek",                           600, NULL },
+  { "thorn",                             600, NULL },
+  { "degree",                            600, NULL },
+  { "registered",                        600, NULL },
+  { "radical",                           600, NULL },
+  { "Aring",                             600, NULL },
+  { "percent",                           600, NULL },
+  { "six",                               600, NULL },
+  { "paragraph",                         600, NULL },
+  { "dcaron",                            600, NULL },
+  { "Uogonek",                           600, NULL },
+  { "two",                               600, NULL },
+  { "summation",                         600, NULL },
+  { "Igrave",                            600, NULL },
+  { "Lacute",                            600, NULL },
+  { "ocircumflex",                       600, NULL },
+  { "oacute",                            600, NULL },
+  { "Uring",                             600, NULL },
+  { "Lcommaaccent",                      600, NULL },
+  { "tcaron",                            600, NULL },
+  { "eogonek",                           600, NULL },
+  { "Delta",                             600, NULL },
+  { "Ohungarumlaut",                     600, NULL },
+  { "asciicircum",                       600, NULL },
+  { "aring",                             600, NULL },
+  { "grave",                             600, NULL },
+  { "uogonek",                           600, NULL },
+  { "bracketright",                      600, NULL },
+  { "Iacute",                            600, NULL },
+  { "ampersand",                         600, NULL },
+  { "igrave",                            600, NULL },
+  { "lacute",                            600, NULL },
+  { "Ncaron",                            600, NULL },
+  { "plus",                              600, NULL },
+  { "uring",                             600, NULL },
+  { "quotesinglbase",                    600, NULL },
+  { "lcommaaccent",                      600, NULL },
+  { "Yacute",                            600, NULL },
+  { "ohungarumlaut",                     600, NULL },
+  { "threesuperior",                     600, NULL },
+  { "acute",                             600, NULL },
+  { "section",                           600, NULL },
+  { "dieresis",                          600, NULL },
+  { "iacute",                            600, NULL },
+  { "quotedblbase",                      600, NULL },
+  { "ncaron",                            600, NULL },
+  { "florin",                            600, NULL },
+  { "yacute",                            600, NULL },
+  { "Rcommaaccent",                      600, NULL },
+  { "fi",                                600, NULL },
+  { "fl",                                600, NULL },
+  { "Acircumflex",                       600, NULL },
+  { "Cacute",                            600, NULL },
+  { "Icircumflex",                       600, NULL },
+  { "guillemotleft",                     600, NULL },
+  { "germandbls",                        600, NULL },
+  { "Amacron",                           600, NULL },
+  { "seven",                             600, NULL },
+  { "Sacute",                            600, NULL },
+  { "ordmasculine",                      600, NULL },
+  { "dotlessi",                          600, NULL },
+  { "sterling",                          600, NULL },
+  { "notequal",                          600, NULL },
+  { "Imacron",                           600, NULL },
+  { "rcommaaccent",                      600, NULL },
+  { "Zdotaccent",                        600, NULL },
+  { "acircumflex",                       600, NULL },
+  { "cacute",                            600, NULL },
+  { "Ecaron",                            600, NULL },
+  { "icircumflex",                       600, NULL },
+  { "braceright",                        600, NULL },
+  { "quotedblright",                     600, NULL },
+  { "amacron",                           600, NULL },
+  { "sacute",                            600, NULL },
+  { "imacron",                           600, NULL },
+  { "cent",                              600, NULL },
+  { "currency",                          600, NULL },
+  { "logicalnot",                        600, NULL },
+  { "zdotaccent",                        600, NULL },
+  { "Atilde",                            600, NULL },
+  { "breve",                             600, NULL },
+  { "bar",                               600, NULL },
+  { "fraction",                          600, NULL },
+  { "less",                              600, NULL },
+  { "ecaron",                            600, NULL },
+  { "guilsinglleft",                     600, NULL },
+  { "exclam",                            600, NULL },
+  { "period",                            600, NULL },
+  { "Rcaron",                            600, NULL },
+  { "Kcommaaccent",                      600, NULL },
+  { "greater",                           600, NULL },
+  { "atilde",                            600, NULL },
+  { "brokenbar",                         600, NULL },
+  { "quoteleft",                         600, NULL },
+  { "Edotaccent",                        600, NULL },
+  { "onesuperior",                       600, NULL }
+};
+
+static BuiltinFontWidth helveticaWidthsTab[] = {
+  { "Ntilde",                            722, NULL },
+  { "rcaron",                            333, NULL },
+  { "kcommaaccent",                      500, NULL },
+  { "Ncommaaccent",                      722, NULL },
+  { "Zacute",                            611, NULL },
+  { "comma",                             278, NULL },
+  { "cedilla",                           333, NULL },
+  { "plusminus",                         584, NULL },
+  { "circumflex",                        333, NULL },
+  { "dotaccent",                         333, NULL },
+  { "edotaccent",                        556, NULL },
+  { "asciitilde",                        584, NULL },
+  { "colon",                             278, NULL },
+  { "onehalf",                           834, NULL },
+  { "dollar",                            556, NULL },
+  { "Lcaron",                            556, NULL },
+  { "ntilde",                            556, NULL },
+  { "Aogonek",                           667, NULL },
+  { "ncommaaccent",                      556, NULL },
+  { "minus",                             584, NULL },
+  { "Iogonek",                           278, NULL },
+  { "zacute",                            500, NULL },
+  { "yen",                               556, NULL },
+  { "space",                             278, NULL },
+  { "Omacron",                           778, NULL },
+  { "questiondown",                      611, NULL },
+  { "emdash",                           1000, NULL },
+  { "Agrave",                            667, NULL },
+  { "three",                             556, NULL },
+  { "numbersign",                        556, NULL },
+  { "lcaron",                            299, NULL },
+  { "A",                                 667, NULL },
+  { "B",                                 667, NULL },
+  { "C",                                 722, NULL },
+  { "aogonek",                           556, NULL },
+  { "D",                                 722, NULL },
+  { "E",                                 667, NULL },
+  { "onequarter",                        834, NULL },
+  { "F",                                 611, NULL },
+  { "G",                                 778, NULL },
+  { "H",                                 722, NULL },
+  { "I",                                 278, NULL },
+  { "J",                                 500, NULL },
+  { "K",                                 667, NULL },
+  { "iogonek",                           222, NULL },
+  { "backslash",                         278, NULL },
+  { "L",                                 556, NULL },
+  { "periodcentered",                    278, NULL },
+  { "M",                                 833, NULL },
+  { "N",                                 722, NULL },
+  { "omacron",                           556, NULL },
+  { "Tcommaaccent",                      611, NULL },
+  { "O",                                 778, NULL },
+  { "P",                                 667, NULL },
+  { "Q",                                 778, NULL },
+  { "Uhungarumlaut",                     722, NULL },
+  { "R",                                 722, NULL },
+  { "Aacute",                            667, NULL },
+  { "caron",                             333, NULL },
+  { "S",                                 667, NULL },
+  { "T",                                 611, NULL },
+  { "U",                                 722, NULL },
+  { "agrave",                            556, NULL },
+  { "V",                                 667, NULL },
+  { "W",                                 944, NULL },
+  { "X",                                 667, NULL },
+  { "question",                          556, NULL },
+  { "equal",                             584, NULL },
+  { "Y",                                 667, NULL },
+  { "Z",                                 611, NULL },
+  { "four",                              556, NULL },
+  { "a",                                 556, NULL },
+  { "Gcommaaccent",                      778, NULL },
+  { "b",                                 556, NULL },
+  { "c",                                 500, NULL },
+  { "d",                                 556, NULL },
+  { "e",                                 556, NULL },
+  { "f",                                 278, NULL },
+  { "g",                                 556, NULL },
+  { "bullet",                            350, NULL },
+  { "h",                                 556, NULL },
+  { "i",                                 222, NULL },
+  { "Oslash",                            778, NULL },
+  { "dagger",                            556, NULL },
+  { "j",                                 222, NULL },
+  { "k",                                 500, NULL },
+  { "l",                                 222, NULL },
+  { "m",                                 833, NULL },
+  { "n",                                 556, NULL },
+  { "tcommaaccent",                      278, NULL },
+  { "o",                                 556, NULL },
+  { "ordfeminine",                       370, NULL },
+  { "ring",                              333, NULL },
+  { "p",                                 556, NULL },
+  { "q",                                 556, NULL },
+  { "uhungarumlaut",                     556, NULL },
+  { "r",                                 333, NULL },
+  { "twosuperior",                       333, NULL },
+  { "aacute",                            556, NULL },
+  { "s",                                 500, NULL },
+  { "OE",                               1000, NULL },
+  { "t",                                 278, NULL },
+  { "divide",                            584, NULL },
+  { "u",                                 556, NULL },
+  { "Ccaron",                            722, NULL },
+  { "v",                                 500, NULL },
+  { "w",                                 722, NULL },
+  { "x",                                 500, NULL },
+  { "y",                                 500, NULL },
+  { "z",                                 500, NULL },
+  { "Gbreve",                            778, NULL },
+  { "commaaccent",                       250, NULL },
+  { "hungarumlaut",                      333, NULL },
+  { "Idotaccent",                        278, NULL },
+  { "Nacute",                            722, NULL },
+  { "quotedbl",                          355, NULL },
+  { "gcommaaccent",                      556, NULL },
+  { "mu",                                556, NULL },
+  { "greaterequal",                      549, NULL },
+  { "Scaron",                            667, NULL },
+  { "Lslash",                            556, NULL },
+  { "semicolon",                         278, NULL },
+  { "oslash",                            611, NULL },
+  { "lessequal",                         549, NULL },
+  { "lozenge",                           471, NULL },
+  { "parenright",                        333, NULL },
+  { "ccaron",                            500, NULL },
+  { "Ecircumflex",                       667, NULL },
+  { "gbreve",                            556, NULL },
+  { "trademark",                        1000, NULL },
+  { "daggerdbl",                         556, NULL },
+  { "nacute",                            556, NULL },
+  { "macron",                            333, NULL },
+  { "Otilde",                            778, NULL },
+  { "Emacron",                           667, NULL },
+  { "ellipsis",                         1000, NULL },
+  { "scaron",                            500, NULL },
+  { "AE",                               1000, NULL },
+  { "Ucircumflex",                       722, NULL },
+  { "lslash",                            222, NULL },
+  { "quotedblleft",                      333, NULL },
+  { "guilsinglright",                    333, NULL },
+  { "hyphen",                            333, NULL },
+  { "quotesingle",                       191, NULL },
+  { "eight",                             556, NULL },
+  { "exclamdown",                        333, NULL },
+  { "endash",                            556, NULL },
+  { "oe",                                944, NULL },
+  { "Abreve",                            667, NULL },
+  { "Umacron",                           722, NULL },
+  { "ecircumflex",                       556, NULL },
+  { "Adieresis",                         667, NULL },
+  { "copyright",                         737, NULL },
+  { "Egrave",                            667, NULL },
+  { "slash",                             278, NULL },
+  { "Edieresis",                         667, NULL },
+  { "otilde",                            556, NULL },
+  { "Idieresis",                         278, NULL },
+  { "parenleft",                         333, NULL },
+  { "one",                               556, NULL },
+  { "emacron",                           556, NULL },
+  { "Odieresis",                         778, NULL },
+  { "ucircumflex",                       556, NULL },
+  { "bracketleft",                       278, NULL },
+  { "Ugrave",                            722, NULL },
+  { "quoteright",                        222, NULL },
+  { "Udieresis",                         722, NULL },
+  { "perthousand",                      1000, NULL },
+  { "Ydieresis",                         667, NULL },
+  { "umacron",                           556, NULL },
+  { "abreve",                            556, NULL },
+  { "Eacute",                            667, NULL },
+  { "adieresis",                         556, NULL },
+  { "egrave",                            556, NULL },
+  { "edieresis",                         556, NULL },
+  { "idieresis",                         278, NULL },
+  { "Eth",                               722, NULL },
+  { "ae",                                889, NULL },
+  { "asterisk",                          389, NULL },
+  { "odieresis",                         556, NULL },
+  { "Uacute",                            722, NULL },
+  { "ugrave",                            556, NULL },
+  { "nine",                              556, NULL },
+  { "five",                              556, NULL },
+  { "udieresis",                         556, NULL },
+  { "Zcaron",                            611, NULL },
+  { "Scommaaccent",                      667, NULL },
+  { "threequarters",                     834, NULL },
+  { "guillemotright",                    556, NULL },
+  { "Ccedilla",                          722, NULL },
+  { "ydieresis",                         500, NULL },
+  { "tilde",                             333, NULL },
+  { "at",                               1015, NULL },
+  { "eacute",                            556, NULL },
+  { "underscore",                        556, NULL },
+  { "Euro",                              556, NULL },
+  { "Dcroat",                            722, NULL },
+  { "multiply",                          584, NULL },
+  { "zero",                              556, NULL },
+  { "eth",                               556, NULL },
+  { "Scedilla",                          667, NULL },
+  { "Ograve",                            778, NULL },
+  { "Racute",                            722, NULL },
+  { "partialdiff",                       476, NULL },
+  { "uacute",                            556, NULL },
+  { "braceleft",                         334, NULL },
+  { "Thorn",                             667, NULL },
+  { "zcaron",                            500, NULL },
+  { "scommaaccent",                      500, NULL },
+  { "ccedilla",                          500, NULL },
+  { "Dcaron",                            722, NULL },
+  { "dcroat",                            556, NULL },
+  { "Ocircumflex",                       778, NULL },
+  { "Oacute",                            778, NULL },
+  { "scedilla",                          500, NULL },
+  { "ogonek",                            333, NULL },
+  { "ograve",                            556, NULL },
+  { "racute",                            333, NULL },
+  { "Tcaron",                            611, NULL },
+  { "Eogonek",                           667, NULL },
+  { "thorn",                             556, NULL },
+  { "degree",                            400, NULL },
+  { "registered",                        737, NULL },
+  { "radical",                           453, NULL },
+  { "Aring",                             667, NULL },
+  { "percent",                           889, NULL },
+  { "six",                               556, NULL },
+  { "paragraph",                         537, NULL },
+  { "dcaron",                            643, NULL },
+  { "Uogonek",                           722, NULL },
+  { "two",                               556, NULL },
+  { "summation",                         600, NULL },
+  { "Igrave",                            278, NULL },
+  { "Lacute",                            556, NULL },
+  { "ocircumflex",                       556, NULL },
+  { "oacute",                            556, NULL },
+  { "Uring",                             722, NULL },
+  { "Lcommaaccent",                      556, NULL },
+  { "tcaron",                            317, NULL },
+  { "eogonek",                           556, NULL },
+  { "Delta",                             612, NULL },
+  { "Ohungarumlaut",                     778, NULL },
+  { "asciicircum",                       469, NULL },
+  { "aring",                             556, NULL },
+  { "grave",                             333, NULL },
+  { "uogonek",                           556, NULL },
+  { "bracketright",                      278, NULL },
+  { "Iacute",                            278, NULL },
+  { "ampersand",                         667, NULL },
+  { "igrave",                            278, NULL },
+  { "lacute",                            222, NULL },
+  { "Ncaron",                            722, NULL },
+  { "plus",                              584, NULL },
+  { "uring",                             556, NULL },
+  { "quotesinglbase",                    222, NULL },
+  { "lcommaaccent",                      222, NULL },
+  { "Yacute",                            667, NULL },
+  { "ohungarumlaut",                     556, NULL },
+  { "threesuperior",                     333, NULL },
+  { "acute",                             333, NULL },
+  { "section",                           556, NULL },
+  { "dieresis",                          333, NULL },
+  { "iacute",                            278, NULL },
+  { "quotedblbase",                      333, NULL },
+  { "ncaron",                            556, NULL },
+  { "florin",                            556, NULL },
+  { "yacute",                            500, NULL },
+  { "Rcommaaccent",                      722, NULL },
+  { "fi",                                500, NULL },
+  { "fl",                                500, NULL },
+  { "Acircumflex",                       667, NULL },
+  { "Cacute",                            722, NULL },
+  { "Icircumflex",                       278, NULL },
+  { "guillemotleft",                     556, NULL },
+  { "germandbls",                        611, NULL },
+  { "Amacron",                           667, NULL },
+  { "seven",                             556, NULL },
+  { "Sacute",                            667, NULL },
+  { "ordmasculine",                      365, NULL },
+  { "dotlessi",                          278, NULL },
+  { "sterling",                          556, NULL },
+  { "notequal",                          549, NULL },
+  { "Imacron",                           278, NULL },
+  { "rcommaaccent",                      333, NULL },
+  { "Zdotaccent",                        611, NULL },
+  { "acircumflex",                       556, NULL },
+  { "cacute",                            500, NULL },
+  { "Ecaron",                            667, NULL },
+  { "icircumflex",                       278, NULL },
+  { "braceright",                        334, NULL },
+  { "quotedblright",                     333, NULL },
+  { "amacron",                           556, NULL },
+  { "sacute",                            500, NULL },
+  { "imacron",                           278, NULL },
+  { "cent",                              556, NULL },
+  { "currency",                          556, NULL },
+  { "logicalnot",                        584, NULL },
+  { "zdotaccent",                        500, NULL },
+  { "Atilde",                            667, NULL },
+  { "breve",                             333, NULL },
+  { "bar",                               260, NULL },
+  { "fraction",                          167, NULL },
+  { "less",                              584, NULL },
+  { "ecaron",                            556, NULL },
+  { "guilsinglleft",                     333, NULL },
+  { "exclam",                            278, NULL },
+  { "period",                            278, NULL },
+  { "Rcaron",                            722, NULL },
+  { "Kcommaaccent",                      667, NULL },
+  { "greater",                           584, NULL },
+  { "atilde",                            556, NULL },
+  { "brokenbar",                         260, NULL },
+  { "quoteleft",                         222, NULL },
+  { "Edotaccent",                        667, NULL },
+  { "onesuperior",                       333, NULL }
+};
+
+static BuiltinFontWidth helveticaBoldWidthsTab[] = {
+  { "Ntilde",                            722, NULL },
+  { "rcaron",                            389, NULL },
+  { "kcommaaccent",                      556, NULL },
+  { "Ncommaaccent",                      722, NULL },
+  { "Zacute",                            611, NULL },
+  { "comma",                             278, NULL },
+  { "cedilla",                           333, NULL },
+  { "plusminus",                         584, NULL },
+  { "circumflex",                        333, NULL },
+  { "dotaccent",                         333, NULL },
+  { "edotaccent",                        556, NULL },
+  { "asciitilde",                        584, NULL },
+  { "colon",                             333, NULL },
+  { "onehalf",                           834, NULL },
+  { "dollar",                            556, NULL },
+  { "Lcaron",                            611, NULL },
+  { "ntilde",                            611, NULL },
+  { "Aogonek",                           722, NULL },
+  { "ncommaaccent",                      611, NULL },
+  { "minus",                             584, NULL },
+  { "Iogonek",                           278, NULL },
+  { "zacute",                            500, NULL },
+  { "yen",                               556, NULL },
+  { "space",                             278, NULL },
+  { "Omacron",                           778, NULL },
+  { "questiondown",                      611, NULL },
+  { "emdash",                           1000, NULL },
+  { "Agrave",                            722, NULL },
+  { "three",                             556, NULL },
+  { "numbersign",                        556, NULL },
+  { "lcaron",                            400, NULL },
+  { "A",                                 722, NULL },
+  { "B",                                 722, NULL },
+  { "C",                                 722, NULL },
+  { "aogonek",                           556, NULL },
+  { "D",                                 722, NULL },
+  { "E",                                 667, NULL },
+  { "onequarter",                        834, NULL },
+  { "F",                                 611, NULL },
+  { "G",                                 778, NULL },
+  { "H",                                 722, NULL },
+  { "I",                                 278, NULL },
+  { "J",                                 556, NULL },
+  { "K",                                 722, NULL },
+  { "iogonek",                           278, NULL },
+  { "backslash",                         278, NULL },
+  { "L",                                 611, NULL },
+  { "periodcentered",                    278, NULL },
+  { "M",                                 833, NULL },
+  { "N",                                 722, NULL },
+  { "omacron",                           611, NULL },
+  { "Tcommaaccent",                      611, NULL },
+  { "O",                                 778, NULL },
+  { "P",                                 667, NULL },
+  { "Q",                                 778, NULL },
+  { "Uhungarumlaut",                     722, NULL },
+  { "R",                                 722, NULL },
+  { "Aacute",                            722, NULL },
+  { "caron",                             333, NULL },
+  { "S",                                 667, NULL },
+  { "T",                                 611, NULL },
+  { "U",                                 722, NULL },
+  { "agrave",                            556, NULL },
+  { "V",                                 667, NULL },
+  { "W",                                 944, NULL },
+  { "X",                                 667, NULL },
+  { "question",                          611, NULL },
+  { "equal",                             584, NULL },
+  { "Y",                                 667, NULL },
+  { "Z",                                 611, NULL },
+  { "four",                              556, NULL },
+  { "a",                                 556, NULL },
+  { "Gcommaaccent",                      778, NULL },
+  { "b",                                 611, NULL },
+  { "c",                                 556, NULL },
+  { "d",                                 611, NULL },
+  { "e",                                 556, NULL },
+  { "f",                                 333, NULL },
+  { "g",                                 611, NULL },
+  { "bullet",                            350, NULL },
+  { "h",                                 611, NULL },
+  { "i",                                 278, NULL },
+  { "Oslash",                            778, NULL },
+  { "dagger",                            556, NULL },
+  { "j",                                 278, NULL },
+  { "k",                                 556, NULL },
+  { "l",                                 278, NULL },
+  { "m",                                 889, NULL },
+  { "n",                                 611, NULL },
+  { "tcommaaccent",                      333, NULL },
+  { "o",                                 611, NULL },
+  { "ordfeminine",                       370, NULL },
+  { "ring",                              333, NULL },
+  { "p",                                 611, NULL },
+  { "q",                                 611, NULL },
+  { "uhungarumlaut",                     611, NULL },
+  { "r",                                 389, NULL },
+  { "twosuperior",                       333, NULL },
+  { "aacute",                            556, NULL },
+  { "s",                                 556, NULL },
+  { "OE",                               1000, NULL },
+  { "t",                                 333, NULL },
+  { "divide",                            584, NULL },
+  { "u",                                 611, NULL },
+  { "Ccaron",                            722, NULL },
+  { "v",                                 556, NULL },
+  { "w",                                 778, NULL },
+  { "x",                                 556, NULL },
+  { "y",                                 556, NULL },
+  { "z",                                 500, NULL },
+  { "Gbreve",                            778, NULL },
+  { "commaaccent",                       250, NULL },
+  { "hungarumlaut",                      333, NULL },
+  { "Idotaccent",                        278, NULL },
+  { "Nacute",                            722, NULL },
+  { "quotedbl",                          474, NULL },
+  { "gcommaaccent",                      611, NULL },
+  { "mu",                                611, NULL },
+  { "greaterequal",                      549, NULL },
+  { "Scaron",                            667, NULL },
+  { "Lslash",                            611, NULL },
+  { "semicolon",                         333, NULL },
+  { "oslash",                            611, NULL },
+  { "lessequal",                         549, NULL },
+  { "lozenge",                           494, NULL },
+  { "parenright",                        333, NULL },
+  { "ccaron",                            556, NULL },
+  { "Ecircumflex",                       667, NULL },
+  { "gbreve",                            611, NULL },
+  { "trademark",                        1000, NULL },
+  { "daggerdbl",                         556, NULL },
+  { "nacute",                            611, NULL },
+  { "macron",                            333, NULL },
+  { "Otilde",                            778, NULL },
+  { "Emacron",                           667, NULL },
+  { "ellipsis",                         1000, NULL },
+  { "scaron",                            556, NULL },
+  { "AE",                               1000, NULL },
+  { "Ucircumflex",                       722, NULL },
+  { "lslash",                            278, NULL },
+  { "quotedblleft",                      500, NULL },
+  { "guilsinglright",                    333, NULL },
+  { "hyphen",                            333, NULL },
+  { "quotesingle",                       238, NULL },
+  { "eight",                             556, NULL },
+  { "exclamdown",                        333, NULL },
+  { "endash",                            556, NULL },
+  { "oe",                                944, NULL },
+  { "Abreve",                            722, NULL },
+  { "Umacron",                           722, NULL },
+  { "ecircumflex",                       556, NULL },
+  { "Adieresis",                         722, NULL },
+  { "copyright",                         737, NULL },
+  { "Egrave",                            667, NULL },
+  { "slash",                             278, NULL },
+  { "Edieresis",                         667, NULL },
+  { "otilde",                            611, NULL },
+  { "Idieresis",                         278, NULL },
+  { "parenleft",                         333, NULL },
+  { "one",                               556, NULL },
+  { "emacron",                           556, NULL },
+  { "Odieresis",                         778, NULL },
+  { "ucircumflex",                       611, NULL },
+  { "bracketleft",                       333, NULL },
+  { "Ugrave",                            722, NULL },
+  { "quoteright",                        278, NULL },
+  { "Udieresis",                         722, NULL },
+  { "perthousand",                      1000, NULL },
+  { "Ydieresis",                         667, NULL },
+  { "umacron",                           611, NULL },
+  { "abreve",                            556, NULL },
+  { "Eacute",                            667, NULL },
+  { "adieresis",                         556, NULL },
+  { "egrave",                            556, NULL },
+  { "edieresis",                         556, NULL },
+  { "idieresis",                         278, NULL },
+  { "Eth",                               722, NULL },
+  { "ae",                                889, NULL },
+  { "asterisk",                          389, NULL },
+  { "odieresis",                         611, NULL },
+  { "Uacute",                            722, NULL },
+  { "ugrave",                            611, NULL },
+  { "nine",                              556, NULL },
+  { "five",                              556, NULL },
+  { "udieresis",                         611, NULL },
+  { "Zcaron",                            611, NULL },
+  { "Scommaaccent",                      667, NULL },
+  { "threequarters",                     834, NULL },
+  { "guillemotright",                    556, NULL },
+  { "Ccedilla",                          722, NULL },
+  { "ydieresis",                         556, NULL },
+  { "tilde",                             333, NULL },
+  { "dbldaggerumlaut",                   556, NULL },
+  { "at",                                975, NULL },
+  { "eacute",                            556, NULL },
+  { "underscore",                        556, NULL },
+  { "Euro",                              556, NULL },
+  { "Dcroat",                            722, NULL },
+  { "multiply",                          584, NULL },
+  { "zero",                              556, NULL },
+  { "eth",                               611, NULL },
+  { "Scedilla",                          667, NULL },
+  { "Ograve",                            778, NULL },
+  { "Racute",                            722, NULL },
+  { "partialdiff",                       494, NULL },
+  { "uacute",                            611, NULL },
+  { "braceleft",                         389, NULL },
+  { "Thorn",                             667, NULL },
+  { "zcaron",                            500, NULL },
+  { "scommaaccent",                      556, NULL },
+  { "ccedilla",                          556, NULL },
+  { "Dcaron",                            722, NULL },
+  { "dcroat",                            611, NULL },
+  { "Ocircumflex",                       778, NULL },
+  { "Oacute",                            778, NULL },
+  { "scedilla",                          556, NULL },
+  { "ogonek",                            333, NULL },
+  { "ograve",                            611, NULL },
+  { "racute",                            389, NULL },
+  { "Tcaron",                            611, NULL },
+  { "Eogonek",                           667, NULL },
+  { "thorn",                             611, NULL },
+  { "degree",                            400, NULL },
+  { "registered",                        737, NULL },
+  { "radical",                           549, NULL },
+  { "Aring",                             722, NULL },
+  { "percent",                           889, NULL },
+  { "six",                               556, NULL },
+  { "paragraph",                         556, NULL },
+  { "dcaron",                            743, NULL },
+  { "Uogonek",                           722, NULL },
+  { "two",                               556, NULL },
+  { "summation",                         600, NULL },
+  { "Igrave",                            278, NULL },
+  { "Lacute",                            611, NULL },
+  { "ocircumflex",                       611, NULL },
+  { "oacute",                            611, NULL },
+  { "Uring",                             722, NULL },
+  { "Lcommaaccent",                      611, NULL },
+  { "tcaron",                            389, NULL },
+  { "eogonek",                           556, NULL },
+  { "Delta",                             612, NULL },
+  { "Ohungarumlaut",                     778, NULL },
+  { "asciicircum",                       584, NULL },
+  { "aring",                             556, NULL },
+  { "grave",                             333, NULL },
+  { "uogonek",                           611, NULL },
+  { "bracketright",                      333, NULL },
+  { "Iacute",                            278, NULL },
+  { "ampersand",                         722, NULL },
+  { "igrave",                            278, NULL },
+  { "lacute",                            278, NULL },
+  { "Ncaron",                            722, NULL },
+  { "plus",                              584, NULL },
+  { "uring",                             611, NULL },
+  { "quotesinglbase",                    278, NULL },
+  { "lcommaaccent",                      278, NULL },
+  { "Yacute",                            667, NULL },
+  { "ohungarumlaut",                     611, NULL },
+  { "threesuperior",                     333, NULL },
+  { "acute",                             333, NULL },
+  { "section",                           556, NULL },
+  { "dieresis",                          333, NULL },
+  { "iacute",                            278, NULL },
+  { "quotedblbase",                      500, NULL },
+  { "ncaron",                            611, NULL },
+  { "florin",                            556, NULL },
+  { "yacute",                            556, NULL },
+  { "Rcommaaccent",                      722, NULL },
+  { "fi",                                611, NULL },
+  { "fl",                                611, NULL },
+  { "Acircumflex",                       722, NULL },
+  { "Cacute",                            722, NULL },
+  { "Icircumflex",                       278, NULL },
+  { "guillemotleft",                     556, NULL },
+  { "germandbls",                        611, NULL },
+  { "Amacron",                           722, NULL },
+  { "seven",                             556, NULL },
+  { "Sacute",                            667, NULL },
+  { "ordmasculine",                      365, NULL },
+  { "dotlessi",                          278, NULL },
+  { "sterling",                          556, NULL },
+  { "notequal",                          549, NULL },
+  { "Imacron",                           278, NULL },
+  { "rcommaaccent",                      389, NULL },
+  { "Zdotaccent",                        611, NULL },
+  { "acircumflex",                       556, NULL },
+  { "cacute",                            556, NULL },
+  { "Ecaron",                            667, NULL },
+  { "icircumflex",                       278, NULL },
+  { "braceright",                        389, NULL },
+  { "quotedblright",                     500, NULL },
+  { "amacron",                           556, NULL },
+  { "sacute",                            556, NULL },
+  { "imacron",                           278, NULL },
+  { "cent",                              556, NULL },
+  { "currency",                          556, NULL },
+  { "logicalnot",                        584, NULL },
+  { "zdotaccent",                        500, NULL },
+  { "Atilde",                            722, NULL },
+  { "breve",                             333, NULL },
+  { "bar",                               280, NULL },
+  { "fraction",                          167, NULL },
+  { "less",                              584, NULL },
+  { "ecaron",                            556, NULL },
+  { "guilsinglleft",                     333, NULL },
+  { "exclam",                            333, NULL },
+  { "period",                            278, NULL },
+  { "Rcaron",                            722, NULL },
+  { "Kcommaaccent",                      722, NULL },
+  { "greater",                           584, NULL },
+  { "atilde",                            556, NULL },
+  { "brokenbar",                         280, NULL },
+  { "quoteleft",                         278, NULL },
+  { "Edotaccent",                        667, NULL },
+  { "onesuperior",                       333, NULL }
+};
+
+static BuiltinFontWidth helveticaBoldObliqueWidthsTab[] = {
+  { "Ntilde",                            722, NULL },
+  { "rcaron",                            389, NULL },
+  { "kcommaaccent",                      556, NULL },
+  { "Ncommaaccent",                      722, NULL },
+  { "Zacute",                            611, NULL },
+  { "comma",                             278, NULL },
+  { "cedilla",                           333, NULL },
+  { "plusminus",                         584, NULL },
+  { "circumflex",                        333, NULL },
+  { "dotaccent",                         333, NULL },
+  { "edotaccent",                        556, NULL },
+  { "asciitilde",                        584, NULL },
+  { "colon",                             333, NULL },
+  { "onehalf",                           834, NULL },
+  { "dollar",                            556, NULL },
+  { "Lcaron",                            611, NULL },
+  { "ntilde",                            611, NULL },
+  { "Aogonek",                           722, NULL },
+  { "ncommaaccent",                      611, NULL },
+  { "minus",                             584, NULL },
+  { "Iogonek",                           278, NULL },
+  { "zacute",                            500, NULL },
+  { "yen",                               556, NULL },
+  { "space",                             278, NULL },
+  { "Omacron",                           778, NULL },
+  { "questiondown",                      611, NULL },
+  { "emdash",                           1000, NULL },
+  { "Agrave",                            722, NULL },
+  { "three",                             556, NULL },
+  { "numbersign",                        556, NULL },
+  { "lcaron",                            400, NULL },
+  { "A",                                 722, NULL },
+  { "B",                                 722, NULL },
+  { "C",                                 722, NULL },
+  { "aogonek",                           556, NULL },
+  { "D",                                 722, NULL },
+  { "E",                                 667, NULL },
+  { "onequarter",                        834, NULL },
+  { "F",                                 611, NULL },
+  { "G",                                 778, NULL },
+  { "H",                                 722, NULL },
+  { "I",                                 278, NULL },
+  { "J",                                 556, NULL },
+  { "K",                                 722, NULL },
+  { "iogonek",                           278, NULL },
+  { "backslash",                         278, NULL },
+  { "L",                                 611, NULL },
+  { "periodcentered",                    278, NULL },
+  { "M",                                 833, NULL },
+  { "N",                                 722, NULL },
+  { "omacron",                           611, NULL },
+  { "Tcommaaccent",                      611, NULL },
+  { "O",                                 778, NULL },
+  { "P",                                 667, NULL },
+  { "Q",                                 778, NULL },
+  { "Uhungarumlaut",                     722, NULL },
+  { "R",                                 722, NULL },
+  { "Aacute",                            722, NULL },
+  { "caron",                             333, NULL },
+  { "S",                                 667, NULL },
+  { "T",                                 611, NULL },
+  { "U",                                 722, NULL },
+  { "agrave",                            556, NULL },
+  { "V",                                 667, NULL },
+  { "W",                                 944, NULL },
+  { "X",                                 667, NULL },
+  { "question",                          611, NULL },
+  { "equal",                             584, NULL },
+  { "Y",                                 667, NULL },
+  { "Z",                                 611, NULL },
+  { "four",                              556, NULL },
+  { "a",                                 556, NULL },
+  { "Gcommaaccent",                      778, NULL },
+  { "b",                                 611, NULL },
+  { "c",                                 556, NULL },
+  { "d",                                 611, NULL },
+  { "e",                                 556, NULL },
+  { "f",                                 333, NULL },
+  { "g",                                 611, NULL },
+  { "bullet",                            350, NULL },
+  { "h",                                 611, NULL },
+  { "i",                                 278, NULL },
+  { "Oslash",                            778, NULL },
+  { "dagger",                            556, NULL },
+  { "j",                                 278, NULL },
+  { "k",                                 556, NULL },
+  { "l",                                 278, NULL },
+  { "m",                                 889, NULL },
+  { "n",                                 611, NULL },
+  { "tcommaaccent",                      333, NULL },
+  { "o",                                 611, NULL },
+  { "ordfeminine",                       370, NULL },
+  { "ring",                              333, NULL },
+  { "p",                                 611, NULL },
+  { "q",                                 611, NULL },
+  { "uhungarumlaut",                     611, NULL },
+  { "r",                                 389, NULL },
+  { "twosuperior",                       333, NULL },
+  { "aacute",                            556, NULL },
+  { "s",                                 556, NULL },
+  { "OE",                               1000, NULL },
+  { "t",                                 333, NULL },
+  { "divide",                            584, NULL },
+  { "u",                                 611, NULL },
+  { "Ccaron",                            722, NULL },
+  { "v",                                 556, NULL },
+  { "w",                                 778, NULL },
+  { "x",                                 556, NULL },
+  { "y",                                 556, NULL },
+  { "z",                                 500, NULL },
+  { "Gbreve",                            778, NULL },
+  { "commaaccent",                       250, NULL },
+  { "hungarumlaut",                      333, NULL },
+  { "Idotaccent",                        278, NULL },
+  { "Nacute",                            722, NULL },
+  { "quotedbl",                          474, NULL },
+  { "gcommaaccent",                      611, NULL },
+  { "mu",                                611, NULL },
+  { "greaterequal",                      549, NULL },
+  { "Scaron",                            667, NULL },
+  { "Lslash",                            611, NULL },
+  { "semicolon",                         333, NULL },
+  { "oslash",                            611, NULL },
+  { "lessequal",                         549, NULL },
+  { "lozenge",                           494, NULL },
+  { "parenright",                        333, NULL },
+  { "ccaron",                            556, NULL },
+  { "Ecircumflex",                       667, NULL },
+  { "gbreve",                            611, NULL },
+  { "trademark",                        1000, NULL },
+  { "daggerdbl",                         556, NULL },
+  { "nacute",                            611, NULL },
+  { "macron",                            333, NULL },
+  { "Otilde",                            778, NULL },
+  { "Emacron",                           667, NULL },
+  { "ellipsis",                         1000, NULL },
+  { "scaron",                            556, NULL },
+  { "AE",                               1000, NULL },
+  { "Ucircumflex",                       722, NULL },
+  { "lslash",                            278, NULL },
+  { "quotedblleft",                      500, NULL },
+  { "guilsinglright",                    333, NULL },
+  { "hyphen",                            333, NULL },
+  { "quotesingle",                       238, NULL },
+  { "eight",                             556, NULL },
+  { "exclamdown",                        333, NULL },
+  { "endash",                            556, NULL },
+  { "oe",                                944, NULL },
+  { "Abreve",                            722, NULL },
+  { "Umacron",                           722, NULL },
+  { "ecircumflex",                       556, NULL },
+  { "Adieresis",                         722, NULL },
+  { "copyright",                         737, NULL },
+  { "Egrave",                            667, NULL },
+  { "slash",                             278, NULL },
+  { "Edieresis",                         667, NULL },
+  { "otilde",                            611, NULL },
+  { "Idieresis",                         278, NULL },
+  { "parenleft",                         333, NULL },
+  { "one",                               556, NULL },
+  { "emacron",                           556, NULL },
+  { "Odieresis",                         778, NULL },
+  { "ucircumflex",                       611, NULL },
+  { "bracketleft",                       333, NULL },
+  { "Ugrave",                            722, NULL },
+  { "quoteright",                        278, NULL },
+  { "Udieresis",                         722, NULL },
+  { "perthousand",                      1000, NULL },
+  { "Ydieresis",                         667, NULL },
+  { "umacron",                           611, NULL },
+  { "abreve",                            556, NULL },
+  { "Eacute",                            667, NULL },
+  { "adieresis",                         556, NULL },
+  { "egrave",                            556, NULL },
+  { "edieresis",                         556, NULL },
+  { "idieresis",                         278, NULL },
+  { "Eth",                               722, NULL },
+  { "ae",                                889, NULL },
+  { "asterisk",                          389, NULL },
+  { "odieresis",                         611, NULL },
+  { "Uacute",                            722, NULL },
+  { "ugrave",                            611, NULL },
+  { "nine",                              556, NULL },
+  { "five",                              556, NULL },
+  { "udieresis",                         611, NULL },
+  { "Zcaron",                            611, NULL },
+  { "Scommaaccent",                      667, NULL },
+  { "threequarters",                     834, NULL },
+  { "guillemotright",                    556, NULL },
+  { "Ccedilla",                          722, NULL },
+  { "ydieresis",                         556, NULL },
+  { "tilde",                             333, NULL },
+  { "at",                                975, NULL },
+  { "eacute",                            556, NULL },
+  { "underscore",                        556, NULL },
+  { "Euro",                              556, NULL },
+  { "Dcroat",                            722, NULL },
+  { "multiply",                          584, NULL },
+  { "zero",                              556, NULL },
+  { "eth",                               611, NULL },
+  { "Scedilla",                          667, NULL },
+  { "Ograve",                            778, NULL },
+  { "Racute",                            722, NULL },
+  { "partialdiff",                       494, NULL },
+  { "uacute",                            611, NULL },
+  { "braceleft",                         389, NULL },
+  { "Thorn",                             667, NULL },
+  { "zcaron",                            500, NULL },
+  { "scommaaccent",                      556, NULL },
+  { "ccedilla",                          556, NULL },
+  { "Dcaron",                            722, NULL },
+  { "dcroat",                            611, NULL },
+  { "Ocircumflex",                       778, NULL },
+  { "Oacute",                            778, NULL },
+  { "scedilla",                          556, NULL },
+  { "ogonek",                            333, NULL },
+  { "ograve",                            611, NULL },
+  { "racute",                            389, NULL },
+  { "Tcaron",                            611, NULL },
+  { "Eogonek",                           667, NULL },
+  { "thorn",                             611, NULL },
+  { "degree",                            400, NULL },
+  { "registered",                        737, NULL },
+  { "radical",                           549, NULL },
+  { "Aring",                             722, NULL },
+  { "percent",                           889, NULL },
+  { "six",                               556, NULL },
+  { "paragraph",                         556, NULL },
+  { "dcaron",                            743, NULL },
+  { "Uogonek",                           722, NULL },
+  { "two",                               556, NULL },
+  { "summation",                         600, NULL },
+  { "Igrave",                            278, NULL },
+  { "Lacute",                            611, NULL },
+  { "ocircumflex",                       611, NULL },
+  { "oacute",                            611, NULL },
+  { "Uring",                             722, NULL },
+  { "Lcommaaccent",                      611, NULL },
+  { "tcaron",                            389, NULL },
+  { "eogonek",                           556, NULL },
+  { "Delta",                             612, NULL },
+  { "Ohungarumlaut",                     778, NULL },
+  { "asciicircum",                       584, NULL },
+  { "aring",                             556, NULL },
+  { "grave",                             333, NULL },
+  { "uogonek",                           611, NULL },
+  { "bracketright",                      333, NULL },
+  { "Iacute",                            278, NULL },
+  { "ampersand",                         722, NULL },
+  { "igrave",                            278, NULL },
+  { "lacute",                            278, NULL },
+  { "Ncaron",                            722, NULL },
+  { "plus",                              584, NULL },
+  { "uring",                             611, NULL },
+  { "quotesinglbase",                    278, NULL },
+  { "lcommaaccent",                      278, NULL },
+  { "Yacute",                            667, NULL },
+  { "ohungarumlaut",                     611, NULL },
+  { "threesuperior",                     333, NULL },
+  { "acute",                             333, NULL },
+  { "section",                           556, NULL },
+  { "dieresis",                          333, NULL },
+  { "iacute",                            278, NULL },
+  { "quotedblbase",                      500, NULL },
+  { "ncaron",                            611, NULL },
+  { "florin",                            556, NULL },
+  { "yacute",                            556, NULL },
+  { "Rcommaaccent",                      722, NULL },
+  { "fi",                                611, NULL },
+  { "fl",                                611, NULL },
+  { "Acircumflex",                       722, NULL },
+  { "Cacute",                            722, NULL },
+  { "Icircumflex",                       278, NULL },
+  { "guillemotleft",                     556, NULL },
+  { "germandbls",                        611, NULL },
+  { "Amacron",                           722, NULL },
+  { "seven",                             556, NULL },
+  { "Sacute",                            667, NULL },
+  { "ordmasculine",                      365, NULL },
+  { "dotlessi",                          278, NULL },
+  { "sterling",                          556, NULL },
+  { "notequal",                          549, NULL },
+  { "Imacron",                           278, NULL },
+  { "rcommaaccent",                      389, NULL },
+  { "Zdotaccent",                        611, NULL },
+  { "acircumflex",                       556, NULL },
+  { "cacute",                            556, NULL },
+  { "Ecaron",                            667, NULL },
+  { "icircumflex",                       278, NULL },
+  { "braceright",                        389, NULL },
+  { "quotedblright",                     500, NULL },
+  { "amacron",                           556, NULL },
+  { "sacute",                            556, NULL },
+  { "imacron",                           278, NULL },
+  { "cent",                              556, NULL },
+  { "currency",                          556, NULL },
+  { "logicalnot",                        584, NULL },
+  { "zdotaccent",                        500, NULL },
+  { "Atilde",                            722, NULL },
+  { "breve",                             333, NULL },
+  { "bar",                               280, NULL },
+  { "fraction",                          167, NULL },
+  { "less",                              584, NULL },
+  { "ecaron",                            556, NULL },
+  { "guilsinglleft",                     333, NULL },
+  { "exclam",                            333, NULL },
+  { "period",                            278, NULL },
+  { "Rcaron",                            722, NULL },
+  { "Kcommaaccent",                      722, NULL },
+  { "greater",                           584, NULL },
+  { "atilde",                            556, NULL },
+  { "brokenbar",                         280, NULL },
+  { "quoteleft",                         278, NULL },
+  { "Edotaccent",                        667, NULL },
+  { "onesuperior",                       333, NULL }
+};
+
+static BuiltinFontWidth helveticaObliqueWidthsTab[] = {
+  { "Ntilde",                            722, NULL },
+  { "rcaron",                            333, NULL },
+  { "kcommaaccent",                      500, NULL },
+  { "Ncommaaccent",                      722, NULL },
+  { "Zacute",                            611, NULL },
+  { "comma",                             278, NULL },
+  { "cedilla",                           333, NULL },
+  { "plusminus",                         584, NULL },
+  { "circumflex",                        333, NULL },
+  { "dotaccent",                         333, NULL },
+  { "edotaccent",                        556, NULL },
+  { "asciitilde",                        584, NULL },
+  { "colon",                             278, NULL },
+  { "onehalf",                           834, NULL },
+  { "dollar",                            556, NULL },
+  { "Lcaron",                            556, NULL },
+  { "ntilde",                            556, NULL },
+  { "Aogonek",                           667, NULL },
+  { "ncommaaccent",                      556, NULL },
+  { "minus",                             584, NULL },
+  { "Iogonek",                           278, NULL },
+  { "zacute",                            500, NULL },
+  { "yen",                               556, NULL },
+  { "space",                             278, NULL },
+  { "Omacron",                           778, NULL },
+  { "questiondown",                      611, NULL },
+  { "emdash",                           1000, NULL },
+  { "Agrave",                            667, NULL },
+  { "three",                             556, NULL },
+  { "numbersign",                        556, NULL },
+  { "lcaron",                            299, NULL },
+  { "A",                                 667, NULL },
+  { "B",                                 667, NULL },
+  { "C",                                 722, NULL },
+  { "aogonek",                           556, NULL },
+  { "D",                                 722, NULL },
+  { "E",                                 667, NULL },
+  { "onequarter",                        834, NULL },
+  { "F",                                 611, NULL },
+  { "G",                                 778, NULL },
+  { "H",                                 722, NULL },
+  { "I",                                 278, NULL },
+  { "J",                                 500, NULL },
+  { "K",                                 667, NULL },
+  { "iogonek",                           222, NULL },
+  { "backslash",                         278, NULL },
+  { "L",                                 556, NULL },
+  { "periodcentered",                    278, NULL },
+  { "M",                                 833, NULL },
+  { "N",                                 722, NULL },
+  { "omacron",                           556, NULL },
+  { "Tcommaaccent",                      611, NULL },
+  { "O",                                 778, NULL },
+  { "P",                                 667, NULL },
+  { "Q",                                 778, NULL },
+  { "Uhungarumlaut",                     722, NULL },
+  { "R",                                 722, NULL },
+  { "Aacute",                            667, NULL },
+  { "caron",                             333, NULL },
+  { "S",                                 667, NULL },
+  { "T",                                 611, NULL },
+  { "U",                                 722, NULL },
+  { "agrave",                            556, NULL },
+  { "V",                                 667, NULL },
+  { "W",                                 944, NULL },
+  { "X",                                 667, NULL },
+  { "question",                          556, NULL },
+  { "equal",                             584, NULL },
+  { "Y",                                 667, NULL },
+  { "Z",                                 611, NULL },
+  { "four",                              556, NULL },
+  { "a",                                 556, NULL },
+  { "Gcommaaccent",                      778, NULL },
+  { "b",                                 556, NULL },
+  { "c",                                 500, NULL },
+  { "d",                                 556, NULL },
+  { "e",                                 556, NULL },
+  { "f",                                 278, NULL },
+  { "g",                                 556, NULL },
+  { "bullet",                            350, NULL },
+  { "h",                                 556, NULL },
+  { "i",                                 222, NULL },
+  { "Oslash",                            778, NULL },
+  { "dagger",                            556, NULL },
+  { "j",                                 222, NULL },
+  { "k",                                 500, NULL },
+  { "l",                                 222, NULL },
+  { "m",                                 833, NULL },
+  { "n",                                 556, NULL },
+  { "tcommaaccent",                      278, NULL },
+  { "o",                                 556, NULL },
+  { "ordfeminine",                       370, NULL },
+  { "ring",                              333, NULL },
+  { "p",                                 556, NULL },
+  { "q",                                 556, NULL },
+  { "uhungarumlaut",                     556, NULL },
+  { "r",                                 333, NULL },
+  { "twosuperior",                       333, NULL },
+  { "aacute",                            556, NULL },
+  { "s",                                 500, NULL },
+  { "OE",                               1000, NULL },
+  { "t",                                 278, NULL },
+  { "divide",                            584, NULL },
+  { "u",                                 556, NULL },
+  { "Ccaron",                            722, NULL },
+  { "v",                                 500, NULL },
+  { "w",                                 722, NULL },
+  { "x",                                 500, NULL },
+  { "y",                                 500, NULL },
+  { "z",                                 500, NULL },
+  { "Gbreve",                            778, NULL },
+  { "commaaccent",                       250, NULL },
+  { "hungarumlaut",                      333, NULL },
+  { "Idotaccent",                        278, NULL },
+  { "Nacute",                            722, NULL },
+  { "quotedbl",                          355, NULL },
+  { "gcommaaccent",                      556, NULL },
+  { "mu",                                556, NULL },
+  { "greaterequal",                      549, NULL },
+  { "Scaron",                            667, NULL },
+  { "Lslash",                            556, NULL },
+  { "semicolon",                         278, NULL },
+  { "oslash",                            611, NULL },
+  { "lessequal",                         549, NULL },
+  { "lozenge",                           471, NULL },
+  { "parenright",                        333, NULL },
+  { "ccaron",                            500, NULL },
+  { "Ecircumflex",                       667, NULL },
+  { "gbreve",                            556, NULL },
+  { "trademark",                        1000, NULL },
+  { "daggerdbl",                         556, NULL },
+  { "nacute",                            556, NULL },
+  { "macron",                            333, NULL },
+  { "Otilde",                            778, NULL },
+  { "Emacron",                           667, NULL },
+  { "ellipsis",                         1000, NULL },
+  { "scaron",                            500, NULL },
+  { "AE",                               1000, NULL },
+  { "Ucircumflex",                       722, NULL },
+  { "lslash",                            222, NULL },
+  { "quotedblleft",                      333, NULL },
+  { "guilsinglright",                    333, NULL },
+  { "hyphen",                            333, NULL },
+  { "quotesingle",                       191, NULL },
+  { "eight",                             556, NULL },
+  { "exclamdown",                        333, NULL },
+  { "endash",                            556, NULL },
+  { "oe",                                944, NULL },
+  { "Abreve",                            667, NULL },
+  { "Umacron",                           722, NULL },
+  { "ecircumflex",                       556, NULL },
+  { "Adieresis",                         667, NULL },
+  { "copyright",                         737, NULL },
+  { "Egrave",                            667, NULL },
+  { "slash",                             278, NULL },
+  { "Edieresis",                         667, NULL },
+  { "otilde",                            556, NULL },
+  { "Idieresis",                         278, NULL },
+  { "parenleft",                         333, NULL },
+  { "one",                               556, NULL },
+  { "emacron",                           556, NULL },
+  { "Odieresis",                         778, NULL },
+  { "ucircumflex",                       556, NULL },
+  { "bracketleft",                       278, NULL },
+  { "Ugrave",                            722, NULL },
+  { "quoteright",                        222, NULL },
+  { "Udieresis",                         722, NULL },
+  { "perthousand",                      1000, NULL },
+  { "Ydieresis",                         667, NULL },
+  { "umacron",                           556, NULL },
+  { "abreve",                            556, NULL },
+  { "Eacute",                            667, NULL },
+  { "adieresis",                         556, NULL },
+  { "egrave",                            556, NULL },
+  { "edieresis",                         556, NULL },
+  { "idieresis",                         278, NULL },
+  { "Eth",                               722, NULL },
+  { "ae",                                889, NULL },
+  { "asterisk",                          389, NULL },
+  { "odieresis",                         556, NULL },
+  { "Uacute",                            722, NULL },
+  { "ugrave",                            556, NULL },
+  { "nine",                              556, NULL },
+  { "five",                              556, NULL },
+  { "udieresis",                         556, NULL },
+  { "Zcaron",                            611, NULL },
+  { "Scommaaccent",                      667, NULL },
+  { "threequarters",                     834, NULL },
+  { "guillemotright",                    556, NULL },
+  { "Ccedilla",                          722, NULL },
+  { "ydieresis",                         500, NULL },
+  { "tilde",                             333, NULL },
+  { "at",                               1015, NULL },
+  { "eacute",                            556, NULL },
+  { "underscore",                        556, NULL },
+  { "Euro",                              556, NULL },
+  { "Dcroat",                            722, NULL },
+  { "multiply",                          584, NULL },
+  { "zero",                              556, NULL },
+  { "eth",                               556, NULL },
+  { "Scedilla",                          667, NULL },
+  { "Ograve",                            778, NULL },
+  { "Racute",                            722, NULL },
+  { "partialdiff",                       476, NULL },
+  { "uacute",                            556, NULL },
+  { "braceleft",                         334, NULL },
+  { "Thorn",                             667, NULL },
+  { "zcaron",                            500, NULL },
+  { "scommaaccent",                      500, NULL },
+  { "ccedilla",                          500, NULL },
+  { "Dcaron",                            722, NULL },
+  { "dcroat",                            556, NULL },
+  { "Ocircumflex",                       778, NULL },
+  { "Oacute",                            778, NULL },
+  { "scedilla",                          500, NULL },
+  { "ogonek",                            333, NULL },
+  { "ograve",                            556, NULL },
+  { "racute",                            333, NULL },
+  { "Tcaron",                            611, NULL },
+  { "Eogonek",                           667, NULL },
+  { "thorn",                             556, NULL },
+  { "degree",                            400, NULL },
+  { "registered",                        737, NULL },
+  { "radical",                           453, NULL },
+  { "Aring",                             667, NULL },
+  { "percent",                           889, NULL },
+  { "six",                               556, NULL },
+  { "paragraph",                         537, NULL },
+  { "dcaron",                            643, NULL },
+  { "Uogonek",                           722, NULL },
+  { "two",                               556, NULL },
+  { "summation",                         600, NULL },
+  { "Igrave",                            278, NULL },
+  { "Lacute",                            556, NULL },
+  { "ocircumflex",                       556, NULL },
+  { "oacute",                            556, NULL },
+  { "Uring",                             722, NULL },
+  { "Lcommaaccent",                      556, NULL },
+  { "tcaron",                            317, NULL },
+  { "eogonek",                           556, NULL },
+  { "Delta",                             612, NULL },
+  { "Ohungarumlaut",                     778, NULL },
+  { "asciicircum",                       469, NULL },
+  { "aring",                             556, NULL },
+  { "grave",                             333, NULL },
+  { "uogonek",                           556, NULL },
+  { "bracketright",                      278, NULL },
+  { "Iacute",                            278, NULL },
+  { "ampersand",                         667, NULL },
+  { "igrave",                            278, NULL },
+  { "lacute",                            222, NULL },
+  { "Ncaron",                            722, NULL },
+  { "plus",                              584, NULL },
+  { "uring",                             556, NULL },
+  { "quotesinglbase",                    222, NULL },
+  { "lcommaaccent",                      222, NULL },
+  { "Yacute",                            667, NULL },
+  { "ohungarumlaut",                     556, NULL },
+  { "threesuperior",                     333, NULL },
+  { "acute",                             333, NULL },
+  { "section",                           556, NULL },
+  { "dieresis",                          333, NULL },
+  { "iacute",                            278, NULL },
+  { "quotedblbase",                      333, NULL },
+  { "ncaron",                            556, NULL },
+  { "florin",                            556, NULL },
+  { "yacute",                            500, NULL },
+  { "Rcommaaccent",                      722, NULL },
+  { "fi",                                500, NULL },
+  { "fl",                                500, NULL },
+  { "Acircumflex",                       667, NULL },
+  { "Cacute",                            722, NULL },
+  { "Icircumflex",                       278, NULL },
+  { "guillemotleft",                     556, NULL },
+  { "germandbls",                        611, NULL },
+  { "Amacron",                           667, NULL },
+  { "seven",                             556, NULL },
+  { "Sacute",                            667, NULL },
+  { "ordmasculine",                      365, NULL },
+  { "dotlessi",                          278, NULL },
+  { "sterling",                          556, NULL },
+  { "notequal",                          549, NULL },
+  { "Imacron",                           278, NULL },
+  { "rcommaaccent",                      333, NULL },
+  { "Zdotaccent",                        611, NULL },
+  { "acircumflex",                       556, NULL },
+  { "cacute",                            500, NULL },
+  { "Ecaron",                            667, NULL },
+  { "icircumflex",                       278, NULL },
+  { "braceright",                        334, NULL },
+  { "quotedblright",                     333, NULL },
+  { "amacron",                           556, NULL },
+  { "sacute",                            500, NULL },
+  { "imacron",                           278, NULL },
+  { "cent",                              556, NULL },
+  { "currency",                          556, NULL },
+  { "logicalnot",                        584, NULL },
+  { "zdotaccent",                        500, NULL },
+  { "Atilde",                            667, NULL },
+  { "breve",                             333, NULL },
+  { "bar",                               260, NULL },
+  { "fraction",                          167, NULL },
+  { "less",                              584, NULL },
+  { "ecaron",                            556, NULL },
+  { "guilsinglleft",                     333, NULL },
+  { "exclam",                            278, NULL },
+  { "period",                            278, NULL },
+  { "Rcaron",                            722, NULL },
+  { "Kcommaaccent",                      667, NULL },
+  { "greater",                           584, NULL },
+  { "atilde",                            556, NULL },
+  { "brokenbar",                         260, NULL },
+  { "quoteleft",                         222, NULL },
+  { "Edotaccent",                        667, NULL },
+  { "onesuperior",                       333, NULL }
+};
+
+static BuiltinFontWidth symbolWidthsTab[] = {
+  { "bracketleftex",                     384, NULL },
+  { "alpha",                             631, NULL },
+  { "union",                             768, NULL },
+  { "infinity",                          713, NULL },
+  { "comma",                             250, NULL },
+  { "copyrightsans",                     790, NULL },
+  { "plusminus",                         549, NULL },
+  { "arrowup",                           603, NULL },
+  { "apple",                             790, NULL },
+  { "parenleftbt",                       384, NULL },
+  { "notelement",                        713, NULL },
+  { "colon",                             278, NULL },
+  { "beta",                              549, NULL },
+  { "braceleftbt",                       494, NULL },
+  { "Lambda",                            686, NULL },
+  { "Phi",                               763, NULL },
+  { "minus",                             549, NULL },
+  { "space",                             250, NULL },
+  { "Sigma",                             592, NULL },
+  { "approxequal",                       549, NULL },
+  { "minute",                            247, NULL },
+  { "circleplus",                        768, NULL },
+  { "Omicron",                           722, NULL },
+  { "three",                             500, NULL },
+  { "numbersign",                        500, NULL },
+  { "lambda",                            549, NULL },
+  { "phi",                               521, NULL },
+  { "aleph",                             823, NULL },
+  { "Tau",                               611, NULL },
+  { "spade",                             753, NULL },
+  { "logicaland",                        603, NULL },
+  { "sigma",                             603, NULL },
+  { "propersuperset",                    713, NULL },
+  { "omicron",                           549, NULL },
+  { "question",                          444, NULL },
+  { "equal",                             549, NULL },
+  { "Epsilon",                           611, NULL },
+  { "emptyset",                          823, NULL },
+  { "diamond",                           753, NULL },
+  { "four",                              500, NULL },
+  { "Mu",                                889, NULL },
+  { "parenlefttp",                       384, NULL },
+  { "club",                              753, NULL },
+  { "bullet",                            460, NULL },
+  { "Omega",                             768, NULL },
+  { "tau",                               439, NULL },
+  { "Upsilon",                           690, NULL },
+  { "bracelefttp",                       494, NULL },
+  { "heart",                             753, NULL },
+  { "divide",                            549, NULL },
+  { "epsilon",                           439, NULL },
+  { "logicalor",                         603, NULL },
+  { "parenleftex",                       384, NULL },
+  { "greaterequal",                      549, NULL },
+  { "mu",                                576, NULL },
+  { "Nu",                                722, NULL },
+  { "therefore",                         863, NULL },
+  { "notsubset",                         713, NULL },
+  { "omega",                             686, NULL },
+  { "semicolon",                         278, NULL },
+  { "element",                           713, NULL },
+  { "upsilon",                           576, NULL },
+  { "existential",                       549, NULL },
+  { "integralbt",                        686, NULL },
+  { "lessequal",                         549, NULL },
+  { "phi1",                              603, NULL },
+  { "lozenge",                           494, NULL },
+  { "trademarkserif",                    890, NULL },
+  { "parenright",                        333, NULL },
+  { "reflexsuperset",                    713, NULL },
+  { "sigma1",                            439, NULL },
+  { "nu",                                521, NULL },
+  { "Gamma",                             603, NULL },
+  { "angleright",                        329, NULL },
+  { "ellipsis",                         1000, NULL },
+  { "Rho",                               556, NULL },
+  { "parenrightbt",                      384, NULL },
+  { "radicalex",                         500, NULL },
+  { "eight",                             500, NULL },
+  { "angleleft",                         329, NULL },
+  { "arrowdbldown",                      603, NULL },
+  { "congruent",                         549, NULL },
+  { "Theta",                             741, NULL },
+  { "intersection",                      768, NULL },
+  { "Pi",                                768, NULL },
+  { "slash",                             278, NULL },
+  { "registerserif",                     790, NULL },
+  { "parenleft",                         333, NULL },
+  { "one",                               500, NULL },
+  { "gamma",                             411, NULL },
+  { "bracketleft",                       333, NULL },
+  { "rho",                               549, NULL },
+  { "circlemultiply",                    768, NULL },
+  { "Chi",                               722, NULL },
+  { "theta",                             521, NULL },
+  { "pi",                                549, NULL },
+  { "integraltp",                        686, NULL },
+  { "Eta",                               722, NULL },
+  { "product",                           823, NULL },
+  { "nine",                              500, NULL },
+  { "five",                              500, NULL },
+  { "propersubset",                      713, NULL },
+  { "bracketrightbt",                    384, NULL },
+  { "trademarksans",                     786, NULL },
+  { "dotmath",                           250, NULL },
+  { "integralex",                        686, NULL },
+  { "chi",                               549, NULL },
+  { "parenrighttp",                      384, NULL },
+  { "eta",                               603, NULL },
+  { "underscore",                        500, NULL },
+  { "Euro",                              750, NULL },
+  { "multiply",                          549, NULL },
+  { "zero",                              500, NULL },
+  { "partialdiff",                       494, NULL },
+  { "angle",                             768, NULL },
+  { "arrowdblleft",                      987, NULL },
+  { "braceleft",                         480, NULL },
+  { "parenrightex",                      384, NULL },
+  { "Rfraktur",                          795, NULL },
+  { "Zeta",                              611, NULL },
+  { "braceex",                           494, NULL },
+  { "arrowdblup",                        603, NULL },
+  { "arrowdown",                         603, NULL },
+  { "Ifraktur",                          686, NULL },
+  { "degree",                            400, NULL },
+  { "Iota",                              333, NULL },
+  { "perpendicular",                     658, NULL },
+  { "radical",                           549, NULL },
+  { "asteriskmath",                      500, NULL },
+  { "percent",                           833, NULL },
+  { "zeta",                              494, NULL },
+  { "six",                               500, NULL },
+  { "two",                               500, NULL },
+  { "weierstrass",                       987, NULL },
+  { "summation",                         713, NULL },
+  { "bracketrighttp",                    384, NULL },
+  { "carriagereturn",                    658, NULL },
+  { "suchthat",                          439, NULL },
+  { "arrowvertex",                       603, NULL },
+  { "Delta",                             612, NULL },
+  { "iota",                              329, NULL },
+  { "arrowhorizex",                     1000, NULL },
+  { "bracketrightex",                    384, NULL },
+  { "bracketright",                      333, NULL },
+  { "ampersand",                         778, NULL },
+  { "plus",                              549, NULL },
+  { "proportional",                      713, NULL },
+  { "delta",                             494, NULL },
+  { "copyrightserif",                    790, NULL },
+  { "bracerightmid",                     494, NULL },
+  { "arrowleft",                         987, NULL },
+  { "second",                            411, NULL },
+  { "arrowdblboth",                     1042, NULL },
+  { "florin",                            500, NULL },
+  { "Psi",                               795, NULL },
+  { "bracerightbt",                      494, NULL },
+  { "bracketleftbt",                     384, NULL },
+  { "seven",                             500, NULL },
+  { "braceleftmid",                      494, NULL },
+  { "notequal",                          549, NULL },
+  { "psi",                               686, NULL },
+  { "equivalence",                       549, NULL },
+  { "universal",                         713, NULL },
+  { "arrowdblright",                     987, NULL },
+  { "braceright",                        480, NULL },
+  { "reflexsubset",                      713, NULL },
+  { "Xi",                                645, NULL },
+  { "theta1",                            631, NULL },
+  { "logicalnot",                        713, NULL },
+  { "Kappa",                             722, NULL },
+  { "similar",                           549, NULL },
+  { "bar",                               200, NULL },
+  { "fraction",                          167, NULL },
+  { "less",                              549, NULL },
+  { "registersans",                      790, NULL },
+  { "omega1",                            713, NULL },
+  { "exclam",                            333, NULL },
+  { "Upsilon1",                          620, NULL },
+  { "bracerighttp",                      494, NULL },
+  { "xi",                                493, NULL },
+  { "period",                            250, NULL },
+  { "Alpha",                             722, NULL },
+  { "arrowright",                        987, NULL },
+  { "greater",                           549, NULL },
+  { "bracketlefttp",                     384, NULL },
+  { "kappa",                             549, NULL },
+  { "gradient",                          713, NULL },
+  { "integral",                          274, NULL },
+  { "arrowboth",                        1042, NULL },
+  { "Beta",                              667, NULL }
+};
+
+static BuiltinFontWidth timesBoldWidthsTab[] = {
+  { "Ntilde",                            722, NULL },
+  { "rcaron",                            444, NULL },
+  { "kcommaaccent",                      556, NULL },
+  { "Ncommaaccent",                      722, NULL },
+  { "Zacute",                            667, NULL },
+  { "comma",                             250, NULL },
+  { "cedilla",                           333, NULL },
+  { "plusminus",                         570, NULL },
+  { "circumflex",                        333, NULL },
+  { "dotaccent",                         333, NULL },
+  { "edotaccent",                        444, NULL },
+  { "asciitilde",                        520, NULL },
+  { "colon",                             333, NULL },
+  { "onehalf",                           750, NULL },
+  { "dollar",                            500, NULL },
+  { "Lcaron",                            667, NULL },
+  { "ntilde",                            556, NULL },
+  { "Aogonek",                           722, NULL },
+  { "ncommaaccent",                      556, NULL },
+  { "minus",                             570, NULL },
+  { "Iogonek",                           389, NULL },
+  { "zacute",                            444, NULL },
+  { "yen",                               500, NULL },
+  { "space",                             250, NULL },
+  { "Omacron",                           778, NULL },
+  { "questiondown",                      500, NULL },
+  { "emdash",                           1000, NULL },
+  { "Agrave",                            722, NULL },
+  { "three",                             500, NULL },
+  { "numbersign",                        500, NULL },
+  { "lcaron",                            394, NULL },
+  { "A",                                 722, NULL },
+  { "B",                                 667, NULL },
+  { "C",                                 722, NULL },
+  { "aogonek",                           500, NULL },
+  { "D",                                 722, NULL },
+  { "E",                                 667, NULL },
+  { "onequarter",                        750, NULL },
+  { "F",                                 611, NULL },
+  { "G",                                 778, NULL },
+  { "H",                                 778, NULL },
+  { "I",                                 389, NULL },
+  { "J",                                 500, NULL },
+  { "K",                                 778, NULL },
+  { "iogonek",                           278, NULL },
+  { "backslash",                         278, NULL },
+  { "L",                                 667, NULL },
+  { "periodcentered",                    250, NULL },
+  { "M",                                 944, NULL },
+  { "N",                                 722, NULL },
+  { "omacron",                           500, NULL },
+  { "Tcommaaccent",                      667, NULL },
+  { "O",                                 778, NULL },
+  { "P",                                 611, NULL },
+  { "Q",                                 778, NULL },
+  { "Uhungarumlaut",                     722, NULL },
+  { "R",                                 722, NULL },
+  { "Aacute",                            722, NULL },
+  { "caron",                             333, NULL },
+  { "S",                                 556, NULL },
+  { "T",                                 667, NULL },
+  { "U",                                 722, NULL },
+  { "agrave",                            500, NULL },
+  { "V",                                 722, NULL },
+  { "W",                                1000, NULL },
+  { "X",                                 722, NULL },
+  { "question",                          500, NULL },
+  { "equal",                             570, NULL },
+  { "Y",                                 722, NULL },
+  { "Z",                                 667, NULL },
+  { "four",                              500, NULL },
+  { "a",                                 500, NULL },
+  { "Gcommaaccent",                      778, NULL },
+  { "b",                                 556, NULL },
+  { "c",                                 444, NULL },
+  { "d",                                 556, NULL },
+  { "e",                                 444, NULL },
+  { "f",                                 333, NULL },
+  { "g",                                 500, NULL },
+  { "bullet",                            350, NULL },
+  { "h",                                 556, NULL },
+  { "i",                                 278, NULL },
+  { "Oslash",                            778, NULL },
+  { "dagger",                            500, NULL },
+  { "j",                                 333, NULL },
+  { "k",                                 556, NULL },
+  { "l",                                 278, NULL },
+  { "m",                                 833, NULL },
+  { "n",                                 556, NULL },
+  { "tcommaaccent",                      333, NULL },
+  { "o",                                 500, NULL },
+  { "ordfeminine",                       300, NULL },
+  { "ring",                              333, NULL },
+  { "p",                                 556, NULL },
+  { "q",                                 556, NULL },
+  { "uhungarumlaut",                     556, NULL },
+  { "r",                                 444, NULL },
+  { "twosuperior",                       300, NULL },
+  { "aacute",                            500, NULL },
+  { "s",                                 389, NULL },
+  { "OE",                               1000, NULL },
+  { "t",                                 333, NULL },
+  { "divide",                            570, NULL },
+  { "u",                                 556, NULL },
+  { "Ccaron",                            722, NULL },
+  { "v",                                 500, NULL },
+  { "w",                                 722, NULL },
+  { "x",                                 500, NULL },
+  { "y",                                 500, NULL },
+  { "z",                                 444, NULL },
+  { "Gbreve",                            778, NULL },
+  { "commaaccent",                       250, NULL },
+  { "hungarumlaut",                      333, NULL },
+  { "Idotaccent",                        389, NULL },
+  { "Nacute",                            722, NULL },
+  { "quotedbl",                          555, NULL },
+  { "gcommaaccent",                      500, NULL },
+  { "mu",                                556, NULL },
+  { "greaterequal",                      549, NULL },
+  { "Scaron",                            556, NULL },
+  { "Lslash",                            667, NULL },
+  { "semicolon",                         333, NULL },
+  { "oslash",                            500, NULL },
+  { "lessequal",                         549, NULL },
+  { "lozenge",                           494, NULL },
+  { "parenright",                        333, NULL },
+  { "ccaron",                            444, NULL },
+  { "Ecircumflex",                       667, NULL },
+  { "gbreve",                            500, NULL },
+  { "trademark",                        1000, NULL },
+  { "daggerdbl",                         500, NULL },
+  { "nacute",                            556, NULL },
+  { "macron",                            333, NULL },
+  { "Otilde",                            778, NULL },
+  { "Emacron",                           667, NULL },
+  { "ellipsis",                         1000, NULL },
+  { "scaron",                            389, NULL },
+  { "AE",                               1000, NULL },
+  { "Ucircumflex",                       722, NULL },
+  { "lslash",                            278, NULL },
+  { "quotedblleft",                      500, NULL },
+  { "guilsinglright",                    333, NULL },
+  { "hyphen",                            333, NULL },
+  { "quotesingle",                       278, NULL },
+  { "eight",                             500, NULL },
+  { "exclamdown",                        333, NULL },
+  { "endash",                            500, NULL },
+  { "oe",                                722, NULL },
+  { "Abreve",                            722, NULL },
+  { "Umacron",                           722, NULL },
+  { "ecircumflex",                       444, NULL },
+  { "Adieresis",                         722, NULL },
+  { "copyright",                         747, NULL },
+  { "Egrave",                            667, NULL },
+  { "slash",                             278, NULL },
+  { "Edieresis",                         667, NULL },
+  { "otilde",                            500, NULL },
+  { "Idieresis",                         389, NULL },
+  { "parenleft",                         333, NULL },
+  { "one",                               500, NULL },
+  { "emacron",                           444, NULL },
+  { "Odieresis",                         778, NULL },
+  { "ucircumflex",                       556, NULL },
+  { "bracketleft",                       333, NULL },
+  { "Ugrave",                            722, NULL },
+  { "quoteright",                        333, NULL },
+  { "Udieresis",                         722, NULL },
+  { "perthousand",                      1000, NULL },
+  { "Ydieresis",                         722, NULL },
+  { "umacron",                           556, NULL },
+  { "abreve",                            500, NULL },
+  { "Eacute",                            667, NULL },
+  { "adieresis",                         500, NULL },
+  { "egrave",                            444, NULL },
+  { "edieresis",                         444, NULL },
+  { "idieresis",                         278, NULL },
+  { "Eth",                               722, NULL },
+  { "ae",                                722, NULL },
+  { "asterisk",                          500, NULL },
+  { "odieresis",                         500, NULL },
+  { "Uacute",                            722, NULL },
+  { "ugrave",                            556, NULL },
+  { "nine",                              500, NULL },
+  { "five",                              500, NULL },
+  { "udieresis",                         556, NULL },
+  { "Zcaron",                            667, NULL },
+  { "Scommaaccent",                      556, NULL },
+  { "threequarters",                     750, NULL },
+  { "guillemotright",                    500, NULL },
+  { "Ccedilla",                          722, NULL },
+  { "ydieresis",                         500, NULL },
+  { "tilde",                             333, NULL },
+  { "at",                                930, NULL },
+  { "eacute",                            444, NULL },
+  { "underscore",                        500, NULL },
+  { "Euro",                              500, NULL },
+  { "Dcroat",                            722, NULL },
+  { "multiply",                          570, NULL },
+  { "zero",                              500, NULL },
+  { "eth",                               500, NULL },
+  { "Scedilla",                          556, NULL },
+  { "Ograve",                            778, NULL },
+  { "Racute",                            722, NULL },
+  { "partialdiff",                       494, NULL },
+  { "uacute",                            556, NULL },
+  { "braceleft",                         394, NULL },
+  { "Thorn",                             611, NULL },
+  { "zcaron",                            444, NULL },
+  { "scommaaccent",                      389, NULL },
+  { "ccedilla",                          444, NULL },
+  { "Dcaron",                            722, NULL },
+  { "dcroat",                            556, NULL },
+  { "Ocircumflex",                       778, NULL },
+  { "Oacute",                            778, NULL },
+  { "scedilla",                          389, NULL },
+  { "ogonek",                            333, NULL },
+  { "ograve",                            500, NULL },
+  { "racute",                            444, NULL },
+  { "Tcaron",                            667, NULL },
+  { "Eogonek",                           667, NULL },
+  { "thorn",                             556, NULL },
+  { "degree",                            400, NULL },
+  { "registered",                        747, NULL },
+  { "radical",                           549, NULL },
+  { "Aring",                             722, NULL },
+  { "percent",                          1000, NULL },
+  { "six",                               500, NULL },
+  { "paragraph",                         540, NULL },
+  { "dcaron",                            672, NULL },
+  { "Uogonek",                           722, NULL },
+  { "two",                               500, NULL },
+  { "summation",                         600, NULL },
+  { "Igrave",                            389, NULL },
+  { "Lacute",                            667, NULL },
+  { "ocircumflex",                       500, NULL },
+  { "oacute",                            500, NULL },
+  { "Uring",                             722, NULL },
+  { "Lcommaaccent",                      667, NULL },
+  { "tcaron",                            416, NULL },
+  { "eogonek",                           444, NULL },
+  { "Delta",                             612, NULL },
+  { "Ohungarumlaut",                     778, NULL },
+  { "asciicircum",                       581, NULL },
+  { "aring",                             500, NULL },
+  { "grave",                             333, NULL },
+  { "uogonek",                           556, NULL },
+  { "bracketright",                      333, NULL },
+  { "Iacute",                            389, NULL },
+  { "ampersand",                         833, NULL },
+  { "igrave",                            278, NULL },
+  { "lacute",                            278, NULL },
+  { "Ncaron",                            722, NULL },
+  { "plus",                              570, NULL },
+  { "uring",                             556, NULL },
+  { "quotesinglbase",                    333, NULL },
+  { "lcommaaccent",                      278, NULL },
+  { "Yacute",                            722, NULL },
+  { "ohungarumlaut",                     500, NULL },
+  { "threesuperior",                     300, NULL },
+  { "acute",                             333, NULL },
+  { "section",                           500, NULL },
+  { "dieresis",                          333, NULL },
+  { "iacute",                            278, NULL },
+  { "quotedblbase",                      500, NULL },
+  { "ncaron",                            556, NULL },
+  { "florin",                            500, NULL },
+  { "yacute",                            500, NULL },
+  { "Rcommaaccent",                      722, NULL },
+  { "fi",                                556, NULL },
+  { "fl",                                556, NULL },
+  { "Acircumflex",                       722, NULL },
+  { "Cacute",                            722, NULL },
+  { "Icircumflex",                       389, NULL },
+  { "guillemotleft",                     500, NULL },
+  { "germandbls",                        556, NULL },
+  { "Amacron",                           722, NULL },
+  { "seven",                             500, NULL },
+  { "Sacute",                            556, NULL },
+  { "ordmasculine",                      330, NULL },
+  { "dotlessi",                          278, NULL },
+  { "sterling",                          500, NULL },
+  { "notequal",                          549, NULL },
+  { "Imacron",                           389, NULL },
+  { "rcommaaccent",                      444, NULL },
+  { "Zdotaccent",                        667, NULL },
+  { "acircumflex",                       500, NULL },
+  { "cacute",                            444, NULL },
+  { "Ecaron",                            667, NULL },
+  { "icircumflex",                       278, NULL },
+  { "braceright",                        394, NULL },
+  { "quotedblright",                     500, NULL },
+  { "amacron",                           500, NULL },
+  { "sacute",                            389, NULL },
+  { "imacron",                           278, NULL },
+  { "cent",                              500, NULL },
+  { "currency",                          500, NULL },
+  { "logicalnot",                        570, NULL },
+  { "zdotaccent",                        444, NULL },
+  { "Atilde",                            722, NULL },
+  { "breve",                             333, NULL },
+  { "bar",                               220, NULL },
+  { "fraction",                          167, NULL },
+  { "less",                              570, NULL },
+  { "ecaron",                            444, NULL },
+  { "guilsinglleft",                     333, NULL },
+  { "exclam",                            333, NULL },
+  { "period",                            250, NULL },
+  { "Rcaron",                            722, NULL },
+  { "Kcommaaccent",                      778, NULL },
+  { "greater",                           570, NULL },
+  { "atilde",                            500, NULL },
+  { "brokenbar",                         220, NULL },
+  { "quoteleft",                         333, NULL },
+  { "Edotaccent",                        667, NULL },
+  { "onesuperior",                       300, NULL }
+};
+
+static BuiltinFontWidth timesBoldItalicWidthsTab[] = {
+  { "Ntilde",                            722, NULL },
+  { "rcaron",                            389, NULL },
+  { "kcommaaccent",                      500, NULL },
+  { "Ncommaaccent",                      722, NULL },
+  { "Zacute",                            611, NULL },
+  { "comma",                             250, NULL },
+  { "cedilla",                           333, NULL },
+  { "plusminus",                         570, NULL },
+  { "circumflex",                        333, NULL },
+  { "dotaccent",                         333, NULL },
+  { "edotaccent",                        444, NULL },
+  { "asciitilde",                        570, NULL },
+  { "colon",                             333, NULL },
+  { "onehalf",                           750, NULL },
+  { "dollar",                            500, NULL },
+  { "Lcaron",                            611, NULL },
+  { "ntilde",                            556, NULL },
+  { "Aogonek",                           667, NULL },
+  { "ncommaaccent",                      556, NULL },
+  { "minus",                             606, NULL },
+  { "Iogonek",                           389, NULL },
+  { "zacute",                            389, NULL },
+  { "yen",                               500, NULL },
+  { "space",                             250, NULL },
+  { "Omacron",                           722, NULL },
+  { "questiondown",                      500, NULL },
+  { "emdash",                           1000, NULL },
+  { "Agrave",                            667, NULL },
+  { "three",                             500, NULL },
+  { "numbersign",                        500, NULL },
+  { "lcaron",                            382, NULL },
+  { "A",                                 667, NULL },
+  { "B",                                 667, NULL },
+  { "C",                                 667, NULL },
+  { "aogonek",                           500, NULL },
+  { "D",                                 722, NULL },
+  { "E",                                 667, NULL },
+  { "onequarter",                        750, NULL },
+  { "F",                                 667, NULL },
+  { "G",                                 722, NULL },
+  { "H",                                 778, NULL },
+  { "I",                                 389, NULL },
+  { "J",                                 500, NULL },
+  { "K",                                 667, NULL },
+  { "iogonek",                           278, NULL },
+  { "backslash",                         278, NULL },
+  { "L",                                 611, NULL },
+  { "periodcentered",                    250, NULL },
+  { "M",                                 889, NULL },
+  { "N",                                 722, NULL },
+  { "omacron",                           500, NULL },
+  { "Tcommaaccent",                      611, NULL },
+  { "O",                                 722, NULL },
+  { "P",                                 611, NULL },
+  { "Q",                                 722, NULL },
+  { "Uhungarumlaut",                     722, NULL },
+  { "R",                                 667, NULL },
+  { "Aacute",                            667, NULL },
+  { "caron",                             333, NULL },
+  { "S",                                 556, NULL },
+  { "T",                                 611, NULL },
+  { "U",                                 722, NULL },
+  { "agrave",                            500, NULL },
+  { "V",                                 667, NULL },
+  { "W",                                 889, NULL },
+  { "X",                                 667, NULL },
+  { "question",                          500, NULL },
+  { "equal",                             570, NULL },
+  { "Y",                                 611, NULL },
+  { "Z",                                 611, NULL },
+  { "four",                              500, NULL },
+  { "a",                                 500, NULL },
+  { "Gcommaaccent",                      722, NULL },
+  { "b",                                 500, NULL },
+  { "c",                                 444, NULL },
+  { "d",                                 500, NULL },
+  { "e",                                 444, NULL },
+  { "f",                                 333, NULL },
+  { "g",                                 500, NULL },
+  { "bullet",                            350, NULL },
+  { "h",                                 556, NULL },
+  { "i",                                 278, NULL },
+  { "Oslash",                            722, NULL },
+  { "dagger",                            500, NULL },
+  { "j",                                 278, NULL },
+  { "k",                                 500, NULL },
+  { "l",                                 278, NULL },
+  { "m",                                 778, NULL },
+  { "n",                                 556, NULL },
+  { "tcommaaccent",                      278, NULL },
+  { "o",                                 500, NULL },
+  { "ordfeminine",                       266, NULL },
+  { "ring",                              333, NULL },
+  { "p",                                 500, NULL },
+  { "q",                                 500, NULL },
+  { "uhungarumlaut",                     556, NULL },
+  { "r",                                 389, NULL },
+  { "twosuperior",                       300, NULL },
+  { "aacute",                            500, NULL },
+  { "s",                                 389, NULL },
+  { "OE",                                944, NULL },
+  { "t",                                 278, NULL },
+  { "divide",                            570, NULL },
+  { "u",                                 556, NULL },
+  { "Ccaron",                            667, NULL },
+  { "v",                                 444, NULL },
+  { "w",                                 667, NULL },
+  { "x",                                 500, NULL },
+  { "y",                                 444, NULL },
+  { "z",                                 389, NULL },
+  { "Gbreve",                            722, NULL },
+  { "commaaccent",                       250, NULL },
+  { "hungarumlaut",                      333, NULL },
+  { "Idotaccent",                        389, NULL },
+  { "Nacute",                            722, NULL },
+  { "quotedbl",                          555, NULL },
+  { "gcommaaccent",                      500, NULL },
+  { "mu",                                576, NULL },
+  { "greaterequal",                      549, NULL },
+  { "Scaron",                            556, NULL },
+  { "Lslash",                            611, NULL },
+  { "semicolon",                         333, NULL },
+  { "oslash",                            500, NULL },
+  { "lessequal",                         549, NULL },
+  { "lozenge",                           494, NULL },
+  { "parenright",                        333, NULL },
+  { "ccaron",                            444, NULL },
+  { "Ecircumflex",                       667, NULL },
+  { "gbreve",                            500, NULL },
+  { "trademark",                        1000, NULL },
+  { "daggerdbl",                         500, NULL },
+  { "nacute",                            556, NULL },
+  { "macron",                            333, NULL },
+  { "Otilde",                            722, NULL },
+  { "Emacron",                           667, NULL },
+  { "ellipsis",                         1000, NULL },
+  { "scaron",                            389, NULL },
+  { "AE",                                944, NULL },
+  { "Ucircumflex",                       722, NULL },
+  { "lslash",                            278, NULL },
+  { "quotedblleft",                      500, NULL },
+  { "guilsinglright",                    333, NULL },
+  { "hyphen",                            333, NULL },
+  { "quotesingle",                       278, NULL },
+  { "eight",                             500, NULL },
+  { "exclamdown",                        389, NULL },
+  { "endash",                            500, NULL },
+  { "oe",                                722, NULL },
+  { "Abreve",                            667, NULL },
+  { "Umacron",                           722, NULL },
+  { "ecircumflex",                       444, NULL },
+  { "Adieresis",                         667, NULL },
+  { "copyright",                         747, NULL },
+  { "Egrave",                            667, NULL },
+  { "slash",                             278, NULL },
+  { "Edieresis",                         667, NULL },
+  { "otilde",                            500, NULL },
+  { "Idieresis",                         389, NULL },
+  { "parenleft",                         333, NULL },
+  { "one",                               500, NULL },
+  { "emacron",                           444, NULL },
+  { "Odieresis",                         722, NULL },
+  { "ucircumflex",                       556, NULL },
+  { "bracketleft",                       333, NULL },
+  { "Ugrave",                            722, NULL },
+  { "quoteright",                        333, NULL },
+  { "Udieresis",                         722, NULL },
+  { "perthousand",                      1000, NULL },
+  { "Ydieresis",                         611, NULL },
+  { "umacron",                           556, NULL },
+  { "abreve",                            500, NULL },
+  { "Eacute",                            667, NULL },
+  { "adieresis",                         500, NULL },
+  { "egrave",                            444, NULL },
+  { "edieresis",                         444, NULL },
+  { "idieresis",                         278, NULL },
+  { "Eth",                               722, NULL },
+  { "ae",                                722, NULL },
+  { "asterisk",                          500, NULL },
+  { "odieresis",                         500, NULL },
+  { "Uacute",                            722, NULL },
+  { "ugrave",                            556, NULL },
+  { "nine",                              500, NULL },
+  { "five",                              500, NULL },
+  { "udieresis",                         556, NULL },
+  { "Zcaron",                            611, NULL },
+  { "Scommaaccent",                      556, NULL },
+  { "threequarters",                     750, NULL },
+  { "guillemotright",                    500, NULL },
+  { "Ccedilla",                          667, NULL },
+  { "ydieresis",                         444, NULL },
+  { "tilde",                             333, NULL },
+  { "at",                                832, NULL },
+  { "eacute",                            444, NULL },
+  { "underscore",                        500, NULL },
+  { "Euro",                              500, NULL },
+  { "Dcroat",                            722, NULL },
+  { "multiply",                          570, NULL },
+  { "zero",                              500, NULL },
+  { "eth",                               500, NULL },
+  { "Scedilla",                          556, NULL },
+  { "Ograve",                            722, NULL },
+  { "Racute",                            667, NULL },
+  { "partialdiff",                       494, NULL },
+  { "uacute",                            556, NULL },
+  { "braceleft",                         348, NULL },
+  { "Thorn",                             611, NULL },
+  { "zcaron",                            389, NULL },
+  { "scommaaccent",                      389, NULL },
+  { "ccedilla",                          444, NULL },
+  { "Dcaron",                            722, NULL },
+  { "dcroat",                            500, NULL },
+  { "Ocircumflex",                       722, NULL },
+  { "Oacute",                            722, NULL },
+  { "scedilla",                          389, NULL },
+  { "ogonek",                            333, NULL },
+  { "ograve",                            500, NULL },
+  { "racute",                            389, NULL },
+  { "Tcaron",                            611, NULL },
+  { "Eogonek",                           667, NULL },
+  { "thorn",                             500, NULL },
+  { "degree",                            400, NULL },
+  { "registered",                        747, NULL },
+  { "radical",                           549, NULL },
+  { "Aring",                             667, NULL },
+  { "percent",                           833, NULL },
+  { "six",                               500, NULL },
+  { "paragraph",                         500, NULL },
+  { "dcaron",                            608, NULL },
+  { "Uogonek",                           722, NULL },
+  { "two",                               500, NULL },
+  { "summation",                         600, NULL },
+  { "Igrave",                            389, NULL },
+  { "Lacute",                            611, NULL },
+  { "ocircumflex",                       500, NULL },
+  { "oacute",                            500, NULL },
+  { "Uring",                             722, NULL },
+  { "Lcommaaccent",                      611, NULL },
+  { "tcaron",                            366, NULL },
+  { "eogonek",                           444, NULL },
+  { "Delta",                             612, NULL },
+  { "Ohungarumlaut",                     722, NULL },
+  { "asciicircum",                       570, NULL },
+  { "aring",                             500, NULL },
+  { "grave",                             333, NULL },
+  { "uogonek",                           556, NULL },
+  { "bracketright",                      333, NULL },
+  { "Iacute",                            389, NULL },
+  { "ampersand",                         778, NULL },
+  { "igrave",                            278, NULL },
+  { "lacute",                            278, NULL },
+  { "Ncaron",                            722, NULL },
+  { "plus",                              570, NULL },
+  { "uring",                             556, NULL },
+  { "quotesinglbase",                    333, NULL },
+  { "lcommaaccent",                      278, NULL },
+  { "Yacute",                            611, NULL },
+  { "ohungarumlaut",                     500, NULL },
+  { "threesuperior",                     300, NULL },
+  { "acute",                             333, NULL },
+  { "section",                           500, NULL },
+  { "dieresis",                          333, NULL },
+  { "iacute",                            278, NULL },
+  { "quotedblbase",                      500, NULL },
+  { "ncaron",                            556, NULL },
+  { "florin",                            500, NULL },
+  { "yacute",                            444, NULL },
+  { "Rcommaaccent",                      667, NULL },
+  { "fi",                                556, NULL },
+  { "fl",                                556, NULL },
+  { "Acircumflex",                       667, NULL },
+  { "Cacute",                            667, NULL },
+  { "Icircumflex",                       389, NULL },
+  { "guillemotleft",                     500, NULL },
+  { "germandbls",                        500, NULL },
+  { "Amacron",                           667, NULL },
+  { "seven",                             500, NULL },
+  { "Sacute",                            556, NULL },
+  { "ordmasculine",                      300, NULL },
+  { "dotlessi",                          278, NULL },
+  { "sterling",                          500, NULL },
+  { "notequal",                          549, NULL },
+  { "Imacron",                           389, NULL },
+  { "rcommaaccent",                      389, NULL },
+  { "Zdotaccent",                        611, NULL },
+  { "acircumflex",                       500, NULL },
+  { "cacute",                            444, NULL },
+  { "Ecaron",                            667, NULL },
+  { "icircumflex",                       278, NULL },
+  { "braceright",                        348, NULL },
+  { "quotedblright",                     500, NULL },
+  { "amacron",                           500, NULL },
+  { "sacute",                            389, NULL },
+  { "imacron",                           278, NULL },
+  { "cent",                              500, NULL },
+  { "currency",                          500, NULL },
+  { "logicalnot",                        606, NULL },
+  { "zdotaccent",                        389, NULL },
+  { "Atilde",                            667, NULL },
+  { "breve",                             333, NULL },
+  { "bar",                               220, NULL },
+  { "fraction",                          167, NULL },
+  { "less",                              570, NULL },
+  { "ecaron",                            444, NULL },
+  { "guilsinglleft",                     333, NULL },
+  { "exclam",                            389, NULL },
+  { "period",                            250, NULL },
+  { "Rcaron",                            667, NULL },
+  { "Kcommaaccent",                      667, NULL },
+  { "greater",                           570, NULL },
+  { "atilde",                            500, NULL },
+  { "brokenbar",                         220, NULL },
+  { "quoteleft",                         333, NULL },
+  { "Edotaccent",                        667, NULL },
+  { "onesuperior",                       300, NULL }
+};
+
+static BuiltinFontWidth timesItalicWidthsTab[] = {
+  { "Ntilde",                            667, NULL },
+  { "rcaron",                            389, NULL },
+  { "kcommaaccent",                      444, NULL },
+  { "Ncommaaccent",                      667, NULL },
+  { "Zacute",                            556, NULL },
+  { "comma",                             250, NULL },
+  { "cedilla",                           333, NULL },
+  { "plusminus",                         675, NULL },
+  { "circumflex",                        333, NULL },
+  { "dotaccent",                         333, NULL },
+  { "edotaccent",                        444, NULL },
+  { "asciitilde",                        541, NULL },
+  { "colon",                             333, NULL },
+  { "onehalf",                           750, NULL },
+  { "dollar",                            500, NULL },
+  { "Lcaron",                            611, NULL },
+  { "ntilde",                            500, NULL },
+  { "Aogonek",                           611, NULL },
+  { "ncommaaccent",                      500, NULL },
+  { "minus",                             675, NULL },
+  { "Iogonek",                           333, NULL },
+  { "zacute",                            389, NULL },
+  { "yen",                               500, NULL },
+  { "space",                             250, NULL },
+  { "Omacron",                           722, NULL },
+  { "questiondown",                      500, NULL },
+  { "emdash",                            889, NULL },
+  { "Agrave",                            611, NULL },
+  { "three",                             500, NULL },
+  { "numbersign",                        500, NULL },
+  { "lcaron",                            300, NULL },
+  { "A",                                 611, NULL },
+  { "B",                                 611, NULL },
+  { "C",                                 667, NULL },
+  { "aogonek",                           500, NULL },
+  { "D",                                 722, NULL },
+  { "E",                                 611, NULL },
+  { "onequarter",                        750, NULL },
+  { "F",                                 611, NULL },
+  { "G",                                 722, NULL },
+  { "H",                                 722, NULL },
+  { "I",                                 333, NULL },
+  { "J",                                 444, NULL },
+  { "K",                                 667, NULL },
+  { "iogonek",                           278, NULL },
+  { "backslash",                         278, NULL },
+  { "L",                                 556, NULL },
+  { "periodcentered",                    250, NULL },
+  { "M",                                 833, NULL },
+  { "N",                                 667, NULL },
+  { "omacron",                           500, NULL },
+  { "Tcommaaccent",                      556, NULL },
+  { "O",                                 722, NULL },
+  { "P",                                 611, NULL },
+  { "Q",                                 722, NULL },
+  { "Uhungarumlaut",                     722, NULL },
+  { "R",                                 611, NULL },
+  { "Aacute",                            611, NULL },
+  { "caron",                             333, NULL },
+  { "S",                                 500, NULL },
+  { "T",                                 556, NULL },
+  { "U",                                 722, NULL },
+  { "agrave",                            500, NULL },
+  { "V",                                 611, NULL },
+  { "W",                                 833, NULL },
+  { "X",                                 611, NULL },
+  { "question",                          500, NULL },
+  { "equal",                             675, NULL },
+  { "Y",                                 556, NULL },
+  { "Z",                                 556, NULL },
+  { "four",                              500, NULL },
+  { "a",                                 500, NULL },
+  { "Gcommaaccent",                      722, NULL },
+  { "b",                                 500, NULL },
+  { "c",                                 444, NULL },
+  { "d",                                 500, NULL },
+  { "e",                                 444, NULL },
+  { "f",                                 278, NULL },
+  { "g",                                 500, NULL },
+  { "bullet",                            350, NULL },
+  { "h",                                 500, NULL },
+  { "i",                                 278, NULL },
+  { "Oslash",                            722, NULL },
+  { "dagger",                            500, NULL },
+  { "j",                                 278, NULL },
+  { "k",                                 444, NULL },
+  { "l",                                 278, NULL },
+  { "m",                                 722, NULL },
+  { "n",                                 500, NULL },
+  { "tcommaaccent",                      278, NULL },
+  { "o",                                 500, NULL },
+  { "ordfeminine",                       276, NULL },
+  { "ring",                              333, NULL },
+  { "p",                                 500, NULL },
+  { "q",                                 500, NULL },
+  { "uhungarumlaut",                     500, NULL },
+  { "r",                                 389, NULL },
+  { "twosuperior",                       300, NULL },
+  { "aacute",                            500, NULL },
+  { "s",                                 389, NULL },
+  { "OE",                                944, NULL },
+  { "t",                                 278, NULL },
+  { "divide",                            675, NULL },
+  { "u",                                 500, NULL },
+  { "Ccaron",                            667, NULL },
+  { "v",                                 444, NULL },
+  { "w",                                 667, NULL },
+  { "x",                                 444, NULL },
+  { "y",                                 444, NULL },
+  { "z",                                 389, NULL },
+  { "Gbreve",                            722, NULL },
+  { "commaaccent",                       250, NULL },
+  { "hungarumlaut",                      333, NULL },
+  { "Idotaccent",                        333, NULL },
+  { "Nacute",                            667, NULL },
+  { "quotedbl",                          420, NULL },
+  { "gcommaaccent",                      500, NULL },
+  { "mu",                                500, NULL },
+  { "greaterequal",                      549, NULL },
+  { "Scaron",                            500, NULL },
+  { "Lslash",                            556, NULL },
+  { "semicolon",                         333, NULL },
+  { "oslash",                            500, NULL },
+  { "lessequal",                         549, NULL },
+  { "lozenge",                           471, NULL },
+  { "parenright",                        333, NULL },
+  { "ccaron",                            444, NULL },
+  { "Ecircumflex",                       611, NULL },
+  { "gbreve",                            500, NULL },
+  { "trademark",                         980, NULL },
+  { "daggerdbl",                         500, NULL },
+  { "nacute",                            500, NULL },
+  { "macron",                            333, NULL },
+  { "Otilde",                            722, NULL },
+  { "Emacron",                           611, NULL },
+  { "ellipsis",                          889, NULL },
+  { "scaron",                            389, NULL },
+  { "AE",                                889, NULL },
+  { "Ucircumflex",                       722, NULL },
+  { "lslash",                            278, NULL },
+  { "quotedblleft",                      556, NULL },
+  { "guilsinglright",                    333, NULL },
+  { "hyphen",                            333, NULL },
+  { "quotesingle",                       214, NULL },
+  { "eight",                             500, NULL },
+  { "exclamdown",                        389, NULL },
+  { "endash",                            500, NULL },
+  { "oe",                                667, NULL },
+  { "Abreve",                            611, NULL },
+  { "Umacron",                           722, NULL },
+  { "ecircumflex",                       444, NULL },
+  { "Adieresis",                         611, NULL },
+  { "copyright",                         760, NULL },
+  { "Egrave",                            611, NULL },
+  { "slash",                             278, NULL },
+  { "Edieresis",                         611, NULL },
+  { "otilde",                            500, NULL },
+  { "Idieresis",                         333, NULL },
+  { "parenleft",                         333, NULL },
+  { "one",                               500, NULL },
+  { "emacron",                           444, NULL },
+  { "Odieresis",                         722, NULL },
+  { "ucircumflex",                       500, NULL },
+  { "bracketleft",                       389, NULL },
+  { "Ugrave",                            722, NULL },
+  { "quoteright",                        333, NULL },
+  { "Udieresis",                         722, NULL },
+  { "perthousand",                      1000, NULL },
+  { "Ydieresis",                         556, NULL },
+  { "umacron",                           500, NULL },
+  { "abreve",                            500, NULL },
+  { "Eacute",                            611, NULL },
+  { "adieresis",                         500, NULL },
+  { "egrave",                            444, NULL },
+  { "edieresis",                         444, NULL },
+  { "idieresis",                         278, NULL },
+  { "Eth",                               722, NULL },
+  { "ae",                                667, NULL },
+  { "asterisk",                          500, NULL },
+  { "odieresis",                         500, NULL },
+  { "Uacute",                            722, NULL },
+  { "ugrave",                            500, NULL },
+  { "nine",                              500, NULL },
+  { "five",                              500, NULL },
+  { "udieresis",                         500, NULL },
+  { "Zcaron",                            556, NULL },
+  { "Scommaaccent",                      500, NULL },
+  { "threequarters",                     750, NULL },
+  { "guillemotright",                    500, NULL },
+  { "Ccedilla",                          667, NULL },
+  { "ydieresis",                         444, NULL },
+  { "tilde",                             333, NULL },
+  { "at",                                920, NULL },
+  { "eacute",                            444, NULL },
+  { "underscore",                        500, NULL },
+  { "Euro",                              500, NULL },
+  { "Dcroat",                            722, NULL },
+  { "multiply",                          675, NULL },
+  { "zero",                              500, NULL },
+  { "eth",                               500, NULL },
+  { "Scedilla",                          500, NULL },
+  { "Ograve",                            722, NULL },
+  { "Racute",                            611, NULL },
+  { "partialdiff",                       476, NULL },
+  { "uacute",                            500, NULL },
+  { "braceleft",                         400, NULL },
+  { "Thorn",                             611, NULL },
+  { "zcaron",                            389, NULL },
+  { "scommaaccent",                      389, NULL },
+  { "ccedilla",                          444, NULL },
+  { "Dcaron",                            722, NULL },
+  { "dcroat",                            500, NULL },
+  { "Ocircumflex",                       722, NULL },
+  { "Oacute",                            722, NULL },
+  { "scedilla",                          389, NULL },
+  { "ogonek",                            333, NULL },
+  { "ograve",                            500, NULL },
+  { "racute",                            389, NULL },
+  { "Tcaron",                            556, NULL },
+  { "Eogonek",                           611, NULL },
+  { "thorn",                             500, NULL },
+  { "degree",                            400, NULL },
+  { "registered",                        760, NULL },
+  { "radical",                           453, NULL },
+  { "Aring",                             611, NULL },
+  { "percent",                           833, NULL },
+  { "six",                               500, NULL },
+  { "paragraph",                         523, NULL },
+  { "dcaron",                            544, NULL },
+  { "Uogonek",                           722, NULL },
+  { "two",                               500, NULL },
+  { "summation",                         600, NULL },
+  { "Igrave",                            333, NULL },
+  { "Lacute",                            556, NULL },
+  { "ocircumflex",                       500, NULL },
+  { "oacute",                            500, NULL },
+  { "Uring",                             722, NULL },
+  { "Lcommaaccent",                      556, NULL },
+  { "tcaron",                            300, NULL },
+  { "eogonek",                           444, NULL },
+  { "Delta",                             612, NULL },
+  { "Ohungarumlaut",                     722, NULL },
+  { "asciicircum",                       422, NULL },
+  { "aring",                             500, NULL },
+  { "grave",                             333, NULL },
+  { "uogonek",                           500, NULL },
+  { "bracketright",                      389, NULL },
+  { "Iacute",                            333, NULL },
+  { "ampersand",                         778, NULL },
+  { "igrave",                            278, NULL },
+  { "lacute",                            278, NULL },
+  { "Ncaron",                            667, NULL },
+  { "plus",                              675, NULL },
+  { "uring",                             500, NULL },
+  { "quotesinglbase",                    333, NULL },
+  { "lcommaaccent",                      278, NULL },
+  { "Yacute",                            556, NULL },
+  { "ohungarumlaut",                     500, NULL },
+  { "threesuperior",                     300, NULL },
+  { "acute",                             333, NULL },
+  { "section",                           500, NULL },
+  { "dieresis",                          333, NULL },
+  { "iacute",                            278, NULL },
+  { "quotedblbase",                      556, NULL },
+  { "ncaron",                            500, NULL },
+  { "florin",                            500, NULL },
+  { "yacute",                            444, NULL },
+  { "Rcommaaccent",                      611, NULL },
+  { "fi",                                500, NULL },
+  { "fl",                                500, NULL },
+  { "Acircumflex",                       611, NULL },
+  { "Cacute",                            667, NULL },
+  { "Icircumflex",                       333, NULL },
+  { "guillemotleft",                     500, NULL },
+  { "germandbls",                        500, NULL },
+  { "Amacron",                           611, NULL },
+  { "seven",                             500, NULL },
+  { "Sacute",                            500, NULL },
+  { "ordmasculine",                      310, NULL },
+  { "dotlessi",                          278, NULL },
+  { "sterling",                          500, NULL },
+  { "notequal",                          549, NULL },
+  { "Imacron",                           333, NULL },
+  { "rcommaaccent",                      389, NULL },
+  { "Zdotaccent",                        556, NULL },
+  { "acircumflex",                       500, NULL },
+  { "cacute",                            444, NULL },
+  { "Ecaron",                            611, NULL },
+  { "icircumflex",                       278, NULL },
+  { "braceright",                        400, NULL },
+  { "quotedblright",                     556, NULL },
+  { "amacron",                           500, NULL },
+  { "sacute",                            389, NULL },
+  { "imacron",                           278, NULL },
+  { "cent",                              500, NULL },
+  { "currency",                          500, NULL },
+  { "logicalnot",                        675, NULL },
+  { "zdotaccent",                        389, NULL },
+  { "Atilde",                            611, NULL },
+  { "breve",                             333, NULL },
+  { "bar",                               275, NULL },
+  { "fraction",                          167, NULL },
+  { "less",                              675, NULL },
+  { "ecaron",                            444, NULL },
+  { "guilsinglleft",                     333, NULL },
+  { "exclam",                            333, NULL },
+  { "period",                            250, NULL },
+  { "Rcaron",                            611, NULL },
+  { "Kcommaaccent",                      667, NULL },
+  { "greater",                           675, NULL },
+  { "atilde",                            500, NULL },
+  { "brokenbar",                         275, NULL },
+  { "quoteleft",                         333, NULL },
+  { "Edotaccent",                        611, NULL },
+  { "onesuperior",                       300, NULL }
+};
+
+static BuiltinFontWidth timesRomanWidthsTab[] = {
+  { "Ntilde",                            722, NULL },
+  { "rcaron",                            333, NULL },
+  { "kcommaaccent",                      500, NULL },
+  { "Ncommaaccent",                      722, NULL },
+  { "Zacute",                            611, NULL },
+  { "comma",                             250, NULL },
+  { "cedilla",                           333, NULL },
+  { "plusminus",                         564, NULL },
+  { "circumflex",                        333, NULL },
+  { "dotaccent",                         333, NULL },
+  { "edotaccent",                        444, NULL },
+  { "asciitilde",                        541, NULL },
+  { "colon",                             278, NULL },
+  { "onehalf",                           750, NULL },
+  { "dollar",                            500, NULL },
+  { "Lcaron",                            611, NULL },
+  { "ntilde",                            500, NULL },
+  { "Aogonek",                           722, NULL },
+  { "ncommaaccent",                      500, NULL },
+  { "minus",                             564, NULL },
+  { "Iogonek",                           333, NULL },
+  { "zacute",                            444, NULL },
+  { "yen",                               500, NULL },
+  { "space",                             250, NULL },
+  { "Omacron",                           722, NULL },
+  { "questiondown",                      444, NULL },
+  { "emdash",                           1000, NULL },
+  { "Agrave",                            722, NULL },
+  { "three",                             500, NULL },
+  { "numbersign",                        500, NULL },
+  { "lcaron",                            344, NULL },
+  { "A",                                 722, NULL },
+  { "B",                                 667, NULL },
+  { "C",                                 667, NULL },
+  { "aogonek",                           444, NULL },
+  { "D",                                 722, NULL },
+  { "E",                                 611, NULL },
+  { "onequarter",                        750, NULL },
+  { "F",                                 556, NULL },
+  { "G",                                 722, NULL },
+  { "H",                                 722, NULL },
+  { "I",                                 333, NULL },
+  { "J",                                 389, NULL },
+  { "K",                                 722, NULL },
+  { "iogonek",                           278, NULL },
+  { "backslash",                         278, NULL },
+  { "L",                                 611, NULL },
+  { "periodcentered",                    250, NULL },
+  { "M",                                 889, NULL },
+  { "N",                                 722, NULL },
+  { "omacron",                           500, NULL },
+  { "Tcommaaccent",                      611, NULL },
+  { "O",                                 722, NULL },
+  { "P",                                 556, NULL },
+  { "Q",                                 722, NULL },
+  { "Uhungarumlaut",                     722, NULL },
+  { "R",                                 667, NULL },
+  { "Aacute",                            722, NULL },
+  { "caron",                             333, NULL },
+  { "S",                                 556, NULL },
+  { "T",                                 611, NULL },
+  { "U",                                 722, NULL },
+  { "agrave",                            444, NULL },
+  { "V",                                 722, NULL },
+  { "W",                                 944, NULL },
+  { "X",                                 722, NULL },
+  { "question",                          444, NULL },
+  { "equal",                             564, NULL },
+  { "Y",                                 722, NULL },
+  { "Z",                                 611, NULL },
+  { "four",                              500, NULL },
+  { "a",                                 444, NULL },
+  { "Gcommaaccent",                      722, NULL },
+  { "b",                                 500, NULL },
+  { "c",                                 444, NULL },
+  { "d",                                 500, NULL },
+  { "e",                                 444, NULL },
+  { "f",                                 333, NULL },
+  { "g",                                 500, NULL },
+  { "bullet",                            350, NULL },
+  { "h",                                 500, NULL },
+  { "i",                                 278, NULL },
+  { "Oslash",                            722, NULL },
+  { "dagger",                            500, NULL },
+  { "j",                                 278, NULL },
+  { "k",                                 500, NULL },
+  { "l",                                 278, NULL },
+  { "m",                                 778, NULL },
+  { "n",                                 500, NULL },
+  { "tcommaaccent",                      278, NULL },
+  { "o",                                 500, NULL },
+  { "ordfeminine",                       276, NULL },
+  { "ring",                              333, NULL },
+  { "p",                                 500, NULL },
+  { "q",                                 500, NULL },
+  { "uhungarumlaut",                     500, NULL },
+  { "r",                                 333, NULL },
+  { "twosuperior",                       300, NULL },
+  { "aacute",                            444, NULL },
+  { "s",                                 389, NULL },
+  { "OE",                                889, NULL },
+  { "t",                                 278, NULL },
+  { "divide",                            564, NULL },
+  { "u",                                 500, NULL },
+  { "Ccaron",                            667, NULL },
+  { "v",                                 500, NULL },
+  { "w",                                 722, NULL },
+  { "x",                                 500, NULL },
+  { "y",                                 500, NULL },
+  { "z",                                 444, NULL },
+  { "Gbreve",                            722, NULL },
+  { "commaaccent",                       250, NULL },
+  { "hungarumlaut",                      333, NULL },
+  { "Idotaccent",                        333, NULL },
+  { "Nacute",                            722, NULL },
+  { "quotedbl",                          408, NULL },
+  { "gcommaaccent",                      500, NULL },
+  { "mu",                                500, NULL },
+  { "greaterequal",                      549, NULL },
+  { "Scaron",                            556, NULL },
+  { "Lslash",                            611, NULL },
+  { "semicolon",                         278, NULL },
+  { "oslash",                            500, NULL },
+  { "lessequal",                         549, NULL },
+  { "lozenge",                           471, NULL },
+  { "parenright",                        333, NULL },
+  { "ccaron",                            444, NULL },
+  { "Ecircumflex",                       611, NULL },
+  { "gbreve",                            500, NULL },
+  { "trademark",                         980, NULL },
+  { "daggerdbl",                         500, NULL },
+  { "nacute",                            500, NULL },
+  { "macron",                            333, NULL },
+  { "Otilde",                            722, NULL },
+  { "Emacron",                           611, NULL },
+  { "ellipsis",                         1000, NULL },
+  { "scaron",                            389, NULL },
+  { "AE",                                889, NULL },
+  { "Ucircumflex",                       722, NULL },
+  { "lslash",                            278, NULL },
+  { "quotedblleft",                      444, NULL },
+  { "guilsinglright",                    333, NULL },
+  { "hyphen",                            333, NULL },
+  { "quotesingle",                       180, NULL },
+  { "eight",                             500, NULL },
+  { "exclamdown",                        333, NULL },
+  { "endash",                            500, NULL },
+  { "oe",                                722, NULL },
+  { "Abreve",                            722, NULL },
+  { "Umacron",                           722, NULL },
+  { "ecircumflex",                       444, NULL },
+  { "Adieresis",                         722, NULL },
+  { "copyright",                         760, NULL },
+  { "Egrave",                            611, NULL },
+  { "slash",                             278, NULL },
+  { "Edieresis",                         611, NULL },
+  { "otilde",                            500, NULL },
+  { "Idieresis",                         333, NULL },
+  { "parenleft",                         333, NULL },
+  { "one",                               500, NULL },
+  { "emacron",                           444, NULL },
+  { "Odieresis",                         722, NULL },
+  { "ucircumflex",                       500, NULL },
+  { "bracketleft",                       333, NULL },
+  { "Ugrave",                            722, NULL },
+  { "quoteright",                        333, NULL },
+  { "Udieresis",                         722, NULL },
+  { "perthousand",                      1000, NULL },
+  { "Ydieresis",                         722, NULL },
+  { "umacron",                           500, NULL },
+  { "abreve",                            444, NULL },
+  { "Eacute",                            611, NULL },
+  { "adieresis",                         444, NULL },
+  { "egrave",                            444, NULL },
+  { "edieresis",                         444, NULL },
+  { "idieresis",                         278, NULL },
+  { "Eth",                               722, NULL },
+  { "ae",                                667, NULL },
+  { "asterisk",                          500, NULL },
+  { "odieresis",                         500, NULL },
+  { "Uacute",                            722, NULL },
+  { "ugrave",                            500, NULL },
+  { "nine",                              500, NULL },
+  { "five",                              500, NULL },
+  { "udieresis",                         500, NULL },
+  { "Zcaron",                            611, NULL },
+  { "Scommaaccent",                      556, NULL },
+  { "threequarters",                     750, NULL },
+  { "guillemotright",                    500, NULL },
+  { "Ccedilla",                          667, NULL },
+  { "ydieresis",                         500, NULL },
+  { "tilde",                             333, NULL },
+  { "at",                                921, NULL },
+  { "eacute",                            444, NULL },
+  { "underscore",                        500, NULL },
+  { "Euro",                              500, NULL },
+  { "Dcroat",                            722, NULL },
+  { "multiply",                          564, NULL },
+  { "zero",                              500, NULL },
+  { "eth",                               500, NULL },
+  { "Scedilla",                          556, NULL },
+  { "Ograve",                            722, NULL },
+  { "Racute",                            667, NULL },
+  { "partialdiff",                       476, NULL },
+  { "uacute",                            500, NULL },
+  { "braceleft",                         480, NULL },
+  { "Thorn",                             556, NULL },
+  { "zcaron",                            444, NULL },
+  { "scommaaccent",                      389, NULL },
+  { "ccedilla",                          444, NULL },
+  { "Dcaron",                            722, NULL },
+  { "dcroat",                            500, NULL },
+  { "Ocircumflex",                       722, NULL },
+  { "Oacute",                            722, NULL },
+  { "scedilla",                          389, NULL },
+  { "ogonek",                            333, NULL },
+  { "ograve",                            500, NULL },
+  { "racute",                            333, NULL },
+  { "Tcaron",                            611, NULL },
+  { "Eogonek",                           611, NULL },
+  { "thorn",                             500, NULL },
+  { "degree",                            400, NULL },
+  { "registered",                        760, NULL },
+  { "radical",                           453, NULL },
+  { "Aring",                             722, NULL },
+  { "percent",                           833, NULL },
+  { "six",                               500, NULL },
+  { "paragraph",                         453, NULL },
+  { "dcaron",                            588, NULL },
+  { "Uogonek",                           722, NULL },
+  { "two",                               500, NULL },
+  { "summation",                         600, NULL },
+  { "Igrave",                            333, NULL },
+  { "Lacute",                            611, NULL },
+  { "ocircumflex",                       500, NULL },
+  { "oacute",                            500, NULL },
+  { "Uring",                             722, NULL },
+  { "Lcommaaccent",                      611, NULL },
+  { "tcaron",                            326, NULL },
+  { "eogonek",                           444, NULL },
+  { "Delta",                             612, NULL },
+  { "Ohungarumlaut",                     722, NULL },
+  { "asciicircum",                       469, NULL },
+  { "aring",                             444, NULL },
+  { "grave",                             333, NULL },
+  { "uogonek",                           500, NULL },
+  { "bracketright",                      333, NULL },
+  { "Iacute",                            333, NULL },
+  { "ampersand",                         778, NULL },
+  { "igrave",                            278, NULL },
+  { "lacute",                            278, NULL },
+  { "Ncaron",                            722, NULL },
+  { "plus",                              564, NULL },
+  { "uring",                             500, NULL },
+  { "quotesinglbase",                    333, NULL },
+  { "lcommaaccent",                      278, NULL },
+  { "Yacute",                            722, NULL },
+  { "ohungarumlaut",                     500, NULL },
+  { "threesuperior",                     300, NULL },
+  { "acute",                             333, NULL },
+  { "section",                           500, NULL },
+  { "dieresis",                          333, NULL },
+  { "iacute",                            278, NULL },
+  { "quotedblbase",                      444, NULL },
+  { "ncaron",                            500, NULL },
+  { "florin",                            500, NULL },
+  { "yacute",                            500, NULL },
+  { "Rcommaaccent",                      667, NULL },
+  { "fi",                                556, NULL },
+  { "fl",                                556, NULL },
+  { "Acircumflex",                       722, NULL },
+  { "Cacute",                            667, NULL },
+  { "Icircumflex",                       333, NULL },
+  { "guillemotleft",                     500, NULL },
+  { "germandbls",                        500, NULL },
+  { "Amacron",                           722, NULL },
+  { "seven",                             500, NULL },
+  { "Sacute",                            556, NULL },
+  { "ordmasculine",                      310, NULL },
+  { "dotlessi",                          278, NULL },
+  { "sterling",                          500, NULL },
+  { "notequal",                          549, NULL },
+  { "Imacron",                           333, NULL },
+  { "rcommaaccent",                      333, NULL },
+  { "Zdotaccent",                        611, NULL },
+  { "acircumflex",                       444, NULL },
+  { "cacute",                            444, NULL },
+  { "Ecaron",                            611, NULL },
+  { "icircumflex",                       278, NULL },
+  { "braceright",                        480, NULL },
+  { "quotedblright",                     444, NULL },
+  { "amacron",                           444, NULL },
+  { "sacute",                            389, NULL },
+  { "imacron",                           278, NULL },
+  { "cent",                              500, NULL },
+  { "currency",                          500, NULL },
+  { "logicalnot",                        564, NULL },
+  { "zdotaccent",                        444, NULL },
+  { "Atilde",                            722, NULL },
+  { "breve",                             333, NULL },
+  { "bar",                               200, NULL },
+  { "fraction",                          167, NULL },
+  { "less",                              564, NULL },
+  { "ecaron",                            444, NULL },
+  { "guilsinglleft",                     333, NULL },
+  { "exclam",                            333, NULL },
+  { "period",                            250, NULL },
+  { "Rcaron",                            667, NULL },
+  { "Kcommaaccent",                      722, NULL },
+  { "greater",                           564, NULL },
+  { "atilde",                            444, NULL },
+  { "brokenbar",                         200, NULL },
+  { "quoteleft",                         333, NULL },
+  { "Edotaccent",                        611, NULL },
+  { "onesuperior",                       300, NULL }
+};
+
+static BuiltinFontWidth zapfDingbatsWidthsTab[] = {
+  { "a81",                               438, NULL },
+  { "a82",                               138, NULL },
+  { "a83",                               277, NULL },
+  { "a84",                               415, NULL },
+  { "a85",                               509, NULL },
+  { "a86",                               410, NULL },
+  { "a87",                               234, NULL },
+  { "a88",                               234, NULL },
+  { "a89",                               390, NULL },
+  { "a140",                              788, NULL },
+  { "a141",                              788, NULL },
+  { "a142",                              788, NULL },
+  { "a143",                              788, NULL },
+  { "a144",                              788, NULL },
+  { "a145",                              788, NULL },
+  { "a146",                              788, NULL },
+  { "a147",                              788, NULL },
+  { "a148",                              788, NULL },
+  { "a149",                              788, NULL },
+  { "a90",                               390, NULL },
+  { "a91",                               276, NULL },
+  { "a92",                               276, NULL },
+  { "space",                             278, NULL },
+  { "a93",                               317, NULL },
+  { "a94",                               317, NULL },
+  { "a95",                               334, NULL },
+  { "a96",                               334, NULL },
+  { "a97",                               392, NULL },
+  { "a98",                               392, NULL },
+  { "a99",                               668, NULL },
+  { "a150",                              788, NULL },
+  { "a151",                              788, NULL },
+  { "a152",                              788, NULL },
+  { "a153",                              788, NULL },
+  { "a154",                              788, NULL },
+  { "a155",                              788, NULL },
+  { "a156",                              788, NULL },
+  { "a157",                              788, NULL },
+  { "a158",                              788, NULL },
+  { "a159",                              788, NULL },
+  { "a160",                              894, NULL },
+  { "a161",                              838, NULL },
+  { "a162",                              924, NULL },
+  { "a163",                             1016, NULL },
+  { "a164",                              458, NULL },
+  { "a165",                              924, NULL },
+  { "a166",                              918, NULL },
+  { "a167",                              927, NULL },
+  { "a168",                              928, NULL },
+  { "a169",                              928, NULL },
+  { "a170",                              834, NULL },
+  { "a171",                              873, NULL },
+  { "a172",                              828, NULL },
+  { "a173",                              924, NULL },
+  { "a174",                              917, NULL },
+  { "a175",                              930, NULL },
+  { "a176",                              931, NULL },
+  { "a177",                              463, NULL },
+  { "a178",                              883, NULL },
+  { "a179",                              836, NULL },
+  { "a180",                              867, NULL },
+  { "a181",                              696, NULL },
+  { "a182",                              874, NULL },
+  { "a183",                              760, NULL },
+  { "a184",                              946, NULL },
+  { "a185",                              865, NULL },
+  { "a186",                              967, NULL },
+  { "a187",                              831, NULL },
+  { "a188",                              873, NULL },
+  { "a189",                              927, NULL },
+  { "a1",                                974, NULL },
+  { "a2",                                961, NULL },
+  { "a3",                                980, NULL },
+  { "a4",                                719, NULL },
+  { "a5",                                789, NULL },
+  { "a6",                                494, NULL },
+  { "a7",                                552, NULL },
+  { "a8",                                537, NULL },
+  { "a9",                                577, NULL },
+  { "a190",                              970, NULL },
+  { "a191",                              918, NULL },
+  { "a192",                              748, NULL },
+  { "a193",                              836, NULL },
+  { "a194",                              771, NULL },
+  { "a195",                              888, NULL },
+  { "a196",                              748, NULL },
+  { "a197",                              771, NULL },
+  { "a198",                              888, NULL },
+  { "a199",                              867, NULL },
+  { "a10",                               692, NULL },
+  { "a11",                               960, NULL },
+  { "a12",                               939, NULL },
+  { "a13",                               549, NULL },
+  { "a14",                               855, NULL },
+  { "a15",                               911, NULL },
+  { "a16",                               933, NULL },
+  { "a17",                               945, NULL },
+  { "a18",                               974, NULL },
+  { "a19",                               755, NULL },
+  { "a20",                               846, NULL },
+  { "a21",                               762, NULL },
+  { "a22",                               761, NULL },
+  { "a23",                               571, NULL },
+  { "a24",                               677, NULL },
+  { "a25",                               763, NULL },
+  { "a26",                               760, NULL },
+  { "a27",                               759, NULL },
+  { "a28",                               754, NULL },
+  { "a29",                               786, NULL },
+  { "a30",                               788, NULL },
+  { "a31",                               788, NULL },
+  { "a32",                               790, NULL },
+  { "a33",                               793, NULL },
+  { "a34",                               794, NULL },
+  { "a35",                               816, NULL },
+  { "a36",                               823, NULL },
+  { "a37",                               789, NULL },
+  { "a38",                               841, NULL },
+  { "a39",                               823, NULL },
+  { "a40",                               833, NULL },
+  { "a41",                               816, NULL },
+  { "a42",                               831, NULL },
+  { "a43",                               923, NULL },
+  { "a44",                               744, NULL },
+  { "a45",                               723, NULL },
+  { "a46",                               749, NULL },
+  { "a47",                               790, NULL },
+  { "a48",                               792, NULL },
+  { "a49",                               695, NULL },
+  { "a100",                              668, NULL },
+  { "a101",                              732, NULL },
+  { "a102",                              544, NULL },
+  { "a103",                              544, NULL },
+  { "a104",                              910, NULL },
+  { "a105",                              911, NULL },
+  { "a106",                              667, NULL },
+  { "a107",                              760, NULL },
+  { "a108",                              760, NULL },
+  { "a109",                              626, NULL },
+  { "a50",                               776, NULL },
+  { "a51",                               768, NULL },
+  { "a52",                               792, NULL },
+  { "a53",                               759, NULL },
+  { "a54",                               707, NULL },
+  { "a55",                               708, NULL },
+  { "a56",                               682, NULL },
+  { "a57",                               701, NULL },
+  { "a58",                               826, NULL },
+  { "a59",                               815, NULL },
+  { "a110",                              694, NULL },
+  { "a111",                              595, NULL },
+  { "a112",                              776, NULL },
+  { "a117",                              690, NULL },
+  { "a118",                              791, NULL },
+  { "a119",                              790, NULL },
+  { "a60",                               789, NULL },
+  { "a61",                               789, NULL },
+  { "a62",                               707, NULL },
+  { "a63",                               687, NULL },
+  { "a64",                               696, NULL },
+  { "a65",                               689, NULL },
+  { "a66",                               786, NULL },
+  { "a67",                               787, NULL },
+  { "a68",                               713, NULL },
+  { "a69",                               791, NULL },
+  { "a200",                              696, NULL },
+  { "a201",                              874, NULL },
+  { "a120",                              788, NULL },
+  { "a121",                              788, NULL },
+  { "a202",                              974, NULL },
+  { "a122",                              788, NULL },
+  { "a203",                              762, NULL },
+  { "a123",                              788, NULL },
+  { "a204",                              759, NULL },
+  { "a124",                              788, NULL },
+  { "a205",                              509, NULL },
+  { "a125",                              788, NULL },
+  { "a206",                              410, NULL },
+  { "a126",                              788, NULL },
+  { "a127",                              788, NULL },
+  { "a128",                              788, NULL },
+  { "a129",                              788, NULL },
+  { "a70",                               785, NULL },
+  { "a71",                               791, NULL },
+  { "a72",                               873, NULL },
+  { "a73",                               761, NULL },
+  { "a74",                               762, NULL },
+  { "a75",                               759, NULL },
+  { "a76",                               892, NULL },
+  { "a77",                               892, NULL },
+  { "a78",                               788, NULL },
+  { "a79",                               784, NULL },
+  { "a130",                              788, NULL },
+  { "a131",                              788, NULL },
+  { "a132",                              788, NULL },
+  { "a133",                              788, NULL },
+  { "a134",                              788, NULL },
+  { "a135",                              788, NULL },
+  { "a136",                              788, NULL },
+  { "a137",                              788, NULL },
+  { "a138",                              788, NULL },
+  { "a139",                              788, NULL }
+};
+
+BuiltinFont builtinFonts[] = {
+  { "Courier",               standardEncoding,            629, -157, { -23, -250,  715,  805}, NULL },
+  { "Courier-Bold",          standardEncoding,            629, -157, {-113, -250,  749,  801}, NULL },
+  { "Courier-BoldOblique",   standardEncoding,            629, -157, { -57, -250,  869,  801}, NULL },
+  { "Courier-Oblique",       standardEncoding,            629, -157, { -27, -250,  849,  805}, NULL },
+  { "Helvetica",             standardEncoding,            718, -207, {-166, -225, 1000,  931}, NULL },
+  { "Helvetica-Bold",        standardEncoding,            718, -207, {-170, -228, 1003,  962}, NULL },
+  { "Helvetica-BoldOblique", standardEncoding,            718, -207, {-174, -228, 1114,  962}, NULL },
+  { "Helvetica-Oblique",     standardEncoding,            718, -207, {-170, -225, 1116,  931}, NULL },
+  { "Symbol",                symbolEncoding,             1010, -293, {-180, -293, 1090, 1010}, NULL },
+  { "Times-Bold",            standardEncoding,            683, -217, {-168, -218, 1000,  935}, NULL },
+  { "Times-BoldItalic",      standardEncoding,            683, -217, {-200, -218,  996,  921}, NULL },
+  { "Times-Italic",          standardEncoding,            683, -217, {-169, -217, 1010,  883}, NULL },
+  { "Times-Roman",           standardEncoding,            683, -217, {-168, -218, 1000,  898}, NULL },
+  { "ZapfDingbats",          zapfDingbatsEncoding,        820, -143, {  -1, -143,  981,  820}, NULL }
+};
+
+BuiltinFont *builtinFontSubst[] = {
+  &builtinFonts[0],
+  &builtinFonts[3],
+  &builtinFonts[1],
+  &builtinFonts[2],
+  &builtinFonts[4],
+  &builtinFonts[7],
+  &builtinFonts[5],
+  &builtinFonts[6],
+  &builtinFonts[12],
+  &builtinFonts[11],
+  &builtinFonts[9],
+  &builtinFonts[10]
+};
+
+void initBuiltinFontTables() {
+  builtinFonts[0].widths = new BuiltinFontWidths(courierWidthsTab, 315);
+  builtinFonts[1].widths = new BuiltinFontWidths(courierBoldWidthsTab, 315);
+  builtinFonts[2].widths = new BuiltinFontWidths(courierBoldObliqueWidthsTab, 315);
+  builtinFonts[3].widths = new BuiltinFontWidths(courierObliqueWidthsTab, 315);
+  builtinFonts[4].widths = new BuiltinFontWidths(helveticaWidthsTab, 315);
+  builtinFonts[5].widths = new BuiltinFontWidths(helveticaBoldWidthsTab, 316);
+  builtinFonts[6].widths = new BuiltinFontWidths(helveticaBoldObliqueWidthsTab, 315);
+  builtinFonts[7].widths = new BuiltinFontWidths(helveticaObliqueWidthsTab, 315);
+  builtinFonts[8].widths = new BuiltinFontWidths(symbolWidthsTab, 190);
+  builtinFonts[9].widths = new BuiltinFontWidths(timesBoldWidthsTab, 315);
+  builtinFonts[10].widths = new BuiltinFontWidths(timesBoldItalicWidthsTab, 315);
+  builtinFonts[11].widths = new BuiltinFontWidths(timesItalicWidthsTab, 315);
+  builtinFonts[12].widths = new BuiltinFontWidths(timesRomanWidthsTab, 315);
+  builtinFonts[13].widths = new BuiltinFontWidths(zapfDingbatsWidthsTab, 202);
+}
+
+void freeBuiltinFontTables() {
+  int i;
+
+  for (i = 0; i < 14; ++i) {
+    delete builtinFonts[i].widths;
+  }
+}
diff --git a/lib/xpdf/BuiltinFontTables.h b/lib/xpdf/BuiltinFontTables.h
new file mode 100644 (file)
index 0000000..eb45549
--- /dev/null
@@ -0,0 +1,23 @@
+//========================================================================
+//
+// BuiltinFontTables.h
+//
+// Copyright 2001-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef BUILTINFONTTABLES_H
+#define BUILTINFONTTABLES_H
+
+#include "BuiltinFont.h"
+
+#define nBuiltinFonts      14
+#define nBuiltinFontSubsts 12
+
+extern BuiltinFont builtinFonts[nBuiltinFonts];
+extern BuiltinFont *builtinFontSubst[nBuiltinFontSubsts];
+
+extern void initBuiltinFontTables();
+extern void freeBuiltinFontTables();
+
+#endif
diff --git a/lib/xpdf/CMap.cc b/lib/xpdf/CMap.cc
new file mode 100644 (file)
index 0000000..303cf09
--- /dev/null
@@ -0,0 +1,408 @@
+//========================================================================
+//
+// CMap.cc
+//
+// Copyright 2001-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include "gmem.h"
+#include "gfile.h"
+#include "GString.h"
+#include "Error.h"
+#include "GlobalParams.h"
+#include "PSTokenizer.h"
+#include "CMap.h"
+
+//------------------------------------------------------------------------
+
+struct CMapVectorEntry {
+  GBool isVector;
+  union {
+    CMapVectorEntry *vector;
+    CID cid;
+  };
+};
+
+//------------------------------------------------------------------------
+
+static int getCharFromFile(void *data) {
+  return fgetc((FILE *)data);
+}
+
+//------------------------------------------------------------------------
+
+CMap *CMap::parse(CMapCache *cache, GString *collectionA,
+                 GString *cMapNameA) {
+  FILE *f;
+  CMap *cmap;
+  PSTokenizer *pst;
+  char tok1[256], tok2[256], tok3[256];
+  int n1, n2, n3;
+  Guint start, end, code;
+
+  if (!(f = globalParams->findCMapFile(collectionA, cMapNameA))) {
+
+    // Check for an identity CMap.
+    if (!cMapNameA->cmp("Identity") || !cMapNameA->cmp("Identity-H")) {
+      return new CMap(collectionA->copy(), cMapNameA->copy(), 0);
+    }
+    if (!cMapNameA->cmp("Identity-V")) {
+      return new CMap(collectionA->copy(), cMapNameA->copy(), 1);
+    }
+
+    error(-1, "Couldn't find '%s' CMap file for '%s' collection",
+         cMapNameA->getCString(), collectionA->getCString());
+    return NULL;
+  }
+
+  cmap = new CMap(collectionA->copy(), cMapNameA->copy());
+
+  pst = new PSTokenizer(&getCharFromFile, f);
+  pst->getToken(tok1, sizeof(tok1), &n1);
+  while (pst->getToken(tok2, sizeof(tok2), &n2)) {
+    if (!strcmp(tok2, "usecmap")) {
+      if (tok1[0] == '/') {
+       cmap->useCMap(cache, tok1 + 1);
+      }
+      pst->getToken(tok1, sizeof(tok1), &n1);
+    } else if (!strcmp(tok1, "/WMode")) {
+      cmap->wMode = atoi(tok2);
+      pst->getToken(tok1, sizeof(tok1), &n1);
+    } else if (!strcmp(tok2, "begincodespacerange")) {
+      while (pst->getToken(tok1, sizeof(tok1), &n1)) {
+       if (!strcmp(tok1, "endcodespacerange")) {
+         break;
+       }
+       if (!pst->getToken(tok2, sizeof(tok2), &n2) ||
+           !strcmp(tok2, "endcodespacerange")) {
+         error(-1, "Illegal entry in codespacerange block in CMap");
+         break;
+       }
+       if (tok1[0] == '<' && tok2[0] == '<' &&
+           n1 == n2 && n1 >= 4 && (n1 & 1) == 0) {
+         tok1[n1 - 1] = tok2[n1 - 1] = '\0';
+         sscanf(tok1 + 1, "%x", &start);
+         sscanf(tok2 + 1, "%x", &end);
+         n1 = (n1 - 2) / 2;
+         cmap->addCodeSpace(cmap->vector, start, end, n1);
+       }
+      }
+      pst->getToken(tok1, sizeof(tok1), &n1);
+    } else if (!strcmp(tok2, "begincidchar")) {
+      while (pst->getToken(tok1, sizeof(tok1), &n1)) {
+       if (!strcmp(tok1, "endcidchar")) {
+         break;
+       }
+       if (!pst->getToken(tok2, sizeof(tok2), &n2) ||
+           !strcmp(tok2, "endcidchar")) {
+         error(-1, "Illegal entry in cidchar block in CMap");
+         break;
+       }
+       if (!(tok1[0] == '<' && tok1[n1 - 1] == '>' &&
+             n1 >= 4 && (n1 & 1) == 0)) {
+         error(-1, "Illegal entry in cidchar block in CMap");
+         continue;
+       }
+       tok1[n1 - 1] = '\0';
+       if (sscanf(tok1 + 1, "%x", &code) != 1) {
+         error(-1, "Illegal entry in cidchar block in CMap");
+         continue;
+       }
+       n1 = (n1 - 2) / 2;
+       cmap->addCIDs(code, code, n1, (CID)atoi(tok2));
+      }
+      pst->getToken(tok1, sizeof(tok1), &n1);
+    } else if (!strcmp(tok2, "begincidrange")) {
+      while (pst->getToken(tok1, sizeof(tok1), &n1)) {
+       if (!strcmp(tok1, "endcidrange")) {
+         break;
+       }
+       if (!pst->getToken(tok2, sizeof(tok2), &n2) ||
+           !strcmp(tok2, "endcidrange") ||
+           !pst->getToken(tok3, sizeof(tok3), &n3) ||
+           !strcmp(tok3, "endcidrange")) {
+         error(-1, "Illegal entry in cidrange block in CMap");
+         break;
+       }
+       if (tok1[0] == '<' && tok2[0] == '<' &&
+           n1 == n2 && n1 >= 4 && (n1 & 1) == 0) {
+         tok1[n1 - 1] = tok2[n1 - 1] = '\0';
+         sscanf(tok1 + 1, "%x", &start);
+         sscanf(tok2 + 1, "%x", &end);
+         n1 = (n1 - 2) / 2;
+         cmap->addCIDs(start, end, n1, (CID)atoi(tok3));
+       }
+      }
+      pst->getToken(tok1, sizeof(tok1), &n1);
+    } else {
+      strcpy(tok1, tok2);
+    }
+  }
+  delete pst;
+
+  fclose(f);
+
+  return cmap;
+}
+
+CMap::CMap(GString *collectionA, GString *cMapNameA) {
+  int i;
+
+  collection = collectionA;
+  cMapName = cMapNameA;
+  wMode = 0;
+  vector = (CMapVectorEntry *)gmallocn(256, sizeof(CMapVectorEntry));
+  for (i = 0; i < 256; ++i) {
+    vector[i].isVector = gFalse;
+    vector[i].cid = 0;
+  }
+  refCnt = 1;
+#if MULTITHREADED
+  gInitMutex(&mutex);
+#endif
+}
+
+CMap::CMap(GString *collectionA, GString *cMapNameA, int wModeA) {
+  collection = collectionA;
+  cMapName = cMapNameA;
+  wMode = wModeA;
+  vector = NULL;
+  refCnt = 1;
+#if MULTITHREADED
+  gInitMutex(&mutex);
+#endif
+}
+
+void CMap::useCMap(CMapCache *cache, char *useName) {
+  GString *useNameStr;
+  CMap *subCMap;
+
+  useNameStr = new GString(useName);
+  subCMap = cache->getCMap(collection, useNameStr);
+  delete useNameStr;
+  if (!subCMap) {
+    return;
+  }
+  copyVector(vector, subCMap->vector);
+  subCMap->decRefCnt();
+}
+
+void CMap::copyVector(CMapVectorEntry *dest, CMapVectorEntry *src) {
+  int i, j;
+
+  for (i = 0; i < 256; ++i) {
+    if (src[i].isVector) {
+      if (!dest[i].isVector) {
+       dest[i].isVector = gTrue;
+       dest[i].vector =
+         (CMapVectorEntry *)gmallocn(256, sizeof(CMapVectorEntry));
+       for (j = 0; j < 256; ++j) {
+         dest[i].vector[j].isVector = gFalse;
+         dest[i].vector[j].cid = 0;
+       }
+      }
+      copyVector(dest[i].vector, src[i].vector);
+    } else {
+      if (dest[i].isVector) {
+       error(-1, "Collision in usecmap");
+      } else {
+       dest[i].cid = src[i].cid;
+      }
+    }
+  }
+}
+
+void CMap::addCodeSpace(CMapVectorEntry *vec, Guint start, Guint end,
+                       Guint nBytes) {
+  Guint start2, end2;
+  int startByte, endByte, i, j;
+
+  if (nBytes > 1) {
+    startByte = (start >> (8 * (nBytes - 1))) & 0xff;
+    endByte = (end >> (8 * (nBytes - 1))) & 0xff;
+    start2 = start & ((1 << (8 * (nBytes - 1))) - 1);
+    end2 = end & ((1 << (8 * (nBytes - 1))) - 1);
+    for (i = startByte; i <= endByte; ++i) {
+      if (!vec[i].isVector) {
+       vec[i].isVector = gTrue;
+       vec[i].vector =
+         (CMapVectorEntry *)gmallocn(256, sizeof(CMapVectorEntry));
+       for (j = 0; j < 256; ++j) {
+         vec[i].vector[j].isVector = gFalse;
+         vec[i].vector[j].cid = 0;
+       }
+      }
+      addCodeSpace(vec[i].vector, start2, end2, nBytes - 1);
+    }
+  }
+}
+
+void CMap::addCIDs(Guint start, Guint end, Guint nBytes, CID firstCID) {
+  CMapVectorEntry *vec;
+  CID cid;
+  int byte;
+  Guint i;
+
+  vec = vector;
+  for (i = nBytes - 1; i >= 1; --i) {
+    byte = (start >> (8 * i)) & 0xff;
+    if (!vec[byte].isVector) {
+      error(-1, "Invalid CID (%0*x - %0*x) in CMap",
+           2*nBytes, start, 2*nBytes, end);
+      return;
+    }
+    vec = vec[byte].vector;
+  }
+  cid = firstCID;
+  for (byte = (int)(start & 0xff); byte <= (int)(end & 0xff); ++byte) {
+    if (vec[byte].isVector) {
+      error(-1, "Invalid CID (%0*x - %0*x) in CMap",
+           2*nBytes, start, 2*nBytes, end);
+    } else {
+      vec[byte].cid = cid;
+    }
+    ++cid;
+  }
+}
+
+CMap::~CMap() {
+  delete collection;
+  delete cMapName;
+  if (vector) {
+    freeCMapVector(vector);
+  }
+#if MULTITHREADED
+  gDestroyMutex(&mutex);
+#endif
+}
+
+void CMap::freeCMapVector(CMapVectorEntry *vec) {
+  int i;
+
+  for (i = 0; i < 256; ++i) {
+    if (vec[i].isVector) {
+      freeCMapVector(vec[i].vector);
+    }
+  }
+  gfree(vec);
+}
+
+void CMap::incRefCnt() {
+#if MULTITHREADED
+  gLockMutex(&mutex);
+#endif
+  ++refCnt;
+#if MULTITHREADED
+  gUnlockMutex(&mutex);
+#endif
+}
+
+void CMap::decRefCnt() {
+  GBool done;
+
+#if MULTITHREADED
+  gLockMutex(&mutex);
+#endif
+  done = --refCnt == 0;
+#if MULTITHREADED
+  gUnlockMutex(&mutex);
+#endif
+  if (done) {
+    delete this;
+  }
+}
+
+GBool CMap::match(GString *collectionA, GString *cMapNameA) {
+  return !collection->cmp(collectionA) && !cMapName->cmp(cMapNameA);
+}
+
+CID CMap::getCID(char *s, int len, int *nUsed) {
+  CMapVectorEntry *vec;
+  int n, i;
+
+  if (!(vec = vector)) {
+    // identity CMap
+    *nUsed = 2;
+    if (len < 2) {
+      return 0;
+    }
+    return ((s[0] & 0xff) << 8) + (s[1] & 0xff);
+  }
+  n = 0;
+  while (1) {
+    if (n >= len) {
+      *nUsed = n;
+      return 0;
+    }
+    i = s[n++] & 0xff;
+    if (!vec[i].isVector) {
+      *nUsed = n;
+      return vec[i].cid;
+    }
+    vec = vec[i].vector;
+  }
+}
+
+//------------------------------------------------------------------------
+
+CMapCache::CMapCache() {
+  int i;
+
+  for (i = 0; i < cMapCacheSize; ++i) {
+    cache[i] = NULL;
+  }
+}
+
+CMapCache::~CMapCache() {
+  int i;
+
+  for (i = 0; i < cMapCacheSize; ++i) {
+    if (cache[i]) {
+      cache[i]->decRefCnt();
+    }
+  }
+}
+
+CMap *CMapCache::getCMap(GString *collection, GString *cMapName) {
+  CMap *cmap;
+  int i, j;
+
+  if (cache[0] && cache[0]->match(collection, cMapName)) {
+    cache[0]->incRefCnt();
+    return cache[0];
+  }
+  for (i = 1; i < cMapCacheSize; ++i) {
+    if (cache[i] && cache[i]->match(collection, cMapName)) {
+      cmap = cache[i];
+      for (j = i; j >= 1; --j) {
+       cache[j] = cache[j - 1];
+      }
+      cache[0] = cmap;
+      cmap->incRefCnt();
+      return cmap;
+    }
+  }
+  if ((cmap = CMap::parse(this, collection, cMapName))) {
+    if (cache[cMapCacheSize - 1]) {
+      cache[cMapCacheSize - 1]->decRefCnt();
+    }
+    for (j = cMapCacheSize - 1; j >= 1; --j) {
+      cache[j] = cache[j - 1];
+    }
+    cache[0] = cmap;
+    cmap->incRefCnt();
+    return cmap;
+  }
+  return NULL;
+}
diff --git a/lib/xpdf/CMap.h b/lib/xpdf/CMap.h
new file mode 100644 (file)
index 0000000..c321a57
--- /dev/null
@@ -0,0 +1,102 @@
+//========================================================================
+//
+// CMap.h
+//
+// Copyright 2001-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef CMAP_H
+#define CMAP_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#include "gtypes.h"
+#include "CharTypes.h"
+
+#if MULTITHREADED
+#include "GMutex.h"
+#endif
+
+class GString;
+struct CMapVectorEntry;
+class CMapCache;
+
+//------------------------------------------------------------------------
+
+class CMap {
+public:
+
+  // Create the CMap specified by <collection> and <cMapName>.  Sets
+  // the initial reference count to 1.  Returns NULL on failure.
+  static CMap *parse(CMapCache *cache, GString *collectionA,
+                    GString *cMapNameA);
+
+  ~CMap();
+
+  void incRefCnt();
+  void decRefCnt();
+
+  // Return collection name (<registry>-<ordering>).
+  GString *getCollection() { return collection; }
+
+  // Return true if this CMap matches the specified <collectionA>, and
+  // <cMapNameA>.
+  GBool match(GString *collectionA, GString *cMapNameA);
+
+  // Return the CID corresponding to the character code starting at
+  // <s>, which contains <len> bytes.  Sets *<nUsed> to the number of
+  // bytes used by the char code.
+  CID getCID(char *s, int len, int *nUsed);
+
+  // Return the writing mode (0=horizontal, 1=vertical).
+  int getWMode() { return wMode; }
+
+private:
+
+  CMap(GString *collectionA, GString *cMapNameA);
+  CMap(GString *collectionA, GString *cMapNameA, int wModeA);
+  void useCMap(CMapCache *cache, char *useName);
+  void copyVector(CMapVectorEntry *dest, CMapVectorEntry *src);
+  void addCodeSpace(CMapVectorEntry *vec, Guint start, Guint end,
+                   Guint nBytes);
+  void addCIDs(Guint start, Guint end, Guint nBytes, CID firstCID);
+  void freeCMapVector(CMapVectorEntry *vec);
+
+  GString *collection;
+  GString *cMapName;
+  int wMode;                   // writing mode (0=horizontal, 1=vertical)
+  CMapVectorEntry *vector;     // vector for first byte (NULL for
+                               //   identity CMap)
+  int refCnt;
+#if MULTITHREADED
+  GMutex mutex;
+#endif
+};
+
+//------------------------------------------------------------------------
+
+#define cMapCacheSize 4
+
+class CMapCache {
+public:
+
+  CMapCache();
+  ~CMapCache();
+
+  // Get the <cMapName> CMap for the specified character collection.
+  // Increments its reference count; there will be one reference for
+  // the cache plus one for the caller of this function.  Returns NULL
+  // on failure.
+  CMap *getCMap(GString *collection, GString *cMapName);
+
+private:
+
+  CMap *cache[cMapCacheSize];
+};
+
+#endif
diff --git a/lib/xpdf/Catalog.cc b/lib/xpdf/Catalog.cc
new file mode 100644 (file)
index 0000000..fd235d6
--- /dev/null
@@ -0,0 +1,353 @@
+//========================================================================
+//
+// Catalog.cc
+//
+// Copyright 1996-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <stddef.h>
+#include "gmem.h"
+#include "Object.h"
+#include "XRef.h"
+#include "Array.h"
+#include "Dict.h"
+#include "Page.h"
+#include "Error.h"
+#include "Link.h"
+#include "Catalog.h"
+
+//------------------------------------------------------------------------
+// Catalog
+//------------------------------------------------------------------------
+
+Catalog::Catalog(XRef *xrefA) {
+  Object catDict, pagesDict;
+  Object obj, obj2;
+  int numPages0;
+  int i;
+
+  ok = gTrue;
+  xref = xrefA;
+  pages = NULL;
+  pageRefs = NULL;
+  numPages = pagesSize = 0;
+  baseURI = NULL;
+
+  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);
+  // This should really be isDict("Pages"), but I've seen at least one
+  // PDF file where the /Type entry is missing.
+  if (!pagesDict.isDict()) {
+    error(-1, "Top-level pages object is wrong type (%s)",
+         pagesDict.getTypeName());
+    goto err2;
+  }
+  pagesDict.dictLookup("Count", &obj);
+  // some PDF files actually use real numbers here ("/Count 9.0")
+  if (!obj.isNum()) {
+    error(-1, "Page count in top-level pages object is wrong type (%s)",
+         obj.getTypeName());
+    goto err3;
+  }
+  pagesSize = numPages0 = (int)obj.getNum();
+  obj.free();
+  pages = (Page **)gmallocn(pagesSize, sizeof(Page *));
+  pageRefs = (Ref *)gmallocn(pagesSize, sizeof(Ref));
+  for (i = 0; i < pagesSize; ++i) {
+    pages[i] = NULL;
+    pageRefs[i].num = -1;
+    pageRefs[i].gen = -1;
+  }
+  numPages = readPageTree(pagesDict.getDict(), NULL, 0);
+  if (numPages != numPages0) {
+    error(-1, "Page count in top-level pages object is incorrect");
+  }
+  pagesDict.free();
+
+  // read named destination dictionary
+  catDict.dictLookup("Dests", &dests);
+
+  // read root of named destination tree
+  if (catDict.dictLookup("Names", &obj)->isDict())
+    obj.dictLookup("Dests", &nameTree);
+  else
+    nameTree.initNull();
+  obj.free();
+
+  // read base URI
+  if (catDict.dictLookup("URI", &obj)->isDict()) {
+    if (obj.dictLookup("Base", &obj2)->isString()) {
+      baseURI = obj2.getString()->copy();
+    }
+    obj2.free();
+  }
+  obj.free();
+
+  // get the metadata stream
+  catDict.dictLookup("Metadata", &metadata);
+
+  // get the structure tree root
+  catDict.dictLookup("StructTreeRoot", &structTreeRoot);
+
+  // get the outline dictionary
+  catDict.dictLookup("Outlines", &outline);
+
+  // get the AcroForm dictionary
+  catDict.dictLookup("AcroForm", &acroForm);
+
+  catDict.free();
+  return;
+
+ err3:
+  obj.free();
+ err2:
+  pagesDict.free();
+ err1:
+  catDict.free();
+  dests.initNull();
+  nameTree.initNull();
+  ok = gFalse;
+}
+
+Catalog::~Catalog() {
+  int i;
+
+  if (pages) {
+    for (i = 0; i < pagesSize; ++i) {
+      if (pages[i]) {
+       delete pages[i];
+      }
+    }
+    gfree(pages);
+    gfree(pageRefs);
+  }
+  dests.free();
+  nameTree.free();
+  if (baseURI) {
+    delete baseURI;
+  }
+  metadata.free();
+  structTreeRoot.free();
+  outline.free();
+  acroForm.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) {
+  Object kids;
+  Object kid;
+  Object kidRef;
+  PageAttrs *attrs1, *attrs2;
+  Page *page;
+  int i, j;
+
+  attrs1 = new PageAttrs(attrs, pagesDict);
+  pagesDict->lookup("Kids", &kids);
+  if (!kids.isArray()) {
+    error(-1, "Kids object (page %d) is wrong type (%s)",
+         start+1, kids.getTypeName());
+    goto err1;
+  }
+  for (i = 0; i < kids.arrayGetLength(); ++i) {
+    kids.arrayGet(i, &kid);
+    if (kid.isDict("Page")) {
+      attrs2 = new PageAttrs(attrs1, kid.getDict());
+      page = new Page(xref, start+1, kid.getDict(), attrs2);
+      if (!page->isOk()) {
+       ++start;
+       goto err3;
+      }
+      if (start >= pagesSize) {
+       pagesSize += 32;
+       pages = (Page **)greallocn(pages, pagesSize, sizeof(Page *));
+       pageRefs = (Ref *)greallocn(pageRefs, pagesSize, sizeof(Ref));
+       for (j = pagesSize - 32; j < pagesSize; ++j) {
+         pages[j] = NULL;
+         pageRefs[j].num = -1;
+         pageRefs[j].gen = -1;
+       }
+      }
+      pages[start] = page;
+      kids.arrayGetNF(i, &kidRef);
+      if (kidRef.isRef()) {
+       pageRefs[start].num = kidRef.getRefNum();
+       pageRefs[start].gen = kidRef.getRefGen();
+      }
+      kidRef.free();
+      ++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)
+       goto err2;
+    } else {
+      error(-1, "Kid object (page %d) is wrong type (%s)",
+           start+1, kid.getTypeName());
+    }
+    kid.free();
+  }
+  delete attrs1;
+  kids.free();
+  return start;
+
+ err3:
+  delete page;
+ err2:
+  kid.free();
+ err1:
+  kids.free();
+  delete attrs1;
+  ok = gFalse;
+  return -1;
+}
+
+int Catalog::findPage(int num, int gen) {
+  int i;
+
+  for (i = 0; i < numPages; ++i) {
+    if (pageRefs[i].num == num && pageRefs[i].gen == gen)
+      return i + 1;
+  }
+  return 0;
+}
+
+LinkDest *Catalog::findDest(GString *name) {
+  LinkDest *dest;
+  Object obj1, obj2;
+  GBool found;
+
+  // try named destination dictionary then name tree
+  found = gFalse;
+  if (dests.isDict()) {
+    if (!dests.dictLookup(name->getCString(), &obj1)->isNull())
+      found = gTrue;
+    else
+      obj1.free();
+  }
+  if (!found && nameTree.isDict()) {
+    if (!findDestInTree(&nameTree, name, &obj1)->isNull())
+      found = gTrue;
+    else
+      obj1.free();
+  }
+  if (!found)
+    return NULL;
+
+  // construct LinkDest
+  dest = NULL;
+  if (obj1.isArray()) {
+    dest = new LinkDest(obj1.getArray());
+  } else if (obj1.isDict()) {
+    if (obj1.dictLookup("D", &obj2)->isArray())
+      dest = new LinkDest(obj2.getArray());
+    else
+      error(-1, "Bad named destination value");
+    obj2.free();
+  } else {
+    error(-1, "Bad named destination value");
+  }
+  obj1.free();
+  if (dest && !dest->isOk()) {
+    delete dest;
+    dest = NULL;
+  }
+
+  return dest;
+}
+
+Object *Catalog::findDestInTree(Object *tree, GString *name, Object *obj) {
+  Object names, name1;
+  Object kids, kid, limits, low, high;
+  GBool done, found;
+  int cmp, i;
+
+  // leaf node
+  if (tree->dictLookup("Names", &names)->isArray()) {
+    done = found = gFalse;
+    for (i = 0; !done && i < names.arrayGetLength(); i += 2) {
+      if (names.arrayGet(i, &name1)->isString()) {
+       cmp = name->cmp(name1.getString());
+       if (cmp == 0) {
+         names.arrayGet(i+1, obj);
+         found = gTrue;
+         done = gTrue;
+       } else if (cmp < 0) {
+         done = gTrue;
+       }
+      }
+      name1.free();
+    }
+    names.free();
+    if (!found)
+      obj->initNull();
+    return obj;
+  }
+  names.free();
+
+  // root or intermediate node
+  done = gFalse;
+  if (tree->dictLookup("Kids", &kids)->isArray()) {
+    for (i = 0; !done && i < kids.arrayGetLength(); ++i) {
+      if (kids.arrayGet(i, &kid)->isDict()) {
+       if (kid.dictLookup("Limits", &limits)->isArray()) {
+         if (limits.arrayGet(0, &low)->isString() &&
+             name->cmp(low.getString()) >= 0) {
+           if (limits.arrayGet(1, &high)->isString() &&
+               name->cmp(high.getString()) <= 0) {
+             findDestInTree(&kid, name, obj);
+             done = gTrue;
+           }
+           high.free();
+         }
+         low.free();
+       }
+       limits.free();
+      }
+      kid.free();
+    }
+  }
+  kids.free();
+
+  // name was outside of ranges of all kids
+  if (!done)
+    obj->initNull();
+
+  return obj;
+}
diff --git a/lib/xpdf/Catalog.h b/lib/xpdf/Catalog.h
new file mode 100644 (file)
index 0000000..c38f109
--- /dev/null
@@ -0,0 +1,92 @@
+//========================================================================
+//
+// Catalog.h
+//
+// Copyright 1996-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef CATALOG_H
+#define CATALOG_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+class XRef;
+class Object;
+class Page;
+class PageAttrs;
+struct Ref;
+class LinkDest;
+
+//------------------------------------------------------------------------
+// Catalog
+//------------------------------------------------------------------------
+
+class Catalog {
+public:
+
+  // Constructor.
+  Catalog(XRef *xrefA);
+
+  // Destructor.
+  ~Catalog();
+
+  // Is catalog valid?
+  GBool isOk() { return ok; }
+
+  // Get number of pages.
+  int getNumPages() { return numPages; }
+
+  // Get a page.
+  Page *getPage(int i) { return pages[i-1]; }
+
+  // Get the reference for a page object.
+  Ref *getPageRef(int i) { return &pageRefs[i-1]; }
+
+  // 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);
+
+  // Find a named destination.  Returns the link destination, or
+  // NULL if <name> is not a destination.
+  LinkDest *findDest(GString *name);
+
+  Object *getOutline() { return &outline; }
+
+  Object *getAcroForm() { return &acroForm; }
+
+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
+  int pagesSize;               // size of pages array
+  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
+  Object outline;              // outline dictionary
+  Object acroForm;             // AcroForm dictionary
+  GBool ok;                    // true if catalog is valid
+
+  int readPageTree(Dict *pages, PageAttrs *attrs, int start);
+  Object *findDestInTree(Object *tree, GString *name, Object *obj);
+};
+
+#endif
diff --git a/lib/xpdf/CharCodeToUnicode.cc b/lib/xpdf/CharCodeToUnicode.cc
new file mode 100644 (file)
index 0000000..3702a16
--- /dev/null
@@ -0,0 +1,540 @@
+//========================================================================
+//
+// CharCodeToUnicode.cc
+//
+// Copyright 2001-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include "gmem.h"
+#include "gfile.h"
+#include "GString.h"
+#include "Error.h"
+#include "GlobalParams.h"
+#include "PSTokenizer.h"
+#include "CharCodeToUnicode.h"
+
+//------------------------------------------------------------------------
+
+#define maxUnicodeString 8
+
+struct CharCodeToUnicodeString {
+  CharCode c;
+  Unicode u[maxUnicodeString];
+  int len;
+};
+
+//------------------------------------------------------------------------
+
+static int getCharFromString(void *data) {
+  char *p;
+  int c;
+
+  p = *(char **)data;
+  if (*p) {
+    c = *p++;
+    *(char **)data = p;
+  } else {
+    c = EOF;
+  }
+  return c;
+}
+
+static int getCharFromFile(void *data) {
+  return fgetc((FILE *)data);
+}
+
+//------------------------------------------------------------------------
+
+CharCodeToUnicode *CharCodeToUnicode::parseCIDToUnicode(GString *fileName,
+                                                       GString *collection) {
+  FILE *f;
+  Unicode *mapA;
+  CharCode size, mapLenA;
+  char buf[64];
+  Unicode u;
+  CharCodeToUnicode *ctu;
+
+  if (!(f = fopen(fileName->getCString(), "r"))) {
+    error(-1, "Couldn't open cidToUnicode file '%s'",
+         fileName->getCString());
+    return NULL;
+  }
+
+  size = 32768;
+  mapA = (Unicode *)gmallocn(size, sizeof(Unicode));
+  mapLenA = 0;
+
+  while (getLine(buf, sizeof(buf), f)) {
+    if (mapLenA == size) {
+      size *= 2;
+      mapA = (Unicode *)greallocn(mapA, size, sizeof(Unicode));
+    }
+    if (sscanf(buf, "%x", &u) == 1) {
+      mapA[mapLenA] = u;
+    } else {
+      error(-1, "Bad line (%d) in cidToUnicode file '%s'",
+           (int)(mapLenA + 1), fileName->getCString());
+      mapA[mapLenA] = 0;
+    }
+    ++mapLenA;
+  }
+  fclose(f);
+
+  ctu = new CharCodeToUnicode(collection->copy(), mapA, mapLenA, gTrue,
+                             NULL, 0, 0);
+  gfree(mapA);
+  return ctu;
+}
+
+CharCodeToUnicode *CharCodeToUnicode::parseUnicodeToUnicode(
+                                                   GString *fileName) {
+  FILE *f;
+  Unicode *mapA;
+  CharCodeToUnicodeString *sMapA;
+  CharCode size, oldSize, len, sMapSizeA, sMapLenA;
+  char buf[256];
+  char *tok;
+  Unicode u0;
+  Unicode uBuf[maxUnicodeString];
+  CharCodeToUnicode *ctu;
+  int line, n, i;
+
+  if (!(f = fopen(fileName->getCString(), "r"))) {
+    error(-1, "Couldn't open unicodeToUnicode file '%s'",
+         fileName->getCString());
+    return NULL;
+  }
+
+  size = 4096;
+  mapA = (Unicode *)gmallocn(size, sizeof(Unicode));
+  memset(mapA, 0, size * sizeof(Unicode));
+  len = 0;
+  sMapA = NULL;
+  sMapSizeA = sMapLenA = 0;
+
+  line = 0;
+  while (getLine(buf, sizeof(buf), f)) {
+    ++line;
+    if (!(tok = strtok(buf, " \t\r\n")) ||
+       sscanf(tok, "%x", &u0) != 1) {
+      error(-1, "Bad line (%d) in unicodeToUnicode file '%s'",
+           line, fileName->getCString());
+      continue;
+    }
+    n = 0;
+    while (n < maxUnicodeString) {
+      if (!(tok = strtok(NULL, " \t\r\n"))) {
+       break;
+      }
+      if (sscanf(tok, "%x", &uBuf[n]) != 1) {
+       error(-1, "Bad line (%d) in unicodeToUnicode file '%s'",
+             line, fileName->getCString());
+       break;
+      }
+      ++n;
+    }
+    if (n < 1) {
+      error(-1, "Bad line (%d) in unicodeToUnicode file '%s'",
+           line, fileName->getCString());
+      continue;
+    }
+    if (u0 >= size) {
+      oldSize = size;
+      while (u0 >= size) {
+       size *= 2;
+      }
+      mapA = (Unicode *)greallocn(mapA, size, sizeof(Unicode));
+      memset(mapA + oldSize, 0, (size - oldSize) * sizeof(Unicode));
+    }
+    if (n == 1) {
+      mapA[u0] = uBuf[0];
+    } else {
+      mapA[u0] = 0;
+      if (sMapLenA == sMapSizeA) {
+       sMapSizeA += 16;
+       sMapA = (CharCodeToUnicodeString *)
+                 greallocn(sMapA, sMapSizeA, sizeof(CharCodeToUnicodeString));
+      }
+      sMapA[sMapLenA].c = u0;
+      for (i = 0; i < n; ++i) {
+       sMapA[sMapLenA].u[i] = uBuf[i];
+      }
+      sMapA[sMapLenA].len = n;
+      ++sMapLenA;
+    }
+    if (u0 >= len) {
+      len = u0 + 1;
+    }
+  }
+  fclose(f);
+
+  ctu = new CharCodeToUnicode(fileName->copy(), mapA, len, gTrue,
+                             sMapA, sMapLenA, sMapSizeA);
+  gfree(mapA);
+  return ctu;
+}
+
+CharCodeToUnicode *CharCodeToUnicode::make8BitToUnicode(Unicode *toUnicode) {
+  return new CharCodeToUnicode(NULL, toUnicode, 256, gTrue, NULL, 0, 0);
+}
+
+CharCodeToUnicode *CharCodeToUnicode::parseCMap(GString *buf, int nBits) {
+  CharCodeToUnicode *ctu;
+  char *p;
+
+  ctu = new CharCodeToUnicode(NULL);
+  p = buf->getCString();
+  ctu->parseCMap1(&getCharFromString, &p, nBits);
+  return ctu;
+}
+
+void CharCodeToUnicode::mergeCMap(GString *buf, int nBits) {
+  char *p;
+
+  p = buf->getCString();
+  parseCMap1(&getCharFromString, &p, nBits);
+}
+
+void CharCodeToUnicode::parseCMap1(int (*getCharFunc)(void *), void *data,
+                                  int nBits) {
+  PSTokenizer *pst;
+  char tok1[256], tok2[256], tok3[256];
+  int nDigits, n1, n2, n3;
+  CharCode i;
+  CharCode code1, code2;
+  GString *name;
+  FILE *f;
+
+  nDigits = nBits / 4;
+  pst = new PSTokenizer(getCharFunc, data);
+  pst->getToken(tok1, sizeof(tok1), &n1);
+  while (pst->getToken(tok2, sizeof(tok2), &n2)) {
+    if (!strcmp(tok2, "usecmap")) {
+      if (tok1[0] == '/') {
+       name = new GString(tok1 + 1);
+       if ((f = globalParams->findToUnicodeFile(name))) {
+         parseCMap1(&getCharFromFile, f, nBits);
+         fclose(f);
+       } else {
+         error(-1, "Couldn't find ToUnicode CMap file for '%s'",
+               name->getCString());
+       }
+       delete name;
+      }
+      pst->getToken(tok1, sizeof(tok1), &n1);
+    } else if (!strcmp(tok2, "beginbfchar")) {
+      while (pst->getToken(tok1, sizeof(tok1), &n1)) {
+       if (!strcmp(tok1, "endbfchar")) {
+         break;
+       }
+       if (!pst->getToken(tok2, sizeof(tok2), &n2) ||
+           !strcmp(tok2, "endbfchar")) {
+         error(-1, "Illegal entry in bfchar block in ToUnicode CMap");
+         break;
+       }
+       if (!(n1 == 2 + nDigits && tok1[0] == '<' && tok1[n1 - 1] == '>' &&
+             tok2[0] == '<' && tok2[n2 - 1] == '>')) {
+         error(-1, "Illegal entry in bfchar block in ToUnicode CMap");
+         continue;
+       }
+       tok1[n1 - 1] = tok2[n2 - 1] = '\0';
+       if (sscanf(tok1 + 1, "%x", &code1) != 1) {
+         error(-1, "Illegal entry in bfchar block in ToUnicode CMap");
+         continue;
+       }
+       addMapping(code1, tok2 + 1, n2 - 2, 0);
+      }
+      pst->getToken(tok1, sizeof(tok1), &n1);
+    } else if (!strcmp(tok2, "beginbfrange")) {
+      while (pst->getToken(tok1, sizeof(tok1), &n1)) {
+       if (!strcmp(tok1, "endbfrange")) {
+         break;
+       }
+       if (!pst->getToken(tok2, sizeof(tok2), &n2) ||
+           !strcmp(tok2, "endbfrange") ||
+           !pst->getToken(tok3, sizeof(tok3), &n3) ||
+           !strcmp(tok3, "endbfrange")) {
+         error(-1, "Illegal entry in bfrange block in ToUnicode CMap");
+         break;
+       }
+       if (!(n1 == 2 + nDigits && tok1[0] == '<' && tok1[n1 - 1] == '>' &&
+             n2 == 2 + nDigits && tok2[0] == '<' && tok2[n2 - 1] == '>')) {
+         error(-1, "Illegal entry in bfrange block in ToUnicode CMap");
+         continue;
+       }
+       tok1[n1 - 1] = tok2[n2 - 1] = '\0';
+       if (sscanf(tok1 + 1, "%x", &code1) != 1 ||
+           sscanf(tok2 + 1, "%x", &code2) != 1) {
+         error(-1, "Illegal entry in bfrange block in ToUnicode CMap");
+         continue;
+       }
+       if (!strcmp(tok3, "[")) {
+         i = 0;
+         while (pst->getToken(tok1, sizeof(tok1), &n1) &&
+                code1 + i <= code2) {
+           if (!strcmp(tok1, "]")) {
+             break;
+           }
+           if (tok1[0] == '<' && tok1[n1 - 1] == '>') {
+             tok1[n1 - 1] = '\0';
+             addMapping(code1 + i, tok1 + 1, n1 - 2, 0);
+           } else {
+             error(-1, "Illegal entry in bfrange block in ToUnicode CMap");
+           }
+           ++i;
+         }
+       } else if (tok3[0] == '<' && tok3[n3 - 1] == '>') {
+         tok3[n3 - 1] = '\0';
+         for (i = 0; code1 <= code2; ++code1, ++i) {
+           addMapping(code1, tok3 + 1, n3 - 2, i);
+         }
+
+       } else {
+         error(-1, "Illegal entry in bfrange block in ToUnicode CMap");
+       }
+      }
+      pst->getToken(tok1, sizeof(tok1), &n1);
+    } else {
+      strcpy(tok1, tok2);
+    }
+  }
+  delete pst;
+}
+
+void CharCodeToUnicode::addMapping(CharCode code, char *uStr, int n,
+                                  int offset) {
+  CharCode oldLen, i;
+  Unicode u;
+  char uHex[5];
+  int j;
+
+  if (code >= mapLen) {
+    oldLen = mapLen;
+    mapLen = (code + 256) & ~255;
+    map = (Unicode *)greallocn(map, mapLen, sizeof(Unicode));
+    for (i = oldLen; i < mapLen; ++i) {
+      map[i] = 0;
+    }
+  }
+  if (n <= 4) {
+    if (sscanf(uStr, "%x", &u) != 1) {
+      error(-1, "Illegal entry in ToUnicode CMap");
+      return;
+    }
+    map[code] = u + offset;
+  } else {
+    if (sMapLen >= sMapSize) {
+      sMapSize = sMapSize + 16;
+      sMap = (CharCodeToUnicodeString *)
+              greallocn(sMap, sMapSize, sizeof(CharCodeToUnicodeString));
+    }
+    map[code] = 0;
+    sMap[sMapLen].c = code;
+    sMap[sMapLen].len = n / 4;
+    for (j = 0; j < sMap[sMapLen].len && j < maxUnicodeString; ++j) {
+      strncpy(uHex, uStr + j*4, 4);
+      uHex[4] = '\0';
+      if (sscanf(uHex, "%x", &sMap[sMapLen].u[j]) != 1) {
+       error(-1, "Illegal entry in ToUnicode CMap");
+      }
+    }
+    sMap[sMapLen].u[sMap[sMapLen].len - 1] += offset;
+    ++sMapLen;
+  }
+}
+
+CharCodeToUnicode::CharCodeToUnicode(GString *tagA) {
+  CharCode i;
+
+  tag = tagA;
+  mapLen = 256;
+  map = (Unicode *)gmallocn(mapLen, sizeof(Unicode));
+  for (i = 0; i < mapLen; ++i) {
+    map[i] = 0;
+  }
+  sMap = NULL;
+  sMapLen = sMapSize = 0;
+  refCnt = 1;
+#if MULTITHREADED
+  gInitMutex(&mutex);
+#endif
+}
+
+CharCodeToUnicode::CharCodeToUnicode(GString *tagA, Unicode *mapA,
+                                    CharCode mapLenA, GBool copyMap,
+                                    CharCodeToUnicodeString *sMapA,
+                                    int sMapLenA, int sMapSizeA) {
+  tag = tagA;
+  mapLen = mapLenA;
+  if (copyMap) {
+    map = (Unicode *)gmallocn(mapLen, sizeof(Unicode));
+    memcpy(map, mapA, mapLen * sizeof(Unicode));
+  } else {
+    map = mapA;
+  }
+  sMap = sMapA;
+  sMapLen = sMapLenA;
+  sMapSize = sMapSizeA;
+  refCnt = 1;
+#if MULTITHREADED
+  gInitMutex(&mutex);
+#endif
+}
+
+CharCodeToUnicode::~CharCodeToUnicode() {
+  if (tag) {
+    delete tag;
+  }
+  gfree(map);
+  if (sMap) {
+    gfree(sMap);
+  }
+#if MULTITHREADED
+  gDestroyMutex(&mutex);
+#endif
+}
+
+void CharCodeToUnicode::incRefCnt() {
+#if MULTITHREADED
+  gLockMutex(&mutex);
+#endif
+  ++refCnt;
+#if MULTITHREADED
+  gUnlockMutex(&mutex);
+#endif
+}
+
+void CharCodeToUnicode::decRefCnt() {
+  GBool done;
+
+#if MULTITHREADED
+  gLockMutex(&mutex);
+#endif
+  done = --refCnt == 0;
+#if MULTITHREADED
+  gUnlockMutex(&mutex);
+#endif
+  if (done) {
+    delete this;
+  }
+}
+
+GBool CharCodeToUnicode::match(GString *tagA) {
+  return tag && !tag->cmp(tagA);
+}
+
+void CharCodeToUnicode::setMapping(CharCode c, Unicode *u, int len) {
+  int i, j;
+
+  if (len == 1) {
+    map[c] = u[0];
+  } else {
+    for (i = 0; i < sMapLen; ++i) {
+      if (sMap[i].c == c) {
+       break;
+      }
+    }
+    if (i == sMapLen) {
+      if (sMapLen == sMapSize) {
+       sMapSize += 8;
+       sMap = (CharCodeToUnicodeString *)
+                greallocn(sMap, sMapSize, sizeof(CharCodeToUnicodeString));
+      }
+      ++sMapLen;
+    }
+    map[c] = 0;
+    sMap[i].c = c;
+    sMap[i].len = len;
+    for (j = 0; j < len && j < maxUnicodeString; ++j) {
+      sMap[i].u[j] = u[j];
+    }
+  }
+}
+
+int CharCodeToUnicode::mapToUnicode(CharCode c, Unicode *u, int size) {
+  int i, j;
+
+  if (c >= mapLen) {
+    return 0;
+  }
+  if (map[c]) {
+    u[0] = map[c];
+    return 1;
+  }
+  for (i = 0; i < sMapLen; ++i) {
+    if (sMap[i].c == c) {
+      for (j = 0; j < sMap[i].len && j < size; ++j) {
+       u[j] = sMap[i].u[j];
+      }
+      return j;
+    }
+  }
+  return 0;
+}
+
+//------------------------------------------------------------------------
+
+CharCodeToUnicodeCache::CharCodeToUnicodeCache(int sizeA) {
+  int i;
+
+  size = sizeA;
+  cache = (CharCodeToUnicode **)gmallocn(size, sizeof(CharCodeToUnicode *));
+  for (i = 0; i < size; ++i) {
+    cache[i] = NULL;
+  }
+}
+
+CharCodeToUnicodeCache::~CharCodeToUnicodeCache() {
+  int i;
+
+  for (i = 0; i < size; ++i) {
+    if (cache[i]) {
+      cache[i]->decRefCnt();
+    }
+  }
+  gfree(cache);
+}
+
+CharCodeToUnicode *CharCodeToUnicodeCache::getCharCodeToUnicode(GString *tag) {
+  CharCodeToUnicode *ctu;
+  int i, j;
+
+  if (cache[0] && cache[0]->match(tag)) {
+    cache[0]->incRefCnt();
+    return cache[0];
+  }
+  for (i = 1; i < size; ++i) {
+    if (cache[i] && cache[i]->match(tag)) {
+      ctu = cache[i];
+      for (j = i; j >= 1; --j) {
+       cache[j] = cache[j - 1];
+      }
+      cache[0] = ctu;
+      ctu->incRefCnt();
+      return ctu;
+    }
+  }
+  return NULL;
+}
+
+void CharCodeToUnicodeCache::add(CharCodeToUnicode *ctu) {
+  int i;
+
+  if (cache[size - 1]) {
+    cache[size - 1]->decRefCnt();
+  }
+  for (i = size - 1; i >= 1; --i) {
+    cache[i] = cache[i - 1];
+  }
+  cache[0] = ctu;
+  ctu->incRefCnt();
+}
diff --git a/lib/xpdf/CharCodeToUnicode.h b/lib/xpdf/CharCodeToUnicode.h
new file mode 100644 (file)
index 0000000..04852ae
--- /dev/null
@@ -0,0 +1,117 @@
+//========================================================================
+//
+// CharCodeToUnicode.h
+//
+// Mapping from character codes to Unicode.
+//
+// Copyright 2001-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef CHARCODETOUNICODE_H
+#define CHARCODETOUNICODE_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#include "CharTypes.h"
+
+#if MULTITHREADED
+#include "GMutex.h"
+#endif
+
+struct CharCodeToUnicodeString;
+
+//------------------------------------------------------------------------
+
+class CharCodeToUnicode {
+public:
+
+  // Read the CID-to-Unicode mapping for <collection> from the file
+  // specified by <fileName>.  Sets the initial reference count to 1.
+  // Returns NULL on failure.
+  static CharCodeToUnicode *parseCIDToUnicode(GString *fileName,
+                                             GString *collection);
+
+  // Create a Unicode-to-Unicode mapping from the file specified by
+  // <fileName>.  Sets the initial reference count to 1.  Returns NULL
+  // on failure.
+  static CharCodeToUnicode *parseUnicodeToUnicode(GString *fileName);
+
+  // Create the CharCode-to-Unicode mapping for an 8-bit font.
+  // <toUnicode> is an array of 256 Unicode indexes.  Sets the initial
+  // reference count to 1.
+  static CharCodeToUnicode *make8BitToUnicode(Unicode *toUnicode);
+
+  // Parse a ToUnicode CMap for an 8- or 16-bit font.
+  static CharCodeToUnicode *parseCMap(GString *buf, int nBits);
+
+  // Parse a ToUnicode CMap for an 8- or 16-bit font, merging it into
+  // <this>.
+  void mergeCMap(GString *buf, int nBits);
+
+  ~CharCodeToUnicode();
+
+  void incRefCnt();
+  void decRefCnt();
+
+  // Return true if this mapping matches the specified <tagA>.
+  GBool match(GString *tagA);
+
+  // Set the mapping for <c>.
+  void setMapping(CharCode c, Unicode *u, int len);
+
+  // Map a CharCode to Unicode.
+  int mapToUnicode(CharCode c, Unicode *u, int size);
+
+  // Return the mapping's length, i.e., one more than the max char
+  // code supported by the mapping.
+  CharCode getLength() { return mapLen; }
+
+private:
+
+  void parseCMap1(int (*getCharFunc)(void *), void *data, int nBits);
+  void addMapping(CharCode code, char *uStr, int n, int offset);
+  CharCodeToUnicode(GString *tagA);
+  CharCodeToUnicode(GString *tagA, Unicode *mapA,
+                   CharCode mapLenA, GBool copyMap,
+                   CharCodeToUnicodeString *sMapA,
+                   int sMapLenA, int sMapSizeA);
+
+  GString *tag;
+  Unicode *map;
+  CharCode mapLen;
+  CharCodeToUnicodeString *sMap;
+  int sMapLen, sMapSize;
+  int refCnt;
+#if MULTITHREADED
+  GMutex mutex;
+#endif
+};
+
+//------------------------------------------------------------------------
+
+class CharCodeToUnicodeCache {
+public:
+
+  CharCodeToUnicodeCache(int sizeA);
+  ~CharCodeToUnicodeCache();
+
+  // Get the CharCodeToUnicode object for <tag>.  Increments its
+  // reference count; there will be one reference for the cache plus
+  // one for the caller of this function.  Returns NULL on failure.
+  CharCodeToUnicode *getCharCodeToUnicode(GString *tag);
+
+  // Insert <ctu> into the cache, in the most-recently-used position.
+  void add(CharCodeToUnicode *ctu);
+
+private:
+
+  CharCodeToUnicode **cache;
+  int size;
+};
+
+#endif
diff --git a/lib/xpdf/CharTypes.h b/lib/xpdf/CharTypes.h
new file mode 100644 (file)
index 0000000..d0df630
--- /dev/null
@@ -0,0 +1,24 @@
+//========================================================================
+//
+// CharTypes.h
+//
+// Copyright 2001-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef CHARTYPES_H
+#define CHARTYPES_H
+
+// Unicode character.
+typedef unsigned int Unicode;
+
+// Character ID for CID character collections.
+typedef unsigned int CID;
+
+// This is large enough to hold any of the following:
+// - 8-bit char code
+// - 16-bit CID
+// - Unicode
+typedef unsigned int CharCode;
+
+#endif
diff --git a/lib/xpdf/Decrypt.cc b/lib/xpdf/Decrypt.cc
new file mode 100644 (file)
index 0000000..91eb452
--- /dev/null
@@ -0,0 +1,411 @@
+//========================================================================
+//
+// Decrypt.cc
+//
+// Copyright 1996-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <string.h>
+#include "gmem.h"
+#include "Decrypt.h"
+
+static void rc4InitKey(Guchar *key, int keyLen, Guchar *state);
+static Guchar rc4DecryptByte(Guchar *state, Guchar *x, Guchar *y, Guchar c);
+static void md5(Guchar *msg, int msgLen, Guchar *digest);
+
+static Guchar passwordPad[32] = {
+  0x28, 0xbf, 0x4e, 0x5e, 0x4e, 0x75, 0x8a, 0x41,
+  0x64, 0x00, 0x4e, 0x56, 0xff, 0xfa, 0x01, 0x08, 
+  0x2e, 0x2e, 0x00, 0xb6, 0xd0, 0x68, 0x3e, 0x80, 
+  0x2f, 0x0c, 0xa9, 0xfe, 0x64, 0x53, 0x69, 0x7a
+};
+
+//------------------------------------------------------------------------
+// Decrypt
+//------------------------------------------------------------------------
+
+Decrypt::Decrypt(Guchar *fileKey, int keyLength, int objNum, int objGen) {
+  int i;
+
+  // construct object key
+  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;
+  if ((objKeyLength = keyLength + 5) > 16) {
+    objKeyLength = 16;
+  }
+  rc4InitKey(objKey, objKeyLength, state);
+}
+
+void Decrypt::reset() {
+  x = y = 0;
+  rc4InitKey(objKey, objKeyLength, state);
+}
+
+Guchar Decrypt::decryptByte(Guchar c) {
+  return rc4DecryptByte(state, &x, &y, c);
+}
+
+GBool Decrypt::makeFileKey(int encVersion, int encRevision, int keyLength,
+                          GString *ownerKey, GString *userKey,
+                          int permissions, GString *fileID,
+                          GString *ownerPassword, GString *userPassword,
+                          Guchar *fileKey, GBool encryptMetadata,
+                          GBool *ownerPasswordOk) {
+  Guchar test[32], test2[32];
+  GString *userPassword2;
+  Guchar fState[256];
+  Guchar tmpKey[16];
+  Guchar fx, fy;
+  int len, i, j;
+
+  // try using the supplied owner password to generate the user password
+  *ownerPasswordOk = gFalse;
+  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);
+    }
+    md5(test, 32, test);
+    if (encRevision == 3) {
+      for (i = 0; i < 50; ++i) {
+       md5(test, 16, test);
+      }
+    }
+    if (encRevision == 2) {
+      rc4InitKey(test, keyLength, fState);
+      fx = fy = 0;
+      for (i = 0; i < 32; ++i) {
+       test2[i] = rc4DecryptByte(fState, &fx, &fy, ownerKey->getChar(i));
+      }
+    } else {
+      memcpy(test2, ownerKey->getCString(), 32);
+      for (i = 19; i >= 0; --i) {
+       for (j = 0; j < keyLength; ++j) {
+         tmpKey[j] = test[j] ^ i;
+       }
+       rc4InitKey(tmpKey, keyLength, fState);
+       fx = fy = 0;
+       for (j = 0; j < 32; ++j) {
+         test2[j] = rc4DecryptByte(fState, &fx, &fy, test2[j]);
+       }
+      }
+    }
+    userPassword2 = new GString((char *)test2, 32);
+    if (makeFileKey2(encVersion, encRevision, keyLength, ownerKey, userKey,
+                    permissions, fileID, userPassword2, fileKey,
+                    encryptMetadata)) {
+      *ownerPasswordOk = gTrue;
+      delete userPassword2;
+      return gTrue;
+    }
+    delete userPassword2;
+  }
+
+  // try using the supplied user password
+  return makeFileKey2(encVersion, encRevision, keyLength, ownerKey, userKey,
+                     permissions, fileID, userPassword, fileKey,
+                     encryptMetadata);
+}
+
+GBool Decrypt::makeFileKey2(int encVersion, int encRevision, int keyLength,
+                           GString *ownerKey, GString *userKey,
+                           int permissions, GString *fileID,
+                           GString *userPassword, Guchar *fileKey,
+                           GBool encryptMetadata) {
+  Guchar *buf;
+  Guchar test[32];
+  Guchar fState[256];
+  Guchar tmpKey[16];
+  Guchar fx, fy;
+  int len, i, j;
+  GBool ok;
+
+  // generate file key
+  buf = (Guchar *)gmalloc(72 + fileID->getLength());
+  if (userPassword) {
+    len = userPassword->getLength();
+    if (len < 32) {
+      memcpy(buf, userPassword->getCString(), len);
+      memcpy(buf + len, passwordPad, 32 - len);
+    } else {
+      memcpy(buf, userPassword->getCString(), 32);
+    }
+  } else {
+    memcpy(buf, passwordPad, 32);
+  }
+  memcpy(buf + 32, ownerKey->getCString(), 32);
+  buf[64] = permissions & 0xff;
+  buf[65] = (permissions >> 8) & 0xff;
+  buf[66] = (permissions >> 16) & 0xff;
+  buf[67] = (permissions >> 24) & 0xff;
+  memcpy(buf + 68, fileID->getCString(), fileID->getLength());
+  len = 68 + fileID->getLength();
+  if (!encryptMetadata) {
+    buf[len++] = 0xff;
+    buf[len++] = 0xff;
+    buf[len++] = 0xff;
+    buf[len++] = 0xff;
+  }
+  md5(buf, len, fileKey);
+  if (encRevision == 3) {
+    for (i = 0; i < 50; ++i) {
+      md5(fileKey, keyLength, fileKey);
+    }
+  }
+
+  // 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;
+  }
+
+  gfree(buf);
+  return ok;
+}
+
+//------------------------------------------------------------------------
+// RC4-compatible decryption
+//------------------------------------------------------------------------
+
+static void rc4InitKey(Guchar *key, int keyLen, Guchar *state) {
+  Guchar index1, index2;
+  Guchar t;
+  int i;
+
+  for (i = 0; i < 256; ++i)
+    state[i] = i;
+  index1 = index2 = 0;
+  for (i = 0; i < 256; ++i) {
+    index2 = (key[index1] + state[i] + index2) % 256;
+    t = state[i];
+    state[i] = state[index2];
+    state[index2] = t;
+    index1 = (index1 + 1) % keyLen;
+  }
+}
+
+static Guchar rc4DecryptByte(Guchar *state, Guchar *x, Guchar *y, Guchar c) {
+  Guchar x1, y1, tx, ty;
+
+  x1 = *x = (*x + 1) % 256;
+  y1 = *y = (state[*x] + *y) % 256;
+  tx = state[x1];
+  ty = state[y1];
+  state[x1] = ty;
+  state[y1] = tx;
+  return c ^ state[(tx + ty) % 256];
+}
+
+//------------------------------------------------------------------------
+// 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;
+}
+
+static inline Gulong md5Round1(Gulong a, Gulong b, Gulong c, Gulong d,
+                              Gulong Xk,  Gulong s, Gulong Ti) {
+  return b + rotateLeft((a + ((b & c) | (~b & d)) + Xk + Ti), s);
+}
+
+static inline Gulong md5Round2(Gulong a, Gulong b, Gulong c, Gulong d,
+                              Gulong Xk,  Gulong s, Gulong Ti) {
+  return b + rotateLeft((a + ((b & d) | (c & ~d)) + Xk + Ti), s);
+}
+
+static inline Gulong md5Round3(Gulong a, Gulong b, Gulong c, Gulong d,
+                              Gulong Xk,  Gulong s, Gulong Ti) {
+  return b + rotateLeft((a + (b ^ c ^ d) + Xk + Ti), s);
+}
+
+static inline Gulong md5Round4(Gulong a, Gulong b, Gulong c, Gulong d,
+                              Gulong Xk,  Gulong s, Gulong Ti) {
+  return b + rotateLeft((a + (c ^ (b | ~d)) + Xk + Ti), s);
+}
+
+static void md5(Guchar *msg, int msgLen, Guchar *digest) {
+  Gulong x[16];
+  Gulong a, b, c, d, aa, bb, cc, dd;
+  int n64;
+  int i, j, k;
+
+  // compute number of 64-byte blocks
+  // (length + pad byte (0x80) + 8 bytes for length)
+  n64 = (msgLen + 1 + 8 + 63) / 64;
+
+  // initialize a, b, c, d
+  a = 0x67452301;
+  b = 0xefcdab89;
+  c = 0x98badcfe;
+  d = 0x10325476;
+
+  // loop through blocks
+  k = 0;
+  for (i = 0; i < n64; ++i) {
+
+    // grab a 64-byte block
+    for (j = 0; j < 16 && k < msgLen - 3; ++j, k += 4)
+      x[j] = (((((msg[k+3] << 8) + msg[k+2]) << 8) + msg[k+1]) << 8) + msg[k];
+    if (i == n64 - 1) {
+      if (k == msgLen - 3)
+       x[j] = 0x80000000 + (((msg[k+2] << 8) + msg[k+1]) << 8) + msg[k];
+      else if (k == msgLen - 2)
+       x[j] = 0x800000 + (msg[k+1] << 8) + msg[k];
+      else if (k == msgLen - 1)
+       x[j] = 0x8000 + msg[k];
+      else
+       x[j] = 0x80;
+      ++j;
+      while (j < 16)
+       x[j++] = 0;
+      x[14] = msgLen << 3;
+    }
+
+    // save a, b, c, d
+    aa = a;
+    bb = b;
+    cc = c;
+    dd = d;
+
+    // round 1
+    a = md5Round1(a, b, c, d, x[0],   7, 0xd76aa478);
+    d = md5Round1(d, a, b, c, x[1],  12, 0xe8c7b756);
+    c = md5Round1(c, d, a, b, x[2],  17, 0x242070db);
+    b = md5Round1(b, c, d, a, x[3],  22, 0xc1bdceee);
+    a = md5Round1(a, b, c, d, x[4],   7, 0xf57c0faf);
+    d = md5Round1(d, a, b, c, x[5],  12, 0x4787c62a);
+    c = md5Round1(c, d, a, b, x[6],  17, 0xa8304613);
+    b = md5Round1(b, c, d, a, x[7],  22, 0xfd469501);
+    a = md5Round1(a, b, c, d, x[8],   7, 0x698098d8);
+    d = md5Round1(d, a, b, c, x[9],  12, 0x8b44f7af);
+    c = md5Round1(c, d, a, b, x[10], 17, 0xffff5bb1);
+    b = md5Round1(b, c, d, a, x[11], 22, 0x895cd7be);
+    a = md5Round1(a, b, c, d, x[12],  7, 0x6b901122);
+    d = md5Round1(d, a, b, c, x[13], 12, 0xfd987193);
+    c = md5Round1(c, d, a, b, x[14], 17, 0xa679438e);
+    b = md5Round1(b, c, d, a, x[15], 22, 0x49b40821);
+
+    // round 2
+    a = md5Round2(a, b, c, d, x[1],   5, 0xf61e2562);
+    d = md5Round2(d, a, b, c, x[6],   9, 0xc040b340);
+    c = md5Round2(c, d, a, b, x[11], 14, 0x265e5a51);
+    b = md5Round2(b, c, d, a, x[0],  20, 0xe9b6c7aa);
+    a = md5Round2(a, b, c, d, x[5],   5, 0xd62f105d);
+    d = md5Round2(d, a, b, c, x[10],  9, 0x02441453);
+    c = md5Round2(c, d, a, b, x[15], 14, 0xd8a1e681);
+    b = md5Round2(b, c, d, a, x[4],  20, 0xe7d3fbc8);
+    a = md5Round2(a, b, c, d, x[9],   5, 0x21e1cde6);
+    d = md5Round2(d, a, b, c, x[14],  9, 0xc33707d6);
+    c = md5Round2(c, d, a, b, x[3],  14, 0xf4d50d87);
+    b = md5Round2(b, c, d, a, x[8],  20, 0x455a14ed);
+    a = md5Round2(a, b, c, d, x[13],  5, 0xa9e3e905);
+    d = md5Round2(d, a, b, c, x[2],   9, 0xfcefa3f8);
+    c = md5Round2(c, d, a, b, x[7],  14, 0x676f02d9);
+    b = md5Round2(b, c, d, a, x[12], 20, 0x8d2a4c8a);
+
+    // round 3
+    a = md5Round3(a, b, c, d, x[5],   4, 0xfffa3942);
+    d = md5Round3(d, a, b, c, x[8],  11, 0x8771f681);
+    c = md5Round3(c, d, a, b, x[11], 16, 0x6d9d6122);
+    b = md5Round3(b, c, d, a, x[14], 23, 0xfde5380c);
+    a = md5Round3(a, b, c, d, x[1],   4, 0xa4beea44);
+    d = md5Round3(d, a, b, c, x[4],  11, 0x4bdecfa9);
+    c = md5Round3(c, d, a, b, x[7],  16, 0xf6bb4b60);
+    b = md5Round3(b, c, d, a, x[10], 23, 0xbebfbc70);
+    a = md5Round3(a, b, c, d, x[13],  4, 0x289b7ec6);
+    d = md5Round3(d, a, b, c, x[0],  11, 0xeaa127fa);
+    c = md5Round3(c, d, a, b, x[3],  16, 0xd4ef3085);
+    b = md5Round3(b, c, d, a, x[6],  23, 0x04881d05);
+    a = md5Round3(a, b, c, d, x[9],   4, 0xd9d4d039);
+    d = md5Round3(d, a, b, c, x[12], 11, 0xe6db99e5);
+    c = md5Round3(c, d, a, b, x[15], 16, 0x1fa27cf8);
+    b = md5Round3(b, c, d, a, x[2],  23, 0xc4ac5665);
+
+    // round 4
+    a = md5Round4(a, b, c, d, x[0],   6, 0xf4292244);
+    d = md5Round4(d, a, b, c, x[7],  10, 0x432aff97);
+    c = md5Round4(c, d, a, b, x[14], 15, 0xab9423a7);
+    b = md5Round4(b, c, d, a, x[5],  21, 0xfc93a039);
+    a = md5Round4(a, b, c, d, x[12],  6, 0x655b59c3);
+    d = md5Round4(d, a, b, c, x[3],  10, 0x8f0ccc92);
+    c = md5Round4(c, d, a, b, x[10], 15, 0xffeff47d);
+    b = md5Round4(b, c, d, a, x[1],  21, 0x85845dd1);
+    a = md5Round4(a, b, c, d, x[8],   6, 0x6fa87e4f);
+    d = md5Round4(d, a, b, c, x[15], 10, 0xfe2ce6e0);
+    c = md5Round4(c, d, a, b, x[6],  15, 0xa3014314);
+    b = md5Round4(b, c, d, a, x[13], 21, 0x4e0811a1);
+    a = md5Round4(a, b, c, d, x[4],   6, 0xf7537e82);
+    d = md5Round4(d, a, b, c, x[11], 10, 0xbd3af235);
+    c = md5Round4(c, d, a, b, x[2],  15, 0x2ad7d2bb);
+    b = md5Round4(b, c, d, a, x[9],  21, 0xeb86d391);
+
+    // increment a, b, c, d
+    a += aa;
+    b += bb;
+    c += cc;
+    d += dd;
+  }
+
+  // break digest into bytes
+  digest[0] = (Guchar)(a & 0xff);
+  digest[1] = (Guchar)((a >>= 8) & 0xff);
+  digest[2] = (Guchar)((a >>= 8) & 0xff);
+  digest[3] = (Guchar)((a >>= 8) & 0xff);
+  digest[4] = (Guchar)(b & 0xff);
+  digest[5] = (Guchar)((b >>= 8) & 0xff);
+  digest[6] = (Guchar)((b >>= 8) & 0xff);
+  digest[7] = (Guchar)((b >>= 8) & 0xff);
+  digest[8] = (Guchar)(c & 0xff);
+  digest[9] = (Guchar)((c >>= 8) & 0xff);
+  digest[10] = (Guchar)((c >>= 8) & 0xff);
+  digest[11] = (Guchar)((c >>= 8) & 0xff);
+  digest[12] = (Guchar)(d & 0xff);
+  digest[13] = (Guchar)((d >>= 8) & 0xff);
+  digest[14] = (Guchar)((d >>= 8) & 0xff);
+  digest[15] = (Guchar)((d >>= 8) & 0xff);
+}
diff --git a/lib/xpdf/Decrypt.h b/lib/xpdf/Decrypt.h
new file mode 100644 (file)
index 0000000..2beba58
--- /dev/null
@@ -0,0 +1,63 @@
+//========================================================================
+//
+// Decrypt.h
+//
+// Copyright 1996-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef DECRYPT_H
+#define DECRYPT_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#include "gtypes.h"
+#include "GString.h"
+
+//------------------------------------------------------------------------
+// Decrypt
+//------------------------------------------------------------------------
+
+class Decrypt {
+public:
+
+  // Initialize the decryptor object.
+  Decrypt(Guchar *fileKey, int keyLength, int objNum, int objGen);
+
+  // Reset decryption.
+  void reset();
+
+  // Decrypt one byte.
+  Guchar decryptByte(Guchar c);
+
+  // 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 *ownerPassword, GString *userPassword,
+                          Guchar *fileKey, GBool encryptMetadata,
+                          GBool *ownerPasswordOk);
+
+private:
+
+  static GBool makeFileKey2(int encVersion, int encRevision, int keyLength,
+                           GString *ownerKey, GString *userKey,
+                           int permissions, GString *fileID,
+                           GString *userPassword, Guchar *fileKey,
+                           GBool encryptMetadata);
+
+  int objKeyLength;
+  Guchar objKey[21];
+  Guchar state[256];
+  Guchar x, y;
+};
+
+#endif
diff --git a/lib/xpdf/Dict.cc b/lib/xpdf/Dict.cc
new file mode 100644 (file)
index 0000000..dd1517f
--- /dev/null
@@ -0,0 +1,95 @@
+//========================================================================
+//
+// Dict.cc
+//
+// Copyright 1996-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <stddef.h>
+#include <string.h>
+#include "gmem.h"
+#include "Object.h"
+#include "XRef.h"
+#include "Dict.h"
+
+//------------------------------------------------------------------------
+// Dict
+//------------------------------------------------------------------------
+
+Dict::Dict(XRef *xrefA) {
+  xref = xrefA;
+  entries = NULL;
+  size = length = 0;
+  ref = 1;
+}
+
+Dict::~Dict() {
+  int i;
+
+  for (i = 0; i < length; ++i) {
+    gfree(entries[i].key);
+    entries[i].val.free();
+  }
+  gfree(entries);
+}
+
+void Dict::add(char *key, Object *val) {
+  if (length == size) {
+    if (length == 0) {
+      size = 8;
+    } else {
+      size *= 2;
+    }
+    entries = (DictEntry *)greallocn(entries, size, sizeof(DictEntry));
+  }
+  entries[length].key = key;
+  entries[length].val = *val;
+  ++length;
+}
+
+inline DictEntry *Dict::find(char *key) {
+  int i;
+
+  for (i = 0; i < length; ++i) {
+    if (!strcmp(key, entries[i].key))
+      return &entries[i];
+  }
+  return NULL;
+}
+
+GBool Dict::is(char *type) {
+  DictEntry *e;
+
+  return (e = find("Type")) && e->val.isName(type);
+}
+
+Object *Dict::lookup(char *key, Object *obj) {
+  DictEntry *e;
+
+  return (e = find(key)) ? e->val.fetch(xref, obj) : obj->initNull();
+}
+
+Object *Dict::lookupNF(char *key, Object *obj) {
+  DictEntry *e;
+
+  return (e = find(key)) ? e->val.copy(obj) : obj->initNull();
+}
+
+char *Dict::getKey(int i) {
+  return entries[i].key;
+}
+
+Object *Dict::getVal(int i, Object *obj) {
+  return entries[i].val.fetch(xref, obj);
+}
+
+Object *Dict::getValNF(int i, Object *obj) {
+  return entries[i].val.copy(obj);
+}
diff --git a/lib/xpdf/Dict.h b/lib/xpdf/Dict.h
new file mode 100644 (file)
index 0000000..08f55ec
--- /dev/null
@@ -0,0 +1,77 @@
+//========================================================================
+//
+// Dict.h
+//
+// Copyright 1996-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef DICT_H
+#define DICT_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#include "Object.h"
+
+//------------------------------------------------------------------------
+// Dict
+//------------------------------------------------------------------------
+
+struct DictEntry {
+  char *key;
+  Object val;
+};
+
+class Dict {
+public:
+
+  // Constructor.
+  Dict(XRef *xrefA);
+
+  // Destructor.
+  ~Dict();
+
+  // Reference counting.
+  int incRef() { return ++ref; }
+  int decRef() { return --ref; }
+
+  // Get number of entries.
+  int getLength() { return length; }
+
+  // Add an entry.  NB: does not copy key.
+  void add(char *key, Object *val);
+
+  // Check if dictionary is of specified type.
+  GBool is(char *type);
+
+  // Look up an entry and return the value.  Returns a null object
+  // if <key> is not in the dictionary.
+  Object *lookup(char *key, Object *obj);
+  Object *lookupNF(char *key, Object *obj);
+
+  // Iterative accessors.
+  char *getKey(int i);
+  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);
+};
+
+#endif
diff --git a/lib/xpdf/Error.cc b/lib/xpdf/Error.cc
new file mode 100644 (file)
index 0000000..c03f75f
--- /dev/null
@@ -0,0 +1,38 @@
+//========================================================================
+//
+// Error.cc
+//
+// Copyright 1996-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <stdio.h>
+#include <stddef.h>
+#include <stdarg.h>
+#include "GlobalParams.h"
+#include "Error.h"
+
+void CDECL error(int pos, char *msg, ...) {
+  va_list args;
+
+  // NB: this can be called before the globalParams object is created
+  if (globalParams && globalParams->getErrQuiet()) {
+    return;
+  }
+  if (pos >= 0) {
+    fprintf(stderr, "Error (%d): ", pos);
+  } else {
+    fprintf(stderr, "Error: ");
+  }
+  va_start(args, msg);
+  vfprintf(stderr, msg, args);
+  va_end(args);
+  fprintf(stderr, "\n");
+  fflush(stderr);
+}
diff --git a/lib/xpdf/Error.h b/lib/xpdf/Error.h
new file mode 100644 (file)
index 0000000..0ce55e9
--- /dev/null
@@ -0,0 +1,23 @@
+//========================================================================
+//
+// Error.h
+//
+// Copyright 1996-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef ERROR_H
+#define ERROR_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#include <stdio.h>
+#include "config.h"
+
+extern void CDECL error(int pos, char *msg, ...);
+
+#endif
diff --git a/lib/xpdf/ErrorCodes.h b/lib/xpdf/ErrorCodes.h
new file mode 100644 (file)
index 0000000..b28528d
--- /dev/null
@@ -0,0 +1,36 @@
+//========================================================================
+//
+// ErrorCodes.h
+//
+// Copyright 2002-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef ERRORCODES_H
+#define ERRORCODES_H
+
+#define errNone             0  // no error
+
+#define errOpenFile         1  // couldn't open the PDF file
+
+#define errBadCatalog       2  // couldn't read the page catalog
+
+#define errDamaged          3  // PDF file was damaged and couldn't be
+                               // repaired
+
+#define errEncrypted        4  // file was encrypted and password was
+                               // incorrect or not supplied
+
+#define errHighlightFile    5  // nonexistent or invalid highlight file
+
+#define errBadPrinter       6   // invalid printer
+
+#define errPrinting         7   // error during printing
+
+#define errPermission       8  // PDF file doesn't allow that operation
+
+#define errBadPageNum       9  // invalid page number
+
+#define errFileIO          10   // file I/O error
+
+#endif
diff --git a/lib/xpdf/FoFiBase.cc b/lib/xpdf/FoFiBase.cc
new file mode 100644 (file)
index 0000000..28d0b8c
--- /dev/null
@@ -0,0 +1,156 @@
+//========================================================================
+//
+// FoFiBase.cc
+//
+// Copyright 1999-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <stdio.h>
+#include "gmem.h"
+#include "FoFiBase.h"
+
+//------------------------------------------------------------------------
+// FoFiBase
+//------------------------------------------------------------------------
+
+FoFiBase::FoFiBase(char *fileA, int lenA, GBool freeFileDataA) {
+  fileData = file = (Guchar *)fileA;
+  len = lenA;
+  freeFileData = freeFileDataA;
+}
+
+FoFiBase::~FoFiBase() {
+  if (freeFileData) {
+    gfree(fileData);
+  }
+}
+
+char *FoFiBase::readFile(char *fileName, int *fileLen) {
+  FILE *f;
+  char *buf;
+  int n;
+
+  if (!(f = fopen(fileName, "rb"))) {
+    return NULL;
+  }
+  fseek(f, 0, SEEK_END);
+  n = (int)ftell(f);
+  fseek(f, 0, SEEK_SET);
+  buf = (char *)gmalloc(n);
+  if ((int)fread(buf, 1, n, f) != n) {
+    gfree(buf);
+    fclose(f);
+    return NULL;
+  }
+  fclose(f);
+  *fileLen = n;
+  return buf;
+}
+
+int FoFiBase::getS8(int pos, GBool *ok) {
+  int x;
+
+  if (pos < 0 || pos >= len) {
+    *ok = gFalse;
+    return 0;
+  }
+  x = file[pos];
+  if (x & 0x80) {
+    x |= ~0xff;
+  }
+  return x;
+}
+
+int FoFiBase::getU8(int pos, GBool *ok) {
+  if (pos < 0 || pos >= len) {
+    *ok = gFalse;
+    return 0;
+  }
+  return file[pos];
+}
+
+int FoFiBase::getS16BE(int pos, GBool *ok) {
+  int x;
+
+  if (pos < 0 || pos+1 >= len) {
+    *ok = gFalse;
+    return 0;
+  }
+  x = file[pos];
+  x = (x << 8) + file[pos+1];
+  if (x & 0x8000) {
+    x |= ~0xffff;
+  }
+  return x;
+}
+
+int FoFiBase::getU16BE(int pos, GBool *ok) {
+  int x;
+
+  if (pos < 0 || pos+1 >= len) {
+    *ok = gFalse;
+    return 0;
+  }
+  x = file[pos];
+  x = (x << 8) + file[pos+1];
+  return x;
+}
+
+int FoFiBase::getS32BE(int pos, GBool *ok) {
+  int x;
+
+  if (pos < 0 || pos+3 >= len) {
+    *ok = gFalse;
+    return 0;
+  }
+  x = file[pos];
+  x = (x << 8) + file[pos+1];
+  x = (x << 8) + file[pos+2];
+  x = (x << 8) + file[pos+3];
+  if (x & 0x80000000) {
+    x |= ~0xffffffff;
+  }
+  return x;
+}
+
+Guint FoFiBase::getU32BE(int pos, GBool *ok) {
+  Guint x;
+
+  if (pos < 0 || pos+3 >= len) {
+    *ok = gFalse;
+    return 0;
+  }
+  x = file[pos];
+  x = (x << 8) + file[pos+1];
+  x = (x << 8) + file[pos+2];
+  x = (x << 8) + file[pos+3];
+  return x;
+}
+
+Guint FoFiBase::getUVarBE(int pos, int size, GBool *ok) {
+  Guint x;
+  int i;
+
+  if (pos < 0 || pos + size > len) {
+    *ok = gFalse;
+    return 0;
+  }
+  x = 0;
+  for (i = 0; i < size; ++i) {
+    x = (x << 8) + file[pos + i];
+  }
+  return x;
+}
+
+GBool FoFiBase::checkRegion(int pos, int size) {
+  return pos >= 0 &&
+         pos + size >= pos &&
+         pos + size <= len;
+}
diff --git a/lib/xpdf/FoFiBase.h b/lib/xpdf/FoFiBase.h
new file mode 100644 (file)
index 0000000..b78840b
--- /dev/null
@@ -0,0 +1,57 @@
+//========================================================================
+//
+// FoFiBase.h
+//
+// Copyright 1999-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef FOFIBASE_H
+#define FOFIBASE_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#include "gtypes.h"
+
+//------------------------------------------------------------------------
+
+typedef void (*FoFiOutputFunc)(void *stream, char *data, int len);
+
+//------------------------------------------------------------------------
+// FoFiBase
+//------------------------------------------------------------------------
+
+class FoFiBase {
+public:
+
+  virtual ~FoFiBase();
+
+protected:
+
+  FoFiBase(char *fileA, int lenA, GBool freeFileDataA);
+  static char *readFile(char *fileName, int *fileLen);
+
+  // S = signed / U = unsigned
+  // 8/16/32/Var = word length, in bytes
+  // BE = big endian
+  int getS8(int pos, GBool *ok);
+  int getU8(int pos, GBool *ok);
+  int getS16BE(int pos, GBool *ok);
+  int getU16BE(int pos, GBool *ok);
+  int getS32BE(int pos, GBool *ok);
+  Guint getU32BE(int pos, GBool *ok);
+  Guint getUVarBE(int pos, int size, GBool *ok);
+
+  GBool checkRegion(int pos, int size);
+
+  Guchar *fileData;
+  Guchar *file;
+  int len;
+  GBool freeFileData;
+};
+
+#endif
diff --git a/lib/xpdf/FoFiEncodings.cc b/lib/xpdf/FoFiEncodings.cc
new file mode 100644 (file)
index 0000000..37a17f5
--- /dev/null
@@ -0,0 +1,994 @@
+//========================================================================
+//
+// FoFiEncodings.cc
+//
+// Copyright 1999-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <stdlib.h>
+#include "FoFiEncodings.h"
+
+//------------------------------------------------------------------------
+// Type 1 and 1C font data
+//------------------------------------------------------------------------
+
+char *fofiType1StandardEncoding[256] = {
+  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
+};
+
+char *fofiType1ExpertEncoding[256] = {
+  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"
+};
+
+//------------------------------------------------------------------------
+// Type 1C font data
+//------------------------------------------------------------------------
+
+char *fofiType1CStdStrings[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"
+};
+
+Gushort fofiType1CISOAdobeCharset[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
+};
+
+Gushort fofiType1CExpertCharset[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
+};
+
+Gushort fofiType1CExpertSubsetCharset[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
+};
diff --git a/lib/xpdf/FoFiEncodings.h b/lib/xpdf/FoFiEncodings.h
new file mode 100644 (file)
index 0000000..50e285d
--- /dev/null
@@ -0,0 +1,36 @@
+//========================================================================
+//
+// FoFiEncodings.h
+//
+// Copyright 1999-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef FOFIENCODINGS_H
+#define FOFIENCODINGS_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#include "gtypes.h"
+
+//------------------------------------------------------------------------
+// Type 1 and 1C font data
+//------------------------------------------------------------------------
+
+extern char *fofiType1StandardEncoding[256];
+extern char *fofiType1ExpertEncoding[256];
+
+//------------------------------------------------------------------------
+// Type 1C font data
+//------------------------------------------------------------------------
+
+extern char *fofiType1CStdStrings[391];
+extern Gushort fofiType1CISOAdobeCharset[229];
+extern Gushort fofiType1CExpertCharset[166];
+extern Gushort fofiType1CExpertSubsetCharset[87];
+
+#endif
diff --git a/lib/xpdf/FoFiTrueType.cc b/lib/xpdf/FoFiTrueType.cc
new file mode 100644 (file)
index 0000000..46b61ec
--- /dev/null
@@ -0,0 +1,1753 @@
+//========================================================================
+//
+// FoFiTrueType.cc
+//
+// Copyright 1999-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+#include "gtypes.h"
+#include "gmem.h"
+#include "GString.h"
+#include "GHash.h"
+#include "FoFiTrueType.h"
+
+//
+// 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 = GID = 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] = gid
+//
+// 'post' table: mapping from glyph index to glyph name
+//
+//               post[gid] = 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] = gid
+//
+
+//------------------------------------------------------------------------
+
+#define ttcfTag 0x74746366
+
+//------------------------------------------------------------------------
+
+struct TrueTypeTable {
+  Guint tag;
+  Guint checksum;
+  int offset;
+  int origOffset;
+  int len;
+};
+
+struct TrueTypeCmap {
+  int platform;
+  int encoding;
+  int offset;
+  int len;
+  int fmt;
+};
+
+struct TrueTypeLoca {
+  int idx;
+  int origOffset;
+  int newOffset;
+  int len;
+};
+
+#define cmapTag 0x636d6170
+#define glyfTag 0x676c7966
+#define headTag 0x68656164
+#define locaTag 0x6c6f6361
+#define nameTag 0x6e616d65
+#define postTag 0x706f7374
+
+static int cmpTrueTypeLocaOffset(const void *p1, const void *p2) {
+  TrueTypeLoca *loca1 = (TrueTypeLoca *)p1;
+  TrueTypeLoca *loca2 = (TrueTypeLoca *)p2;
+
+  if (loca1->origOffset == loca2->origOffset) {
+    return loca1->idx - loca2->idx;
+  }
+  return loca1->origOffset - loca2->origOffset;
+}
+
+static int cmpTrueTypeLocaIdx(const void *p1, const void *p2) {
+  TrueTypeLoca *loca1 = (TrueTypeLoca *)p1;
+  TrueTypeLoca *loca2 = (TrueTypeLoca *)p2;
+
+  return loca1->idx - loca2->idx;
+}
+
+static int cmpTrueTypeTableTag(const void *p1, const void *p2) {
+  TrueTypeTable *tab1 = (TrueTypeTable *)p1;
+  TrueTypeTable *tab2 = (TrueTypeTable *)p2;
+
+  return (int)tab1->tag - (int)tab2->tag;
+}
+
+//------------------------------------------------------------------------
+
+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
+#define t42VheaTable  9
+#define t42VmtxTable 10
+
+//------------------------------------------------------------------------
+
+// Glyph names in some arbitrary standard order 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"
+};
+
+//------------------------------------------------------------------------
+// FoFiTrueType
+//------------------------------------------------------------------------
+
+FoFiTrueType *FoFiTrueType::make(char *fileA, int lenA) {
+  FoFiTrueType *ff;
+
+  ff = new FoFiTrueType(fileA, lenA, gFalse);
+  if (!ff->parsedOk) {
+    delete ff;
+    return NULL;
+  }
+  return ff;
+}
+
+FoFiTrueType *FoFiTrueType::load(char *fileName) {
+  FoFiTrueType *ff;
+  char *fileA;
+  int lenA;
+
+  if (!(fileA = FoFiBase::readFile(fileName, &lenA))) {
+    return NULL;
+  }
+  ff = new FoFiTrueType(fileA, lenA, gTrue);
+  if (!ff->parsedOk) {
+    delete ff;
+    return NULL;
+  }
+  return ff;
+}
+
+FoFiTrueType::FoFiTrueType(char *fileA, int lenA, GBool freeFileDataA):
+  FoFiBase(fileA, lenA, freeFileDataA)
+{
+  tables = NULL;
+  nTables = 0;
+  cmaps = NULL;
+  nCmaps = 0;
+  nameToGID = NULL;
+  parsedOk = gFalse;
+
+  parse();
+}
+
+FoFiTrueType::~FoFiTrueType() {
+  gfree(tables);
+  gfree(cmaps);
+  if (nameToGID) {
+    delete nameToGID;
+  }
+}
+
+int FoFiTrueType::getNumCmaps() {
+  return nCmaps;
+}
+
+int FoFiTrueType::getCmapPlatform(int i) {
+  return cmaps[i].platform;
+}
+
+int FoFiTrueType::getCmapEncoding(int i) {
+  return cmaps[i].encoding;
+}
+
+int FoFiTrueType::findCmap(int platform, int encoding) {
+  int i;
+
+  for (i = 0; i < nCmaps; ++i) {
+    if (cmaps[i].platform == platform && cmaps[i].encoding == encoding) {
+      return i;
+    }
+  }
+  return -1;
+}
+
+Gushort FoFiTrueType::mapCodeToGID(int i, int c) {
+  Gushort gid;
+  int segCnt, segEnd, segStart, segDelta, segOffset;
+  int cmapFirst, cmapLen;
+  int pos, a, b, m;
+  GBool ok;
+
+  if (i < 0 || i >= nCmaps) {
+    return 0;
+  }
+  ok = gTrue;
+  pos = cmaps[i].offset;
+  switch (cmaps[i].fmt) {
+  case 0:
+    if (c < 0 || c >= cmaps[i].len - 6) {
+      return 0;
+    }
+    gid = getU8(cmaps[i].offset + 6 + c, &ok);
+    break;
+  case 4:
+    segCnt = getU16BE(pos + 6, &ok) / 2;
+    a = -1;
+    b = segCnt - 1;
+    segEnd = getU16BE(pos + 14 + 2*b, &ok);
+    if (c > 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 && ok) {
+      m = (a + b) / 2;
+      segEnd = getU16BE(pos + 14 + 2*m, &ok);
+      if (segEnd < c) {
+       a = m;
+      } else {
+       b = m;
+      }
+    }
+    segStart = getU16BE(pos + 16 + 2*segCnt + 2*b, &ok);
+    segDelta = getU16BE(pos + 16 + 4*segCnt + 2*b, &ok);
+    segOffset = getU16BE(pos + 16 + 6*segCnt + 2*b, &ok);
+    if (c < segStart) {
+      return 0;
+    }
+    if (segOffset == 0) {
+      gid = (c + segDelta) & 0xffff;
+    } else {
+      gid = getU16BE(pos + 16 + 6*segCnt + 2*b +
+                      segOffset + 2 * (c - segStart), &ok);
+      if (gid != 0) {
+       gid = (gid + segDelta) & 0xffff;
+      }
+    }
+    break;
+  case 6:
+    cmapFirst = getU16BE(pos + 6, &ok);
+    cmapLen = getU16BE(pos + 8, &ok);
+    if (c < cmapFirst || c >= cmapFirst + cmapLen) {
+      return 0;
+    }
+    gid = getU16BE(pos + 10 + 2 * (c - cmapFirst), &ok);
+    break;
+  default:
+    return 0;
+  }
+  if (!ok) {
+    return 0;
+  }
+  return gid;
+}
+
+int FoFiTrueType::mapNameToGID(char *name) {
+  if (!nameToGID) {
+    return 0;
+  }
+  return nameToGID->lookupInt(name);
+}
+
+int FoFiTrueType::getEmbeddingRights() {
+  int i, fsType;
+  GBool ok;
+
+  if ((i = seekTable("OS/2")) < 0) {
+    return 4;
+  }
+  ok = gTrue;
+  fsType = getU16BE(tables[i].offset + 8, &ok);
+  if (!ok) {
+    return 4;
+  }
+  if (fsType & 0x0008) {
+    return 2;
+  }
+  if (fsType & 0x0004) {
+    return 1;
+  }
+  if (fsType & 0x0002) {
+    return 0;
+  }
+  return 3;
+}
+
+void FoFiTrueType::convertToType42(char *psName, char **encoding,
+                                  Gushort *codeToGID,
+                                  FoFiOutputFunc outputFunc,
+                                  void *outputStream) {
+  char buf[512];
+  GBool ok;
+
+  // write the header
+  ok = gTrue;
+  sprintf(buf, "%%!PS-TrueTypeFont-%g\n", (double)getS32BE(0, &ok) / 65536.0);
+  (*outputFunc)(outputStream, buf, strlen(buf));
+
+  // begin the font dictionary
+  (*outputFunc)(outputStream, "10 dict begin\n", 14);
+  (*outputFunc)(outputStream, "/FontName /", 11);
+  (*outputFunc)(outputStream, psName, strlen(psName));
+  (*outputFunc)(outputStream, " def\n", 5);
+  (*outputFunc)(outputStream, "/FontType 42 def\n", 17);
+  (*outputFunc)(outputStream, "/FontMatrix [1 0 0 1 0 0] def\n", 30);
+  sprintf(buf, "/FontBBox [%d %d %d %d] def\n",
+         bbox[0], bbox[1], bbox[2], bbox[3]);
+  (*outputFunc)(outputStream, buf, strlen(buf));
+  (*outputFunc)(outputStream, "/PaintType 0 def\n", 17);
+
+  // write the guts of the dictionary
+  cvtEncoding(encoding, outputFunc, outputStream);
+  cvtCharStrings(encoding, codeToGID, outputFunc, outputStream);
+  cvtSfnts(outputFunc, outputStream, NULL, gFalse);
+
+  // end the dictionary and define the font
+  (*outputFunc)(outputStream, "FontName currentdict end definefont pop\n", 40);
+}
+
+void FoFiTrueType::convertToCIDType2(char *psName,
+                                    Gushort *cidMap, int nCIDs,
+                                    GBool needVerticalMetrics,
+                                    FoFiOutputFunc outputFunc,
+                                    void *outputStream) {
+  char buf[512];
+  Gushort cid;
+  GBool ok;
+  int i, j, k;
+
+  // write the header
+  ok = gTrue;
+  sprintf(buf, "%%!PS-TrueTypeFont-%g\n", (double)getS32BE(0, &ok) / 65536.0);
+  (*outputFunc)(outputStream, buf, strlen(buf));
+
+  // begin the font dictionary
+  (*outputFunc)(outputStream, "20 dict begin\n", 14);
+  (*outputFunc)(outputStream, "/CIDFontName /", 14);
+  (*outputFunc)(outputStream, psName, strlen(psName));
+  (*outputFunc)(outputStream, " def\n", 5);
+  (*outputFunc)(outputStream, "/CIDFontType 2 def\n", 19);
+  (*outputFunc)(outputStream, "/FontType 42 def\n", 17);
+  (*outputFunc)(outputStream, "/CIDSystemInfo 3 dict dup begin\n", 32);
+  (*outputFunc)(outputStream, "  /Registry (Adobe) def\n", 24);
+  (*outputFunc)(outputStream, "  /Ordering (Identity) def\n", 27);
+  (*outputFunc)(outputStream, "  /Supplement 0 def\n", 20);
+  (*outputFunc)(outputStream, "  end def\n", 10);
+  (*outputFunc)(outputStream, "/GDBytes 2 def\n", 15);
+  if (cidMap) {
+    sprintf(buf, "/CIDCount %d def\n", nCIDs);
+    (*outputFunc)(outputStream, buf, strlen(buf));
+    if (nCIDs > 32767) {
+      (*outputFunc)(outputStream, "/CIDMap [", 9);
+      for (i = 0; i < nCIDs; i += 32768 - 16) {
+       (*outputFunc)(outputStream, "<\n", 2);
+       for (j = 0; j < 32768 - 16 && i+j < nCIDs; j += 16) {
+         (*outputFunc)(outputStream, "  ", 2);
+         for (k = 0; k < 16 && i+j+k < nCIDs; ++k) {
+           cid = cidMap[i+j+k];
+           sprintf(buf, "%02x%02x", (cid >> 8) & 0xff, cid & 0xff);
+           (*outputFunc)(outputStream, buf, strlen(buf));
+         }
+         (*outputFunc)(outputStream, "\n", 1);
+       }
+       (*outputFunc)(outputStream, "  >", 3);
+      }
+      (*outputFunc)(outputStream, "\n", 1);
+      (*outputFunc)(outputStream, "] def\n", 6);
+    } else {
+      (*outputFunc)(outputStream, "/CIDMap <\n", 10);
+      for (i = 0; i < nCIDs; i += 16) {
+       (*outputFunc)(outputStream, "  ", 2);
+       for (j = 0; j < 16 && i+j < nCIDs; ++j) {
+         cid = cidMap[i+j];
+         sprintf(buf, "%02x%02x", (cid >> 8) & 0xff, cid & 0xff);
+         (*outputFunc)(outputStream, buf, strlen(buf));
+       }
+       (*outputFunc)(outputStream, "\n", 1);
+      }
+      (*outputFunc)(outputStream, "> def\n", 6);
+    }
+  } else {
+    // direct mapping - just fill the string(s) with s[i]=i
+    sprintf(buf, "/CIDCount %d def\n", nGlyphs);
+    (*outputFunc)(outputStream, buf, strlen(buf));
+    if (nGlyphs > 32767) {
+      (*outputFunc)(outputStream, "/CIDMap [\n", 10);
+      for (i = 0; i < nGlyphs; i += 32767) {
+       j = nGlyphs - i < 32767 ? nGlyphs - i : 32767;
+       sprintf(buf, "  %d string 0 1 %d {\n", 2 * j, j - 1);
+       (*outputFunc)(outputStream, buf, strlen(buf));
+       sprintf(buf, "    2 copy dup 2 mul exch %d add -8 bitshift put\n", i);
+       (*outputFunc)(outputStream, buf, strlen(buf));
+       sprintf(buf, "    1 index exch dup 2 mul 1 add exch %d add"
+               " 255 and put\n", i);
+       (*outputFunc)(outputStream, buf, strlen(buf));
+       (*outputFunc)(outputStream, "  } for\n", 8);
+      }
+      (*outputFunc)(outputStream, "] def\n", 6);
+    } else {
+      sprintf(buf, "/CIDMap %d string\n", 2 * nGlyphs);
+      (*outputFunc)(outputStream, buf, strlen(buf));
+      sprintf(buf, "  0 1 %d {\n", nGlyphs - 1);
+      (*outputFunc)(outputStream, buf, strlen(buf));
+      (*outputFunc)(outputStream,
+                   "    2 copy dup 2 mul exch -8 bitshift put\n", 42);
+      (*outputFunc)(outputStream,
+                   "    1 index exch dup 2 mul 1 add exch 255 and put\n", 50);
+      (*outputFunc)(outputStream, "  } for\n", 8);
+      (*outputFunc)(outputStream, "def\n", 4);
+    }
+  }
+  (*outputFunc)(outputStream, "/FontMatrix [1 0 0 1 0 0] def\n", 30);
+  sprintf(buf, "/FontBBox [%d %d %d %d] def\n",
+         bbox[0], bbox[1], bbox[2], bbox[3]);
+  (*outputFunc)(outputStream, buf, strlen(buf));
+  (*outputFunc)(outputStream, "/PaintType 0 def\n", 17);
+  (*outputFunc)(outputStream, "/Encoding [] readonly def\n", 26);
+  (*outputFunc)(outputStream, "/CharStrings 1 dict dup begin\n", 30);
+  (*outputFunc)(outputStream, "  /.notdef 0 def\n", 17);
+  (*outputFunc)(outputStream, "  end readonly def\n", 19);
+
+  // write the guts of the dictionary
+  cvtSfnts(outputFunc, outputStream, NULL, needVerticalMetrics);
+
+  // end the dictionary and define the font
+  (*outputFunc)(outputStream,
+               "CIDFontName currentdict end /CIDFont defineresource pop\n",
+               56);
+}
+
+void FoFiTrueType::convertToType0(char *psName, Gushort *cidMap, int nCIDs,
+                                 GBool needVerticalMetrics,
+                                 FoFiOutputFunc outputFunc,
+                                 void *outputStream) {
+  char buf[512];
+  GString *sfntsName;
+  int n, i, j;
+
+  // write the Type 42 sfnts array
+  sfntsName = (new GString(psName))->append("_sfnts");
+  cvtSfnts(outputFunc, outputStream, sfntsName, needVerticalMetrics);
+  delete sfntsName;
+
+  // write the descendant Type 42 fonts
+  n = cidMap ? nCIDs : nGlyphs;
+  for (i = 0; i < n; i += 256) {
+    (*outputFunc)(outputStream, "10 dict begin\n", 14);
+    (*outputFunc)(outputStream, "/FontName /", 11);
+    (*outputFunc)(outputStream, psName, strlen(psName));
+    sprintf(buf, "_%02x def\n", i >> 8);
+    (*outputFunc)(outputStream, buf, strlen(buf));
+    (*outputFunc)(outputStream, "/FontType 42 def\n", 17);
+    (*outputFunc)(outputStream, "/FontMatrix [1 0 0 1 0 0] def\n", 30);
+    sprintf(buf, "/FontBBox [%d %d %d %d] def\n",
+           bbox[0], bbox[1], bbox[2], bbox[3]);
+    (*outputFunc)(outputStream, buf, strlen(buf));
+    (*outputFunc)(outputStream, "/PaintType 0 def\n", 17);
+    (*outputFunc)(outputStream, "/sfnts ", 7);
+    (*outputFunc)(outputStream, psName, strlen(psName));
+    (*outputFunc)(outputStream, "_sfnts def\n", 11);
+    (*outputFunc)(outputStream, "/Encoding 256 array\n", 20);
+    for (j = 0; j < 256 && i+j < n; ++j) {
+      sprintf(buf, "dup %d /c%02x put\n", j, j);
+      (*outputFunc)(outputStream, buf, strlen(buf));
+    }
+    (*outputFunc)(outputStream, "readonly def\n", 13);
+    (*outputFunc)(outputStream, "/CharStrings 257 dict dup begin\n", 32);
+    (*outputFunc)(outputStream, "/.notdef 0 def\n", 15);
+    for (j = 0; j < 256 && i+j < n; ++j) {
+      sprintf(buf, "/c%02x %d def\n", j, cidMap ? cidMap[i+j] : i+j);
+      (*outputFunc)(outputStream, buf, strlen(buf));
+    }
+    (*outputFunc)(outputStream, "end readonly def\n", 17);
+    (*outputFunc)(outputStream,
+                 "FontName currentdict end definefont pop\n", 40);
+  }
+
+  // write the Type 0 parent font
+  (*outputFunc)(outputStream, "16 dict begin\n", 14);
+  (*outputFunc)(outputStream, "/FontName /", 11);
+  (*outputFunc)(outputStream, psName, strlen(psName));
+  (*outputFunc)(outputStream, " def\n", 5);
+  (*outputFunc)(outputStream, "/FontType 0 def\n", 16);
+  (*outputFunc)(outputStream, "/FontMatrix [1 0 0 1 0 0] def\n", 30);
+  (*outputFunc)(outputStream, "/FMapType 2 def\n", 16);
+  (*outputFunc)(outputStream, "/Encoding [\n", 12);
+  for (i = 0; i < n; i += 256) {
+    sprintf(buf, "%d\n", i >> 8);
+    (*outputFunc)(outputStream, buf, strlen(buf));
+  }
+  (*outputFunc)(outputStream, "] def\n", 6);
+  (*outputFunc)(outputStream, "/FDepVector [\n", 14);
+  for (i = 0; i < n; i += 256) {
+    (*outputFunc)(outputStream, "/", 1);
+    (*outputFunc)(outputStream, psName, strlen(psName));
+    sprintf(buf, "_%02x findfont\n", i >> 8);
+    (*outputFunc)(outputStream, buf, strlen(buf));
+  }
+  (*outputFunc)(outputStream, "] def\n", 6);
+  (*outputFunc)(outputStream, "FontName currentdict end definefont pop\n", 40);
+}
+
+void FoFiTrueType::writeTTF(FoFiOutputFunc outputFunc,
+                           void *outputStream, char *name,
+                           Gushort *codeToGID) {
+  // this substitute cmap table maps char codes 0000-ffff directly to
+  // glyphs 0000-ffff
+  static char cmapTab[36] = {
+    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, 4,                      // subtable format
+    0, 24,                     // subtable length
+    0, 0,                      // subtable version
+    0, 2,                      // segment count * 2
+    0, 2,                      // 2 * 2 ^ floor(log2(segCount))
+    0, 0,                      // floor(log2(segCount))
+    0, 0,                      // 2*segCount - 2*2^floor(log2(segCount))
+    (char)0xff, (char)0xff,    // endCount[0]
+    0, 0,                      // reserved
+    0, 0,                      // startCount[0]
+    0, 0,                      // idDelta[0]
+    0, 0                       // pad to a mulitple 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 missingCmap, missingName, missingPost, unsortedLoca, badCmapLen;
+  int nZeroLengthTables;
+  TrueTypeLoca *locaTable;
+  TrueTypeTable *newTables;
+  char *newNameTab, *newCmapTab;
+  int nNewTables, cmapIdx, cmapLen, glyfLen, newNameLen, newCmapLen, next;
+  Guint locaChecksum, glyfChecksum, fileChecksum;
+  char *tableDir;
+  char locaBuf[4], checksumBuf[4];
+  GBool ok;
+  Guint t;
+  int pos, i, j, k, n;
+
+  // check for missing tables
+  missingCmap = (cmapIdx = seekTable("cmap")) < 0;
+  missingName = seekTable("name") < 0;
+  missingPost = seekTable("post") < 0;
+
+  // read the loca table, check to see if it's sorted
+  locaTable = (TrueTypeLoca *)gmallocn(nGlyphs + 1, sizeof(TrueTypeLoca));
+  unsortedLoca = gFalse;
+  i = seekTable("loca");
+  pos = tables[i].offset;
+  ok = gTrue;
+  for (i = 0; i <= nGlyphs; ++i) {
+    if (locaFmt) {
+      locaTable[i].origOffset = (int)getU32BE(pos + i*4, &ok);
+    } else {
+      locaTable[i].origOffset = 2 * getU16BE(pos + i*2, &ok);
+    }
+    if (i > 0 && locaTable[i].origOffset < locaTable[i-1].origOffset) {
+      unsortedLoca = gTrue;
+    }
+    locaTable[i].idx = i;
+  }
+
+  // check for zero-length tables
+  nZeroLengthTables = 0;
+  for (i = 0; i < nTables; ++i) {
+    if (tables[i].len == 0) {
+      ++nZeroLengthTables;
+    }
+  }
+
+  // check for an incorrect cmap table length
+  badCmapLen = gFalse;
+  cmapLen = 0; // make gcc happy
+  if (!missingCmap) {
+    cmapLen = cmaps[0].offset + cmaps[0].len;
+    for (i = 1; i < nCmaps; ++i) {
+      if (cmaps[i].offset + cmaps[i].len > cmapLen) {
+       cmapLen = cmaps[i].offset + cmaps[i].len;
+      }
+    }
+    cmapLen -= tables[cmapIdx].offset;
+    if (cmapLen > tables[cmapIdx].len) {
+      badCmapLen = gTrue;
+    }
+  }
+
+  // if nothing is broken, just write the TTF file as is
+  if (!missingCmap && !missingName && !missingPost && !unsortedLoca &&
+      !badCmapLen && nZeroLengthTables == 0 && !name && !codeToGID) {
+    (*outputFunc)(outputStream, (char *)file, len);
+    goto done1;
+  }
+
+  // sort the 'loca' table: some (non-compliant) fonts have
+  // out-of-order loca tables; in order to correctly handle the case
+  // where (compliant) fonts have empty entries in the middle of the
+  // table, cmpTrueTypeLocaOffset uses offset as its primary sort key,
+  // and idx as its secondary key (ensuring that adjacent entries with
+  // the same pos value remain in the same order)
+  glyfLen = 0; // make gcc happy
+  if (unsortedLoca) {
+    qsort(locaTable, nGlyphs + 1, sizeof(TrueTypeLoca),
+         &cmpTrueTypeLocaOffset);
+    for (i = 0; i < nGlyphs; ++i) {
+      locaTable[i].len = locaTable[i+1].origOffset - locaTable[i].origOffset;
+    }
+    locaTable[nGlyphs].len = 0;
+    qsort(locaTable, nGlyphs + 1, sizeof(TrueTypeLoca),
+         &cmpTrueTypeLocaIdx);
+    pos = 0;
+    for (i = 0; i <= nGlyphs; ++i) {
+      locaTable[i].newOffset = pos;
+      pos += locaTable[i].len;
+      if (pos & 3) {
+       pos += 4 - (pos & 3);
+      }
+    }
+    glyfLen = pos;
+  }
+
+  // compute checksums for the loca and glyf tables
+  locaChecksum = glyfChecksum = 0;
+  if (unsortedLoca) {
+    if (locaFmt) {
+      for (j = 0; j <= nGlyphs; ++j) {
+       locaChecksum += locaTable[j].newOffset;
+      }
+    } else {
+      for (j = 0; j <= nGlyphs; j += 2) {
+       locaChecksum += locaTable[j].newOffset << 16;
+       if (j + 1 <= nGlyphs) {
+         locaChecksum += locaTable[j+1].newOffset;
+       }
+      }
+    }
+    pos = tables[seekTable("glyf")].offset;
+    for (j = 0; j < nGlyphs; ++j) {
+      n = locaTable[j].len;
+      if (n > 0) {
+       k = locaTable[j].origOffset;
+       if (checkRegion(pos + k, n)) {
+         glyfChecksum += computeTableChecksum(file + pos + k, n);
+       }
+      }
+    }
+  }
+
+  // construct the new name table
+  if (name) {
+    n = strlen(name);
+    newNameLen = (6 + 4*12 + 2 * (3*n + 7) + 3) & ~3;
+    newNameTab = (char *)gmalloc(newNameLen);
+    memset(newNameTab, 0, newNameLen);
+    newNameTab[0] = 0;         // format selector
+    newNameTab[1] = 0;
+    newNameTab[2] = 0;         // number of name records
+    newNameTab[3] = 4;
+    newNameTab[4] = 0;         // offset to start of string storage
+    newNameTab[5] = 6 + 4*12;
+    next = 0;
+    for (i = 0; i < 4; ++i) {
+      newNameTab[6 + i*12 + 0] = 0;    // platform ID = Microsoft
+      newNameTab[6 + i*12 + 1] = 3;
+      newNameTab[6 + i*12 + 2] = 0;    // encoding ID = Unicode
+      newNameTab[6 + i*12 + 3] = 1;
+      newNameTab[6 + i*12 + 4] = 0x04; // language ID = American English
+      newNameTab[6 + i*12 + 5] = 0x09;
+      newNameTab[6 + i*12 + 6] = 0;    // name ID
+      newNameTab[6 + i*12 + 7] = i + 1;
+      newNameTab[6 + i*12 + 8] = i+1 == 2 ? 0 : ((2*n) >> 8); // string length
+      newNameTab[6 + i*12 + 9] = i+1 == 2 ? 14 : ((2*n) & 0xff);
+      newNameTab[6 + i*12 + 10] = next >> 8;               // string offset
+      newNameTab[6 + i*12 + 11] = next & 0xff;
+      if (i+1 == 2) {
+       memcpy(newNameTab + 6 + 4*12 + next, "\0R\0e\0g\0u\0l\0a\0r", 14);
+       next += 14;
+      } else {
+       for (j = 0; j < n; ++j) {
+         newNameTab[6 + 4*12 + next + 2*j] = 0;
+         newNameTab[6 + 4*12 + next + 2*j + 1] = name[j];
+       }
+       next += 2*n;
+      }
+    }
+  } else {
+    newNameLen = 0;
+    newNameTab = NULL;
+  }
+
+  // construct the new cmap table
+  if (codeToGID) {
+    newCmapLen = 44 + 256 * 2;
+    newCmapTab = (char *)gmalloc(newCmapLen);
+    newCmapTab[0] = 0;         // table version number = 0
+    newCmapTab[1] = 0;
+    newCmapTab[2] = 0;         // number of encoding tables = 1
+    newCmapTab[3] = 1;
+    newCmapTab[4] = 0;         // platform ID = Microsoft
+    newCmapTab[5] = 3;
+    newCmapTab[6] = 0;         // encoding ID = Unicode
+    newCmapTab[7] = 1;
+    newCmapTab[8] = 0;         // offset of subtable
+    newCmapTab[9] = 0;
+    newCmapTab[10] = 0;
+    newCmapTab[11] = 12;
+    newCmapTab[12] = 0;                // subtable format = 4
+    newCmapTab[13] = 4;
+    newCmapTab[14] = 0x02;     // subtable length
+    newCmapTab[15] = 0x20;
+    newCmapTab[16] = 0;                // subtable version = 0
+    newCmapTab[17] = 0;
+    newCmapTab[18] = 0;                // segment count * 2
+    newCmapTab[19] = 4;
+    newCmapTab[20] = 0;                // 2 * 2 ^ floor(log2(segCount))
+    newCmapTab[21] = 4;
+    newCmapTab[22] = 0;                // floor(log2(segCount))
+    newCmapTab[23] = 1;
+    newCmapTab[24] = 0;                // 2*segCount - 2*2^floor(log2(segCount))
+    newCmapTab[25] = 0;
+    newCmapTab[26] = 0x00;     // endCount[0]
+    newCmapTab[27] = (char)0xff;
+    newCmapTab[28] = (char)0xff; // endCount[1]
+    newCmapTab[29] = (char)0xff;
+    newCmapTab[30] = 0;                // reserved
+    newCmapTab[31] = 0;
+    newCmapTab[32] = 0x00;     // startCount[0]
+    newCmapTab[33] = 0x00;
+    newCmapTab[34] = (char)0xff; // startCount[1]
+    newCmapTab[35] = (char)0xff;
+    newCmapTab[36] = 0;                // idDelta[0]
+    newCmapTab[37] = 0;
+    newCmapTab[38] = 0;                // idDelta[1]
+    newCmapTab[39] = 1;
+    newCmapTab[40] = 0;                // idRangeOffset[0]
+    newCmapTab[41] = 4;
+    newCmapTab[42] = 0;                // idRangeOffset[1]
+    newCmapTab[43] = 0;
+    for (i = 0; i < 256; ++i) {
+      newCmapTab[44 + 2*i] = codeToGID[i] >> 8;
+      newCmapTab[44 + 2*i + 1] = codeToGID[i] & 0xff;
+    }
+  } else {
+    newCmapLen = 0;
+    newCmapTab = NULL;
+  }
+
+  // construct the new table directory:
+  // - keep all original tables with non-zero length
+  // - fix the cmap table's length, if necessary
+  // - add missing tables
+  // - sort the table by tag
+  // - compute new table positions, including 4-byte alignment
+  // - (re)compute table checksums
+  nNewTables = nTables - nZeroLengthTables +
+               (missingCmap ? 1 : 0) + (missingName ? 1 : 0) +
+               (missingPost ? 1 : 0);
+  newTables = (TrueTypeTable *)gmallocn(nNewTables, sizeof(TrueTypeTable));
+  j = 0;
+  for (i = 0; i < nTables; ++i) {
+    if (tables[i].len > 0) {
+      newTables[j] = tables[i];
+      newTables[j].origOffset = tables[i].offset;
+      if (checkRegion(tables[i].offset, newTables[i].len)) {
+       newTables[j].checksum =
+           computeTableChecksum(file + tables[i].offset, tables[i].len);
+       if (tables[i].tag == headTag) {
+         // don't include the file checksum
+         newTables[j].checksum -= getU32BE(tables[i].offset + 8, &ok);
+       }
+      }
+      if (newTables[j].tag == cmapTag && codeToGID) {
+       newTables[j].len = newCmapLen;
+       newTables[j].checksum = computeTableChecksum((Guchar *)newCmapTab,
+                                                    newCmapLen);
+      } else if (newTables[j].tag == cmapTag && badCmapLen) {
+       newTables[j].len = cmapLen;
+      } else if (newTables[j].tag == locaTag && unsortedLoca) {
+       newTables[j].len = (nGlyphs + 1) * (locaFmt ? 4 : 2);
+       newTables[j].checksum = locaChecksum;
+      } else if (newTables[j].tag == glyfTag && unsortedLoca) {
+       newTables[j].len = glyfLen;
+       newTables[j].checksum = glyfChecksum;
+      } else if (newTables[j].tag == nameTag && name) {
+       newTables[j].len = newNameLen;
+       newTables[j].checksum = computeTableChecksum((Guchar *)newNameTab,
+                                                    newNameLen);
+      }
+      ++j;
+    }
+  }
+  if (missingCmap) {
+    newTables[j].tag = cmapTag;
+    if (codeToGID) {
+      newTables[j].checksum = computeTableChecksum((Guchar *)newCmapTab,
+                                                  newCmapLen);
+      newTables[j].len = newCmapLen;
+    } else {
+      newTables[j].checksum = computeTableChecksum((Guchar *)cmapTab,
+                                                  sizeof(cmapTab));
+      newTables[j].len = sizeof(cmapTab);
+    }
+    ++j;
+  }
+  if (missingName) {
+    newTables[j].tag = nameTag;
+    if (name) {
+      newTables[j].checksum = computeTableChecksum((Guchar *)newNameTab,
+                                                  newNameLen);
+      newTables[j].len = newNameLen;
+    } else {
+      newTables[j].checksum = computeTableChecksum((Guchar *)nameTab,
+                                                  sizeof(nameTab));
+      newTables[j].len = sizeof(nameTab);
+    }
+    ++j;
+  }
+  if (missingPost) {
+    newTables[j].tag = postTag;
+    newTables[j].checksum = computeTableChecksum((Guchar *)postTab,
+                                                sizeof(postTab));
+    newTables[j].len = sizeof(postTab);
+    ++j;
+  }
+  qsort(newTables, nNewTables, sizeof(TrueTypeTable),
+       &cmpTrueTypeTableTag);
+  pos = 12 + nNewTables * 16;
+  for (i = 0; i < nNewTables; ++i) {
+    newTables[i].offset = pos;
+    pos += newTables[i].len;
+    if (pos & 3) {
+      pos += 4 - (pos & 3);
+    }
+  }
+
+  // write the table directory
+  tableDir = (char *)gmalloc(12 + nNewTables * 16);
+  tableDir[0] = 0x00;                                  // sfnt version
+  tableDir[1] = 0x01;
+  tableDir[2] = 0x00;
+  tableDir[3] = 0x00;
+  tableDir[4] = (char)((nNewTables >> 8) & 0xff);      // numTables
+  tableDir[5] = (char)(nNewTables & 0xff);
+  for (i = -1, t = (Guint)nNewTables; t; ++i, t >>= 1) ;
+  t = 1 << (4 + i);
+  tableDir[6] = (char)((t >> 8) & 0xff);               // searchRange
+  tableDir[7] = (char)(t & 0xff);
+  tableDir[8] = (char)((i >> 8) & 0xff);               // entrySelector
+  tableDir[9] = (char)(i & 0xff);
+  t = nNewTables * 16 - t;
+  tableDir[10] = (char)((t >> 8) & 0xff);              // rangeShift
+  tableDir[11] = (char)(t & 0xff);
+  pos = 12;
+  for (i = 0; i < nNewTables; ++i) {
+    tableDir[pos   ] = (char)(newTables[i].tag >> 24);
+    tableDir[pos+ 1] = (char)(newTables[i].tag >> 16);
+    tableDir[pos+ 2] = (char)(newTables[i].tag >>  8);
+    tableDir[pos+ 3] = (char) newTables[i].tag;
+    tableDir[pos+ 4] = (char)(newTables[i].checksum >> 24);
+    tableDir[pos+ 5] = (char)(newTables[i].checksum >> 16);
+    tableDir[pos+ 6] = (char)(newTables[i].checksum >>  8);
+    tableDir[pos+ 7] = (char) newTables[i].checksum;
+    tableDir[pos+ 8] = (char)(newTables[i].offset >> 24);
+    tableDir[pos+ 9] = (char)(newTables[i].offset >> 16);
+    tableDir[pos+10] = (char)(newTables[i].offset >>  8);
+    tableDir[pos+11] = (char) newTables[i].offset;
+    tableDir[pos+12] = (char)(newTables[i].len >> 24);
+    tableDir[pos+13] = (char)(newTables[i].len >> 16);
+    tableDir[pos+14] = (char)(newTables[i].len >>  8);
+    tableDir[pos+15] = (char) newTables[i].len;
+    pos += 16;
+  }
+  (*outputFunc)(outputStream, tableDir, 12 + nNewTables * 16);
+
+  // compute the file checksum
+  fileChecksum = computeTableChecksum((Guchar *)tableDir,
+                                     12 + nNewTables * 16);
+  for (i = 0; i < nNewTables; ++i) {
+    fileChecksum += newTables[i].checksum;
+  }
+  fileChecksum = 0xb1b0afba - fileChecksum;
+
+  // write the tables
+  for (i = 0; i < nNewTables; ++i) {
+    if (newTables[i].tag == headTag) {
+      if (checkRegion(newTables[i].origOffset, newTables[i].len)) {
+       (*outputFunc)(outputStream, (char *)file + newTables[i].origOffset, 8);
+       checksumBuf[0] = fileChecksum >> 24;
+       checksumBuf[1] = fileChecksum >> 16;
+       checksumBuf[2] = fileChecksum >> 8;
+       checksumBuf[3] = fileChecksum;
+       (*outputFunc)(outputStream, checksumBuf, 4);
+       (*outputFunc)(outputStream,
+                     (char *)file + newTables[i].origOffset + 12,
+                     newTables[i].len - 12);
+      } else {
+       for (j = 0; j < newTables[i].len; ++j) {
+         (*outputFunc)(outputStream, "\0", 1);
+       }
+      }
+    } else if (newTables[i].tag == cmapTag && codeToGID) {
+      (*outputFunc)(outputStream, newCmapTab, newTables[i].len);
+    } else if (newTables[i].tag == cmapTag && missingCmap) {
+      (*outputFunc)(outputStream, cmapTab, newTables[i].len);
+    } else if (newTables[i].tag == nameTag && name) {
+      (*outputFunc)(outputStream, newNameTab, newTables[i].len);
+    } else if (newTables[i].tag == nameTag && missingName) {
+      (*outputFunc)(outputStream, nameTab, newTables[i].len);
+    } else if (newTables[i].tag == postTag && missingPost) {
+      (*outputFunc)(outputStream, postTab, newTables[i].len);
+    } else if (newTables[i].tag == locaTag && unsortedLoca) {
+      for (j = 0; j <= nGlyphs; ++j) {
+       if (locaFmt) {
+         locaBuf[0] = (char)(locaTable[j].newOffset >> 24);
+         locaBuf[1] = (char)(locaTable[j].newOffset >> 16);
+         locaBuf[2] = (char)(locaTable[j].newOffset >>  8);
+         locaBuf[3] = (char) locaTable[j].newOffset;
+         (*outputFunc)(outputStream, locaBuf, 4);
+       } else {
+         locaBuf[0] = (char)(locaTable[j].newOffset >> 9);
+         locaBuf[1] = (char)(locaTable[j].newOffset >> 1);
+         (*outputFunc)(outputStream, locaBuf, 2);
+       }
+      }
+    } else if (newTables[i].tag == glyfTag && unsortedLoca) {
+      pos = tables[seekTable("glyf")].offset;
+      for (j = 0; j < nGlyphs; ++j) {
+       n = locaTable[j].len;
+       if (n > 0) {
+         k = locaTable[j].origOffset;
+         if (checkRegion(pos + k, n)) {
+           (*outputFunc)(outputStream, (char *)file + pos + k, n);
+         } else {
+           for (k = 0; k < n; ++k) {
+             (*outputFunc)(outputStream, "\0", 1);
+           }
+         }
+         if ((k = locaTable[j].len & 3)) {
+           (*outputFunc)(outputStream, "\0\0\0\0", 4 - k);
+         }
+       }
+      }
+    } else {
+      if (checkRegion(newTables[i].origOffset, newTables[i].len)) {
+       (*outputFunc)(outputStream, (char *)file + newTables[i].origOffset,
+                     newTables[i].len);
+      } else {
+       for (j = 0; j < newTables[i].len; ++j) {
+         (*outputFunc)(outputStream, "\0", 1);
+       }
+      }
+    }
+    if (newTables[i].len & 3) {
+      (*outputFunc)(outputStream, "\0\0\0", 4 - (newTables[i].len & 3));
+    }
+  }
+
+  gfree(newCmapTab);
+  gfree(newNameTab);
+  gfree(tableDir);
+  gfree(newTables);
+ done1:
+  gfree(locaTable);
+}
+
+void FoFiTrueType::cvtEncoding(char **encoding,
+                              FoFiOutputFunc outputFunc,
+                              void *outputStream) {
+  char *name;
+  char buf[64];
+  int i;
+
+  (*outputFunc)(outputStream, "/Encoding 256 array\n", 20);
+  if (encoding) {
+    for (i = 0; i < 256; ++i) {
+      if (!(name = encoding[i])) {
+       name = ".notdef";
+      }
+      sprintf(buf, "dup %d /", i);
+      (*outputFunc)(outputStream, buf, strlen(buf));
+      (*outputFunc)(outputStream, name, strlen(name));
+      (*outputFunc)(outputStream, " put\n", 5);
+    }
+  } else {
+    for (i = 0; i < 256; ++i) {
+      sprintf(buf, "dup %d /c%02x put\n", i, i);
+      (*outputFunc)(outputStream, buf, strlen(buf));
+    }
+  }
+  (*outputFunc)(outputStream, "readonly def\n", 13);
+}
+
+void FoFiTrueType::cvtCharStrings(char **encoding,
+                                 Gushort *codeToGID,
+                                 FoFiOutputFunc outputFunc,
+                                 void *outputStream) {
+  char *name;
+  char buf[64], buf2[16];
+  int i, k;
+
+  // always define '.notdef'
+  (*outputFunc)(outputStream, "/CharStrings 256 dict dup begin\n", 32);
+  (*outputFunc)(outputStream, "/.notdef 0 def\n", 15);
+
+  // if there's no 'cmap' table, punt
+  if (nCmaps == 0) {
+    goto err;
+  }
+
+  // map char name to glyph index:
+  // 1. use encoding to map name to char code
+  // 2. use codeToGID to map char code to glyph index
+  // N.B. We do this in reverse order because font subsets can have
+  //      weird encodings that use the same character name twice, and
+  //      the first definition is probably the one we want.
+  k = 0; // make gcc happy
+  for (i = 255; i >= 0; --i) {
+    if (encoding) {
+      name = encoding[i];
+    } else {
+      sprintf(buf2, "c%02x", i);
+      name = buf2;
+    }
+    if (name && strcmp(name, ".notdef")) {
+      k = codeToGID[i];
+      // 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 > 0 && k < nGlyphs) {
+       (*outputFunc)(outputStream, "/", 1);
+       (*outputFunc)(outputStream, name, strlen(name));
+       sprintf(buf, " %d def\n", k);
+       (*outputFunc)(outputStream, buf, strlen(buf));
+      }
+    }
+  }
+
+ err:
+  (*outputFunc)(outputStream, "end readonly def\n", 17);
+}
+
+void FoFiTrueType::cvtSfnts(FoFiOutputFunc outputFunc,
+                           void *outputStream, GString *name,
+                           GBool needVerticalMetrics) {
+  Guchar headData[54];
+  TrueTypeLoca *locaTable;
+  Guchar *locaData;
+  TrueTypeTable newTables[nT42Tables];
+  Guchar tableDir[12 + nT42Tables*16];
+  GBool ok;
+  Guint checksum;
+  int nNewTables;
+  int length, pos, glyfPos, i, j, k;
+  Guchar vheaTab[36] = {
+    0, 1, 0, 0,                        // table version number
+    0, 0,                      // ascent
+    0, 0,                      // descent
+    0, 0,                      // reserved
+    0, 0,                      // max advance height
+    0, 0,                      // min top side bearing
+    0, 0,                      // min bottom side bearing
+    0, 0,                      // y max extent
+    0, 0,                      // caret slope rise
+    0, 1,                      // caret slope run
+    0, 0,                      // caret offset
+    0, 0,                      // reserved
+    0, 0,                      // reserved
+    0, 0,                      // reserved
+    0, 0,                      // reserved
+    0, 0,                      // metric data format
+    0, 1                       // number of advance heights in vmtx table
+  };
+  Guchar *vmtxTab;
+  GBool needVhea, needVmtx;
+  int advance;
+
+  // construct the 'head' table, zero out the font checksum
+  i = seekTable("head");
+  pos = tables[i].offset;
+  if (!checkRegion(pos, 54)) {
+    return;
+  }
+  memcpy(headData, file + pos, 54);
+  headData[8] = headData[9] = headData[10] = headData[11] = (Guchar)0;
+
+  // read the original 'loca' table, pad entries out to 4 bytes, and
+  // sort it into proper order -- some (non-compliant) fonts have
+  // out-of-order loca tables; in order to correctly handle the case
+  // where (compliant) fonts have empty entries in the middle of the
+  // table, cmpTrueTypeLocaPos uses offset as its primary sort key,
+  // and idx as its secondary key (ensuring that adjacent entries with
+  // the same pos value remain in the same order)
+  locaTable = (TrueTypeLoca *)gmallocn(nGlyphs + 1, sizeof(TrueTypeLoca));
+  i = seekTable("loca");
+  pos = tables[i].offset;
+  ok = gTrue;
+  for (i = 0; i <= nGlyphs; ++i) {
+    locaTable[i].idx = i;
+    if (locaFmt) {
+      locaTable[i].origOffset = (int)getU32BE(pos + i*4, &ok);
+    } else {
+      locaTable[i].origOffset = 2 * getU16BE(pos + i*2, &ok);
+    }
+  }
+  qsort(locaTable, nGlyphs + 1, sizeof(TrueTypeLoca),
+       &cmpTrueTypeLocaOffset);
+  for (i = 0; i < nGlyphs; ++i) {
+    locaTable[i].len = locaTable[i+1].origOffset - locaTable[i].origOffset;
+  }
+  locaTable[nGlyphs].len = 0;
+  qsort(locaTable, nGlyphs + 1, sizeof(TrueTypeLoca),
+       &cmpTrueTypeLocaIdx);
+  pos = 0;
+  for (i = 0; i <= nGlyphs; ++i) {
+    locaTable[i].newOffset = pos;
+    pos += locaTable[i].len;
+    if (pos & 3) {
+      pos += 4 - (pos & 3);
+    }
+  }
+
+  // construct the new 'loca' table
+  locaData = (Guchar *)gmallocn(nGlyphs + 1, (locaFmt ? 4 : 2));
+  for (i = 0; i <= nGlyphs; ++i) {
+    pos = locaTable[i].newOffset;
+    if (locaFmt) {
+      locaData[4*i  ] = (Guchar)(pos >> 24);
+      locaData[4*i+1] = (Guchar)(pos >> 16);
+      locaData[4*i+2] = (Guchar)(pos >>  8);
+      locaData[4*i+3] = (Guchar) pos;
+    } else {
+      locaData[2*i  ] = (Guchar)(pos >> 9);
+      locaData[2*i+1] = (Guchar)(pos >> 1);
+    }
+  }
+
+  // count the number of tables
+  nNewTables = 0;
+  for (i = 0; i < nT42Tables; ++i) {
+    if (t42Tables[i].required ||
+       seekTable(t42Tables[i].tag) >= 0) {
+      ++nNewTables;
+    }
+  }
+  vmtxTab = NULL; // make gcc happy
+  advance = 0; // make gcc happy
+  if (needVerticalMetrics) {
+    needVhea = seekTable("vhea") < 0;
+    needVmtx = seekTable("vmtx") < 0;
+    if (needVhea || needVmtx) {
+      i = seekTable("head");
+      advance = getU16BE(tables[i].offset + 18, &ok); // units per em
+      if (needVhea) {
+       ++nNewTables;
+      }
+      if (needVmtx) {
+       ++nNewTables;
+      }
+    }
+  }
+
+  // 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(headData, 54);
+    } else if (i == t42LocaTable) {
+      length = (nGlyphs + 1) * (locaFmt ? 4 : 2);
+      checksum = computeTableChecksum(locaData, length);
+    } else if (i == t42GlyfTable) {
+      length = 0;
+      checksum = 0;
+      glyfPos = tables[seekTable("glyf")].offset;
+      for (j = 0; j < nGlyphs; ++j) {
+       length += locaTable[j].len;
+       if (length & 3) {
+         length += 4 - (length & 3);
+       }
+       if (checkRegion(glyfPos + locaTable[j].origOffset, locaTable[j].len)) {
+         checksum +=
+             computeTableChecksum(file + glyfPos + locaTable[j].origOffset,
+                                  locaTable[j].len);
+       }
+      }
+    } else {
+      if ((j = seekTable(t42Tables[i].tag)) >= 0) {
+       length = tables[j].len;
+       if (checkRegion(tables[j].offset, length)) {
+         checksum = computeTableChecksum(file + tables[j].offset, length);
+       }
+      } else if (needVerticalMetrics && i == t42VheaTable) {
+       vheaTab[10] = advance / 256;    // max advance height
+       vheaTab[11] = advance % 256;
+       length = sizeof(vheaTab);
+       checksum = computeTableChecksum(vheaTab, length);
+      } else if (needVerticalMetrics && i == t42VmtxTable) {
+       length = 4 + (nGlyphs - 1) * 4;
+       vmtxTab = (Guchar *)gmalloc(length);
+       vmtxTab[0] = advance / 256;
+       vmtxTab[1] = advance % 256;
+       for (j = 2; j < length; j += 2) {
+         vmtxTab[j] = 0;
+         vmtxTab[j+1] = 0;
+       }
+       checksum = computeTableChecksum(vmtxTab, 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) {
+      newTables[k].tag = ((t42Tables[i].tag[0] & 0xff) << 24) |
+                        ((t42Tables[i].tag[1] & 0xff) << 16) |
+                        ((t42Tables[i].tag[2] & 0xff) <<  8) |
+                         (t42Tables[i].tag[3] & 0xff);
+      newTables[k].checksum = checksum;
+      newTables[k].offset = pos;
+      newTables[k].len = length;
+      pos += length;
+      if (pos & 3) {
+       pos += 4 - (length & 3);
+      }
+      ++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] = (Guchar)128;
+  tableDir[8] = 0;             // entrySelector
+  tableDir[9] = 3;
+  tableDir[10] = 0;            // rangeShift
+  tableDir[11] = (Guchar)(16 * nNewTables - 128);
+  pos = 12;
+  for (i = 0; i < nNewTables; ++i) {
+    tableDir[pos   ] = (Guchar)(newTables[i].tag >> 24);
+    tableDir[pos+ 1] = (Guchar)(newTables[i].tag >> 16);
+    tableDir[pos+ 2] = (Guchar)(newTables[i].tag >>  8);
+    tableDir[pos+ 3] = (Guchar) newTables[i].tag;
+    tableDir[pos+ 4] = (Guchar)(newTables[i].checksum >> 24);
+    tableDir[pos+ 5] = (Guchar)(newTables[i].checksum >> 16);
+    tableDir[pos+ 6] = (Guchar)(newTables[i].checksum >>  8);
+    tableDir[pos+ 7] = (Guchar) newTables[i].checksum;
+    tableDir[pos+ 8] = (Guchar)(newTables[i].offset >> 24);
+    tableDir[pos+ 9] = (Guchar)(newTables[i].offset >> 16);
+    tableDir[pos+10] = (Guchar)(newTables[i].offset >>  8);
+    tableDir[pos+11] = (Guchar) newTables[i].offset;
+    tableDir[pos+12] = (Guchar)(newTables[i].len >> 24);
+    tableDir[pos+13] = (Guchar)(newTables[i].len >> 16);
+    tableDir[pos+14] = (Guchar)(newTables[i].len >>  8);
+    tableDir[pos+15] = (Guchar) newTables[i].len;
+    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 += newTables[i].checksum;
+  }
+  checksum = 0xb1b0afba - checksum; // because the TrueType spec says so
+  headData[ 8] = (Guchar)(checksum >> 24);
+  headData[ 9] = (Guchar)(checksum >> 16);
+  headData[10] = (Guchar)(checksum >>  8);
+  headData[11] = (Guchar) checksum;
+
+  // start the sfnts array
+  if (name) {
+    (*outputFunc)(outputStream, "/", 1);
+    (*outputFunc)(outputStream, name->getCString(), name->getLength());
+    (*outputFunc)(outputStream, " [\n", 3);
+  } else {
+    (*outputFunc)(outputStream, "/sfnts [\n", 9);
+  }
+
+  // write the table directory
+  dumpString(tableDir, 12 + nNewTables*16, outputFunc, outputStream);
+
+  // write the tables
+  for (i = 0; i < nNewTables; ++i) {
+    if (i == t42HeadTable) {
+      dumpString(headData, 54, outputFunc, outputStream);
+    } else if (i == t42LocaTable) {
+      length = (nGlyphs + 1) * (locaFmt ? 4 : 2);
+      dumpString(locaData, length, outputFunc, outputStream);
+    } else if (i == t42GlyfTable) {
+      glyfPos = tables[seekTable("glyf")].offset;
+      for (j = 0; j < nGlyphs; ++j) {
+       if (locaTable[j].len > 0 &&
+           checkRegion(glyfPos + locaTable[j].origOffset, locaTable[j].len)) {
+         dumpString(file + glyfPos + locaTable[j].origOffset,
+                    locaTable[j].len, outputFunc, outputStream);
+       }
+      }
+    } else {
+      // length == 0 means the table is missing and the error was
+      // already reported during the construction of the table
+      // headers
+      if ((length = newTables[i].len) > 0) {
+       if ((j = seekTable(t42Tables[i].tag)) >= 0 &&
+           checkRegion(tables[j].offset, tables[j].len)) {
+         dumpString(file + tables[j].offset, tables[j].len,
+                    outputFunc, outputStream);
+       } else if (needVerticalMetrics && i == t42VheaTable) {
+         dumpString(vheaTab, length, outputFunc, outputStream);
+       } else if (needVerticalMetrics && i == t42VmtxTable) {
+         dumpString(vmtxTab, length, outputFunc, outputStream);
+         gfree(vmtxTab);
+       }
+      }
+    }
+  }
+
+  // end the sfnts array
+  (*outputFunc)(outputStream, "] def\n", 6);
+
+  gfree(locaData);
+  gfree(locaTable);
+}
+
+void FoFiTrueType::dumpString(Guchar *s, int length,
+                             FoFiOutputFunc outputFunc,
+                             void *outputStream) {
+  char buf[64];
+  int pad, i, j;
+
+  (*outputFunc)(outputStream, "<", 1);
+  for (i = 0; i < length; i += 32) {
+    for (j = 0; j < 32 && i+j < length; ++j) {
+      sprintf(buf, "%02X", s[i+j] & 0xff);
+      (*outputFunc)(outputStream, buf, strlen(buf));
+    }
+    if (i % (65536 - 32) == 65536 - 64) {
+      (*outputFunc)(outputStream, ">\n<", 3);
+    } else if (i+32 < length) {
+      (*outputFunc)(outputStream, "\n", 1);
+    }
+  }
+  if (length & 3) {
+    pad = 4 - (length & 3);
+    for (i = 0; i < pad; ++i) {
+      (*outputFunc)(outputStream, "00", 2);
+    }
+  }
+  // add an extra zero byte because the Adobe Type 42 spec says so
+  (*outputFunc)(outputStream, "00>\n", 4);
+}
+
+Guint FoFiTrueType::computeTableChecksum(Guchar *data, int length) {
+  Guint checksum, word;
+  int i;
+
+  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 FoFiTrueType::parse() {
+  Guint topTag;
+  int pos, i, j;
+
+  parsedOk = gTrue;
+
+  // look for a collection (TTC)
+  topTag = getU32BE(0, &parsedOk);
+  if (!parsedOk) {
+    return;
+  }
+  if (topTag == ttcfTag) {
+    pos = getU32BE(12, &parsedOk);
+    if (!parsedOk) {
+      return;
+    }
+  } else {
+    pos = 0;
+  }
+
+  // read the table directory
+  nTables = getU16BE(pos + 4, &parsedOk);
+  if (!parsedOk) {
+    return;
+  }
+  tables = (TrueTypeTable *)gmallocn(nTables, sizeof(TrueTypeTable));
+  pos += 12;
+  for (i = 0; i < nTables; ++i) {
+    tables[i].tag = getU32BE(pos, &parsedOk);
+    tables[i].checksum = getU32BE(pos + 4, &parsedOk);
+    tables[i].offset = (int)getU32BE(pos + 8, &parsedOk);
+    tables[i].len = (int)getU32BE(pos + 12, &parsedOk);
+    if (tables[i].offset + tables[i].len < tables[i].offset ||
+       tables[i].offset + tables[i].len > len) {
+      parsedOk = gFalse;
+    }
+    pos += 16;
+  }
+  if (!parsedOk) {
+    return;
+  }
+
+  // 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) {
+    parsedOk = gFalse;
+    return;
+  }
+
+  // read the cmaps
+  if ((i = seekTable("cmap")) >= 0) {
+    pos = tables[i].offset + 2;
+    nCmaps = getU16BE(pos, &parsedOk);
+    pos += 2;
+    if (!parsedOk) {
+      return;
+    }
+    cmaps = (TrueTypeCmap *)gmallocn(nCmaps, sizeof(TrueTypeCmap));
+    for (j = 0; j < nCmaps; ++j) {
+      cmaps[j].platform = getU16BE(pos, &parsedOk);
+      cmaps[j].encoding = getU16BE(pos + 2, &parsedOk);
+      cmaps[j].offset = tables[i].offset + getU32BE(pos + 4, &parsedOk);
+      pos += 8;
+      cmaps[j].fmt = getU16BE(cmaps[j].offset, &parsedOk);
+      cmaps[j].len = getU16BE(cmaps[j].offset + 2, &parsedOk);
+    }
+    if (!parsedOk) {
+      return;
+    }
+  } else {
+    nCmaps = 0;
+  }
+
+  // get the number of glyphs from the maxp table
+  i = seekTable("maxp");
+  nGlyphs = getU16BE(tables[i].offset + 4, &parsedOk);
+  if (!parsedOk) {
+    return;
+  }
+
+  // get the bbox and loca table format from the head table
+  i = seekTable("head");
+  bbox[0] = getS16BE(tables[i].offset + 36, &parsedOk);
+  bbox[1] = getS16BE(tables[i].offset + 38, &parsedOk);
+  bbox[2] = getS16BE(tables[i].offset + 40, &parsedOk);
+  bbox[3] = getS16BE(tables[i].offset + 42, &parsedOk);
+  locaFmt = getS16BE(tables[i].offset + 50, &parsedOk);
+  if (!parsedOk) {
+    return;
+  }
+
+  // make sure the loca table is sane (correct length and entries are
+  // in bounds)
+  i = seekTable("loca");
+  if (tables[i].len < (nGlyphs + 1) * (locaFmt ? 4 : 2)) {
+    parsedOk = gFalse;
+    return;
+  }
+  for (j = 0; j <= nGlyphs; ++j) {
+    if (locaFmt) {
+      pos = (int)getU32BE(tables[i].offset + j*4, &parsedOk);
+    } else {
+      pos = getU16BE(tables[i].offset + j*2, &parsedOk);
+    }
+    if (pos < 0 || pos > len) {
+      parsedOk = gFalse;
+    }
+  }
+  if (!parsedOk) {
+    return;
+  }
+
+  // read the post table
+  readPostTable();
+}
+
+void FoFiTrueType::readPostTable() {
+  GString *name;
+  int tablePos, postFmt, stringIdx, stringPos;
+  GBool ok;
+  int i, j, n, m;
+
+  ok = gTrue;
+  if ((i = seekTable("post")) < 0) {
+    return;
+  }
+  tablePos = tables[i].offset;
+  postFmt = getU32BE(tablePos, &ok);
+  if (!ok) {
+    goto err;
+  }
+  if (postFmt == 0x00010000) {
+    nameToGID = new GHash(gTrue);
+    for (i = 0; i < 258; ++i) {
+      nameToGID->add(new GString(macGlyphNames[i]), i);
+    }
+  } else if (postFmt == 0x00020000) {
+    nameToGID = new GHash(gTrue);
+    n = getU16BE(tablePos + 32, &ok);
+    if (!ok) {
+      goto err;
+    }
+    if (n > nGlyphs) {
+      n = nGlyphs;
+    }
+    stringIdx = 0;
+    stringPos = tablePos + 34 + 2*n;
+    for (i = 0; i < n; ++i) {
+      j = getU16BE(tablePos + 34 + 2*i, &ok);
+      if (j < 258) {
+       nameToGID->removeInt(macGlyphNames[j]);
+       nameToGID->add(new GString(macGlyphNames[j]), i);
+      } else {
+       j -= 258;
+       if (j != stringIdx) {
+         for (stringIdx = 0, stringPos = tablePos + 34 + 2*n;
+              stringIdx < j;
+              ++stringIdx, stringPos += 1 + getU8(stringPos, &ok)) ;
+         if (!ok) {
+           goto err;
+         }
+       }
+       m = getU8(stringPos, &ok);
+       if (!ok || !checkRegion(stringPos + 1, m)) {
+         goto err;
+       }
+       name = new GString((char *)&file[stringPos + 1], m);
+       nameToGID->removeInt(name);
+       nameToGID->add(name, i);
+       ++stringIdx;
+       stringPos += 1 + m;
+      }
+    }
+  } else if (postFmt == 0x00028000) {
+    nameToGID = new GHash(gTrue);
+    for (i = 0; i < nGlyphs; ++i) {
+      j = getU8(tablePos + 32 + i, &ok);
+      if (!ok) {
+       goto err;
+      }
+      if (j < 258) {
+       nameToGID->removeInt(macGlyphNames[j]);
+       nameToGID->add(new GString(macGlyphNames[j]), i);
+      }
+    }
+  }
+
+  return;
+
+ err:
+  if (nameToGID) {
+    delete nameToGID;
+    nameToGID = NULL;
+  }
+}
+
+int FoFiTrueType::seekTable(char *tag) {
+  Guint tagI;
+  int i;
+
+  tagI = ((tag[0] & 0xff) << 24) |
+         ((tag[1] & 0xff) << 16) |
+         ((tag[2] & 0xff) << 8) |
+          (tag[3] & 0xff);
+  for (i = 0; i < nTables; ++i) {
+    if (tables[i].tag == tagI) {
+      return i;
+    }
+  }
+  return -1;
+}
diff --git a/lib/xpdf/FoFiTrueType.h b/lib/xpdf/FoFiTrueType.h
new file mode 100644 (file)
index 0000000..f7b09d6
--- /dev/null
@@ -0,0 +1,140 @@
+//========================================================================
+//
+// FoFiTrueType.h
+//
+// Copyright 1999-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef FOFITRUETYPE_H
+#define FOFITRUETYPE_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#include "gtypes.h"
+#include "FoFiBase.h"
+
+class GString;
+class GHash;
+struct TrueTypeTable;
+struct TrueTypeCmap;
+
+//------------------------------------------------------------------------
+// FoFiTrueType
+//------------------------------------------------------------------------
+
+class FoFiTrueType: public FoFiBase {
+public:
+
+  // Create a FoFiTrueType object from a memory buffer.
+  static FoFiTrueType *make(char *fileA, int lenA);
+
+  // Create a FoFiTrueType object from a file on disk.
+  static FoFiTrueType *load(char *fileName);
+
+  virtual ~FoFiTrueType();
+
+  // Return the number of cmaps defined by this font.
+  int getNumCmaps();
+
+  // Return the platform ID of the <i>th cmap.
+  int getCmapPlatform(int i);
+
+  // Return the encoding ID of the <i>th cmap.
+  int getCmapEncoding(int i);
+
+  // Return the index of the cmap for <platform>, <encoding>.  Returns
+  // -1 if there is no corresponding cmap.
+  int findCmap(int platform, int encoding);
+
+  // Return the GID corresponding to <c> according to the <i>th cmap.
+  Gushort mapCodeToGID(int i, int c);
+
+  // Returns the GID corresponding to <name> according to the post
+  // table.  Returns 0 if there is no mapping for <name> or if the
+  // font does not have a post table.
+  int mapNameToGID(char *name);
+
+  // Returns the least restrictive embedding licensing right (as
+  // defined by the TrueType spec):
+  // * 4: OS/2 table is missing or invalid
+  // * 3: installable embedding
+  // * 2: editable embedding
+  // * 1: preview & print embedding
+  // * 0: restricted license embedding
+  int getEmbeddingRights();
+
+  // Convert to a Type 42 font, suitable for embedding in a PostScript
+  // file.  <psName> will be used as the PostScript font name (so we
+  // don't need to depend on the 'name' table in the font).  The
+  // <encoding> array specifies the mapping from char codes to names.
+  // If <encoding> is NULL, the encoding is unknown or undefined.  The
+  // <codeToGID> array specifies the mapping from char codes to GIDs.
+  void convertToType42(char *psName, char **encoding,
+                      Gushort *codeToGID,
+                      FoFiOutputFunc outputFunc, void *outputStream);
+
+  // Convert to a Type 2 CIDFont, suitable for embedding in a
+  // PostScript file.  <psName> will be used as the PostScript font
+  // name (so we don't need to depend on the 'name' table in the
+  // font).  The <cidMap> array maps CIDs to GIDs; it has <nCIDs>
+  // entries.
+  void convertToCIDType2(char *psName, Gushort *cidMap, int nCIDs,
+                        GBool needVerticalMetrics,
+                        FoFiOutputFunc outputFunc, void *outputStream);
+
+  // Convert to a Type 0 (but non-CID) composite font, suitable for
+  // embedding in a PostScript file.  <psName> will be used as the
+  // PostScript font name (so we don't need to depend on the 'name'
+  // table in the font).  The <cidMap> array maps CIDs to GIDs; it has
+  // <nCIDs> entries.
+  void convertToType0(char *psName, Gushort *cidMap, int nCIDs,
+                     GBool needVerticalMetrics,
+                     FoFiOutputFunc outputFunc, void *outputStream);
+
+  // Write a clean TTF file, filling in missing tables and correcting
+  // various other errors.  If <name> is non-NULL, the font is renamed
+  // to <name>.  If <codeToGID> is non-NULL, the font is re-encoded,
+  // using a Windows Unicode cmap.  If <name> is NULL and the font is
+  // complete and correct, it will be written unmodified.
+  void writeTTF(FoFiOutputFunc outputFunc, void *outputStream,
+               char *name = NULL, Gushort *codeToGID = NULL);
+
+private:
+
+  FoFiTrueType(char *fileA, int lenA, GBool freeFileDataA);
+  void cvtEncoding(char **encoding,
+                  FoFiOutputFunc outputFunc,
+                  void *outputStream);
+  void cvtCharStrings(char **encoding,
+                     Gushort *codeToGID,
+                     FoFiOutputFunc outputFunc,
+                     void *outputStream);
+  void cvtSfnts(FoFiOutputFunc outputFunc,
+               void *outputStream, GString *name,
+               GBool needVerticalMetrics);
+  void dumpString(Guchar *s, int length,
+                 FoFiOutputFunc outputFunc,
+                 void *outputStream);
+  Guint computeTableChecksum(Guchar *data, int length);
+  void parse();
+  void readPostTable();
+  int seekTable(char *tag);
+
+  TrueTypeTable *tables;
+  int nTables;
+  TrueTypeCmap *cmaps;
+  int nCmaps;
+  int nGlyphs;
+  int locaFmt;
+  int bbox[4];
+  GHash *nameToGID;
+
+  GBool parsedOk;
+};
+
+#endif
diff --git a/lib/xpdf/FoFiType1.cc b/lib/xpdf/FoFiType1.cc
new file mode 100644 (file)
index 0000000..a8a69fd
--- /dev/null
@@ -0,0 +1,212 @@
+//========================================================================
+//
+// FoFiType1.cc
+//
+// Copyright 1999-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+#include "gmem.h"
+#include "FoFiEncodings.h"
+#include "FoFiType1.h"
+
+//------------------------------------------------------------------------
+// FoFiType1
+//------------------------------------------------------------------------
+
+FoFiType1 *FoFiType1::make(char *fileA, int lenA) {
+  return new FoFiType1(fileA, lenA, gFalse);
+}
+
+FoFiType1 *FoFiType1::load(char *fileName) {
+  char *fileA;
+  int lenA;
+
+  if (!(fileA = FoFiBase::readFile(fileName, &lenA))) {
+    return NULL;
+  }
+  return new FoFiType1(fileA, lenA, gTrue);
+}
+
+FoFiType1::FoFiType1(char *fileA, int lenA, GBool freeFileDataA):
+  FoFiBase(fileA, lenA, freeFileDataA)
+{
+  name = NULL;
+  encoding = NULL;
+  parsed = gFalse;
+}
+
+FoFiType1::~FoFiType1() {
+  int i;
+
+  if (name) {
+    gfree(name);
+  }
+  if (encoding && encoding != fofiType1StandardEncoding) {
+    for (i = 0; i < 256; ++i) {
+      gfree(encoding[i]);
+    }
+    gfree(encoding);
+  }
+}
+
+char *FoFiType1::getName() {
+  if (!parsed) {
+    parse();
+  }
+  return name;
+}
+
+char **FoFiType1::getEncoding() {
+  if (!parsed) {
+    parse();
+  }
+  return encoding;
+}
+
+void FoFiType1::writeEncoded(char **newEncoding,
+                            FoFiOutputFunc outputFunc, void *outputStream) {
+  char buf[512];
+  char *line;
+  int i;
+
+  // copy everything up to the encoding
+  for (line = (char *)file;
+       line && strncmp(line, "/Encoding", 9);
+       line = getNextLine(line)) ;
+  if (!line) {
+    // no encoding - just copy the whole font file
+    (*outputFunc)(outputStream, (char *)file, len);
+    return;
+  }
+  (*outputFunc)(outputStream, (char *)file, line - (char *)file);
+
+  // write the new encoding
+  (*outputFunc)(outputStream, "/Encoding 256 array\n", 20);
+  (*outputFunc)(outputStream,
+               "0 1 255 {1 index exch /.notdef put} for\n", 40);
+  for (i = 0; i < 256; ++i) {
+    if (newEncoding[i]) {
+      sprintf(buf, "dup %d /%s put\n", i, newEncoding[i]);
+      (*outputFunc)(outputStream, buf, strlen(buf));
+    }
+  }
+  (*outputFunc)(outputStream, "readonly def\n", 13);
+  
+  // copy everything after the encoding
+  if (!strncmp(line, "/Encoding StandardEncoding def", 30)) {
+    line = getNextLine(line);
+  } else {
+    for (line = getNextLine(line);
+        line && strncmp(line, "readonly def", 12);
+        line = getNextLine(line)) ;
+    if (line) {
+      line = getNextLine(line);
+    }
+  }
+  if (line) {
+    (*outputFunc)(outputStream, line, ((char *)file + len) - line);
+  }
+}
+
+char *FoFiType1::getNextLine(char *line) {
+  while (line < (char *)file + len && *line != '\x0a' && *line != '\x0d') {
+    ++line;
+  }
+  if (line < (char *)file + len && *line == '\x0d') {
+    ++line;
+  }
+  if (line < (char *)file + len && *line == '\x0a') {
+    ++line;
+  }
+  if (line >= (char *)file + len) {
+    return NULL;
+  }
+  return line;
+}
+
+void FoFiType1::parse() {
+  char *line, *line1, *p, *p2;
+  char buf[256];
+  char c;
+  int n, code, i, j;
+
+  for (i = 1, line = (char *)file;
+       i <= 100 && line && (!name || !encoding);
+       ++i) {
+
+    // get font name
+    if (!name && !strncmp(line, "/FontName", 9)) {
+      strncpy(buf, line, 255);
+      buf[255] = '\0';
+      if ((p = strchr(buf+9, '/')) &&
+         (p = strtok(p+1, " \t\n\r"))) {
+       name = copyString(p);
+      }
+      line = getNextLine(line);
+
+    // get encoding
+    } else if (!encoding &&
+              !strncmp(line, "/Encoding StandardEncoding def", 30)) {
+      encoding = fofiType1StandardEncoding;
+    } else if (!encoding &&
+              !strncmp(line, "/Encoding 256 array", 19)) {
+      encoding = (char **)gmallocn(256, sizeof(char *));
+      for (j = 0; j < 256; ++j) {
+       encoding[j] = NULL;
+      }
+      for (j = 0, line = getNextLine(line);
+          j < 300 && line && (line1 = getNextLine(line));
+          ++j, line = line1) {
+       if ((n = line1 - line) > 255) {
+         n = 255;
+       }
+       strncpy(buf, line, n);
+       buf[n] = '\0';
+       for (p = buf; *p == ' ' || *p == '\t'; ++p) ;
+       if (!strncmp(p, "dup", 3)) {
+         for (p += 3; *p == ' ' || *p == '\t'; ++p) ;
+         for (p2 = p; *p2 >= '0' && *p2 <= '9'; ++p2) ;
+         if (*p2) {
+           c = *p2;
+           *p2 = '\0';
+           if ((code = atoi(p)) < 256) {
+             *p2 = c;
+             for (p = p2; *p == ' ' || *p == '\t'; ++p) ;
+             if (*p == '/') {
+               ++p;
+               for (p2 = p; *p2 && *p2 != ' ' && *p2 != '\t'; ++p2) ;
+               *p2 = '\0';
+               encoding[code] = copyString(p);
+             }
+           }
+         }
+       } else {
+         p = strtok(buf, " \t\n\r");
+         if (p)
+         {
+           if (!strcmp(p, "def")) break;
+           if (!strcmp(p, "readonly")) break;
+           // the spec does not says this but i'm mantaining old xpdf behaviour that accepts "foo def" as end of the encoding array
+           p = strtok(buf, " \t\n\r");
+           if (p && !strcmp(p, "def")) break;
+         }
+       }
+      }
+      //~ check for getinterval/putinterval junk
+
+    } else {
+      line = getNextLine(line);
+    }
+  }
+
+  parsed = gTrue;
+}
diff --git a/lib/xpdf/FoFiType1.h b/lib/xpdf/FoFiType1.h
new file mode 100644 (file)
index 0000000..843352b
--- /dev/null
@@ -0,0 +1,59 @@
+//========================================================================
+//
+// FoFiType1.h
+//
+// Copyright 1999-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef FOFITYPE1_H
+#define FOFITYPE1_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#include "gtypes.h"
+#include "FoFiBase.h"
+
+//------------------------------------------------------------------------
+// FoFiType1
+//------------------------------------------------------------------------
+
+class FoFiType1: public FoFiBase {
+public:
+
+  // Create a FoFiType1 object from a memory buffer.
+  static FoFiType1 *make(char *fileA, int lenA);
+
+  // Create a FoFiType1 object from a file on disk.
+  static FoFiType1 *load(char *fileName);
+
+  virtual ~FoFiType1();
+
+  // Return the font name.
+  char *getName();
+
+  // Return the encoding, as an array of 256 names (any of which may
+  // be NULL).
+  char **getEncoding();
+
+  // Write a version of the Type 1 font file with a new encoding.
+  void writeEncoded(char **newEncoding,
+                   FoFiOutputFunc outputFunc, void *outputStream);
+
+private:
+
+  FoFiType1(char *fileA, int lenA, GBool freeFileDataA);
+
+  char *getNextLine(char *line);
+  void parse();
+
+  char *name;
+  char **encoding;
+  GBool parsed;
+};
+
+#endif
diff --git a/lib/xpdf/FoFiType1C.cc b/lib/xpdf/FoFiType1C.cc
new file mode 100644 (file)
index 0000000..a173e7c
--- /dev/null
@@ -0,0 +1,2484 @@
+//========================================================================
+//
+// FoFiType1C.cc
+//
+// Copyright 1999-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+#include "gmem.h"
+#include "GString.h"
+#include "FoFiEncodings.h"
+#include "FoFiType1C.h"
+
+//------------------------------------------------------------------------
+
+static char hexChars[17] = "0123456789ABCDEF";
+
+//------------------------------------------------------------------------
+// FoFiType1C
+//------------------------------------------------------------------------
+
+FoFiType1C *FoFiType1C::make(char *fileA, int lenA) {
+  FoFiType1C *ff;
+
+  ff = new FoFiType1C(fileA, lenA, gFalse);
+  if (!ff->parse()) {
+    delete ff;
+    return NULL;
+  }
+  return ff;
+}
+
+FoFiType1C *FoFiType1C::load(char *fileName) {
+  FoFiType1C *ff;
+  char *fileA;
+  int lenA;
+
+  if (!(fileA = FoFiBase::readFile(fileName, &lenA))) {
+    return NULL;
+  }
+  ff = new FoFiType1C(fileA, lenA, gTrue);
+  if (!ff->parse()) {
+    delete ff;
+    return NULL;
+  }
+  return ff;
+}
+
+FoFiType1C::FoFiType1C(char *fileA, int lenA, GBool freeFileDataA):
+  FoFiBase(fileA, lenA, freeFileDataA)
+{
+  name = NULL;
+  encoding = NULL;
+  privateDicts = NULL;
+  fdSelect = NULL;
+  charset = NULL;
+}
+
+FoFiType1C::~FoFiType1C() {
+  int i;
+
+  if (name) {
+    delete name;
+  }
+  if (encoding &&
+      encoding != fofiType1StandardEncoding &&
+      encoding != fofiType1ExpertEncoding) {
+    for (i = 0; i < 256; ++i) {
+      gfree(encoding[i]);
+    }
+    gfree(encoding);
+  }
+  if (privateDicts) {
+    gfree(privateDicts);
+  }
+  if (fdSelect) {
+    gfree(fdSelect);
+  }
+  if (charset &&
+      charset != fofiType1CISOAdobeCharset &&
+      charset != fofiType1CExpertCharset &&
+      charset != fofiType1CExpertSubsetCharset) {
+    gfree(charset);
+  }
+}
+
+char *FoFiType1C::getName() {
+  return name ? name->getCString() : (char *)NULL;
+}
+
+char **FoFiType1C::getEncoding() {
+  return encoding;
+}
+
+Gushort *FoFiType1C::getCIDToGIDMap(int *nCIDs) {
+  Gushort *map;
+  int n, i;
+
+  // a CID font's top dict has ROS as the first operator
+  if (topDict.firstOp != 0x0c1e) {
+    *nCIDs = 0;
+    return NULL;
+  }
+
+  // in a CID font, the charset data is the GID-to-CID mapping, so all
+  // we have to do is reverse it
+  n = 0;
+  for (i = 0; i < nGlyphs; ++i) {
+    if (charset[i] > n) {
+      n = charset[i];
+    }
+  }
+  ++n;
+  map = (Gushort *)gmallocn(n, sizeof(Gushort));
+  memset(map, 0, n * sizeof(Gushort));
+  for (i = 0; i < nGlyphs; ++i) {
+    map[charset[i]] = i;
+  }
+  *nCIDs = n;
+  return map;
+}
+
+void FoFiType1C::convertToType1(char **newEncoding, GBool ascii,
+                               FoFiOutputFunc outputFunc,
+                               void *outputStream) {
+  Type1CEexecBuf eb;
+  Type1CIndex subrIdx;
+  Type1CIndexVal val;
+  char buf[512];
+  char **enc;
+  GBool ok;
+  int i;
+
+  // write header and font dictionary, up to encoding
+  ok = gTrue;
+  (*outputFunc)(outputStream, "%!FontType1-1.0: ", 17);
+  (*outputFunc)(outputStream, name->getCString(), name->getLength());
+  if (topDict.versionSID != 0) {
+    getString(topDict.versionSID, buf, &ok);
+    (*outputFunc)(outputStream, buf, strlen(buf));
+  }
+  (*outputFunc)(outputStream, "\n", 1);
+  // the dictionary needs room for 12 entries: the following 9, plus
+  // Private and CharStrings (in the eexec section) and FID (which is
+  // added by definefont)
+  (*outputFunc)(outputStream, "12 dict begin\n", 14);
+  (*outputFunc)(outputStream, "/FontInfo 10 dict dup begin\n", 28);
+  if (topDict.versionSID != 0) {
+    (*outputFunc)(outputStream, "/version (", 10);
+    (*outputFunc)(outputStream, buf, strlen(buf));
+    (*outputFunc)(outputStream, ") readonly def\n", 15);
+  }
+  if (topDict.noticeSID != 0) {
+    getString(topDict.noticeSID, buf, &ok);
+    (*outputFunc)(outputStream, "/Notice (", 9);
+    (*outputFunc)(outputStream, buf, strlen(buf));
+    (*outputFunc)(outputStream, ") readonly def\n", 15);
+  }
+  if (topDict.copyrightSID != 0) {
+    getString(topDict.copyrightSID, buf, &ok);
+    (*outputFunc)(outputStream, "/Copyright (", 12);
+    (*outputFunc)(outputStream, buf, strlen(buf));
+    (*outputFunc)(outputStream, ") readonly def\n", 15);
+  }
+  if (topDict.fullNameSID != 0) {
+    getString(topDict.fullNameSID, buf, &ok);
+    (*outputFunc)(outputStream, "/FullName (", 11);
+    (*outputFunc)(outputStream, buf, strlen(buf));
+    (*outputFunc)(outputStream, ") readonly def\n", 15);
+  }
+  if (topDict.familyNameSID != 0) {
+    getString(topDict.familyNameSID, buf, &ok);
+    (*outputFunc)(outputStream, "/FamilyName (", 13);
+    (*outputFunc)(outputStream, buf, strlen(buf));
+    (*outputFunc)(outputStream, ") readonly def\n", 15);
+  }
+  if (topDict.weightSID != 0) {
+    getString(topDict.weightSID, buf, &ok);
+    (*outputFunc)(outputStream, "/Weight (", 9);
+    (*outputFunc)(outputStream, buf, strlen(buf));
+    (*outputFunc)(outputStream, ") readonly def\n", 15);
+  }
+  if (topDict.isFixedPitch) {
+    (*outputFunc)(outputStream, "/isFixedPitch true def\n", 23);
+  } else {
+    (*outputFunc)(outputStream, "/isFixedPitch false def\n", 24);
+  }
+  sprintf(buf, "/ItalicAngle %g def\n", topDict.italicAngle);
+  (*outputFunc)(outputStream, buf, strlen(buf));
+  sprintf(buf, "/UnderlinePosition %g def\n", topDict.underlinePosition);
+  (*outputFunc)(outputStream, buf, strlen(buf));
+  sprintf(buf, "/UnderlineThickness %g def\n", topDict.underlineThickness);
+  (*outputFunc)(outputStream, buf, strlen(buf));
+  (*outputFunc)(outputStream, "end readonly def\n", 17);
+  (*outputFunc)(outputStream, "/FontName /", 11);
+  (*outputFunc)(outputStream, name->getCString(), name->getLength());
+  (*outputFunc)(outputStream, " def\n", 5);
+  sprintf(buf, "/PaintType %d def\n", topDict.paintType);
+  (*outputFunc)(outputStream, buf, strlen(buf));
+  (*outputFunc)(outputStream, "/FontType 1 def\n", 16);
+  sprintf(buf, "/FontMatrix [%g %g %g %g %g %g] readonly def\n",
+         topDict.fontMatrix[0], topDict.fontMatrix[1], topDict.fontMatrix[2],
+         topDict.fontMatrix[3], topDict.fontMatrix[4], topDict.fontMatrix[5]);
+  (*outputFunc)(outputStream, buf, strlen(buf));
+  sprintf(buf, "/FontBBox [%g %g %g %g] readonly def\n",
+         topDict.fontBBox[0], topDict.fontBBox[1],
+         topDict.fontBBox[2], topDict.fontBBox[3]);
+  (*outputFunc)(outputStream, buf, strlen(buf));
+  sprintf(buf, "/StrokeWidth %g def\n", topDict.strokeWidth);
+  (*outputFunc)(outputStream, buf, strlen(buf));
+  if (topDict.uniqueID != 0) {
+    sprintf(buf, "/UniqueID %d def\n", topDict.uniqueID);
+    (*outputFunc)(outputStream, buf, strlen(buf));
+  }
+
+  // write the encoding
+  (*outputFunc)(outputStream, "/Encoding ", 10);
+  if (!newEncoding && encoding == fofiType1StandardEncoding) {
+    (*outputFunc)(outputStream, "StandardEncoding def\n", 21);
+  } else {
+    (*outputFunc)(outputStream, "256 array\n", 10);
+    (*outputFunc)(outputStream,
+                 "0 1 255 {1 index exch /.notdef put} for\n", 40);
+    enc = newEncoding ? newEncoding : encoding;
+    if(!enc) {
+       fprintf(stderr, "convertToType1: Warning: No Encoding\n");
+    }
+    for (i = 0; i < 256; ++i) {
+      if (enc && enc[i]) {
+       sprintf(buf, "dup %d /%s put\n", i, enc[i]);
+       (*outputFunc)(outputStream, buf, strlen(buf));
+      }
+    }
+    (*outputFunc)(outputStream, "readonly def\n", 13);
+  }
+  (*outputFunc)(outputStream, "currentdict end\n", 16);
+
+  // start the binary section
+  (*outputFunc)(outputStream, "currentfile eexec\n", 18);
+  eb.outputFunc = outputFunc;
+  eb.outputStream = outputStream;
+  eb.ascii = ascii;
+  eb.r1 = 55665;
+  eb.line = 0;
+
+  // write the private dictionary
+  eexecWrite(&eb, "\x83\xca\x73\xd5");
+  eexecWrite(&eb, "dup /Private 32 dict dup begin\n");
+  eexecWrite(&eb, "/RD {string currentfile exch readstring pop}"
+            " executeonly def\n");
+  eexecWrite(&eb, "/ND {noaccess def} executeonly def\n");
+  eexecWrite(&eb, "/NP {noaccess put} executeonly def\n");
+  eexecWrite(&eb, "/MinFeature {16 16} def\n");
+  eexecWrite(&eb, "/password 5839 def\n");
+  if (privateDicts[0].nBlueValues) {
+    eexecWrite(&eb, "/BlueValues [");
+    for (i = 0; i < privateDicts[0].nBlueValues; ++i) {
+      sprintf(buf, "%s%d", i > 0 ? " " : "", privateDicts[0].blueValues[i]);
+      eexecWrite(&eb, buf);
+    }
+    eexecWrite(&eb, "] def\n");
+  }
+  if (privateDicts[0].nOtherBlues) {
+    eexecWrite(&eb, "/OtherBlues [");
+    for (i = 0; i < privateDicts[0].nOtherBlues; ++i) {
+      sprintf(buf, "%s%d", i > 0 ? " " : "", privateDicts[0].otherBlues[i]);
+      eexecWrite(&eb, buf);
+    }
+    eexecWrite(&eb, "] def\n");
+  }
+  if (privateDicts[0].nFamilyBlues) {
+    eexecWrite(&eb, "/FamilyBlues [");
+    for (i = 0; i < privateDicts[0].nFamilyBlues; ++i) {
+      sprintf(buf, "%s%d", i > 0 ? " " : "", privateDicts[0].familyBlues[i]);
+      eexecWrite(&eb, buf);
+    }
+    eexecWrite(&eb, "] def\n");
+  }
+  if (privateDicts[0].nFamilyOtherBlues) {
+    eexecWrite(&eb, "/FamilyOtherBlues [");
+    for (i = 0; i < privateDicts[0].nFamilyOtherBlues; ++i) {
+      sprintf(buf, "%s%d", i > 0 ? " " : "",
+             privateDicts[0].familyOtherBlues[i]);
+      eexecWrite(&eb, buf);
+    }
+    eexecWrite(&eb, "] def\n");
+  }
+  if (privateDicts[0].blueScale != 0.039625) {
+    sprintf(buf, "/BlueScale %g def\n", privateDicts[0].blueScale);
+    eexecWrite(&eb, buf);
+  }
+  if (privateDicts[0].blueShift != 7) {
+    sprintf(buf, "/BlueShift %d def\n", privateDicts[0].blueShift);
+    eexecWrite(&eb, buf);
+  }
+  if (privateDicts[0].blueFuzz != 1) {
+    sprintf(buf, "/BlueFuzz %d def\n", privateDicts[0].blueFuzz);
+    eexecWrite(&eb, buf);
+  }
+  if (privateDicts[0].hasStdHW) {
+    sprintf(buf, "/StdHW [%g] def\n", privateDicts[0].stdHW);
+    eexecWrite(&eb, buf);
+  }
+  if (privateDicts[0].hasStdVW) {
+    sprintf(buf, "/StdVW [%g] def\n", privateDicts[0].stdVW);
+    eexecWrite(&eb, buf);
+  }
+  if (privateDicts[0].nStemSnapH) {
+    eexecWrite(&eb, "/StemSnapH [");
+    for (i = 0; i < privateDicts[0].nStemSnapH; ++i) {
+      sprintf(buf, "%s%g", i > 0 ? " " : "", privateDicts[0].stemSnapH[i]);
+      eexecWrite(&eb, buf);
+    }
+    eexecWrite(&eb, "] def\n");
+  }
+  if (privateDicts[0].nStemSnapV) {
+    eexecWrite(&eb, "/StemSnapV [");
+    for (i = 0; i < privateDicts[0].nStemSnapV; ++i) {
+      sprintf(buf, "%s%g", i > 0 ? " " : "", privateDicts[0].stemSnapV[i]);
+      eexecWrite(&eb, buf);
+    }
+    eexecWrite(&eb, "] def\n");
+  }
+  if (privateDicts[0].hasForceBold) {
+    sprintf(buf, "/ForceBold %s def\n",
+           privateDicts[0].forceBold ? "true" : "false");
+    eexecWrite(&eb, buf);
+  }
+  if (privateDicts[0].forceBoldThreshold != 0) {
+    sprintf(buf, "/ForceBoldThreshold %g def\n",
+           privateDicts[0].forceBoldThreshold);
+    eexecWrite(&eb, buf);
+  }
+  if (privateDicts[0].languageGroup != 0) {
+    sprintf(buf, "/LanguageGroup %d def\n", privateDicts[0].languageGroup);
+    eexecWrite(&eb, buf);
+  }
+  if (privateDicts[0].expansionFactor != 0.06) {
+    sprintf(buf, "/ExpansionFactor %g def\n", privateDicts[0].expansionFactor);
+    eexecWrite(&eb, buf);
+  }
+
+  // set up subroutines
+  ok = gTrue;
+  getIndex(privateDicts[0].subrsOffset, &subrIdx, &ok);
+  if (!ok) {
+    subrIdx.pos = -1;
+  }
+
+  // write the CharStrings
+  sprintf(buf, "2 index /CharStrings %d dict dup begin\n", nGlyphs);
+  eexecWrite(&eb, buf);
+  for (i = 0; i < nGlyphs; ++i) {
+    ok = gTrue;
+    getIndexVal(&charStringsIdx, i, &val, &ok);
+    if (ok) {
+      getString(charset[i], buf, &ok);
+      if (ok) {
+       eexecCvtGlyph(&eb, buf, val.pos, val.len, &subrIdx, &privateDicts[0]);
+      }
+    }
+  }
+  eexecWrite(&eb, "end\n");
+  eexecWrite(&eb, "end\n");
+  eexecWrite(&eb, "readonly put\n");
+  eexecWrite(&eb, "noaccess put\n");
+  eexecWrite(&eb, "dup /FontName get exch definefont pop\n");
+  eexecWrite(&eb, "mark currentfile closefile\n");
+
+  // trailer
+  if (ascii && eb.line > 0) {
+    (*outputFunc)(outputStream, "\n", 1);
+  }
+  for (i = 0; i < 8; ++i) {
+    (*outputFunc)(outputStream, "0000000000000000000000000000000000000000000000000000000000000000\n", 65);
+  }
+  (*outputFunc)(outputStream, "cleartomark\n", 12);
+}
+
+void FoFiType1C::convertToCIDType0(char *psName,
+                                  FoFiOutputFunc outputFunc,
+                                  void *outputStream) {
+  int *cidMap;
+  GString *charStrings;
+  int *charStringOffsets;
+  Type1CIndex subrIdx;
+  Type1CIndexVal val;
+  int nCIDs, gdBytes;
+  char buf[512], buf2[512];
+  GBool ok;
+  int gid, offset, n, i, j, k;
+
+  // compute the CID count and build the CID-to-GID mapping
+  nCIDs = 0;
+  for (i = 0; i < nGlyphs; ++i) {
+    if (charset[i] >= nCIDs) {
+      nCIDs = charset[i] + 1;
+    }
+  }
+  cidMap = (int *)gmallocn(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 *)gmallocn(nCIDs + 1, sizeof(int));
+  for (i = 0; i < nCIDs; ++i) {
+    charStringOffsets[i] = charStrings->getLength();
+    if ((gid = cidMap[i]) >= 0) {
+      ok = gTrue;
+      getIndexVal(&charStringsIdx, gid, &val, &ok);
+      if (ok) {
+       getIndex(privateDicts[fdSelect[gid]].subrsOffset, &subrIdx, &ok);
+       if (!ok) {
+         subrIdx.pos = -1;
+       }
+       cvtGlyph(val.pos, val.len, charStrings,
+                &subrIdx, &privateDicts[fdSelect[gid]], gTrue);
+      }
+    }
+  }
+  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
+  (*outputFunc)(outputStream, "/CIDInit /ProcSet findresource begin\n", 37);
+  (*outputFunc)(outputStream, "20 dict begin\n", 14);
+  (*outputFunc)(outputStream, "/CIDFontName /", 14);
+  (*outputFunc)(outputStream, psName, strlen(psName));
+  (*outputFunc)(outputStream, " def\n", 5);
+  (*outputFunc)(outputStream, "/CIDFontType 0 def\n", 19);
+  (*outputFunc)(outputStream, "/CIDSystemInfo 3 dict dup begin\n", 32);
+  if (topDict.registrySID > 0 && topDict.orderingSID > 0) {
+    ok = gTrue;
+    getString(topDict.registrySID, buf, &ok);
+    if (ok) {
+      (*outputFunc)(outputStream, "  /Registry (", 13);
+      (*outputFunc)(outputStream, buf, strlen(buf));
+      (*outputFunc)(outputStream, ") def\n", 6);
+    }
+    ok = gTrue;
+    getString(topDict.orderingSID, buf, &ok);
+    if (ok) {
+      (*outputFunc)(outputStream, "  /Ordering (", 13);
+      (*outputFunc)(outputStream, buf, strlen(buf));
+      (*outputFunc)(outputStream, ") def\n", 6);
+    }
+  } else {
+    (*outputFunc)(outputStream, "  /Registry (Adobe) def\n", 24);
+    (*outputFunc)(outputStream, "  /Ordering (Identity) def\n", 27);
+  }
+  sprintf(buf, "  /Supplement %d def\n", topDict.supplement);
+  (*outputFunc)(outputStream, buf, strlen(buf));
+  (*outputFunc)(outputStream, "end def\n", 8);
+  if (topDict.hasFontMatrix) {
+    sprintf(buf, "/FontMatrix [%g %g %g %g %g %g] def\n",
+           topDict.fontMatrix[0], topDict.fontMatrix[1],
+           topDict.fontMatrix[2], topDict.fontMatrix[3],
+           topDict.fontMatrix[4], topDict.fontMatrix[5]);
+    (*outputFunc)(outputStream, buf, strlen(buf));
+  } else if (privateDicts[0].hasFontMatrix) {
+    (*outputFunc)(outputStream, "/FontMatrix [1 0 0 1 0 0] def\n", 30);
+  } else {
+    (*outputFunc)(outputStream,
+                 "/FontMatrix [0.001 0 0 0.001 0 0] def\n", 38);
+  }
+  sprintf(buf, "/FontBBox [%g %g %g %g] def\n",
+         topDict.fontBBox[0], topDict.fontBBox[1],
+         topDict.fontBBox[2], topDict.fontBBox[3]);
+  (*outputFunc)(outputStream, buf, strlen(buf));
+  (*outputFunc)(outputStream, "/FontInfo 1 dict dup begin\n", 27);
+  (*outputFunc)(outputStream, "  /FSType 8 def\n", 16);
+  (*outputFunc)(outputStream, "end def\n", 8);
+
+  // CIDFont-specific entries
+  sprintf(buf, "/CIDCount %d def\n", nCIDs);
+  (*outputFunc)(outputStream, buf, strlen(buf));
+  (*outputFunc)(outputStream, "/FDBytes 1 def\n", 15);
+  sprintf(buf, "/GDBytes %d def\n", gdBytes);
+  (*outputFunc)(outputStream, buf, strlen(buf));
+  (*outputFunc)(outputStream, "/CIDMapOffset 0 def\n", 20);
+  if (topDict.paintType != 0) {
+    sprintf(buf, "/PaintType %d def\n", topDict.paintType);
+    (*outputFunc)(outputStream, buf, strlen(buf));
+    sprintf(buf, "/StrokeWidth %g def\n", topDict.strokeWidth);
+    (*outputFunc)(outputStream, buf, strlen(buf));
+  }
+
+  // FDArray entry
+  sprintf(buf, "/FDArray %d array\n", nFDs);
+  (*outputFunc)(outputStream, buf, strlen(buf));
+  for (i = 0; i < nFDs; ++i) {
+    sprintf(buf, "dup %d 10 dict begin\n", i);
+    (*outputFunc)(outputStream, buf, strlen(buf));
+    (*outputFunc)(outputStream, "/FontType 1 def\n", 16);
+    if (privateDicts[i].hasFontMatrix) {
+      sprintf(buf, "/FontMatrix [%g %g %g %g %g %g] def\n",
+             privateDicts[i].fontMatrix[0],
+             privateDicts[i].fontMatrix[1],
+             privateDicts[i].fontMatrix[2],
+             privateDicts[i].fontMatrix[3],
+             privateDicts[i].fontMatrix[4],
+             privateDicts[i].fontMatrix[5]);
+      (*outputFunc)(outputStream, buf, strlen(buf));
+    } else {
+      (*outputFunc)(outputStream, "/FontMatrix [1 0 0 1 0 0] def\n", 30);
+    }
+    sprintf(buf, "/PaintType %d def\n", topDict.paintType);
+    (*outputFunc)(outputStream, buf, strlen(buf));
+    (*outputFunc)(outputStream, "/Private 32 dict begin\n", 23);
+    if (privateDicts[i].nBlueValues) {
+      (*outputFunc)(outputStream, "/BlueValues [", 13);
+      for (j = 0; j < privateDicts[i].nBlueValues; ++j) {
+       sprintf(buf, "%s%d", j > 0 ? " " : "", privateDicts[i].blueValues[j]);
+       (*outputFunc)(outputStream, buf, strlen(buf));
+      }
+      (*outputFunc)(outputStream, "] def\n", 6);
+    }
+    if (privateDicts[i].nOtherBlues) {
+      (*outputFunc)(outputStream, "/OtherBlues [", 13);
+      for (j = 0; j < privateDicts[i].nOtherBlues; ++j) {
+       sprintf(buf, "%s%d", j > 0 ? " " : "", privateDicts[i].otherBlues[j]);
+       (*outputFunc)(outputStream, buf, strlen(buf));
+      }
+      (*outputFunc)(outputStream, "] def\n", 6);
+    }
+    if (privateDicts[i].nFamilyBlues) {
+      (*outputFunc)(outputStream, "/FamilyBlues [", 14);
+      for (j = 0; j < privateDicts[i].nFamilyBlues; ++j) {
+       sprintf(buf, "%s%d", j > 0 ? " " : "", privateDicts[i].familyBlues[j]);
+       (*outputFunc)(outputStream, buf, strlen(buf));
+      }
+      (*outputFunc)(outputStream, "] def\n", 6);
+    }
+    if (privateDicts[i].nFamilyOtherBlues) {
+      (*outputFunc)(outputStream, "/FamilyOtherBlues [", 19);
+      for (j = 0; j < privateDicts[i].nFamilyOtherBlues; ++j) {
+       sprintf(buf, "%s%d", j > 0 ? " " : "",
+               privateDicts[i].familyOtherBlues[j]);
+       (*outputFunc)(outputStream, buf, strlen(buf));
+      }
+      (*outputFunc)(outputStream, "] def\n", 6);
+    }
+    if (privateDicts[i].blueScale != 0.039625) {
+      sprintf(buf, "/BlueScale %g def\n", privateDicts[i].blueScale);
+      (*outputFunc)(outputStream, buf, strlen(buf));
+    }
+    if (privateDicts[i].blueShift != 7) {
+      sprintf(buf, "/BlueShift %d def\n", privateDicts[i].blueShift);
+      (*outputFunc)(outputStream, buf, strlen(buf));
+    }
+    if (privateDicts[i].blueFuzz != 1) {
+      sprintf(buf, "/BlueFuzz %d def\n", privateDicts[i].blueFuzz);
+      (*outputFunc)(outputStream, buf, strlen(buf));
+    }
+    if (privateDicts[i].hasStdHW) {
+      sprintf(buf, "/StdHW [%g] def\n", privateDicts[i].stdHW);
+      (*outputFunc)(outputStream, buf, strlen(buf));
+    }
+    if (privateDicts[i].hasStdVW) {
+      sprintf(buf, "/StdVW [%g] def\n", privateDicts[i].stdVW);
+      (*outputFunc)(outputStream, buf, strlen(buf));
+    }
+    if (privateDicts[i].nStemSnapH) {
+      (*outputFunc)(outputStream, "/StemSnapH [", 12);
+      for (j = 0; j < privateDicts[i].nStemSnapH; ++j) {
+       sprintf(buf, "%s%g", j > 0 ? " " : "", privateDicts[i].stemSnapH[j]);
+       (*outputFunc)(outputStream, buf, strlen(buf));
+      }
+      (*outputFunc)(outputStream, "] def\n", 6);
+    }
+    if (privateDicts[i].nStemSnapV) {
+      (*outputFunc)(outputStream, "/StemSnapV [", 12);
+      for (j = 0; j < privateDicts[i].nStemSnapV; ++j) {
+       sprintf(buf, "%s%g", j > 0 ? " " : "", privateDicts[i].stemSnapV[j]);
+       (*outputFunc)(outputStream, buf, strlen(buf));
+      }
+      (*outputFunc)(outputStream, "] def\n", 6);
+    }
+    if (privateDicts[i].hasForceBold) {
+      sprintf(buf, "/ForceBold %s def\n",
+             privateDicts[i].forceBold ? "true" : "false");
+      (*outputFunc)(outputStream, buf, strlen(buf));
+    }
+    if (privateDicts[i].forceBoldThreshold != 0) {
+      sprintf(buf, "/ForceBoldThreshold %g def\n",
+             privateDicts[i].forceBoldThreshold);
+      (*outputFunc)(outputStream, buf, strlen(buf));
+    }
+    if (privateDicts[i].languageGroup != 0) {
+      sprintf(buf, "/LanguageGroup %d def\n", privateDicts[i].languageGroup);
+      (*outputFunc)(outputStream, buf, strlen(buf));
+    }
+    if (privateDicts[i].expansionFactor != 0.06) {
+      sprintf(buf, "/ExpansionFactor %g def\n",
+             privateDicts[i].expansionFactor);
+      (*outputFunc)(outputStream, buf, strlen(buf));
+    }
+    (*outputFunc)(outputStream, "currentdict end def\n", 20);
+    (*outputFunc)(outputStream, "currentdict end put\n", 20);
+  }
+  (*outputFunc)(outputStream, "def\n", 4);
+
+  // start the binary section
+  offset = (nCIDs + 1) * (1 + gdBytes);
+  sprintf(buf, "(Hex) %d StartData\n",
+         offset + charStrings->getLength());
+  (*outputFunc)(outputStream, buf, strlen(buf));
+
+  // write the charstring offset (CIDMap) table
+  for (i = 0; i <= nCIDs; i += 6) {
+    for (j = 0; j < 6 && i+j <= nCIDs; ++j) {
+      if (i+j < nCIDs && 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) {
+       sprintf(buf2, "%02x", buf[k] & 0xff);
+       (*outputFunc)(outputStream, buf2, 2);
+      }
+    }
+    (*outputFunc)(outputStream, "\n", 1);
+  }
+
+  // write the charstring data
+  n = charStrings->getLength();
+  for (i = 0; i < n; i += 32) {
+    for (j = 0; j < 32 && i+j < n; ++j) {
+      sprintf(buf, "%02x", charStrings->getChar(i+j) & 0xff);
+      (*outputFunc)(outputStream, buf, strlen(buf));
+    }
+    if (i + 32 >= n) {
+      (*outputFunc)(outputStream, ">", 1);
+    }
+    (*outputFunc)(outputStream, "\n", 1);
+  }
+
+  gfree(charStringOffsets);
+  delete charStrings;
+  gfree(cidMap);
+}
+
+void FoFiType1C::convertToType0(char *psName,
+                               FoFiOutputFunc outputFunc,
+                               void *outputStream) {
+  int *cidMap;
+  Type1CIndex subrIdx;
+  Type1CIndexVal val;
+  int nCIDs;
+  char buf[512];
+  Type1CEexecBuf eb;
+  GBool ok;
+  int fd, i, j, k;
+
+  // compute the CID count and build the CID-to-GID mapping
+  nCIDs = 0;
+  for (i = 0; i < nGlyphs; ++i) {
+    if (charset[i] >= nCIDs) {
+      nCIDs = charset[i] + 1;
+    }
+  }
+  cidMap = (int *)gmallocn(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;
+      }
+    }
+
+    // font dictionary (unencrypted section)
+    (*outputFunc)(outputStream, "16 dict begin\n", 14);
+    (*outputFunc)(outputStream, "/FontName /", 11);
+    (*outputFunc)(outputStream, psName, strlen(psName));
+    sprintf(buf, "_%02x def\n", i >> 8);
+    (*outputFunc)(outputStream, buf, strlen(buf));
+    (*outputFunc)(outputStream, "/FontType 1 def\n", 16);
+    if (privateDicts[fd].hasFontMatrix) {
+      sprintf(buf, "/FontMatrix [%g %g %g %g %g %g] def\n",
+             privateDicts[fd].fontMatrix[0],
+             privateDicts[fd].fontMatrix[1],
+             privateDicts[fd].fontMatrix[2],
+             privateDicts[fd].fontMatrix[3],
+             privateDicts[fd].fontMatrix[4],
+             privateDicts[fd].fontMatrix[5]);
+      (*outputFunc)(outputStream, buf, strlen(buf));
+    } else if (topDict.hasFontMatrix) {
+      (*outputFunc)(outputStream, "/FontMatrix [1 0 0 1 0 0] def\n", 30);
+    } else {
+      (*outputFunc)(outputStream,
+                   "/FontMatrix [0.001 0 0 0.001 0 0] def\n", 38);
+    }
+    sprintf(buf, "/FontBBox [%g %g %g %g] def\n",
+           topDict.fontBBox[0], topDict.fontBBox[1],
+           topDict.fontBBox[2], topDict.fontBBox[3]);
+    (*outputFunc)(outputStream, buf, strlen(buf));
+    sprintf(buf, "/PaintType %d def\n", topDict.paintType);
+    (*outputFunc)(outputStream, buf, strlen(buf));
+    if (topDict.paintType != 0) {
+      sprintf(buf, "/StrokeWidth %g def\n", topDict.strokeWidth);
+      (*outputFunc)(outputStream, buf, strlen(buf));
+    }
+    (*outputFunc)(outputStream, "/Encoding 256 array\n", 20);
+    for (j = 0; j < 256 && i+j < nCIDs; ++j) {
+      sprintf(buf, "dup %d /c%02x put\n", j, j);
+      (*outputFunc)(outputStream, buf, strlen(buf));
+    }
+    if (j < 256) {
+      sprintf(buf, "%d 1 255 { 1 index exch /.notdef put } for\n", j);
+      (*outputFunc)(outputStream, buf, strlen(buf));
+    }
+    (*outputFunc)(outputStream, "readonly def\n", 13);
+    (*outputFunc)(outputStream, "currentdict end\n", 16);
+
+    // start the binary section
+    (*outputFunc)(outputStream, "currentfile eexec\n", 18);
+    eb.outputFunc = outputFunc;
+    eb.outputStream = outputStream;
+    eb.ascii = gTrue;
+    eb.r1 = 55665;
+    eb.line = 0;
+
+    // start the private dictionary
+    eexecWrite(&eb, "\x83\xca\x73\xd5");
+    eexecWrite(&eb, "dup /Private 32 dict dup begin\n");
+    eexecWrite(&eb, "/RD {string currentfile exch readstring pop}"
+              " executeonly def\n");
+    eexecWrite(&eb, "/ND {noaccess def} executeonly def\n");
+    eexecWrite(&eb, "/NP {noaccess put} executeonly def\n");
+    eexecWrite(&eb, "/MinFeature {16 16} def\n");
+    eexecWrite(&eb, "/password 5839 def\n");
+    if (privateDicts[fd].nBlueValues) {
+      eexecWrite(&eb, "/BlueValues [");
+      for (k = 0; k < privateDicts[fd].nBlueValues; ++k) {
+       sprintf(buf, "%s%d", k > 0 ? " " : "", privateDicts[fd].blueValues[k]);
+       eexecWrite(&eb, buf);
+      }
+      eexecWrite(&eb, "] def\n");
+    }
+    if (privateDicts[fd].nOtherBlues) {
+      eexecWrite(&eb, "/OtherBlues [");
+      for (k = 0; k < privateDicts[fd].nOtherBlues; ++k) {
+       sprintf(buf, "%s%d", k > 0 ? " " : "", privateDicts[fd].otherBlues[k]);
+       eexecWrite(&eb, buf);
+      }
+      eexecWrite(&eb, "] def\n");
+    }
+    if (privateDicts[fd].nFamilyBlues) {
+      eexecWrite(&eb, "/FamilyBlues [");
+      for (k = 0; k < privateDicts[fd].nFamilyBlues; ++k) {
+       sprintf(buf, "%s%d", k > 0 ? " " : "",
+               privateDicts[fd].familyBlues[k]);
+       eexecWrite(&eb, buf);
+      }
+      eexecWrite(&eb, "] def\n");
+    }
+    if (privateDicts[fd].nFamilyOtherBlues) {
+      eexecWrite(&eb, "/FamilyOtherBlues [");
+      for (k = 0; k < privateDicts[fd].nFamilyOtherBlues; ++k) {
+       sprintf(buf, "%s%d", k > 0 ? " " : "",
+               privateDicts[fd].familyOtherBlues[k]);
+       eexecWrite(&eb, buf);
+      }
+      eexecWrite(&eb, "] def\n");
+    }
+    if (privateDicts[fd].blueScale != 0.039625) {
+      sprintf(buf, "/BlueScale %g def\n", privateDicts[fd].blueScale);
+      eexecWrite(&eb, buf);
+    }
+    if (privateDicts[fd].blueShift != 7) {
+      sprintf(buf, "/BlueShift %d def\n", privateDicts[fd].blueShift);
+      eexecWrite(&eb, buf);
+    }
+    if (privateDicts[fd].blueFuzz != 1) {
+      sprintf(buf, "/BlueFuzz %d def\n", privateDicts[fd].blueFuzz);
+      eexecWrite(&eb, buf);
+    }
+    if (privateDicts[fd].hasStdHW) {
+      sprintf(buf, "/StdHW [%g] def\n", privateDicts[fd].stdHW);
+      eexecWrite(&eb, buf);
+    }
+    if (privateDicts[fd].hasStdVW) {
+      sprintf(buf, "/StdVW [%g] def\n", privateDicts[fd].stdVW);
+      eexecWrite(&eb, buf);
+    }
+    if (privateDicts[fd].nStemSnapH) {
+      eexecWrite(&eb, "/StemSnapH [");
+      for (k = 0; k < privateDicts[fd].nStemSnapH; ++k) {
+       sprintf(buf, "%s%g", k > 0 ? " " : "", privateDicts[fd].stemSnapH[k]);
+       eexecWrite(&eb, buf);
+      }
+      eexecWrite(&eb, "] def\n");
+    }
+    if (privateDicts[fd].nStemSnapV) {
+      eexecWrite(&eb, "/StemSnapV [");
+      for (k = 0; k < privateDicts[fd].nStemSnapV; ++k) {
+       sprintf(buf, "%s%g", k > 0 ? " " : "", privateDicts[fd].stemSnapV[k]);
+       eexecWrite(&eb, buf);
+      }
+      eexecWrite(&eb, "] def\n");
+    }
+    if (privateDicts[fd].hasForceBold) {
+      sprintf(buf, "/ForceBold %s def\n",
+             privateDicts[fd].forceBold ? "true" : "false");
+      eexecWrite(&eb, buf);
+    }
+    if (privateDicts[fd].forceBoldThreshold != 0) {
+      sprintf(buf, "/ForceBoldThreshold %g def\n",
+             privateDicts[fd].forceBoldThreshold);
+      eexecWrite(&eb, buf);
+    }
+    if (privateDicts[fd].languageGroup != 0) {
+      sprintf(buf, "/LanguageGroup %d def\n", privateDicts[fd].languageGroup);
+      eexecWrite(&eb, buf);
+    }
+    if (privateDicts[fd].expansionFactor != 0.06) {
+      sprintf(buf, "/ExpansionFactor %g def\n",
+             privateDicts[fd].expansionFactor);
+      eexecWrite(&eb, buf);
+    }
+
+    // set up the subroutines
+    ok = gTrue;
+    getIndex(privateDicts[fd].subrsOffset, &subrIdx, &ok);
+    if (!ok) {
+      subrIdx.pos = -1;
+    }
+
+    // start the CharStrings
+    sprintf(buf, "2 index /CharStrings 256 dict dup begin\n");
+    eexecWrite(&eb, buf);
+
+    // write the .notdef CharString
+    ok = gTrue;
+    getIndexVal(&charStringsIdx, 0, &val, &ok);
+    if (ok) {
+      eexecCvtGlyph(&eb, ".notdef", val.pos, val.len,
+                   &subrIdx, &privateDicts[fd]);
+    }
+
+    // write the CharStrings
+    for (j = 0; j < 256 && i+j < nCIDs; ++j) {
+      if (cidMap[i+j] >= 0) {
+       ok = gTrue;
+       getIndexVal(&charStringsIdx, cidMap[i+j], &val, &ok);
+       if (ok) {
+         sprintf(buf, "c%02x", j);
+         eexecCvtGlyph(&eb, buf, val.pos, val.len,
+                       &subrIdx, &privateDicts[fd]);
+       }
+      }
+    }
+    eexecWrite(&eb, "end\n");
+    eexecWrite(&eb, "end\n");
+    eexecWrite(&eb, "readonly put\n");
+    eexecWrite(&eb, "noaccess put\n");
+    eexecWrite(&eb, "dup /FontName get exch definefont pop\n");
+    eexecWrite(&eb, "mark currentfile closefile\n");
+
+    // trailer
+    if (eb.line > 0) {
+      (*outputFunc)(outputStream, "\n", 1);
+    }
+    for (j = 0; j < 8; ++j) {
+      (*outputFunc)(outputStream, "0000000000000000000000000000000000000000000000000000000000000000\n", 65);
+    }
+    (*outputFunc)(outputStream, "cleartomark\n", 12);
+  }
+
+  // write the Type 0 parent font
+  (*outputFunc)(outputStream, "16 dict begin\n", 14);
+  (*outputFunc)(outputStream, "/FontName /", 11);
+  (*outputFunc)(outputStream, psName, strlen(psName));
+  (*outputFunc)(outputStream, " def\n", 5);
+  (*outputFunc)(outputStream, "/FontType 0 def\n", 16);
+  if (topDict.hasFontMatrix) {
+    sprintf(buf, "/FontMatrix [%g %g %g %g %g %g] def\n",
+           topDict.fontMatrix[0], topDict.fontMatrix[1],
+           topDict.fontMatrix[2], topDict.fontMatrix[3],
+           topDict.fontMatrix[4], topDict.fontMatrix[5]);
+    (*outputFunc)(outputStream, buf, strlen(buf));
+  } else {
+    (*outputFunc)(outputStream, "/FontMatrix [1 0 0 1 0 0] def\n", 30);
+  }
+  (*outputFunc)(outputStream, "/FMapType 2 def\n", 16);
+  (*outputFunc)(outputStream, "/Encoding [\n", 12);
+  for (i = 0; i < nCIDs; i += 256) {
+    sprintf(buf, "%d\n", i >> 8);
+    (*outputFunc)(outputStream, buf, strlen(buf));
+  }
+  (*outputFunc)(outputStream, "] def\n", 6);
+  (*outputFunc)(outputStream, "/FDepVector [\n", 14);
+  for (i = 0; i < nCIDs; i += 256) {
+    (*outputFunc)(outputStream, "/", 1);
+    (*outputFunc)(outputStream, psName, strlen(psName));
+    sprintf(buf, "_%02x findfont\n", i >> 8);
+    (*outputFunc)(outputStream, buf, strlen(buf));
+  }
+  (*outputFunc)(outputStream, "] def\n", 6);
+  (*outputFunc)(outputStream, "FontName currentdict end definefont pop\n", 40);
+
+  gfree(cidMap);
+}
+
+void FoFiType1C::eexecCvtGlyph(Type1CEexecBuf *eb, char *glyphName,
+                              int offset, int nBytes,
+                              Type1CIndex *subrIdx,
+                              Type1CPrivateDict *pDict) {
+  char buf[512];
+  GString *charBuf;
+
+  // generate the charstring
+  charBuf = new GString();
+  cvtGlyph(offset, nBytes, charBuf, subrIdx, pDict, gTrue);
+
+  sprintf(buf, "/%s %d RD ", glyphName, charBuf->getLength());
+  eexecWrite(eb, buf);
+  eexecWriteCharstring(eb, (Guchar *)charBuf->getCString(),
+                      charBuf->getLength());
+  eexecWrite(eb, " ND\n");
+
+  delete charBuf;
+}
+
+void FoFiType1C::cvtGlyph(int offset, int nBytes, GString *charBuf,
+                         Type1CIndex *subrIdx, Type1CPrivateDict *pDict,
+                         GBool top) {
+  Type1CIndexVal val;
+  GBool ok, dFP;
+  double d, dx, dy;
+  Gushort r2;
+  Guchar byte;
+  int pos, subrBias, start, i, k;
+
+  start = charBuf->getLength();
+  if (top) {
+    charBuf->append((char)73);
+    charBuf->append((char)58);
+    charBuf->append((char)147);
+    charBuf->append((char)134);
+    nOps = 0;
+    nHints = 0;
+    firstOp = gTrue;
+    openPath = gFalse;
+  }
+
+  pos = offset;
+  while (pos < offset + nBytes) {
+    ok = gTrue;
+    pos = getOp(pos, gTrue, &ok);
+    if (!ok) {
+      break;
+    }
+    if (!ops[nOps - 1].isNum) {
+      --nOps; // drop the operator
+      switch (ops[nOps].op) {
+      case 0x0001:             // hstem
+       if (firstOp) {
+         cvtGlyphWidth(nOps & 1, charBuf, pDict);
+         firstOp = 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) {
+         // convert Type 2 edge hints (-20 or -21) to Type 1 ghost hints
+         if (ops[k+1].num < 0) {
+           d += ops[k].num + ops[k+1].num;
+           dFP |= ops[k].isFP | ops[k+1].isFP;
+           cvtNum(d, dFP, charBuf);
+           cvtNum(-ops[k+1].num, ops[k+1].isFP, charBuf);
+         } else {
+           d += ops[k].num;
+           dFP |= ops[k].isFP;
+           cvtNum(d, dFP, charBuf);
+           cvtNum(ops[k+1].num, ops[k+1].isFP, charBuf);
+           d += ops[k+1].num;
+           dFP |= ops[k+1].isFP;
+         }
+         charBuf->append((char)1);
+       }
+       nHints += nOps / 2;
+       nOps = 0;
+       break;
+      case 0x0003:             // vstem
+       if (firstOp) {
+         cvtGlyphWidth(nOps & 1, charBuf, pDict);
+         firstOp = 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) {
+         // convert Type 2 edge hints (-20 or -21) to Type 1 ghost hints
+         if (ops[k+1].num < 0) {
+           d += ops[k].num + ops[k+1].num;
+           dFP |= ops[k].isFP | ops[k+1].isFP;
+           cvtNum(d, dFP, charBuf);
+           cvtNum(-ops[k+1].num, ops[k+1].isFP, charBuf);
+         } else {
+           d += ops[k].num;
+           dFP |= ops[k].isFP;
+           cvtNum(d, dFP, charBuf);
+           cvtNum(ops[k+1].num, ops[k+1].isFP, charBuf);
+           d += ops[k+1].num;
+           dFP |= ops[k+1].isFP;
+         }
+         charBuf->append((char)3);
+       }
+       nHints += nOps / 2;
+       nOps = 0;
+       break;
+      case 0x0004:             // vmoveto
+       if (firstOp) {
+         cvtGlyphWidth(nOps == 2, charBuf, pDict);
+         firstOp = gFalse;
+       }
+       if (openPath) {
+         charBuf->append((char)9);
+         openPath = gFalse;
+       }
+       if (nOps != 1) {
+         //~ error(-1, "Wrong number of args (%d) to Type 2 vmoveto", nOps);
+       }
+       cvtNum(ops[0].num, ops[0].isFP, charBuf);
+       charBuf->append((char)4);
+       nOps = 0;
+       break;
+      case 0x0005:             // 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) {
+         cvtNum(ops[k].num, ops[k].isFP, charBuf);
+         cvtNum(ops[k+1].num, ops[k+1].isFP, charBuf);
+         charBuf->append((char)5);
+       }
+       nOps = 0;
+       openPath = gTrue;
+       break;
+      case 0x0006:             // hlineto
+       if (nOps < 1) {
+         //~ error(-1, "Wrong number of args (%d) to Type 2 hlineto", nOps);
+       }
+       for (k = 0; k < nOps; ++k) {
+         cvtNum(ops[k].num, ops[k].isFP, charBuf);
+         charBuf->append((char)((k & 1) ? 7 : 6));
+       }
+       nOps = 0;
+       openPath = gTrue;
+       break;
+      case 0x0007:             // vlineto
+       if (nOps < 1) {
+         //~ error(-1, "Wrong number of args (%d) to Type 2 vlineto", nOps);
+       }
+       for (k = 0; k < nOps; ++k) {
+         cvtNum(ops[k].num, ops[k].isFP, charBuf);
+         charBuf->append((char)((k & 1) ? 6 : 7));
+       }
+       nOps = 0;
+       openPath = gTrue;
+       break;
+      case 0x0008:             // 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) {
+         cvtNum(ops[k].num, ops[k].isFP, charBuf);
+         cvtNum(ops[k+1].num, ops[k+1].isFP, charBuf);
+         cvtNum(ops[k+2].num, ops[k+2].isFP, charBuf);
+         cvtNum(ops[k+3].num, ops[k+3].isFP, charBuf);
+         cvtNum(ops[k+4].num, ops[k+4].isFP, charBuf);
+         cvtNum(ops[k+5].num, ops[k+5].isFP, charBuf);
+         charBuf->append((char)8);
+       }
+       nOps = 0;
+       openPath = gTrue;
+       break;
+      case 0x000a:             // callsubr
+       if (nOps >= 1) {
+         subrBias = (subrIdx->len < 1240)
+                      ? 107 : (subrIdx->len < 33900) ? 1131 : 32768;
+         k = subrBias + (int)ops[nOps - 1].num;
+         --nOps;
+         ok = gTrue;
+         getIndexVal(subrIdx, k, &val, &ok);
+         if (ok) {
+           cvtGlyph(val.pos, val.len, charBuf, subrIdx, pDict, gFalse);
+         }
+       } else {
+         //~ error(-1, "Too few args to Type 2 callsubr");
+       }
+       // don't clear the stack
+       break;
+      case 0x000b:             // return
+       // don't clear the stack
+       break;
+      case 0x000e:             // endchar / seac
+       if (firstOp) {
+         cvtGlyphWidth(nOps == 1 || nOps == 5, charBuf, pDict);
+         firstOp = gFalse;
+       }
+       if (openPath) {
+         charBuf->append((char)9);
+         openPath = gFalse;
+       }
+       if (nOps == 4) {
+         cvtNum(0, gFalse, charBuf);
+         cvtNum(ops[0].num, ops[0].isFP, charBuf);
+         cvtNum(ops[1].num, ops[1].isFP, charBuf);
+         cvtNum(ops[2].num, ops[2].isFP, charBuf);
+         cvtNum(ops[3].num, ops[3].isFP, charBuf);
+         charBuf->append((char)12)->append((char)6);
+       } else if (nOps == 0) {
+         charBuf->append((char)14);
+       } else {
+         //~ error(-1, "Wrong number of args (%d) to Type 2 endchar", nOps);
+       }
+       nOps = 0;
+       break;
+      case 0x000f:             // (obsolete)
+       // this op is ignored, but we need the glyph width
+       if (firstOp) {
+         cvtGlyphWidth(nOps > 0, charBuf, pDict);
+         firstOp = gFalse;
+       }
+       nOps = 0;
+       break;
+      case 0x0010:             // blend
+       //~ error(-1, "Unimplemented Type 2 charstring op: %d", file[i]);
+       nOps = 0;
+       break;
+      case 0x0012:             // hstemhm
+       // ignored
+       if (firstOp) {
+         cvtGlyphWidth(nOps & 1, charBuf, pDict);
+         firstOp = gFalse;
+       }
+       if (nOps & 1) {
+         //~ error(-1, "Wrong number of args (%d) to Type 2 hstemhm", nOps);
+       }
+       nHints += nOps / 2;
+       nOps = 0;
+       break;
+      case 0x0013:             // hintmask
+       // ignored
+       if (firstOp) {
+         cvtGlyphWidth(nOps & 1, charBuf, pDict);
+         firstOp = gFalse;
+       }
+       if (nOps > 0) {
+         if (nOps & 1) {
+           //~ error(-1, "Wrong number of args (%d) to Type 2 hintmask/vstemhm",
+           //~       nOps);
+         }
+         nHints += nOps / 2;
+       }
+       pos += (nHints + 7) >> 3;
+       nOps = 0;
+       break;
+      case 0x0014:             // cntrmask
+       // ignored
+       if (firstOp) {
+         cvtGlyphWidth(nOps & 1, charBuf, pDict);
+         firstOp = gFalse;
+       }
+       if (nOps > 0) {
+         if (nOps & 1) {
+           //~ error(-1, "Wrong number of args (%d) to Type 2 cntrmask/vstemhm",
+           //~       nOps);
+         }
+         nHints += nOps / 2;
+       }
+       pos += (nHints + 7) >> 3;
+       nOps = 0;
+       break;
+      case 0x0015:             // rmoveto
+       if (firstOp) {
+         cvtGlyphWidth(nOps == 3, charBuf, pDict);
+         firstOp = gFalse;
+       }
+       if (openPath) {
+         charBuf->append((char)9);
+         openPath = gFalse;
+       }
+       if (nOps != 2) {
+         //~ error(-1, "Wrong number of args (%d) to Type 2 rmoveto", nOps);
+       }
+       cvtNum(ops[0].num, ops[0].isFP, charBuf);
+       cvtNum(ops[1].num, ops[1].isFP, charBuf);
+       charBuf->append((char)21);
+       nOps = 0;
+       break;
+      case 0x0016:             // hmoveto
+       if (firstOp) {
+         cvtGlyphWidth(nOps == 2, charBuf, pDict);
+         firstOp = gFalse;
+       }
+       if (openPath) {
+         charBuf->append((char)9);
+         openPath = gFalse;
+       }
+       if (nOps != 1) {
+         //~ error(-1, "Wrong number of args (%d) to Type 2 hmoveto", nOps);
+       }
+       cvtNum(ops[0].num, ops[0].isFP, charBuf);
+       charBuf->append((char)22);
+       nOps = 0;
+       break;
+      case 0x0017:             // vstemhm
+       // ignored
+       if (firstOp) {
+         cvtGlyphWidth(nOps & 1, charBuf, pDict);
+         firstOp = gFalse;
+       }
+       if (nOps & 1) {
+         //~ error(-1, "Wrong number of args (%d) to Type 2 vstemhm", nOps);
+       }
+       nHints += nOps / 2;
+       nOps = 0;
+       break;
+      case 0x0018:             // 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) {
+         cvtNum(ops[k].num, ops[k].isFP, charBuf);
+         cvtNum(ops[k+1].num, ops[k+1].isFP, charBuf);
+         cvtNum(ops[k+2].num, ops[k+2].isFP, charBuf);
+         cvtNum(ops[k+3].num, ops[k+3].isFP, charBuf);
+         cvtNum(ops[k+4].num, ops[k+4].isFP, charBuf);
+         cvtNum(ops[k+5].num, ops[k+5].isFP, charBuf);
+         charBuf->append((char)8);
+       }
+       cvtNum(ops[k].num, ops[k].isFP, charBuf);
+       cvtNum(ops[k+1].num, ops[k].isFP, charBuf);
+       charBuf->append((char)5);
+       nOps = 0;
+       openPath = gTrue;
+       break;
+      case 0x0019:             // rlinecurve
+       if (nOps < 8 || (nOps - 6) % 2 != 0) {
+         //~ error(-1, "Wrong number of args (%d) to Type 2 rlinecurve", nOps);
+       }
+       for (k = 0; k < nOps - 6; k += 2) {
+         cvtNum(ops[k].num, ops[k].isFP, charBuf);
+         cvtNum(ops[k+1].num, ops[k].isFP, charBuf);
+         charBuf->append((char)5);
+       }
+       cvtNum(ops[k].num, ops[k].isFP, charBuf);
+       cvtNum(ops[k+1].num, ops[k+1].isFP, charBuf);
+       cvtNum(ops[k+2].num, ops[k+2].isFP, charBuf);
+       cvtNum(ops[k+3].num, ops[k+3].isFP, charBuf);
+       cvtNum(ops[k+4].num, ops[k+4].isFP, charBuf);
+       cvtNum(ops[k+5].num, ops[k+5].isFP, charBuf);
+       charBuf->append((char)8);
+       nOps = 0;
+       openPath = gTrue;
+       break;
+      case 0x001a:             // vvcurveto
+       if (nOps < 4 || !(nOps % 4 == 0 || (nOps-1) % 4 == 0)) {
+         //~ error(-1, "Wrong number of args (%d) to Type 2 vvcurveto", nOps);
+       }
+       if (nOps % 2 == 1) {
+         cvtNum(ops[0].num, ops[0].isFP, charBuf);
+         cvtNum(ops[1].num, ops[1].isFP, charBuf);
+         cvtNum(ops[2].num, ops[2].isFP, charBuf);
+         cvtNum(ops[3].num, ops[3].isFP, charBuf);
+         cvtNum(0, gFalse, charBuf);
+         cvtNum(ops[4].num, ops[4].isFP, charBuf);
+         charBuf->append((char)8);
+         k = 5;
+       } else {
+         k = 0;
+       }
+       for (; k < nOps; k += 4) {
+         cvtNum(0, gFalse, charBuf);
+         cvtNum(ops[k].num, ops[k].isFP, charBuf);
+         cvtNum(ops[k+1].num, ops[k+1].isFP, charBuf);
+         cvtNum(ops[k+2].num, ops[k+2].isFP, charBuf);
+         cvtNum(0, gFalse, charBuf);
+         cvtNum(ops[k+3].num, ops[k+3].isFP, charBuf);
+         charBuf->append((char)8);
+       }
+       nOps = 0;
+       openPath = gTrue;
+       break;
+      case 0x001b:             // 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) {
+         cvtNum(ops[1].num, ops[1].isFP, charBuf);
+         cvtNum(ops[0].num, ops[0].isFP, charBuf);
+         cvtNum(ops[2].num, ops[2].isFP, charBuf);
+         cvtNum(ops[3].num, ops[3].isFP, charBuf);
+         cvtNum(ops[4].num, ops[4].isFP, charBuf);
+         cvtNum(0, gFalse, charBuf);
+         charBuf->append((char)8);
+         k = 5;
+       } else {
+         k = 0;
+       }
+       for (; k < nOps; k += 4) {
+         cvtNum(ops[k].num, ops[k].isFP, charBuf);
+         cvtNum(0, gFalse, charBuf);
+         cvtNum(ops[k+1].num, ops[k+1].isFP, charBuf);
+         cvtNum(ops[k+2].num, ops[k+2].isFP, charBuf);
+         cvtNum(ops[k+3].num, ops[k+3].isFP, charBuf);
+         cvtNum(0, gFalse, charBuf);
+         charBuf->append((char)8);
+       }
+       nOps = 0;
+       openPath = gTrue;
+       break;
+      case 0x001d:             // callgsubr
+       if (nOps >= 1) {
+         k = gsubrBias + (int)ops[nOps - 1].num;
+         --nOps;
+         ok = gTrue;
+         getIndexVal(&gsubrIdx, k, &val, &ok);
+         if (ok) {
+           cvtGlyph(val.pos, val.len, charBuf, subrIdx, pDict, gFalse);
+         }
+       } else {
+         //~ error(-1, "Too few args to Type 2 callgsubr");
+       }
+       // don't clear the stack
+       break;
+      case 0x001e:             // 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) {
+           cvtNum(ops[k].num, ops[k].isFP, charBuf);
+           cvtNum(ops[k+1].num, ops[k+1].isFP, charBuf);
+           cvtNum(ops[k+2].num, ops[k+2].isFP, charBuf);
+           cvtNum(ops[k+3].num, ops[k+3].isFP, charBuf);
+           charBuf->append((char)30);
+         } else {
+           cvtNum(ops[k].num, ops[k].isFP, charBuf);
+           cvtNum(ops[k+1].num, ops[k+1].isFP, charBuf);
+           cvtNum(ops[k+2].num, ops[k+2].isFP, charBuf);
+           cvtNum(ops[k+3].num, ops[k+3].isFP, charBuf);
+           charBuf->append((char)31);
+         }
+       }
+       if (k == nOps-5) {
+         if (k % 8 == 0) {
+           cvtNum(0, gFalse, charBuf);
+           cvtNum(ops[k].num, ops[k].isFP, charBuf);
+           cvtNum(ops[k+1].num, ops[k+1].isFP, charBuf);
+           cvtNum(ops[k+2].num, ops[k+2].isFP, charBuf);
+           cvtNum(ops[k+3].num, ops[k+3].isFP, charBuf);
+           cvtNum(ops[k+4].num, ops[k+4].isFP, charBuf);
+         } else {
+           cvtNum(ops[k].num, ops[k].isFP, charBuf);
+           cvtNum(0, gFalse, charBuf);
+           cvtNum(ops[k+1].num, ops[k+1].isFP, charBuf);
+           cvtNum(ops[k+2].num, ops[k+2].isFP, charBuf);
+           cvtNum(ops[k+4].num, ops[k+4].isFP, charBuf);
+           cvtNum(ops[k+3].num, ops[k+3].isFP, charBuf);
+         }
+         charBuf->append((char)8);
+       }
+       nOps = 0;
+       openPath = gTrue;
+       break;
+      case 0x001f:             // 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) {
+           cvtNum(ops[k].num, ops[k].isFP, charBuf);
+           cvtNum(ops[k+1].num, ops[k+1].isFP, charBuf);
+           cvtNum(ops[k+2].num, ops[k+2].isFP, charBuf);
+           cvtNum(ops[k+3].num, ops[k+3].isFP, charBuf);
+           charBuf->append((char)31);
+         } else {
+           cvtNum(ops[k].num, ops[k].isFP, charBuf);
+           cvtNum(ops[k+1].num, ops[k+1].isFP, charBuf);
+           cvtNum(ops[k+2].num, ops[k+2].isFP, charBuf);
+           cvtNum(ops[k+3].num, ops[k+3].isFP, charBuf);
+           charBuf->append((char)30);
+         }
+       }
+       if (k == nOps-5) {
+         if (k % 8 == 0) {
+           cvtNum(ops[k].num, ops[k].isFP, charBuf);
+           cvtNum(0, gFalse, charBuf);
+           cvtNum(ops[k+1].num, ops[k+1].isFP, charBuf);
+           cvtNum(ops[k+2].num, ops[k+2].isFP, charBuf);
+           cvtNum(ops[k+4].num, ops[k+4].isFP, charBuf);
+           cvtNum(ops[k+3].num, ops[k+3].isFP, charBuf);
+         } else {
+           cvtNum(0, gFalse, charBuf);
+           cvtNum(ops[k].num, ops[k].isFP, charBuf);
+           cvtNum(ops[k+1].num, ops[k+1].isFP, charBuf);
+           cvtNum(ops[k+2].num, ops[k+2].isFP, charBuf);
+           cvtNum(ops[k+3].num, ops[k+3].isFP, charBuf);
+           cvtNum(ops[k+4].num, ops[k+4].isFP, charBuf);
+         }
+         charBuf->append((char)8);
+       }
+       nOps = 0;
+       openPath = gTrue;
+       break;
+      case 0x0c00:             // dotsection (should be Type 1 only?)
+       // ignored
+       nOps = 0;
+       break;
+      case 0x0c03:             // and
+      case 0x0c04:             // or
+      case 0x0c05:             // not
+      case 0x0c08:             // store
+      case 0x0c09:             // abs
+      case 0x0c0a:             // add
+      case 0x0c0b:             // sub
+      case 0x0c0c:             // div
+      case 0x0c0d:             // load
+      case 0x0c0e:             // neg
+      case 0x0c0f:             // eq
+      case 0x0c12:             // drop
+      case 0x0c14:             // put
+      case 0x0c15:             // get
+      case 0x0c16:             // ifelse
+      case 0x0c17:             // random
+      case 0x0c18:             // mul
+      case 0x0c1a:             // sqrt
+      case 0x0c1b:             // dup
+      case 0x0c1c:             // exch
+      case 0x0c1d:             // index
+      case 0x0c1e:             // roll
+       //~ error(-1, "Unimplemented Type 2 charstring op: 12.%d", file[i+1]);
+       nOps = 0;
+       break;
+      case 0x0c22:             // hflex
+       if (nOps != 7) {
+         //~ error(-1, "Wrong number of args (%d) to Type 2 hflex", nOps);
+       }
+       cvtNum(ops[0].num, ops[0].isFP, charBuf);
+       cvtNum(0, gFalse, charBuf);
+       cvtNum(ops[1].num, ops[1].isFP, charBuf);
+       cvtNum(ops[2].num, ops[2].isFP, charBuf);
+       cvtNum(ops[3].num, ops[3].isFP, charBuf);
+       cvtNum(0, gFalse, charBuf);
+       charBuf->append((char)8);
+       cvtNum(ops[4].num, ops[4].isFP, charBuf);
+       cvtNum(0, gFalse, charBuf);
+       cvtNum(ops[5].num, ops[5].isFP, charBuf);
+       cvtNum(-ops[2].num, ops[2].isFP, charBuf);
+       cvtNum(ops[6].num, ops[6].isFP, charBuf);
+       cvtNum(0, gFalse, charBuf);
+       charBuf->append((char)8);
+       nOps = 0;
+       openPath = gTrue;
+       break;
+      case 0x0c23:             // flex
+       if (nOps != 13) {
+         //~ error(-1, "Wrong number of args (%d) to Type 2 flex", nOps);
+       }
+       cvtNum(ops[0].num, ops[0].isFP, charBuf);
+       cvtNum(ops[1].num, ops[1].isFP, charBuf);
+       cvtNum(ops[2].num, ops[2].isFP, charBuf);
+       cvtNum(ops[3].num, ops[3].isFP, charBuf);
+       cvtNum(ops[4].num, ops[4].isFP, charBuf);
+       cvtNum(ops[5].num, ops[5].isFP, charBuf);
+       charBuf->append((char)8);
+       cvtNum(ops[6].num, ops[6].isFP, charBuf);
+       cvtNum(ops[7].num, ops[7].isFP, charBuf);
+       cvtNum(ops[8].num, ops[8].isFP, charBuf);
+       cvtNum(ops[9].num, ops[9].isFP, charBuf);
+       cvtNum(ops[10].num, ops[10].isFP, charBuf);
+       cvtNum(ops[11].num, ops[11].isFP, charBuf);
+       charBuf->append((char)8);
+       nOps = 0;
+       openPath = gTrue;
+       break;
+      case 0x0c24:             // hflex1
+       if (nOps != 9) {
+         //~ error(-1, "Wrong number of args (%d) to Type 2 hflex1", nOps);
+       }
+       cvtNum(ops[0].num, ops[0].isFP, charBuf);
+       cvtNum(ops[1].num, ops[1].isFP, charBuf);
+       cvtNum(ops[2].num, ops[2].isFP, charBuf);
+       cvtNum(ops[3].num, ops[3].isFP, charBuf);
+       cvtNum(ops[4].num, ops[4].isFP, charBuf);
+       cvtNum(0, gFalse, charBuf);
+       charBuf->append((char)8);
+       cvtNum(ops[5].num, ops[5].isFP, charBuf);
+       cvtNum(0, gFalse, charBuf);
+       cvtNum(ops[6].num, ops[6].isFP, charBuf);
+       cvtNum(ops[7].num, ops[7].isFP, charBuf);
+       cvtNum(ops[8].num, ops[8].isFP, charBuf);
+       cvtNum(-(ops[1].num + ops[3].num + ops[7].num),
+              ops[1].isFP | ops[3].isFP | ops[7].isFP, charBuf);
+       charBuf->append((char)8);
+       nOps = 0;
+       openPath = gTrue;
+       break;
+      case 0x0c25:             // flex1
+       if (nOps != 11) {
+         //~ error(-1, "Wrong number of args (%d) to Type 2 flex1", nOps);
+       }
+       cvtNum(ops[0].num, ops[0].isFP, charBuf);
+       cvtNum(ops[1].num, ops[1].isFP, charBuf);
+       cvtNum(ops[2].num, ops[2].isFP, charBuf);
+       cvtNum(ops[3].num, ops[3].isFP, charBuf);
+       cvtNum(ops[4].num, ops[4].isFP, charBuf);
+       cvtNum(ops[5].num, ops[5].isFP, charBuf);
+       charBuf->append((char)8);
+       cvtNum(ops[6].num, ops[6].isFP, charBuf);
+       cvtNum(ops[7].num, ops[7].isFP, charBuf);
+       cvtNum(ops[8].num, ops[8].isFP, charBuf);
+       cvtNum(ops[9].num, ops[9].isFP, charBuf);
+       dx = ops[0].num + ops[2].num + ops[4].num + ops[6].num + ops[8].num;
+       dy = ops[1].num + ops[3].num + ops[5].num + ops[7].num + ops[9].num;
+       if (fabs(dx) > fabs(dy)) {
+         cvtNum(ops[10].num, ops[10].isFP, charBuf);
+         cvtNum(-dy, ops[1].isFP | ops[3].isFP | ops[5].isFP |
+                     ops[7].isFP | ops[9].isFP, charBuf);
+       } else {
+         cvtNum(-dx, ops[0].isFP | ops[2].isFP | ops[4].isFP |
+                     ops[6].isFP | ops[8].isFP, charBuf);
+         cvtNum(ops[10].num, ops[10].isFP, charBuf);
+       }
+       charBuf->append((char)8);
+       nOps = 0;
+       openPath = gTrue;
+       break;
+      default:
+       //~ error(-1, "Illegal Type 2 charstring op: %04x",
+       //~       ops[nOps].op);
+       nOps = 0;
+       break;
+      }
+    }
+  }
+
+  // charstring encryption
+  if (top) {
+    r2 = 4330;
+    for (i = start; i < charBuf->getLength(); ++i) {
+      byte = charBuf->getChar(i) ^ (r2 >> 8);
+      charBuf->setChar(i, byte);
+      r2 = (byte + r2) * 52845 + 22719;
+    }
+  }
+}
+
+void FoFiType1C::cvtGlyphWidth(GBool useOp, GString *charBuf,
+                              Type1CPrivateDict *pDict) {
+  double w;
+  GBool wFP;
+  int i;
+
+  if (useOp) {
+    w = pDict->nominalWidthX + ops[0].num;
+    wFP = pDict->nominalWidthXFP | ops[0].isFP;
+    for (i = 1; i < nOps; ++i) {
+      ops[i-1] = ops[i];
+    }
+    --nOps;
+  } else {
+    w = pDict->defaultWidthX;
+    wFP = pDict->defaultWidthXFP;
+  }
+  cvtNum(0, gFalse, charBuf);
+  cvtNum(w, wFP, charBuf);
+  charBuf->append((char)13);
+}
+
+void FoFiType1C::cvtNum(double x, GBool isFP, GString *charBuf) {
+  Guchar buf[12];
+  int y, n;
+
+  n = 0;
+  if (isFP) {
+    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 FoFiType1C::eexecWrite(Type1CEexecBuf *eb, char *s) {
+  Guchar *p;
+  Guchar x;
+
+  for (p = (Guchar *)s; *p; ++p) {
+    x = *p ^ (eb->r1 >> 8);
+    eb->r1 = (x + eb->r1) * 52845 + 22719;
+    if (eb->ascii) {
+      (*eb->outputFunc)(eb->outputStream, &hexChars[x >> 4], 1);
+      (*eb->outputFunc)(eb->outputStream, &hexChars[x & 0x0f], 1);
+      eb->line += 2;
+      if (eb->line == 64) {
+       (*eb->outputFunc)(eb->outputStream, "\n", 1);
+       eb->line = 0;
+      }
+    } else {
+      (*eb->outputFunc)(eb->outputStream, (char *)&x, 1);
+    }
+  }
+}
+
+void FoFiType1C::eexecWriteCharstring(Type1CEexecBuf *eb,
+                                     Guchar *s, int n) {
+  Guchar x;
+  int i;
+
+  // eexec encryption
+  for (i = 0; i < n; ++i) {
+    x = s[i] ^ (eb->r1 >> 8);
+    eb->r1 = (x + eb->r1) * 52845 + 22719;
+    if (eb->ascii) {
+      (*eb->outputFunc)(eb->outputStream, &hexChars[x >> 4], 1);
+      (*eb->outputFunc)(eb->outputStream, &hexChars[x & 0x0f], 1);
+      eb->line += 2;
+      if (eb->line == 64) {
+       (*eb->outputFunc)(eb->outputStream, "\n", 1);
+       eb->line = 0;
+      }
+    } else {
+      (*eb->outputFunc)(eb->outputStream, (char *)&x, 1);
+    }
+  }
+}
+
+GBool FoFiType1C::parse() {
+  Type1CIndex fdIdx;
+  Type1CIndexVal val;
+  int i;
+
+  parsedOk = gTrue;
+
+  // some tools embed Type 1C fonts with an extra whitespace char at
+  // the beginning
+  if (len > 0 && file[0] != '\x01') {
+    ++file;
+    --len;
+  }
+
+  // find the indexes
+  getIndex(getU8(2, &parsedOk), &nameIdx, &parsedOk);
+  getIndex(nameIdx.endPos, &topDictIdx, &parsedOk);
+  getIndex(topDictIdx.endPos, &stringIdx, &parsedOk);
+  getIndex(stringIdx.endPos, &gsubrIdx, &parsedOk);
+  if (!parsedOk) {
+    return gFalse;
+  }
+  gsubrBias = (gsubrIdx.len < 1240) ? 107
+                                    : (gsubrIdx.len < 33900) ? 1131 : 32768;
+
+  // read the first font name
+  getIndexVal(&nameIdx, 0, &val, &parsedOk);
+  if (!parsedOk) {
+    return gFalse;
+  }
+  name = new GString((char *)&file[val.pos], val.len);
+
+  // read the top dict for the first font
+  readTopDict();
+
+  // for CID fonts: read the FDArray dicts and private dicts
+  if (topDict.firstOp == 0x0c1e) {
+    if (topDict.fdArrayOffset == 0) {
+      nFDs = 1;
+      privateDicts = (Type1CPrivateDict *)gmalloc(sizeof(Type1CPrivateDict));
+      readPrivateDict(0, 0, &privateDicts[0]);
+    } else {
+      getIndex(topDict.fdArrayOffset, &fdIdx, &parsedOk);
+      if (!parsedOk) {
+       return gFalse;
+      }
+      nFDs = fdIdx.len;
+      privateDicts = (Type1CPrivateDict *)
+                        gmallocn(nFDs, sizeof(Type1CPrivateDict));
+      for (i = 0; i < nFDs; ++i) {
+       getIndexVal(&fdIdx, i, &val, &parsedOk);
+       if (!parsedOk) {
+         return gFalse;
+       }
+       readFD(val.pos, val.len, &privateDicts[i]);
+      }
+    }
+
+  // for 8-bit fonts: read the private dict
+  } else {
+    privateDicts = (Type1CPrivateDict *)gmalloc(sizeof(Type1CPrivateDict));
+    readPrivateDict(topDict.privateOffset, topDict.privateSize,
+                   &privateDicts[0]);
+  }
+
+  // check for parse errors in the private dict(s)
+  if (!parsedOk) {
+    return gFalse;
+  }
+
+  // get the charstrings index
+  if (topDict.charStringsOffset <= 0) {
+    parsedOk = gFalse;
+    return gFalse;
+  }
+  getIndex(topDict.charStringsOffset, &charStringsIdx, &parsedOk);
+  if (!parsedOk) {
+    return gFalse;
+  }
+  nGlyphs = charStringsIdx.len;
+
+  // for CID fonts: read the FDSelect table
+  if (topDict.firstOp == 0x0c1e) {
+    readFDSelect();
+    if (!parsedOk) {
+      return gFalse;
+    }
+  }
+
+  // read the charset
+  if (!readCharset()) {
+    parsedOk = gFalse;
+    return gFalse;
+  }
+
+  // for 8-bit fonts: build the encoding
+  if (topDict.firstOp != 0x0c14 && topDict.firstOp != 0x0c1e) {
+    buildEncoding();
+    if (!parsedOk) {
+      return gFalse;
+    }
+  }
+
+  return parsedOk;
+}
+
+void FoFiType1C::readTopDict() {
+  Type1CIndexVal topDictPtr;
+  int pos;
+
+  topDict.firstOp = -1;
+  topDict.versionSID = 0;
+  topDict.noticeSID = 0;
+  topDict.copyrightSID = 0;
+  topDict.fullNameSID = 0;
+  topDict.familyNameSID = 0;
+  topDict.weightSID = 0;
+  topDict.isFixedPitch = 0;
+  topDict.italicAngle = 0;
+  topDict.underlinePosition = -100;
+  topDict.underlineThickness = 50;
+  topDict.paintType = 0;
+  topDict.charstringType = 2;
+  topDict.fontMatrix[0] = 0.001;
+  topDict.fontMatrix[1] = 0;
+  topDict.fontMatrix[2] = 0;
+  topDict.fontMatrix[3] = 0.001;
+  topDict.fontMatrix[4] = 0;
+  topDict.fontMatrix[5] = 0;
+  topDict.hasFontMatrix = gFalse;
+  topDict.uniqueID = 0;
+  topDict.fontBBox[0] = 0;
+  topDict.fontBBox[1] = 0;
+  topDict.fontBBox[2] = 0;
+  topDict.fontBBox[3] = 0;
+  topDict.strokeWidth = 0;
+  topDict.charsetOffset = 0;
+  topDict.encodingOffset = 0;
+  topDict.charStringsOffset = 0;
+  topDict.privateSize = 0;
+  topDict.privateOffset = 0;
+  topDict.registrySID = 0;
+  topDict.orderingSID = 0;
+  topDict.supplement = 0;
+  topDict.fdArrayOffset = 0;
+  topDict.fdSelectOffset = 0;
+
+  getIndexVal(&topDictIdx, 0, &topDictPtr, &parsedOk);
+  pos = topDictPtr.pos;
+  nOps = 0;
+  while (pos < topDictPtr.pos + topDictPtr.len) {
+    pos = getOp(pos, gFalse, &parsedOk);
+    if (!parsedOk) {
+      break;
+    }
+    if (!ops[nOps - 1].isNum) {
+      --nOps; // drop the operator
+      if (topDict.firstOp < 0) {
+       topDict.firstOp = ops[nOps].op;
+      }
+      switch (ops[nOps].op) {
+      case 0x0000: topDict.versionSID = (int)ops[0].num; break;
+      case 0x0001: topDict.noticeSID = (int)ops[0].num; break;
+      case 0x0c00: topDict.copyrightSID = (int)ops[0].num; break;
+      case 0x0002: topDict.fullNameSID = (int)ops[0].num; break;
+      case 0x0003: topDict.familyNameSID = (int)ops[0].num; break;
+      case 0x0004: topDict.weightSID = (int)ops[0].num; break;
+      case 0x0c01: topDict.isFixedPitch = (int)ops[0].num; break;
+      case 0x0c02: topDict.italicAngle = ops[0].num; break;
+      case 0x0c03: topDict.underlinePosition = ops[0].num; break;
+      case 0x0c04: topDict.underlineThickness = ops[0].num; break;
+      case 0x0c05: topDict.paintType = (int)ops[0].num; break;
+      case 0x0c06: topDict.charstringType = (int)ops[0].num; break;
+      case 0x0c07: topDict.fontMatrix[0] = ops[0].num;
+                  topDict.fontMatrix[1] = ops[1].num;
+                  topDict.fontMatrix[2] = ops[2].num;
+                  topDict.fontMatrix[3] = ops[3].num;
+                  topDict.fontMatrix[4] = ops[4].num;
+                  topDict.fontMatrix[5] = ops[5].num;
+                  topDict.hasFontMatrix = gTrue; break;
+      case 0x000d: topDict.uniqueID = (int)ops[0].num; break;
+      case 0x0005: topDict.fontBBox[0] = ops[0].num;
+                  topDict.fontBBox[1] = ops[1].num;
+                  topDict.fontBBox[2] = ops[2].num;
+                  topDict.fontBBox[3] = ops[3].num; break;
+      case 0x0c08: topDict.strokeWidth = ops[0].num; break;
+      case 0x000f: topDict.charsetOffset = (int)ops[0].num; break;
+      case 0x0010: topDict.encodingOffset = (int)ops[0].num; break;
+      case 0x0011: topDict.charStringsOffset = (int)ops[0].num; break;
+      case 0x0012: topDict.privateSize = (int)ops[0].num;
+                  topDict.privateOffset = (int)ops[1].num; break;
+      case 0x0c1e: topDict.registrySID = (int)ops[0].num;
+                  topDict.orderingSID = (int)ops[1].num;
+                  topDict.supplement = (int)ops[2].num; break;
+      case 0x0c24: topDict.fdArrayOffset = (int)ops[0].num; break;
+      case 0x0c25: topDict.fdSelectOffset = (int)ops[0].num; break;
+      }
+      nOps = 0;
+    }
+  }
+}
+
+// Read a CID font dict (FD) - this pulls out the private dict
+// pointer, and reads the private dict.  It also pulls the FontMatrix
+// (if any) out of the FD.
+void FoFiType1C::readFD(int offset, int length, Type1CPrivateDict *pDict) {
+  int pos, pSize, pOffset;
+  double fontMatrix[6];
+  GBool hasFontMatrix;
+
+  hasFontMatrix = gFalse;
+  pSize = pOffset = 0;
+  pos = offset;
+  nOps = 0;
+  while (pos < offset + length) {
+    pos = getOp(pos, gFalse, &parsedOk);
+    if (!parsedOk) {
+      return;
+    }
+    if (!ops[nOps - 1].isNum) {
+      if (ops[nOps - 1].op == 0x0012) {
+       if (nOps < 3) {
+         parsedOk = gFalse;
+         return;
+       }
+       pSize = (int)ops[0].num;
+       pOffset = (int)ops[1].num;
+       break;
+      } else if (ops[nOps - 1].op == 0x0c07) {
+       fontMatrix[0] = ops[0].num;
+       fontMatrix[1] = ops[1].num;
+       fontMatrix[2] = ops[2].num;
+       fontMatrix[3] = ops[3].num;
+       fontMatrix[4] = ops[4].num;
+       fontMatrix[5] = ops[5].num;
+       hasFontMatrix = gTrue;
+      }
+      nOps = 0;
+    }
+  }
+  readPrivateDict(pOffset, pSize, pDict);
+  if (hasFontMatrix) {
+    pDict->fontMatrix[0] = fontMatrix[0];
+    pDict->fontMatrix[1] = fontMatrix[1];
+    pDict->fontMatrix[2] = fontMatrix[2];
+    pDict->fontMatrix[3] = fontMatrix[3];
+    pDict->fontMatrix[4] = fontMatrix[4];
+    pDict->fontMatrix[5] = fontMatrix[5];
+    pDict->hasFontMatrix = gTrue;
+  }
+}
+
+void FoFiType1C::readPrivateDict(int offset, int length,
+                                Type1CPrivateDict *pDict) {
+  int pos;
+
+  pDict->hasFontMatrix = gFalse;
+  pDict->nBlueValues = 0;
+  pDict->nOtherBlues = 0;
+  pDict->nFamilyBlues = 0;
+  pDict->nFamilyOtherBlues = 0;
+  pDict->blueScale = 0.039625;
+  pDict->blueShift = 7;
+  pDict->blueFuzz = 1;
+  pDict->hasStdHW = gFalse;
+  pDict->hasStdVW = gFalse;
+  pDict->nStemSnapH = 0;
+  pDict->nStemSnapV = 0;
+  pDict->hasForceBold = gFalse;
+  pDict->forceBoldThreshold = 0;
+  pDict->languageGroup = 0;
+  pDict->expansionFactor = 0.06;
+  pDict->initialRandomSeed = 0;
+  pDict->subrsOffset = 0;
+  pDict->defaultWidthX = 0;
+  pDict->defaultWidthXFP = gFalse;
+  pDict->nominalWidthX = 0;
+  pDict->nominalWidthXFP = gFalse;
+
+  // no dictionary
+  if (offset == 0 || length == 0) {
+    return;
+  }
+
+  pos = offset;
+  nOps = 0;
+  while (pos < offset + length) {
+    pos = getOp(pos, gFalse, &parsedOk);
+    if (!parsedOk) {
+      break;
+    }
+    if (!ops[nOps - 1].isNum) {
+      --nOps; // drop the operator
+      switch (ops[nOps].op) {
+      case 0x0006:
+       pDict->nBlueValues = getDeltaIntArray(pDict->blueValues,
+                                             type1CMaxBlueValues);
+       break;
+      case 0x0007:
+       pDict->nOtherBlues = getDeltaIntArray(pDict->otherBlues,
+                                             type1CMaxOtherBlues);
+       break;
+      case 0x0008:
+       pDict->nFamilyBlues = getDeltaIntArray(pDict->familyBlues,
+                                              type1CMaxBlueValues);
+       break;
+      case 0x0009:
+       pDict->nFamilyOtherBlues = getDeltaIntArray(pDict->familyOtherBlues,
+                                                   type1CMaxOtherBlues);
+       break;
+      case 0x0c09:
+       pDict->blueScale = ops[0].num;
+       break;
+      case 0x0c0a:
+       pDict->blueShift = (int)ops[0].num;
+       break;
+      case 0x0c0b:
+       pDict->blueFuzz = (int)ops[0].num;
+       break;
+      case 0x000a:
+       pDict->stdHW = ops[0].num;
+       pDict->hasStdHW = gTrue;
+       break;
+      case 0x000b:
+       pDict->stdVW = ops[0].num;
+       pDict->hasStdVW = gTrue;
+       break;
+      case 0x0c0c:
+       pDict->nStemSnapH = getDeltaFPArray(pDict->stemSnapH,
+                                           type1CMaxStemSnap);
+       break;
+      case 0x0c0d:
+       pDict->nStemSnapV = getDeltaFPArray(pDict->stemSnapV,
+                                           type1CMaxStemSnap);
+       break;
+      case 0x0c0e:
+       pDict->forceBold = ops[0].num != 0;
+       pDict->hasForceBold = gTrue;
+       break;
+      case 0x0c0f:
+       pDict->forceBoldThreshold = ops[0].num;
+       break;
+      case 0x0c11:
+       pDict->languageGroup = (int)ops[0].num;
+       break;
+      case 0x0c12:
+       pDict->expansionFactor = ops[0].num;
+       break;
+      case 0x0c13:
+       pDict->initialRandomSeed = (int)ops[0].num;
+       break;
+      case 0x0013:
+       pDict->subrsOffset = offset + (int)ops[0].num;
+       break;
+      case 0x0014:
+       pDict->defaultWidthX = ops[0].num;
+       pDict->defaultWidthXFP = ops[0].isFP;
+       break;
+      case 0x0015:
+       pDict->nominalWidthX = ops[0].num;
+       pDict->nominalWidthXFP = ops[0].isFP;
+       break;
+      }
+      nOps = 0;
+    }
+  }
+}
+
+void FoFiType1C::readFDSelect() {
+  int fdSelectFmt, pos, nRanges, gid0, gid1, fd, i, j;
+
+  fdSelect = (Guchar *)gmalloc(nGlyphs);
+  if (topDict.fdSelectOffset == 0) {
+    for (i = 0; i < nGlyphs; ++i) {
+      fdSelect[i] = 0;
+    }
+  } else {
+    pos = topDict.fdSelectOffset;
+    fdSelectFmt = getU8(pos++, &parsedOk);
+    if (!parsedOk) {
+      return;
+    }
+    if (fdSelectFmt == 0) {
+      if (!checkRegion(pos, nGlyphs)) {
+       parsedOk = gFalse;
+       return;
+      }
+      memcpy(fdSelect, file + pos, nGlyphs);
+    } else if (fdSelectFmt == 3) {
+      nRanges = getU16BE(pos, &parsedOk);
+      pos += 2;
+      gid0 = getU16BE(pos, &parsedOk);
+      pos += 2;
+      for (i = 1; i <= nRanges; ++i) {
+       fd = getU8(pos++, &parsedOk);
+       gid1 = getU16BE(pos, &parsedOk);
+       if (!parsedOk) {
+         return;
+       }
+       pos += 2;
+       if (gid0 > gid1 || gid1 > nGlyphs) {
+         //~ error(-1, "Bad FDSelect table in CID font");
+         parsedOk = gFalse;
+         return;
+       }
+       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;
+      }
+    }
+  }
+}
+
+void FoFiType1C::buildEncoding() {
+  char buf[256];
+  int nCodes, nRanges, encFormat;
+  int pos, c, sid, nLeft, nSups, i, j;
+
+  if (topDict.encodingOffset == 0) {
+    encoding = fofiType1StandardEncoding;
+
+  } else if (topDict.encodingOffset == 1) {
+    encoding = fofiType1ExpertEncoding;
+
+  } else {
+    encoding = (char **)gmallocn(256, sizeof(char *));
+    for (i = 0; i < 256; ++i) {
+      encoding[i] = NULL;
+    }
+    pos = topDict.encodingOffset;
+    encFormat = getU8(pos++, &parsedOk);
+    if (!parsedOk) {
+      return;
+    }
+    if ((encFormat & 0x7f) == 0) {
+      nCodes = 1 + getU8(pos++, &parsedOk);
+      if (!parsedOk) {
+       return;
+      }
+      if (nCodes > nGlyphs) {
+       nCodes = nGlyphs;
+      }
+      for (i = 1; i < nCodes; ++i) {
+       c = getU8(pos++, &parsedOk);
+       if (!parsedOk) {
+         return;
+       }
+       if (encoding[c]) {
+         gfree(encoding[c]);
+       }
+       encoding[c] = copyString(getString(charset[i], buf, &parsedOk));
+      }
+    } else if ((encFormat & 0x7f) == 1) {
+      nRanges = getU8(pos++, &parsedOk);
+      if (!parsedOk) {
+       return;
+      }
+      nCodes = 1;
+      for (i = 0; i < nRanges; ++i) {
+       c = getU8(pos++, &parsedOk);
+       nLeft = getU8(pos++, &parsedOk);
+       if (!parsedOk) {
+         return;
+       }
+       for (j = 0; j <= nLeft && nCodes < nGlyphs; ++j) {
+         if (c < 256) {
+           if (encoding[c]) {
+             gfree(encoding[c]);
+           }
+           encoding[c] = copyString(getString(charset[nCodes], buf,
+                                              &parsedOk));
+         }
+         ++nCodes;
+         ++c;
+       }
+      }
+    }
+    if (encFormat & 0x80) {
+      nSups = getU8(pos++, &parsedOk);
+      if (!parsedOk) {
+       return;
+      }
+      for (i = 0; i < nSups; ++i) {
+       c = getU8(pos++, &parsedOk);;
+       if (!parsedOk) {
+         return;;
+       }
+       sid = getU16BE(pos, &parsedOk);
+       pos += 2;
+       if (!parsedOk) {
+         return;
+       }
+       if (encoding[c]) {
+         gfree(encoding[c]);
+       }
+       encoding[c] = copyString(getString(sid, buf, &parsedOk));
+      }
+    }
+  }
+}
+
+GBool FoFiType1C::readCharset() {
+  int charsetFormat, c, pos;
+  int nLeft, i, j;
+
+  if (topDict.charsetOffset == 0) {
+    charset = fofiType1CISOAdobeCharset;
+  } else if (topDict.charsetOffset == 1) {
+    charset = fofiType1CExpertCharset;
+  } else if (topDict.charsetOffset == 2) {
+    charset = fofiType1CExpertSubsetCharset;
+  } else {
+    charset = (Gushort *)gmallocn(nGlyphs, sizeof(Gushort));
+    for (i = 0; i < nGlyphs; ++i) {
+      charset[i] = 0;
+    }
+    pos = topDict.charsetOffset;
+    charsetFormat = getU8(pos++, &parsedOk);
+    if (charsetFormat == 0) {
+      for (i = 1; i < nGlyphs; ++i) {
+       charset[i] = (Gushort)getU16BE(pos, &parsedOk);
+       pos += 2;
+       if (!parsedOk) {
+         break;
+       }
+      }
+    } else if (charsetFormat == 1) {
+      i = 1;
+      while (i < nGlyphs) {
+       c = getU16BE(pos, &parsedOk);
+       pos += 2;
+       nLeft = getU8(pos++, &parsedOk);
+       if (!parsedOk) {
+         break;
+       }
+       for (j = 0; j <= nLeft && i < nGlyphs; ++j) {
+         charset[i++] = (Gushort)c++;
+       }
+      }
+    } else if (charsetFormat == 2) {
+      i = 1;
+      while (i < nGlyphs) {
+       c = getU16BE(pos, &parsedOk);
+       pos += 2;
+       nLeft = getU16BE(pos, &parsedOk);
+       pos += 2;
+       if (!parsedOk) {
+         break;
+       }
+       for (j = 0; j <= nLeft && i < nGlyphs; ++j) {
+         charset[i++] = (Gushort)c++;
+       }
+      }
+    }
+    if (!parsedOk) {
+      gfree(charset);
+      charset = NULL;
+      return gFalse;
+    }
+  }
+  return gTrue;
+}
+
+int FoFiType1C::getOp(int pos, GBool charstring, GBool *ok) {
+  static char nybChars[16] = "0123456789.ee -";
+  Type1COp op;
+  char buf[65];
+  int b0, b1, nyb0, nyb1, x, i;
+
+  b0 = getU8(pos++, ok);
+  op.isNum = gTrue;
+  op.isFP = gFalse;
+
+  if (b0 == 28) {
+    x = getU8(pos++, ok);
+    x = (x << 8) | getU8(pos++, ok);
+    if (x & 0x8000) {
+      x |= ~0xffff;
+    }
+    op.num = x;
+
+  } else if (!charstring && b0 == 29) {
+    x = getU8(pos++, ok);
+    x = (x << 8) | getU8(pos++, ok);
+    x = (x << 8) | getU8(pos++, ok);
+    x = (x << 8) | getU8(pos++, ok);
+    if (x & 0x80000000) {
+      x |= ~0xffffffff;
+    }
+    op.num = x;
+
+  } else if (!charstring && b0 == 30) {
+    i = 0;
+    do {
+      b1 = getU8(pos++, ok);
+      nyb0 = b1 >> 4;
+      nyb1 = b1 & 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';
+    op.num = atof(buf);
+    op.isFP = gTrue;
+
+  } else if (b0 >= 32 && b0 <= 246) {
+    op.num = b0 - 139;
+
+  } else if (b0 >= 247 && b0 <= 250) {
+    op.num = ((b0 - 247) << 8) + getU8(pos++, ok) + 108;
+
+  } else if (b0 >= 251 && b0 <= 254) {
+    op.num = -((b0 - 251) << 8) - getU8(pos++, ok) - 108;
+
+  } else if (charstring && b0 == 255) {
+    x = getU8(pos++, ok);
+    x = (x << 8) | getU8(pos++, ok);
+    x = (x << 8) | getU8(pos++, ok);
+    x = (x << 8) | getU8(pos++, ok);
+    if (x & 0x80000000) {
+      x |= ~0xffffffff;
+    }
+    op.num = (double)x / 65536.0;
+    op.isFP = gTrue;
+
+  } else if (b0 == 12) {
+    op.isNum = gFalse;
+    op.op = 0x0c00 + getU8(pos++, ok);
+
+  } else {
+    op.isNum = gFalse;
+    op.op = b0;
+  }
+
+  if (nOps < 49) {
+    ops[nOps++] = op;
+  }
+
+  return pos;
+}
+
+// Convert the delta-encoded ops array to an array of ints.
+int FoFiType1C::getDeltaIntArray(int *arr, int maxLen) {
+  int x;
+  int n, i;
+
+  if ((n = nOps) > maxLen) {
+    n = maxLen;
+  }
+  x = 0;
+  for (i = 0; i < n; ++i) {
+    x += (int)ops[i].num;
+    arr[i] = x;
+  }
+  return n;
+}
+
+// Convert the delta-encoded ops array to an array of doubles.
+int FoFiType1C::getDeltaFPArray(double *arr, int maxLen) {
+  double x;
+  int n, i;
+
+  if ((n = nOps) > maxLen) {
+    n = maxLen;
+  }
+  x = 0;
+  for (i = 0; i < n; ++i) {
+    x += ops[i].num;
+    arr[i] = x;
+  }
+  return n;
+}
+
+void FoFiType1C::getIndex(int pos, Type1CIndex *idx, GBool *ok) {
+  idx->pos = pos;
+  idx->len = getU16BE(pos, ok);
+  if (idx->len == 0) {
+    // empty indexes are legal and contain just the length field
+    idx->offSize = 0;
+    idx->startPos = idx->endPos = pos + 2;
+  } else {
+    idx->offSize = getU8(pos + 2, ok);
+    if (idx->offSize < 1 || idx->offSize > 4) {
+      *ok = gFalse;
+    }
+    idx->startPos = pos + 3 + (idx->len + 1) * idx->offSize - 1;
+    if (idx->startPos < 0 || idx->startPos >= len) {
+      *ok = gFalse;
+    }
+    idx->endPos = idx->startPos + getUVarBE(pos + 3 + idx->len * idx->offSize,
+                                           idx->offSize, ok);
+    if (idx->endPos < idx->startPos || idx->endPos > len) {
+      *ok = gFalse;
+    }
+  }
+}
+
+void FoFiType1C::getIndexVal(Type1CIndex *idx, int i,
+                            Type1CIndexVal *val, GBool *ok) {
+  int pos0, pos1;
+
+  if (i < 0 || i >= idx->len) {
+    *ok = gFalse;
+    return;
+  }
+  pos0 = idx->startPos + getUVarBE(idx->pos + 3 + i * idx->offSize,
+                                  idx->offSize, ok);
+  pos1 = idx->startPos + getUVarBE(idx->pos + 3 + (i + 1) * idx->offSize,
+                                  idx->offSize, ok);
+  if (pos0 < idx->startPos || pos0 > idx->endPos ||
+      pos1 <= idx->startPos || pos1 > idx->endPos ||
+      pos1 < pos0) {
+    *ok = gFalse;
+  }
+  val->pos = pos0;
+  val->len = pos1 - pos0;
+}
+
+char *FoFiType1C::getString(int sid, char *buf, GBool *ok) {
+  Type1CIndexVal val;
+  int n;
+
+  if (sid < 391) {
+    strcpy(buf, fofiType1CStdStrings[sid]);
+  } else {
+    sid -= 391;
+    getIndexVal(&stringIdx, sid, &val, ok);
+    if (*ok) {
+      if ((n = val.len) > 255) {
+       n = 255;
+      }
+      strncpy(buf, (char *)&file[val.pos], n);
+      buf[n] = '\0';
+    } else {
+      buf[0] = '\0';
+    }
+  }
+  return buf;
+}
diff --git a/lib/xpdf/FoFiType1C.h b/lib/xpdf/FoFiType1C.h
new file mode 100644 (file)
index 0000000..62649ed
--- /dev/null
@@ -0,0 +1,232 @@
+//========================================================================
+//
+// FoFiType1C.h
+//
+// Copyright 1999-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef FOFITYPE1C_H
+#define FOFITYPE1C_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#include "gtypes.h"
+#include "FoFiBase.h"
+
+class GString;
+
+//------------------------------------------------------------------------
+
+struct Type1CIndex {
+  int pos;                     // absolute position in file
+  int len;                     // length (number of entries)
+  int offSize;                 // offset size
+  int startPos;                        // position of start of index data - 1
+  int endPos;                  // position one byte past end of the index
+};
+
+struct Type1CIndexVal {
+  int pos;                     // absolute position in file
+  int len;                     // length, in bytes
+};
+
+struct Type1CTopDict {
+  int firstOp;
+
+  int versionSID;
+  int noticeSID;
+  int copyrightSID;
+  int fullNameSID;
+  int familyNameSID;
+  int weightSID;
+  int isFixedPitch;
+  double italicAngle;
+  double underlinePosition;
+  double underlineThickness;
+  int paintType;
+  int charstringType;
+  double fontMatrix[6];
+  GBool hasFontMatrix;         // CID fonts are allowed to put their
+                               //   FontMatrix in the FD instead of the
+                               //   top dict
+  int uniqueID;
+  double fontBBox[4];
+  double strokeWidth;
+  int charsetOffset;
+  int encodingOffset;
+  int charStringsOffset;
+  int privateSize;
+  int privateOffset;
+
+  // CIDFont entries
+  int registrySID;
+  int orderingSID;
+  int supplement;
+  int fdArrayOffset;
+  int fdSelectOffset;
+};
+
+#define type1CMaxBlueValues 14
+#define type1CMaxOtherBlues 10
+#define type1CMaxStemSnap   12
+
+struct Type1CPrivateDict {
+  double fontMatrix[6];
+  GBool hasFontMatrix;
+  int blueValues[type1CMaxBlueValues];
+  int nBlueValues;
+  int otherBlues[type1CMaxOtherBlues];
+  int nOtherBlues;
+  int familyBlues[type1CMaxBlueValues];
+  int nFamilyBlues;
+  int familyOtherBlues[type1CMaxOtherBlues];
+  int nFamilyOtherBlues;
+  double blueScale;
+  int blueShift;
+  int blueFuzz;
+  double stdHW;
+  GBool hasStdHW;
+  double stdVW;
+  GBool hasStdVW;
+  double stemSnapH[type1CMaxStemSnap];
+  int nStemSnapH;
+  double stemSnapV[type1CMaxStemSnap];
+  int nStemSnapV;
+  GBool forceBold;
+  GBool hasForceBold;
+  double forceBoldThreshold;
+  int languageGroup;
+  double expansionFactor;
+  int initialRandomSeed;
+  int subrsOffset;
+  double defaultWidthX;
+  GBool defaultWidthXFP;
+  double nominalWidthX;
+  GBool nominalWidthXFP;
+};
+
+struct Type1COp {
+  GBool isNum;                 // true -> number, false -> operator
+  GBool isFP;                  // true -> floating point number, false -> int
+  union {
+    double num;                        // if num is true
+    int op;                    // if num is false
+  };
+};
+
+struct Type1CEexecBuf {
+  FoFiOutputFunc outputFunc;
+  void *outputStream;
+  GBool ascii;                 // ASCII encoding?
+  Gushort r1;                  // eexec encryption key
+  int line;                    // number of eexec chars left on current line
+};
+
+//------------------------------------------------------------------------
+// FoFiType1C
+//------------------------------------------------------------------------
+
+class FoFiType1C: public FoFiBase {
+public:
+
+  // Create a FoFiType1C object from a memory buffer.
+  static FoFiType1C *make(char *fileA, int lenA);
+
+  // Create a FoFiType1C object from a file on disk.
+  static FoFiType1C *load(char *fileName);
+
+  virtual ~FoFiType1C();
+
+  // Return the font name.
+  char *getName();
+
+  // Return the encoding, as an array of 256 names (any of which may
+  // be NULL).  This is only useful with 8-bit fonts.
+  char **getEncoding();
+
+  // Return the mapping from CIDs to GIDs, and return the number of
+  // CIDs in *<nCIDs>.  This is only useful for CID fonts.
+  Gushort *getCIDToGIDMap(int *nCIDs);
+
+  // Convert to a Type 1 font, suitable for embedding in a PostScript
+  // file.  This is only useful with 8-bit fonts.  If <newEncoding> is
+  // not NULL, it will be used in place of the encoding in the Type 1C
+  // font.  If <ascii> is true the eexec section will be hex-encoded,
+  // otherwise it will be left as binary data.
+  void convertToType1(char **newEncoding, GBool ascii,
+                     FoFiOutputFunc outputFunc, void *outputStream);
+
+  // Convert to a Type 0 CIDFont, suitable for embedding in a
+  // PostScript file.  <psName> will be used as the PostScript font
+  // name.
+  void convertToCIDType0(char *psName,
+                        FoFiOutputFunc outputFunc, void *outputStream);
+
+  // Convert to a Type 0 (but non-CID) composite font, suitable for
+  // embedding in a PostScript file.  <psName> will be used as the
+  // PostScript font name.
+  void convertToType0(char *psName,
+                     FoFiOutputFunc outputFunc, void *outputStream);
+
+private:
+
+  FoFiType1C(char *fileA, int lenA, GBool freeFileDataA);
+  void eexecCvtGlyph(Type1CEexecBuf *eb, char *glyphName,
+                    int offset, int nBytes,
+                    Type1CIndex *subrIdx,
+                    Type1CPrivateDict *pDict);
+  void cvtGlyph(int offset, int nBytes, GString *charBuf,
+               Type1CIndex *subrIdx, Type1CPrivateDict *pDict,
+               GBool top);
+  void cvtGlyphWidth(GBool useOp, GString *charBuf,
+                    Type1CPrivateDict *pDict);
+  void cvtNum(double x, GBool isFP, GString *charBuf);
+  void eexecWrite(Type1CEexecBuf *eb, char *s);
+  void eexecWriteCharstring(Type1CEexecBuf *eb, Guchar *s, int n);
+  GBool parse();
+  void readTopDict();
+  void readFD(int offset, int length, Type1CPrivateDict *pDict);
+  void readPrivateDict(int offset, int length, Type1CPrivateDict *pDict);
+  void readFDSelect();
+  void buildEncoding();
+  GBool readCharset();
+  int getOp(int pos, GBool charstring, GBool *ok);
+  int getDeltaIntArray(int *arr, int maxLen);
+  int getDeltaFPArray(double *arr, int maxLen);
+  void getIndex(int pos, Type1CIndex *idx, GBool *ok);
+  void getIndexVal(Type1CIndex *idx, int i, Type1CIndexVal *val, GBool *ok);
+  char *getString(int sid, char *buf, GBool *ok);
+
+  GString *name;
+  char **encoding;
+
+  Type1CIndex nameIdx;
+  Type1CIndex topDictIdx;
+  Type1CIndex stringIdx;
+  Type1CIndex gsubrIdx;
+  Type1CIndex charStringsIdx;
+
+  Type1CTopDict topDict;
+  Type1CPrivateDict *privateDicts;
+
+  int nGlyphs;
+  int nFDs;
+  Guchar *fdSelect;
+  Gushort *charset;
+  int gsubrBias;
+
+  GBool parsedOk;
+
+  Type1COp ops[49];            // operands and operator
+  int nOps;                    // number of operands
+  int nHints;                  // number of hints for the current glyph
+  GBool firstOp;               // true if we haven't hit the first op yet
+  GBool openPath;              // true if there is an unclosed path
+};
+
+#endif
diff --git a/lib/xpdf/FontEncodingTables.cc b/lib/xpdf/FontEncodingTables.cc
new file mode 100644 (file)
index 0000000..f3b9280
--- /dev/null
@@ -0,0 +1,1824 @@
+//========================================================================
+//
+// FontEncodingTables.cc
+//
+// Copyright 2001-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+#include <stdlib.h>
+#include "FontEncodingTables.h"
+
+char *macRomanEncoding[256] = {
+  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",
+  "notequal",
+  "AE",
+  "Oslash",
+  "infinity",
+  "plusminus",
+  "lessequal",
+  "greaterequal",
+  "yen",
+  "mu",
+  "partialdiff",
+  "summation",
+  "product",
+  "pi",
+  "integral",
+  "ordfeminine",
+  "ordmasculine",
+  "Omega",
+  "ae",
+  "oslash",
+  "questiondown",
+  "exclamdown",
+  "logicalnot",
+  "radical",
+  "florin",
+  "approxequal",
+  "Delta",
+  "guillemotleft",
+  "guillemotright",
+  "ellipsis",
+  "space",
+  "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",
+  "apple",
+  "Ograve",
+  "Uacute",
+  "Ucircumflex",
+  "Ugrave",
+  "dotlessi",
+  "circumflex",
+  "tilde",
+  "macron",
+  "breve",
+  "dotaccent",
+  "ring",
+  "cedilla",
+  "hungarumlaut",
+  "ogonek",
+  "caron"
+};
+
+char *macExpertEncoding[256] = {
+  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",
+  "centoldstyle",
+  "dollaroldstyle",
+  "dollarsuperior",
+  "ampersandsmall",
+  "Acutesmall",
+  "parenleftsuperior",
+  "parenrightsuperior",
+  "twodotenleader",
+  "onedotenleader",
+  "comma",
+  "hyphen",
+  "period",
+  "fraction",
+  "zerooldstyle",
+  "oneoldstyle",
+  "twooldstyle",
+  "threeoldstyle",
+  "fouroldstyle",
+  "fiveoldstyle",
+  "sixoldstyle",
+  "sevenoldstyle",
+  "eightoldstyle",
+  "nineoldstyle",
+  "colon",
+  "semicolon",
+  NULL,
+  "threequartersemdash",
+  NULL,
+  "questionsmall",
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  "Ethsmall",
+  NULL,
+  NULL,
+  "onequarter",
+  "onehalf",
+  "threequarters",
+  "oneeighth",
+  "threeeighths",
+  "fiveeighths",
+  "seveneighths",
+  "onethird",
+  "twothirds",
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  "ff",
+  "fi",
+  "fl",
+  "ffi",
+  "ffl",
+  "parenleftinferior",
+  NULL,
+  "parenrightinferior",
+  "Circumflexsmall",
+  "hypheninferior",
+  "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,
+  "asuperior",
+  "centsuperior",
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  "Aacutesmall",
+  "Agravesmall",
+  "Acircumflexsmall",
+  "Adieresissmall",
+  "Atildesmall",
+  "Aringsmall",
+  "Ccedillasmall",
+  "Eacutesmall",
+  "Egravesmall",
+  "Ecircumflexsmall",
+  "Edieresissmall",
+  "Iacutesmall",
+  "Igravesmall",
+  "Icircumflexsmall",
+  "Idieresissmall",
+  "Ntildesmall",
+  "Oacutesmall",
+  "Ogravesmall",
+  "Ocircumflexsmall",
+  "Odieresissmall",
+  "Otildesmall",
+  "Uacutesmall",
+  "Ugravesmall",
+  "Ucircumflexsmall",
+  "Udieresissmall",
+  NULL,
+  "eightsuperior",
+  "fourinferior",
+  "threeinferior",
+  "sixinferior",
+  "eightinferior",
+  "seveninferior",
+  "Scaronsmall",
+  NULL,
+  "centinferior",
+  "twoinferior",
+  NULL,
+  "Dieresissmall",
+  NULL,
+  "Caronsmall",
+  "osuperior",
+  "fiveinferior",
+  NULL,
+  "commainferior",
+  "periodinferior",
+  "Yacutesmall",
+  NULL,
+  "dollarinferior",
+  NULL,
+  NULL,
+  "Thornsmall",
+  NULL,
+  "nineinferior",
+  "zeroinferior",
+  "Zcaronsmall",
+  "AEsmall",
+  "Oslashsmall",
+  "questiondownsmall",
+  "oneinferior",
+  "Lslashsmall",
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  "Cedillasmall",
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  "OEsmall",
+  "figuredash",
+  "hyphensuperior",
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  "exclamdownsmall",
+  NULL,
+  "Ydieresissmall",
+  NULL,
+  "onesuperior",
+  "twosuperior",
+  "threesuperior",
+  "foursuperior",
+  "fivesuperior",
+  "sixsuperior",
+  "sevensuperior",
+  "ninesuperior",
+  "zerosuperior",
+  NULL,
+  "esuperior",
+  "rsuperior",
+  "tsuperior",
+  NULL,
+  NULL,
+  "isuperior",
+  "ssuperior",
+  "dsuperior",
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  "lsuperior",
+  "Ogoneksmall",
+  "Brevesmall",
+  "Macronsmall",
+  "bsuperior",
+  "nsuperior",
+  "msuperior",
+  "commasuperior",
+  "periodsuperior",
+  "Dotaccentsmall",
+  "Ringsmall",
+  NULL,
+  NULL,
+  NULL,
+  NULL
+};
+
+char *winAnsiEncoding[256] = {
+  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",
+  "Euro",
+  "bullet",
+  "quotesinglbase",
+  "florin",
+  "quotedblbase",
+  "ellipsis",
+  "dagger",
+  "daggerdbl",
+  "circumflex",
+  "perthousand",
+  "Scaron",
+  "guilsinglleft",
+  "OE",
+  "bullet",
+  "Zcaron",
+  "bullet",
+  "bullet",
+  "quoteleft",
+  "quoteright",
+  "quotedblleft",
+  "quotedblright",
+  "bullet",
+  "endash",
+  "emdash",
+  "tilde",
+  "trademark",
+  "scaron",
+  "guilsinglright",
+  "oe",
+  "bullet",
+  "zcaron",
+  "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"
+};
+
+char *standardEncoding[256] = {
+  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
+};
+
+char *expertEncoding[256] = {
+  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"
+};
+
+char *symbolEncoding[256] = {
+  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
+};
+
+char *zapfDingbatsEncoding[256] = {
+  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
+};
diff --git a/lib/xpdf/FontEncodingTables.h b/lib/xpdf/FontEncodingTables.h
new file mode 100644 (file)
index 0000000..8b0a1e7
--- /dev/null
@@ -0,0 +1,20 @@
+//========================================================================
+//
+// FontEncodingTables.h
+//
+// Copyright 2001-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef FONTENCODINGTABLES_H
+#define FONTENCODINGTABLES_H
+
+extern char *macRomanEncoding[];
+extern char *macExpertEncoding[];
+extern char *winAnsiEncoding[];
+extern char *standardEncoding[];
+extern char *expertEncoding[];
+extern char *symbolEncoding[];
+extern char *zapfDingbatsEncoding[];
+
+#endif
diff --git a/lib/xpdf/Function.cc b/lib/xpdf/Function.cc
new file mode 100644 (file)
index 0000000..f699641
--- /dev/null
@@ -0,0 +1,1535 @@
+//========================================================================
+//
+// Function.cc
+//
+// Copyright 2001-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <math.h>
+#include "gmem.h"
+#include "Object.h"
+#include "Dict.h"
+#include "Stream.h"
+#include "Error.h"
+#include "Function.h"
+
+//------------------------------------------------------------------------
+// Function
+//------------------------------------------------------------------------
+
+Function::Function() {
+}
+
+Function::~Function() {
+}
+
+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 if (funcObj->isName("Identity")) {
+    return new IdentityFunction();
+  } 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 if (funcType == 3) {
+    func = new StitchingFunction(funcObj, dict);
+  } else if (funcType == 4) {
+    func = new PostScriptFunction(funcObj, dict);
+  } else {
+    error(-1, "Unimplemented function type (%d)", funcType);
+    return NULL;
+  }
+  if (!func->isOk()) {
+    delete func;
+    return NULL;
+  }
+
+  return func;
+}
+
+GBool Function::init(Dict *dict) {
+  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");
+      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");
+      goto err1;
+    }
+    domain[i][1] = obj2.getNum();
+    obj2.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 < n; ++i) {
+      obj1.arrayGet(2*i, &obj2);
+      if (!obj2.isNum()) {
+       error(-1, "Illegal value in function range array");
+       goto err1;
+      }
+      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;
+      }
+      range[i][1] = obj2.getNum();
+      obj2.free();
+    }
+  }
+  obj1.free();
+
+  return gTrue;
+
+ err1:
+  obj2.free();
+ err2:
+  obj1.free();
+  return gFalse;
+}
+
+//------------------------------------------------------------------------
+// IdentityFunction
+//------------------------------------------------------------------------
+
+IdentityFunction::IdentityFunction() {
+  int i;
+
+  // fill these in with arbitrary values just in case they get used
+  // somewhere
+  m = funcMaxInputs;
+  n = funcMaxOutputs;
+  for (i = 0; i < funcMaxInputs; ++i) {
+    domain[i][0] = 0;
+    domain[i][1] = 1;
+  }
+  hasRange = gFalse;
+}
+
+IdentityFunction::~IdentityFunction() {
+}
+
+void IdentityFunction::transform(double *in, double *out) {
+  int i;
+
+  for (i = 0; i < funcMaxOutputs; ++i) {
+    out[i] = in[i];
+  }
+}
+
+//------------------------------------------------------------------------
+// SampledFunction
+//------------------------------------------------------------------------
+
+SampledFunction::SampledFunction(Object *funcObj, Dict *dict) {
+  Stream *str;
+  int sampleBits;
+  double sampleMul;
+  Object obj1, obj2;
+  Guint buf, bitMask;
+  int bits;
+  int s;
+  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;
+  }
+
+  //----- get the stream
+  if (!funcObj->isStream()) {
+    error(-1, "Type 0 function isn't a stream");
+    goto err1;
+  }
+  str = funcObj->getStream();
+
+  //----- 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 < m; ++i) {
+    obj1.arrayGet(i, &obj2);
+    if (!obj2.isInt()) {
+      error(-1, "Illegal value in function size array");
+      goto err3;
+    }
+    sampleSize[i] = obj2.getInt();
+    obj2.free();
+  }
+  obj1.free();
+  idxMul[0] = n;
+  for (i = 1; i < m; ++i) {
+    idxMul[i] = idxMul[i-1] * sampleSize[i-1];
+  }
+
+  //----- BitsPerSample
+  if (!dict->lookup("BitsPerSample", &obj1)->isInt()) {
+    error(-1, "Function has missing or invalid BitsPerSample");
+    goto err2;
+  }
+  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;
+      }
+      encode[i][1] = obj2.getNum();
+      obj2.free();
+    }
+  } else {
+    for (i = 0; i < m; ++i) {
+      encode[i][0] = 0;
+      encode[i][1] = sampleSize[i] - 1;
+    }
+  }
+  obj1.free();
+  for (i = 0; i < m; ++i) {
+    inputMul[i] = (encode[i][1] - encode[i][0]) /
+                  (domain[i][1] - domain[i][0]);
+  }
+
+  //----- 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];
+    }
+  }
+  obj1.free();
+
+  //----- samples
+  nSamples = n;
+  for (i = 0; i < m; ++i)
+    nSamples *= sampleSize[i];
+  samples = (double *)gmallocn(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;
+
+ err3:
+  obj2.free();
+ err2:
+  obj1.free();
+ err1:
+  return;
+}
+
+SampledFunction::~SampledFunction() {
+  if (samples) {
+    gfree(samples);
+  }
+}
+
+SampledFunction::SampledFunction(SampledFunction *func) {
+  memcpy(this, func, sizeof(SampledFunction));
+  samples = (double *)gmallocn(nSamples, sizeof(double));
+  memcpy(samples, func->samples, nSamples * sizeof(double));
+}
+
+void SampledFunction::transform(double *in, double *out) {
+  double x;
+  int e[funcMaxInputs][2];
+  double efrac0[funcMaxInputs];
+  double efrac1[funcMaxInputs];
+  double s[1 << funcMaxInputs];
+  int i, j, k, idx, t;
+
+  // map input values into sample array
+  for (i = 0; i < m; ++i) {
+    x = (in[i] - domain[i][0]) * inputMul[i] + encode[i][0];
+    if (x < 0) {
+      x = 0;
+    } else if (x > sampleSize[i] - 1) {
+      x = sampleSize[i] - 1;
+    }
+    e[i][0] = (int)x;
+    if ((e[i][1] = e[i][0] + 1) >= sampleSize[i]) {
+      // this happens if in[i] = domain[i][1]
+      e[i][1] = e[i][0];
+    }
+    efrac1[i] = x - e[i][0];
+    efrac0[i] = 1 - efrac1[i];
+  }
+
+  // for each output, do m-linear interpolation
+  for (i = 0; i < n; ++i) {
+
+    // pull 2^m values out of the sample array
+    for (j = 0; j < (1<<m); ++j) {
+      idx = i;
+      for (k = 0, t = j; k < m; ++k, t >>= 1) {
+       idx += idxMul[k] * (e[k][t & 1]);
+      }
+      s[j] = samples[idx];
+    }
+
+    // do m sets of interpolations
+    for (j = 0, t = (1<<m); j < m; ++j, t >>= 1) {
+      for (k = 0; k < t; k += 2) {
+       s[k >> 1] = efrac0[j] * s[k] + efrac1[j] * s[k+1];
+      }
+    }
+
+    // map output value to range
+    out[i] = s[0] * (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];
+    }
+  }
+}
+
+//------------------------------------------------------------------------
+// ExponentialFunction
+//------------------------------------------------------------------------
+
+ExponentialFunction::ExponentialFunction(Object *funcObj, Dict *dict) {
+  Object obj1, obj2;
+  int i;
+
+  ok = gFalse;
+
+  //----- initialize the generic stuff
+  if (!init(dict)) {
+    goto err1;
+  }
+  if (m != 1) {
+    error(-1, "Exponential function with more than one input");
+    goto err1;
+  }
+
+  //----- C0
+  if (dict->lookup("C0", &obj1)->isArray()) {
+    if (hasRange && obj1.arrayGetLength() != n) {
+      error(-1, "Function's C0 array is wrong length");
+      goto err2;
+    }
+    n = obj1.arrayGetLength();
+    for (i = 0; i < n; ++i) {
+      obj1.arrayGet(i, &obj2);
+      if (!obj2.isNum()) {
+       error(-1, "Illegal value in function C0 array");
+       goto err3;
+      }
+      c0[i] = obj2.getNum();
+      obj2.free();
+    }
+  } else {
+    if (hasRange && n != 1) {
+      error(-1, "Function's C0 array is wrong length");
+      goto err2;
+    }
+    n = 1;
+    c0[0] = 0;
+  }
+  obj1.free();
+
+  //----- C1
+  if (dict->lookup("C1", &obj1)->isArray()) {
+    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 {
+    if (n != 1) {
+      error(-1, "Function's C1 array is wrong length");
+      goto err2;
+    }
+    c1[0] = 1;
+  }
+  obj1.free();
+
+  //----- N (exponent)
+  if (!dict->lookup("N", &obj1)->isNum()) {
+    error(-1, "Function has missing or invalid N");
+    goto err2;
+  }
+  e = obj1.getNum();
+  obj1.free();
+
+  ok = gTrue;
+  return;
+
+ err3:
+  obj2.free();
+ err2:
+  obj1.free();
+ err1:
+  return;
+}
+
+ExponentialFunction::~ExponentialFunction() {
+}
+
+ExponentialFunction::ExponentialFunction(ExponentialFunction *func) {
+  memcpy(this, func, sizeof(ExponentialFunction));
+}
+
+void ExponentialFunction::transform(double *in, double *out) {
+  double x;
+  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];
+      }
+    }
+  }
+  return;
+}
+
+//------------------------------------------------------------------------
+// StitchingFunction
+//------------------------------------------------------------------------
+
+StitchingFunction::StitchingFunction(Object *funcObj, Dict *dict) {
+  Object obj1, obj2;
+  int i;
+
+  ok = gFalse;
+  funcs = NULL;
+  bounds = NULL;
+  encode = NULL;
+
+  //----- initialize the generic stuff
+  if (!init(dict)) {
+    goto err1;
+  }
+  if (m != 1) {
+    error(-1, "Stitching function with more than one input");
+    goto err1;
+  }
+
+  //----- Functions
+  if (!dict->lookup("Functions", &obj1)->isArray()) {
+    error(-1, "Missing 'Functions' entry in stitching function");
+    goto err1;
+  }
+  k = obj1.arrayGetLength();
+  funcs = (Function **)gmallocn(k, sizeof(Function *));
+  bounds = (double *)gmallocn(k + 1, sizeof(double));
+  encode = (double *)gmallocn(2 * k, sizeof(double));
+  for (i = 0; i < k; ++i) {
+    funcs[i] = NULL;
+  }
+  for (i = 0; i < k; ++i) {
+    if (!(funcs[i] = Function::parse(obj1.arrayGet(i, &obj2)))) {
+      goto err2;
+    }
+    if (i > 0 && (funcs[i]->getInputSize() != 1 ||
+                 funcs[i]->getOutputSize() != funcs[0]->getOutputSize())) {
+      error(-1, "Incompatible subfunctions in stitching function");
+      goto err2;
+    }
+    obj2.free();
+  }
+  obj1.free();
+
+  //----- Bounds
+  if (!dict->lookup("Bounds", &obj1)->isArray() ||
+      obj1.arrayGetLength() != k - 1) {
+    error(-1, "Missing or invalid 'Bounds' entry in stitching function");
+    goto err1;
+  }
+  bounds[0] = domain[0][0];
+  for (i = 1; i < k; ++i) {
+    if (!obj1.arrayGet(i - 1, &obj2)->isNum()) {
+      error(-1, "Invalid type in 'Bounds' array in stitching function");
+      goto err2;
+    }
+    bounds[i] = obj2.getNum();
+    obj2.free();
+  }
+  bounds[k] = domain[0][1];
+  obj1.free();
+
+  //----- Encode
+  if (!dict->lookup("Encode", &obj1)->isArray() ||
+      obj1.arrayGetLength() != 2 * k) {
+    error(-1, "Missing or invalid 'Encode' entry in stitching function");
+    goto err1;
+  }
+  for (i = 0; i < 2 * k; ++i) {
+    if (!obj1.arrayGet(i, &obj2)->isNum()) {
+      error(-1, "Invalid type in 'Encode' array in stitching function");
+      goto err2;
+    }
+    encode[i] = obj2.getNum();
+    obj2.free();
+  }
+  obj1.free();
+
+  ok = gTrue;
+  return;
+
+ err2:
+  obj2.free();
+ err1:
+  obj1.free();
+}
+
+StitchingFunction::StitchingFunction(StitchingFunction *func) {
+  int i;
+
+  k = func->k;
+  funcs = (Function **)gmallocn(k, sizeof(Function *));
+  for (i = 0; i < k; ++i) {
+    funcs[i] = func->funcs[i]->copy();
+  }
+  bounds = (double *)gmallocn(k + 1, sizeof(double));
+  memcpy(bounds, func->bounds, (k + 1) * sizeof(double));
+  encode = (double *)gmallocn(2 * k, sizeof(double));
+  memcpy(encode, func->encode, 2 * k * sizeof(double));
+  ok = gTrue;
+}
+
+StitchingFunction::~StitchingFunction() {
+  int i;
+
+  if (funcs) {
+    for (i = 0; i < k; ++i) {
+      if (funcs[i]) {
+       delete funcs[i];
+      }
+    }
+  }
+  gfree(funcs);
+  gfree(bounds);
+  gfree(encode);
+}
+
+void StitchingFunction::transform(double *in, double *out) {
+  double x;
+  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 < k - 1; ++i) {
+    if (x < bounds[i+1]) {
+      break;
+    }
+  }
+  x = encode[2*i] + ((x - bounds[i]) / (bounds[i+1] - bounds[i])) *
+                    (encode[2*i+1] - encode[2*i]);
+  funcs[i]->transform(&x, out);
+}
+
+//------------------------------------------------------------------------
+// PostScriptFunction
+//------------------------------------------------------------------------
+
+enum PSOp {
+  psOpAbs,
+  psOpAdd,
+  psOpAnd,
+  psOpAtan,
+  psOpBitshift,
+  psOpCeiling,
+  psOpCopy,
+  psOpCos,
+  psOpCvi,
+  psOpCvr,
+  psOpDiv,
+  psOpDup,
+  psOpEq,
+  psOpExch,
+  psOpExp,
+  psOpFalse,
+  psOpFloor,
+  psOpGe,
+  psOpGt,
+  psOpIdiv,
+  psOpIndex,
+  psOpLe,
+  psOpLn,
+  psOpLog,
+  psOpLt,
+  psOpMod,
+  psOpMul,
+  psOpNe,
+  psOpNeg,
+  psOpNot,
+  psOpOr,
+  psOpPop,
+  psOpRoll,
+  psOpRound,
+  psOpSin,
+  psOpSqrt,
+  psOpSub,
+  psOpTrue,
+  psOpTruncate,
+  psOpXor,
+  psOpIf,
+  psOpIfelse,
+  psOpReturn
+};
+
+// Note: 'if' and 'ifelse' are parsed separately.
+// The rest are listed here in alphabetical order.
+// The index in this table is equivalent to the entry in PSOp.
+char *psOpNames[] = {
+  "abs",
+  "add",
+  "and",
+  "atan",
+  "bitshift",
+  "ceiling",
+  "copy",
+  "cos",
+  "cvi",
+  "cvr",
+  "div",
+  "dup",
+  "eq",
+  "exch",
+  "exp",
+  "false",
+  "floor",
+  "ge",
+  "gt",
+  "idiv",
+  "index",
+  "le",
+  "ln",
+  "log",
+  "lt",
+  "mod",
+  "mul",
+  "ne",
+  "neg",
+  "not",
+  "or",
+  "pop",
+  "roll",
+  "round",
+  "sin",
+  "sqrt",
+  "sub",
+  "true",
+  "truncate",
+  "xor"
+};
+
+#define nPSOps (sizeof(psOpNames) / sizeof(char *))
+
+enum PSObjectType {
+  psBool,
+  psInt,
+  psReal,
+  psOperator,
+  psBlock
+};
+
+// In the code array, 'if'/'ifelse' operators take up three slots
+// plus space for the code in the subclause(s).
+//
+//         +---------------------------------+
+//         | psOperator: psOpIf / psOpIfelse |
+//         +---------------------------------+
+//         | psBlock: ptr=<A>                |
+//         +---------------------------------+
+//         | psBlock: ptr=<B>                |
+//         +---------------------------------+
+//         | if clause                       |
+//         | ...                             |
+//         | psOperator: psOpReturn          |
+//         +---------------------------------+
+//     <A> | else clause                     |
+//         | ...                             |
+//         | psOperator: psOpReturn          |
+//         +---------------------------------+
+//     <B> | ...                             |
+//
+// For 'if', pointer <A> is present in the code stream but unused.
+
+struct PSObject {
+  PSObjectType type;
+  union {
+    GBool booln;               // boolean (stack only)
+    int intg;                  // integer (stack and code)
+    double real;               // real (stack and code)
+    PSOp op;                   // operator (code only)
+    int blk;                   // if/ifelse block pointer (code only)
+  };
+};
+
+#define psStackSize 100
+
+class PSStack {
+public:
+
+  PSStack() { sp = psStackSize; }
+  void pushBool(GBool booln);
+  void pushInt(int intg);
+  void pushReal(double real);
+  GBool popBool();
+  int popInt();
+  double popNum();
+  GBool empty() { return sp == psStackSize; }
+  GBool topIsInt() { return sp < psStackSize && stack[sp].type == psInt; }
+  GBool topTwoAreInts()
+    { return sp < psStackSize - 1 &&
+            stack[sp].type == psInt &&
+             stack[sp+1].type == psInt; }
+  GBool topIsReal() { return sp < psStackSize && stack[sp].type == psReal; }
+  GBool topTwoAreNums()
+    { return sp < psStackSize - 1 &&
+            (stack[sp].type == psInt || stack[sp].type == psReal) &&
+            (stack[sp+1].type == psInt || stack[sp+1].type == psReal); }
+  void copy(int n);
+  void roll(int n, int j);
+  void index(int i);
+  void pop();
+
+private:
+
+  GBool checkOverflow(int n = 1);
+  GBool checkUnderflow();
+  GBool checkType(PSObjectType t1, PSObjectType t2);
+
+  PSObject stack[psStackSize];
+  int sp;
+};
+
+GBool PSStack::checkOverflow(int n) {
+  if (sp - n < 0) {
+    error(-1, "Stack overflow in PostScript function");
+    return gFalse;
+  }
+  return gTrue;
+}
+
+GBool PSStack::checkUnderflow() {
+  if (sp == psStackSize) {
+    error(-1, "Stack underflow in PostScript function");
+    return gFalse;
+  }
+  return gTrue;
+}
+
+GBool PSStack::checkType(PSObjectType t1, PSObjectType t2) {
+  if (stack[sp].type != t1 && stack[sp].type != t2) {
+    error(-1, "Type mismatch in PostScript function");
+    return gFalse;
+  }
+  return gTrue;
+}
+
+void PSStack::pushBool(GBool booln) {
+  if (checkOverflow()) {
+    stack[--sp].type = psBool;
+    stack[sp].booln = booln;
+  }
+}
+
+void PSStack::pushInt(int intg) {
+  if (checkOverflow()) {
+    stack[--sp].type = psInt;
+    stack[sp].intg = intg;
+  }
+}
+
+void PSStack::pushReal(double real) {
+  if (checkOverflow()) {
+    stack[--sp].type = psReal;
+    stack[sp].real = real;
+  }
+}
+
+GBool PSStack::popBool() {
+  if (checkUnderflow() && checkType(psBool, psBool)) {
+    return stack[sp++].booln;
+  }
+  return gFalse;
+}
+
+int PSStack::popInt() {
+  if (checkUnderflow() && checkType(psInt, psInt)) {
+    return stack[sp++].intg;
+  }
+  return 0;
+}
+
+double PSStack::popNum() {
+  double ret;
+
+  if (checkUnderflow() && checkType(psInt, psReal)) {
+    ret = (stack[sp].type == psInt) ? (double)stack[sp].intg : stack[sp].real;
+    ++sp;
+    return ret;
+  }
+  return 0;
+}
+
+void PSStack::copy(int n) {
+  int i;
+
+  if (sp + n > psStackSize) {
+    error(-1, "Stack underflow in PostScript function");
+    return;
+  }
+  if (!checkOverflow(n)) {
+    return;
+  }
+  for (i = sp + n - 1; i >= sp; --i) {
+    stack[i - n] = stack[i];
+  }
+  sp -= n;
+}
+
+void PSStack::roll(int n, int j) {
+  PSObject obj;
+  int i, k;
+
+  if (j >= 0) {
+    j %= n;
+  } else {
+    j = -j % n;
+    if (j != 0) {
+      j = n - j;
+    }
+  }
+  if (n <= 0 || j == 0) {
+    return;
+  }
+  for (i = 0; i < j; ++i) {
+    obj = stack[sp];
+    for (k = sp; k < sp + n - 1; ++k) {
+      stack[k] = stack[k+1];
+    }
+    stack[sp + n - 1] = obj;
+  }
+}
+
+void PSStack::index(int i) {
+  if (!checkOverflow()) {
+    return;
+  }
+  --sp;
+  stack[sp] = stack[sp + 1 + i];
+}
+
+void PSStack::pop() {
+  if (!checkUnderflow()) {
+    return;
+  }
+  ++sp;
+}
+
+PostScriptFunction::PostScriptFunction(Object *funcObj, Dict *dict) {
+  Stream *str;
+  int codePtr;
+  GString *tok;
+
+  code = NULL;
+  codeSize = 0;
+  ok = gFalse;
+
+  //----- initialize the generic stuff
+  if (!init(dict)) {
+    goto err1;
+  }
+  if (!hasRange) {
+    error(-1, "Type 4 function is missing range");
+    goto err1;
+  }
+
+  //----- get the stream
+  if (!funcObj->isStream()) {
+    error(-1, "Type 4 function isn't a stream");
+    goto err1;
+  }
+  str = funcObj->getStream();
+
+  //----- parse the function
+  codeString = new GString();
+  str->reset();
+  if (!(tok = getToken(str)) || tok->cmp("{")) {
+    error(-1, "Expected '{' at start of PostScript function");
+    if (tok) {
+      delete tok;
+    }
+    goto err1;
+  }
+  delete tok;
+  codePtr = 0;
+  if (!parseCode(str, &codePtr)) {
+    goto err2;
+  }
+  str->close();
+
+  ok = gTrue;
+
+ err2:
+  str->close();
+ err1:
+  return;
+}
+
+PostScriptFunction::PostScriptFunction(PostScriptFunction *func) {
+  memcpy(this, func, sizeof(PostScriptFunction));
+  code = (PSObject *)gmallocn(codeSize, sizeof(PSObject));
+  memcpy(code, func->code, codeSize * sizeof(PSObject));
+  codeString = func->codeString->copy();
+}
+
+PostScriptFunction::~PostScriptFunction() {
+  gfree(code);
+  delete codeString;
+}
+
+void PostScriptFunction::transform(double *in, double *out) {
+  PSStack *stack;
+  int i;
+
+  stack = new PSStack();
+  for (i = 0; i < m; ++i) {
+    //~ may need to check for integers here
+    stack->pushReal(in[i]);
+  }
+  exec(stack, 0);
+  for (i = n - 1; i >= 0; --i) {
+    out[i] = stack->popNum();
+    if (out[i] < range[i][0]) {
+      out[i] = range[i][0];
+    } else if (out[i] > range[i][1]) {
+      out[i] = range[i][1];
+    }
+  }
+  // if (!stack->empty()) {
+  //   error(-1, "Extra values on stack at end of PostScript function");
+  // }
+  delete stack;
+}
+
+GBool PostScriptFunction::parseCode(Stream *str, int *codePtr) {
+  GString *tok;
+  char *p;
+  GBool isReal;
+  int opPtr, elsePtr;
+  int a, b, mid, cmp;
+
+  while (1) {
+    if (!(tok = getToken(str))) {
+      error(-1, "Unexpected end of PostScript function stream");
+      return gFalse;
+    }
+    p = tok->getCString();
+    if (isdigit(*p) || *p == '.' || *p == '-') {
+      isReal = gFalse;
+      for (++p; *p; ++p) {
+       if (*p == '.') {
+         isReal = gTrue;
+         break;
+       }
+      }
+      resizeCode(*codePtr);
+      if (isReal) {
+       code[*codePtr].type = psReal;
+       code[*codePtr].real = atof(tok->getCString());
+      } else {
+       code[*codePtr].type = psInt;
+       code[*codePtr].intg = atoi(tok->getCString());
+      }
+      ++*codePtr;
+      delete tok;
+    } else if (!tok->cmp("{")) {
+      delete tok;
+      opPtr = *codePtr;
+      *codePtr += 3;
+      resizeCode(opPtr + 2);
+      if (!parseCode(str, codePtr)) {
+       return gFalse;
+      }
+      if (!(tok = getToken(str))) {
+       error(-1, "Unexpected end of PostScript function stream");
+       return gFalse;
+      }
+      if (!tok->cmp("{")) {
+       elsePtr = *codePtr;
+       if (!parseCode(str, codePtr)) {
+         return gFalse;
+       }
+       delete tok;
+       if (!(tok = getToken(str))) {
+         error(-1, "Unexpected end of PostScript function stream");
+         return gFalse;
+       }
+      } else {
+       elsePtr = -1;
+      }
+      if (!tok->cmp("if")) {
+       if (elsePtr >= 0) {
+         error(-1, "Got 'if' operator with two blocks in PostScript function");
+         return gFalse;
+       }
+       code[opPtr].type = psOperator;
+       code[opPtr].op = psOpIf;
+       code[opPtr+2].type = psBlock;
+       code[opPtr+2].blk = *codePtr;
+      } else if (!tok->cmp("ifelse")) {
+       if (elsePtr < 0) {
+         error(-1, "Got 'ifelse' operator with one blocks in PostScript function");
+         return gFalse;
+       }
+       code[opPtr].type = psOperator;
+       code[opPtr].op = psOpIfelse;
+       code[opPtr+1].type = psBlock;
+       code[opPtr+1].blk = elsePtr;
+       code[opPtr+2].type = psBlock;
+       code[opPtr+2].blk = *codePtr;
+      } else {
+       error(-1, "Expected if/ifelse operator in PostScript function");
+       delete tok;
+       return gFalse;
+      }
+      delete tok;
+    } else if (!tok->cmp("}")) {
+      delete tok;
+      resizeCode(*codePtr);
+      code[*codePtr].type = psOperator;
+      code[*codePtr].op = psOpReturn;
+      ++*codePtr;
+      break;
+    } else {
+      a = -1;
+      b = nPSOps;
+      // invariant: psOpNames[a] < tok < psOpNames[b]
+      while (b - a > 1) {
+       mid = (a + b) / 2;
+       cmp = tok->cmp(psOpNames[mid]);
+       if (cmp > 0) {
+         a = mid;
+       } else if (cmp < 0) {
+         b = mid;
+       } else {
+         a = b = mid;
+       }
+      }
+      if (cmp != 0) {
+       error(-1, "Unknown operator '%s' in PostScript function",
+             tok->getCString());
+       delete tok;
+       return gFalse;
+      }
+      delete tok;
+      resizeCode(*codePtr);
+      code[*codePtr].type = psOperator;
+      code[*codePtr].op = (PSOp)a;
+      ++*codePtr;
+    }
+  }
+  return gTrue;
+}
+
+GString *PostScriptFunction::getToken(Stream *str) {
+  GString *s;
+  int c;
+
+  s = new GString();
+  do {
+    c = str->getChar();
+    if (c != EOF) {
+      codeString->append(c);
+    }
+  } while (c != EOF && isspace(c));
+  if (c == '{' || c == '}') {
+    s->append((char)c);
+  } else if (isdigit(c) || c == '.' || c == '-') {
+    while (1) {
+      s->append((char)c);
+      c = str->lookChar();
+      if (c == EOF || !(isdigit(c) || c == '.' || c == '-')) {
+       break;
+      }
+      str->getChar();
+      codeString->append(c);
+    }
+  } else {
+    while (1) {
+      s->append((char)c);
+      c = str->lookChar();
+      if (c == EOF || !isalnum(c)) {
+       break;
+      }
+      str->getChar();
+      codeString->append(c);
+    }
+  }
+  return s;
+}
+
+void PostScriptFunction::resizeCode(int newSize) {
+  if (newSize >= codeSize) {
+    codeSize += 64;
+    code = (PSObject *)greallocn(code, codeSize, sizeof(PSObject));
+  }
+}
+
+void PostScriptFunction::exec(PSStack *stack, int codePtr) {
+  int i1, i2;
+  double r1, r2;
+  GBool b1, b2;
+
+  while (1) {
+    switch (code[codePtr].type) {
+    case psInt:
+      stack->pushInt(code[codePtr++].intg);
+      break;
+    case psReal:
+      stack->pushReal(code[codePtr++].real);
+      break;
+    case psOperator:
+      switch (code[codePtr++].op) {
+      case psOpAbs:
+       if (stack->topIsInt()) {
+         stack->pushInt(abs(stack->popInt()));
+       } else {
+         stack->pushReal(fabs(stack->popNum()));
+       }
+       break;
+      case psOpAdd:
+       if (stack->topTwoAreInts()) {
+         i2 = stack->popInt();
+         i1 = stack->popInt();
+         stack->pushInt(i1 + i2);
+       } else {
+         r2 = stack->popNum();
+         r1 = stack->popNum();
+         stack->pushReal(r1 + r2);
+       }
+       break;
+      case psOpAnd:
+       if (stack->topTwoAreInts()) {
+         i2 = stack->popInt();
+         i1 = stack->popInt();
+         stack->pushInt(i1 & i2);
+       } else {
+         b2 = stack->popBool();
+         b1 = stack->popBool();
+         stack->pushBool(b1 && b2);
+       }
+       break;
+      case psOpAtan:
+       r2 = stack->popNum();
+       r1 = stack->popNum();
+       stack->pushReal(atan2(r1, r2));
+       break;
+      case psOpBitshift:
+       i2 = stack->popInt();
+       i1 = stack->popInt();
+       if (i2 > 0) {
+         stack->pushInt(i1 << i2);
+       } else if (i2 < 0) {
+         stack->pushInt((int)((Guint)i1 >> i2));
+       } else {
+         stack->pushInt(i1);
+       }
+       break;
+      case psOpCeiling:
+       if (!stack->topIsInt()) {
+         stack->pushReal(ceil(stack->popNum()));
+       }
+       break;
+      case psOpCopy:
+       stack->copy(stack->popInt());
+       break;
+      case psOpCos:
+       stack->pushReal(cos(stack->popNum()));
+       break;
+      case psOpCvi:
+       if (!stack->topIsInt()) {
+         stack->pushInt((int)stack->popNum());
+       }
+       break;
+      case psOpCvr:
+       if (!stack->topIsReal()) {
+         stack->pushReal(stack->popNum());
+       }
+       break;
+      case psOpDiv:
+       r2 = stack->popNum();
+       r1 = stack->popNum();
+       stack->pushReal(r1 / r2);
+       break;
+      case psOpDup:
+       stack->copy(1);
+       break;
+      case psOpEq:
+       if (stack->topTwoAreInts()) {
+         i2 = stack->popInt();
+         i1 = stack->popInt();
+         stack->pushBool(i1 == i2);
+       } else if (stack->topTwoAreNums()) {
+         r2 = stack->popNum();
+         r1 = stack->popNum();
+         stack->pushBool(r1 == r2);
+       } else {
+         b2 = stack->popBool();
+         b1 = stack->popBool();
+         stack->pushBool(b1 == b2);
+       }
+       break;
+      case psOpExch:
+       stack->roll(2, 1);
+       break;
+      case psOpExp:
+       r2 = stack->popNum();
+       r1 = stack->popNum();
+       stack->pushReal(pow(r1, r2));
+       break;
+      case psOpFalse:
+       stack->pushBool(gFalse);
+       break;
+      case psOpFloor:
+       if (!stack->topIsInt()) {
+         stack->pushReal(floor(stack->popNum()));
+       }
+       break;
+      case psOpGe:
+       if (stack->topTwoAreInts()) {
+         i2 = stack->popInt();
+         i1 = stack->popInt();
+         stack->pushBool(i1 >= i2);
+       } else {
+         r2 = stack->popNum();
+         r1 = stack->popNum();
+         stack->pushBool(r1 >= r2);
+       }
+       break;
+      case psOpGt:
+       if (stack->topTwoAreInts()) {
+         i2 = stack->popInt();
+         i1 = stack->popInt();
+         stack->pushBool(i1 > i2);
+       } else {
+         r2 = stack->popNum();
+         r1 = stack->popNum();
+         stack->pushBool(r1 > r2);
+       }
+       break;
+      case psOpIdiv:
+       i2 = stack->popInt();
+       i1 = stack->popInt();
+       stack->pushInt(i1 / i2);
+       break;
+      case psOpIndex:
+       stack->index(stack->popInt());
+       break;
+      case psOpLe:
+       if (stack->topTwoAreInts()) {
+         i2 = stack->popInt();
+         i1 = stack->popInt();
+         stack->pushBool(i1 <= i2);
+       } else {
+         r2 = stack->popNum();
+         r1 = stack->popNum();
+         stack->pushBool(r1 <= r2);
+       }
+       break;
+      case psOpLn:
+       stack->pushReal(log(stack->popNum()));
+       break;
+      case psOpLog:
+       stack->pushReal(log10(stack->popNum()));
+       break;
+      case psOpLt:
+       if (stack->topTwoAreInts()) {
+         i2 = stack->popInt();
+         i1 = stack->popInt();
+         stack->pushBool(i1 < i2);
+       } else {
+         r2 = stack->popNum();
+         r1 = stack->popNum();
+         stack->pushBool(r1 < r2);
+       }
+       break;
+      case psOpMod:
+       i2 = stack->popInt();
+       i1 = stack->popInt();
+       stack->pushInt(i1 % i2);
+       break;
+      case psOpMul:
+       if (stack->topTwoAreInts()) {
+         i2 = stack->popInt();
+         i1 = stack->popInt();
+         //~ should check for out-of-range, and push a real instead
+         stack->pushInt(i1 * i2);
+       } else {
+         r2 = stack->popNum();
+         r1 = stack->popNum();
+         stack->pushReal(r1 * r2);
+       }
+       break;
+      case psOpNe:
+       if (stack->topTwoAreInts()) {
+         i2 = stack->popInt();
+         i1 = stack->popInt();
+         stack->pushBool(i1 != i2);
+       } else if (stack->topTwoAreNums()) {
+         r2 = stack->popNum();
+         r1 = stack->popNum();
+         stack->pushBool(r1 != r2);
+       } else {
+         b2 = stack->popBool();
+         b1 = stack->popBool();
+         stack->pushBool(b1 != b2);
+       }
+       break;
+      case psOpNeg:
+       if (stack->topIsInt()) {
+         stack->pushInt(-stack->popInt());
+       } else {
+         stack->pushReal(-stack->popNum());
+       }
+       break;
+      case psOpNot:
+       if (stack->topIsInt()) {
+         stack->pushInt(~stack->popInt());
+       } else {
+         stack->pushBool(!stack->popBool());
+       }
+       break;
+      case psOpOr:
+       if (stack->topTwoAreInts()) {
+         i2 = stack->popInt();
+         i1 = stack->popInt();
+         stack->pushInt(i1 | i2);
+       } else {
+         b2 = stack->popBool();
+         b1 = stack->popBool();
+         stack->pushBool(b1 || b2);
+       }
+       break;
+      case psOpPop:
+       stack->pop();
+       break;
+      case psOpRoll:
+       i2 = stack->popInt();
+       i1 = stack->popInt();
+       stack->roll(i1, i2);
+       break;
+      case psOpRound:
+       if (!stack->topIsInt()) {
+         r1 = stack->popNum();
+         stack->pushReal((r1 >= 0) ? floor(r1 + 0.5) : ceil(r1 - 0.5));
+       }
+       break;
+      case psOpSin:
+       stack->pushReal(sin(stack->popNum()));
+       break;
+      case psOpSqrt:
+       stack->pushReal(sqrt(stack->popNum()));
+       break;
+      case psOpSub:
+       if (stack->topTwoAreInts()) {
+         i2 = stack->popInt();
+         i1 = stack->popInt();
+         stack->pushInt(i1 - i2);
+       } else {
+         r2 = stack->popNum();
+         r1 = stack->popNum();
+         stack->pushReal(r1 - r2);
+       }
+       break;
+      case psOpTrue:
+       stack->pushBool(gTrue);
+       break;
+      case psOpTruncate:
+       if (!stack->topIsInt()) {
+         r1 = stack->popNum();
+         stack->pushReal((r1 >= 0) ? floor(r1) : ceil(r1));
+       }
+       break;
+      case psOpXor:
+       if (stack->topTwoAreInts()) {
+         i2 = stack->popInt();
+         i1 = stack->popInt();
+         stack->pushInt(i1 ^ i2);
+       } else {
+         b2 = stack->popBool();
+         b1 = stack->popBool();
+         stack->pushBool(b1 ^ b2);
+       }
+       break;
+      case psOpIf:
+       b1 = stack->popBool();
+       if (b1) {
+         exec(stack, codePtr + 2);
+       }
+       codePtr = code[codePtr + 1].blk;
+       break;
+      case psOpIfelse:
+       b1 = stack->popBool();
+       if (b1) {
+         exec(stack, codePtr + 2);
+       } else {
+         exec(stack, code[codePtr].blk);
+       }
+       codePtr = code[codePtr + 1].blk;
+       break;
+      case psOpReturn:
+       return;
+      }
+      break;
+    default:
+      error(-1, "Internal: bad object in PostScript function code");
+      break;
+    }
+  }
+}
diff --git a/lib/xpdf/Function.h b/lib/xpdf/Function.h
new file mode 100644 (file)
index 0000000..bfaf83e
--- /dev/null
@@ -0,0 +1,225 @@
+//========================================================================
+//
+// Function.h
+//
+// Copyright 2001-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef FUNCTION_H
+#define FUNCTION_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#include "gtypes.h"
+#include "Object.h"
+
+class Dict;
+class Stream;
+struct PSObject;
+class PSStack;
+
+//------------------------------------------------------------------------
+// Function
+//------------------------------------------------------------------------
+
+#define funcMaxInputs   8
+#define funcMaxOutputs 32
+
+class Function {
+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 the function type:
+  //   -1 : identity
+  //    0 : sampled
+  //    2 : exponential
+  //    3 : stitching
+  //    4 : PostScript
+  virtual int getType() = 0;
+
+  // Return size of input and output tuples.
+  int getInputSize() { return m; }
+  int getOutputSize() { return n; }
+
+  double getDomainMin(int i) { return domain[i][0]; }
+  double getDomainMax(int i) { return domain[i][1]; }
+  double getRangeMin(int i) { return range[i][0]; }
+  double getRangeMax(int i) { return range[i][1]; }
+  GBool getHasRange() { return hasRange; }
+
+  // Transform an input tuple into an output tuple.
+  virtual void transform(double *in, double *out) = 0;
+
+  virtual GBool isOk() = 0;
+
+protected:
+
+  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
+};
+
+//------------------------------------------------------------------------
+// IdentityFunction
+//------------------------------------------------------------------------
+
+class IdentityFunction: public Function {
+public:
+
+  IdentityFunction();
+  virtual ~IdentityFunction();
+  virtual Function *copy() { return new IdentityFunction(); }
+  virtual int getType() { return -1; }
+  virtual void transform(double *in, double *out);
+  virtual GBool isOk() { return gTrue; }
+
+private:
+};
+
+//------------------------------------------------------------------------
+// SampledFunction
+//------------------------------------------------------------------------
+
+class SampledFunction: public Function {
+public:
+
+  SampledFunction(Object *funcObj, Dict *dict);
+  virtual ~SampledFunction();
+  virtual Function *copy() { return new SampledFunction(this); }
+  virtual int getType() { return 0; }
+  virtual void transform(double *in, double *out);
+  virtual GBool isOk() { return ok; }
+
+  int getSampleSize(int i) { return sampleSize[i]; }
+  double getEncodeMin(int i) { return encode[i][0]; }
+  double getEncodeMax(int i) { return encode[i][1]; }
+  double getDecodeMin(int i) { return decode[i][0]; }
+  double getDecodeMax(int i) { return decode[i][1]; }
+  double *getSamples() { return samples; }
+
+private:
+
+  SampledFunction(SampledFunction *func);
+
+  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                       // input multipliers
+    inputMul[funcMaxInputs];
+  int idxMul[funcMaxInputs];   // sample array index multipliers
+  double *samples;             // the samples
+  int nSamples;                        // size of the samples array
+  GBool ok;
+};
+
+//------------------------------------------------------------------------
+// ExponentialFunction
+//------------------------------------------------------------------------
+
+class ExponentialFunction: public Function {
+public:
+
+  ExponentialFunction(Object *funcObj, Dict *dict);
+  virtual ~ExponentialFunction();
+  virtual Function *copy() { return new ExponentialFunction(this); }
+  virtual int getType() { return 2; }
+  virtual void transform(double *in, double *out);
+  virtual GBool isOk() { return ok; }
+
+  double *getC0() { return c0; }
+  double *getC1() { return c1; }
+  double getE() { return e; }
+
+private:
+
+  ExponentialFunction(ExponentialFunction *func);
+
+  double c0[funcMaxOutputs];
+  double c1[funcMaxOutputs];
+  double e;
+  GBool ok;
+};
+
+//------------------------------------------------------------------------
+// StitchingFunction
+//------------------------------------------------------------------------
+
+class StitchingFunction: public Function {
+public:
+
+  StitchingFunction(Object *funcObj, Dict *dict);
+  virtual ~StitchingFunction();
+  virtual Function *copy() { return new StitchingFunction(this); }
+  virtual int getType() { return 3; }
+  virtual void transform(double *in, double *out);
+  virtual GBool isOk() { return ok; }
+
+  int getNumFuncs() { return k; }
+  Function *getFunc(int i) { return funcs[i]; }
+  double *getBounds() { return bounds; }
+  double *getEncode() { return encode; }
+
+private:
+
+  StitchingFunction(StitchingFunction *func);
+
+  int k;
+  Function **funcs;
+  double *bounds;
+  double *encode;
+  GBool ok;
+};
+
+//------------------------------------------------------------------------
+// PostScriptFunction
+//------------------------------------------------------------------------
+
+class PostScriptFunction: public Function {
+public:
+
+  PostScriptFunction(Object *funcObj, Dict *dict);
+  virtual ~PostScriptFunction();
+  virtual Function *copy() { return new PostScriptFunction(this); }
+  virtual int getType() { return 4; }
+  virtual void transform(double *in, double *out);
+  virtual GBool isOk() { return ok; }
+
+  GString *getCodeString() { return codeString; }
+
+private:
+
+  PostScriptFunction(PostScriptFunction *func);
+  GBool parseCode(Stream *str, int *codePtr);
+  GString *getToken(Stream *str);
+  void resizeCode(int newSize);
+  void exec(PSStack *stack, int codePtr);
+
+  GString *codeString;
+  PSObject *code;
+  int codeSize;
+  GBool ok;
+};
+
+#endif
diff --git a/lib/xpdf/GFXOutputDev.cc b/lib/xpdf/GFXOutputDev.cc
new file mode 100644 (file)
index 0000000..7eed590
--- /dev/null
@@ -0,0 +1,2392 @@
+/* pdfswf.cc
+   implements a pdf output device (OutputDev).
+
+   This file is part of swftools.
+
+   Swftools is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   Swftools is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with swftools; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <string.h>
+#include <unistd.h>
+#include "../../config.h"
+#ifdef HAVE_DIRENT_H
+#include <dirent.h>
+#endif
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#ifdef HAVE_FONTCONFIG
+#include <fontconfig.h>
+#endif
+//xpdf header files
+#include "config.h"
+#include "gfile.h"
+#include "GString.h"
+#include "gmem.h"
+#include "Object.h"
+#include "Stream.h"
+#include "Array.h"
+#include "Dict.h"
+#include "XRef.h"
+#include "Catalog.h"
+#include "Page.h"
+#include "PDFDoc.h"
+#include "Error.h"
+#include "Link.h"
+#include "OutputDev.h"
+#include "GfxFont.h"
+#include "GfxState.h"
+#include "CharCodeToUnicode.h"
+#include "NameToUnicodeTable.h"
+#include "GlobalParams.h"
+#include "FoFiType1C.h"
+#include "FoFiTrueType.h"
+#include "GHash.h"
+#include "GFXOutputDev.h"
+
+//swftools header files
+#include "../log.h"
+#include "../gfxdevice.h"
+#include "../gfxtools.h"
+#include "../gfxfont.h"
+
+#include <math.h>
+
+typedef struct _fontfile
+{
+    char*filename;
+    int used;
+} fontfile_t;
+
+// for pdfswf_addfont
+static fontfile_t fonts[2048];
+static int fontnum = 0;
+
+/* config */
+
+static char* lastfontdir = 0;
+
+struct mapping {
+    char*pdffont;
+    char*filename;
+} pdf2t1map[] ={
+{"Times-Roman",           "n021003l"},
+{"Times-Italic",          "n021023l"},
+{"Times-Bold",            "n021004l"},
+{"Times-BoldItalic",      "n021024l"},
+{"Helvetica",             "n019003l"},
+{"Helvetica-Oblique",     "n019023l"},
+{"Helvetica-Bold",        "n019004l"},
+{"Helvetica-BoldOblique", "n019024l"},
+{"Courier",               "n022003l"},
+{"Courier-Oblique",       "n022023l"},
+{"Courier-Bold",          "n022004l"},
+{"Courier-BoldOblique",   "n022024l"},
+{"Symbol",                "s050000l"},
+{"ZapfDingbats",          "d050000l"}};
+
+GFXOutputState::GFXOutputState() {
+    this->clipping = 0;
+    this->textRender = 0;
+}
+
+GBool GFXOutputDev::interpretType3Chars() 
+{
+    return gTrue;
+}
+
+typedef struct _drawnchar
+{
+    gfxcoord_t x,y;
+    int charid;
+    gfxcolor_t color;
+} drawnchar_t;
+
+class CharBuffer
+{
+    drawnchar_t * chars;
+    int buf_size;
+    int num_chars;
+
+public:
+
+    CharBuffer()
+    {
+       buf_size = 32;
+       chars = (drawnchar_t*)malloc(sizeof(drawnchar_t)*buf_size);
+       memset(chars, 0, sizeof(drawnchar_t)*buf_size);
+       num_chars = 0;
+    }
+    ~CharBuffer()
+    {
+       free(chars);chars = 0;
+    }
+
+    void grow(int size)
+    {
+       if(size>=buf_size) {
+           buf_size += 32;
+           chars = (drawnchar_t*)realloc(chars, sizeof(drawnchar_t)*buf_size);
+       }
+    }
+
+    void addChar(int charid, gfxcoord_t x, gfxcoord_t y, gfxcolor_t color)
+    {
+       grow(num_chars);
+       chars[num_chars].x = x;
+       chars[num_chars].y = y;
+       chars[num_chars].color = color;
+       chars[num_chars].charid = charid;
+    }
+};
+
+static char*getFontID(GfxFont*font);
+
+GFXOutputDev::GFXOutputDev(parameter_t*p)
+{
+    this->jpeginfo = 0;
+    this->textmodeinfo = 0;
+    this->ttfinfo = 0;
+    this->linkinfo = 0;
+    this->pbminfo = 0;
+    this->type3active = 0;
+    this->statepos = 0;
+    this->xref = 0;
+    this->substitutepos = 0;
+    this->type3Warning = 0;
+    this->user_movex = 0;
+    this->user_movey = 0;
+    this->user_clipx1 = 0;
+    this->user_clipy1 = 0;
+    this->user_clipx2 = 0;
+    this->user_clipy2 = 0;
+    this->current_text_stroke = 0;
+    this->current_text_clip = 0;
+    this->fontlist = 0;
+    this->outer_clip_box = 0;
+    this->pages = 0;
+    this->pagebuflen = 0;
+    this->pagepos = 0;
+  
+    this->forceType0Fonts=1;
+    this->config_use_fontconfig=1;
+
+    /* configure device */
+    while(p) {
+       if(!strcmp(p->name,"forceType0Fonts")) {
+           this->forceType0Fonts = atoi(p->value);
+       } else if(!strcmp(p->name,"fontconfig")) {
+           this->config_use_fontconfig = atoi(p->value);
+       } else {
+           msg("<warning> Ignored Paramter %s=%s", p->name, p->value);
+       }
+       p = p->next;
+    }
+};
+  
+void GFXOutputDev::setDevice(gfxdevice_t*dev)
+{
+    this->device = dev;
+}
+  
+void GFXOutputDev::setMove(int x,int y)
+{
+    this->user_movex = x;
+    this->user_movey = y;
+}
+
+void GFXOutputDev::setClip(int x1,int y1,int x2,int y2)
+{
+    if(x2<x1) {int x3=x1;x1=x2;x2=x3;}
+    if(y2<y1) {int y3=y1;y1=y2;y2=y3;}
+
+    this->user_clipx1 = x1;
+    this->user_clipy1 = y1;
+    this->user_clipx2 = x2;
+    this->user_clipy2 = y2;
+}
+
+static char*getFontID(GfxFont*font)
+{
+    Ref*ref = font->getID();
+    GString*gstr = font->getName();
+    char* fname = gstr==0?0:gstr->getCString();
+    char buf[128];
+    if(fname==0) {
+       sprintf(buf, "font-%d-%d", ref->num, ref->gen);
+    } else {
+       sprintf(buf, "%s-%d-%d", fname, ref->num, ref->gen);
+    }
+    return strdup(buf);
+}
+
+static char*getFontName(GfxFont*font)
+{
+    char*fontid;
+    GString*gstr = font->getName();
+    char* fname = gstr==0?0:gstr->getCString();
+    if(fname==0) {
+       char buf[32];
+       Ref*r=font->getID();
+       sprintf(buf, "UFONT%d", r->num);
+       fontid = strdup(buf);
+    }
+    fontid = strdup(fname);
+
+    char*fontname= 0;
+    char* plus = strchr(fontid, '+');
+    if(plus && plus < &fontid[strlen(fontid)-1]) {
+       fontname = strdup(plus+1);
+    } else {
+        fontname = strdup(fontid);
+    }
+    free(fontid);
+    return fontname;
+}
+
+static char mybuf[1024];
+static char* gfxstate2str(GfxState *state)
+{
+  char*bufpos = mybuf;
+  GfxRGB rgb;
+  bufpos+=sprintf(bufpos,"CTM[%.3f/%.3f/%.3f/%.3f/%.3f/%.3f] ",
+                                   state->getCTM()[0],
+                                   state->getCTM()[1],
+                                   state->getCTM()[2],
+                                   state->getCTM()[3],
+                                   state->getCTM()[4],
+                                   state->getCTM()[5]);
+  if(state->getX1()!=0.0)
+  bufpos+=sprintf(bufpos,"X1-%.1f ",state->getX1());
+  if(state->getY1()!=0.0)
+  bufpos+=sprintf(bufpos,"Y1-%.1f ",state->getY1());
+  bufpos+=sprintf(bufpos,"X2-%.1f ",state->getX2());
+  bufpos+=sprintf(bufpos,"Y2-%.1f ",state->getY2());
+  bufpos+=sprintf(bufpos,"PW%.1f ",state->getPageWidth());
+  bufpos+=sprintf(bufpos,"PH%.1f ",state->getPageHeight());
+  /*bufpos+=sprintf(bufpos,"FC[%.1f/%.1f] ",
+         state->getFillColor()->c[0], state->getFillColor()->c[1]);
+  bufpos+=sprintf(bufpos,"SC[%.1f/%.1f] ",
+         state->getStrokeColor()->c[0], state->getFillColor()->c[1]);*/
+/*  bufpos+=sprintf(bufpos,"FC[%.1f/%.1f/%.1f/%.1f/%.1f/%.1f/%.1f/%.1f]",
+         state->getFillColor()->c[0], state->getFillColor()->c[1],
+         state->getFillColor()->c[2], state->getFillColor()->c[3],
+         state->getFillColor()->c[4], state->getFillColor()->c[5],
+         state->getFillColor()->c[6], state->getFillColor()->c[7]);
+  bufpos+=sprintf(bufpos,"SC[%.1f/%.1f/%.1f/%.1f/%.1f/%.1f/%.1f/%.1f]",
+         state->getStrokeColor()->c[0], state->getFillColor()->c[1],
+         state->getStrokeColor()->c[2], state->getFillColor()->c[3],
+         state->getStrokeColor()->c[4], state->getFillColor()->c[5],
+         state->getStrokeColor()->c[6], state->getFillColor()->c[7]);*/
+  state->getFillRGB(&rgb);
+  if(rgb.r || rgb.g || rgb.b)
+  bufpos+=sprintf(bufpos,"FR[%.1f/%.1f/%.1f] ", rgb.r,rgb.g,rgb.b);
+  state->getStrokeRGB(&rgb);
+  if(rgb.r || rgb.g || rgb.b)
+  bufpos+=sprintf(bufpos,"SR[%.1f/%.1f/%.1f] ", rgb.r,rgb.g,rgb.b);
+  if(state->getFillColorSpace()->getNComps()>1)
+  bufpos+=sprintf(bufpos,"CS[[%d]] ",state->getFillColorSpace()->getNComps());
+  if(state->getStrokeColorSpace()->getNComps()>1)
+  bufpos+=sprintf(bufpos,"SS[[%d]] ",state->getStrokeColorSpace()->getNComps());
+  if(state->getFillPattern())
+  bufpos+=sprintf(bufpos,"FP%08x ", state->getFillPattern());
+  if(state->getStrokePattern())
+  bufpos+=sprintf(bufpos,"SP%08x ", state->getStrokePattern());
+  if(state->getFillOpacity()!=1.0)
+  bufpos+=sprintf(bufpos,"FO%.1f ", state->getFillOpacity());
+  if(state->getStrokeOpacity()!=1.0)
+  bufpos+=sprintf(bufpos,"SO%.1f ", state->getStrokeOpacity());
+
+  bufpos+=sprintf(bufpos,"LW%.1f ", state->getLineWidth());
+  double * dash;
+  int length;
+  double start;
+  state->getLineDash(&dash, &length, &start);
+  int t;
+  if(length)
+  {
+      bufpos+=sprintf(bufpos,"DASH%.1f[",start);
+      for(t=0;t<length;t++) {
+         bufpos+=sprintf(bufpos,"D%.1f",dash[t]);
+      }
+      bufpos+=sprintf(bufpos,"]");
+  }
+
+  if(state->getFlatness()!=1)
+  bufpos+=sprintf(bufpos,"F%d ", state->getFlatness());
+  if(state->getLineJoin()!=0)
+  bufpos+=sprintf(bufpos,"J%d ", state->getLineJoin());
+  if(state->getLineJoin()!=0)
+  bufpos+=sprintf(bufpos,"C%d ", state->getLineCap());
+  if(state->getLineJoin()!=0)
+  bufpos+=sprintf(bufpos,"ML%d ", state->getMiterLimit());
+
+  if(state->getFont() && getFontID(state->getFont()))
+  bufpos+=sprintf(bufpos,"F\"%s\" ",getFontID(state->getFont()));
+  bufpos+=sprintf(bufpos,"FS%.1f ", state->getFontSize());
+  bufpos+=sprintf(bufpos,"MAT[%.1f/%.1f/%.1f/%.1f/%.1f/%.1f] ", state->getTextMat()[0],state->getTextMat()[1],state->getTextMat()[2],
+                                  state->getTextMat()[3],state->getTextMat()[4],state->getTextMat()[5]);
+  if(state->getCharSpace())
+  bufpos+=sprintf(bufpos,"CS%.5f ", state->getCharSpace());
+  if(state->getWordSpace())
+  bufpos+=sprintf(bufpos,"WS%.5f ", state->getWordSpace());
+  if(state->getHorizScaling()!=1.0)
+  bufpos+=sprintf(bufpos,"SC%.1f ", state->getHorizScaling());
+  if(state->getLeading())
+  bufpos+=sprintf(bufpos,"L%.1f ", state->getLeading());
+  if(state->getRise())
+  bufpos+=sprintf(bufpos,"R%.1f ", state->getRise());
+  if(state->getRender())
+  bufpos+=sprintf(bufpos,"R%d ", state->getRender());
+  bufpos+=sprintf(bufpos,"P%08x ", state->getPath());
+  bufpos+=sprintf(bufpos,"CX%.1f ", state->getCurX());
+  bufpos+=sprintf(bufpos,"CY%.1f ", state->getCurY());
+  if(state->getLineX())
+  bufpos+=sprintf(bufpos,"LX%.1f ", state->getLineX());
+  if(state->getLineY())
+  bufpos+=sprintf(bufpos,"LY%.1f ", state->getLineY());
+  bufpos+=sprintf(bufpos," ");
+  return mybuf;
+}
+
+static void dumpFontInfo(char*loglevel, GfxFont*font);
+static int lastdumps[1024];
+static int lastdumppos = 0;
+/* nr = 0  unknown
+   nr = 1  substituting
+   nr = 2  type 3
+ */
+static void showFontError(GfxFont*font, int nr) 
+{  
+    Ref*r=font->getID();
+    int t;
+    for(t=0;t<lastdumppos;t++)
+       if(lastdumps[t] == r->num)
+           break;
+    if(t < lastdumppos)
+      return;
+    if(lastdumppos<sizeof(lastdumps)/sizeof(int))
+    lastdumps[lastdumppos++] = r->num;
+    if(nr == 0)
+      msg("<warning> The following font caused problems:");
+    else if(nr == 1)
+      msg("<warning> The following font caused problems (substituting):");
+    else if(nr == 2)
+      msg("<warning> The following Type 3 Font will be rendered as bitmap:");
+    dumpFontInfo("<warning>", font);
+}
+
+static void dumpFontInfo(char*loglevel, GfxFont*font)
+{
+  char* id = getFontID(font);
+  char* name = getFontName(font);
+  Ref* r=font->getID();
+  msg("%s=========== %s (ID:%d,%d) ==========\n", loglevel, name, r->num,r->gen);
+
+  GString*gstr  = font->getTag();
+   
+  msg("%s| Tag: %s\n", loglevel, id);
+  
+  if(font->isCIDFont()) msg("%s| is CID font\n", loglevel);
+
+  GfxFontType type=font->getType();
+  switch(type) {
+    case fontUnknownType:
+     msg("%s| Type: unknown\n",loglevel);
+    break;
+    case fontType1:
+     msg("%s| Type: 1\n",loglevel);
+    break;
+    case fontType1C:
+     msg("%s| Type: 1C\n",loglevel);
+    break;
+    case fontType3:
+     msg("%s| Type: 3\n",loglevel);
+    break;
+    case fontTrueType:
+     msg("%s| Type: TrueType\n",loglevel);
+    break;
+    case fontCIDType0:
+     msg("%s| Type: CIDType0\n",loglevel);
+    break;
+    case fontCIDType0C:
+     msg("%s| Type: CIDType0C\n",loglevel);
+    break;
+    case fontCIDType2:
+     msg("%s| Type: CIDType2\n",loglevel);
+    break;
+  }
+  
+  Ref embRef;
+  GBool embedded = font->getEmbeddedFontID(&embRef);
+  char*embeddedName=0;
+  if(font->getEmbeddedFontName()) {
+    embeddedName = font->getEmbeddedFontName()->getCString();
+  }
+  if(embedded)
+   msg("%s| Embedded id: %s id: %d\n",loglevel, FIXNULL(embeddedName), embRef.num);
+
+  gstr = font->getExtFontFile();
+  if(gstr)
+   msg("%s| External Font file: %s\n", loglevel, FIXNULL(gstr->getCString()));
+
+  // Get font descriptor flags.
+  if(font->isFixedWidth()) msg("%s| is fixed width\n", loglevel);
+  if(font->isSerif()) msg("%s| is serif\n", loglevel);
+  if(font->isSymbolic()) msg("%s| is symbolic\n", loglevel);
+  if(font->isItalic()) msg("%s| is italic\n", loglevel);
+  if(font->isBold()) msg("%s| is bold\n", loglevel);
+
+  free(id);
+  free(name);
+}
+
+//void GFXOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str, int width, int height, GBool invert, GBool inlineImg) {printf("void GFXOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str, int width, int height, GBool invert, GBool inlineImg) \n");}
+//void GFXOutputDev::drawImage(GfxState *state, Object *ref, Stream *str, int width, int height, GfxImageColorMap *colorMap, GBool inlineImg) {printf("void GFXOutputDev::drawImage(GfxState *state, Object *ref, Stream *str, int width, int height, GfxImageColorMap *colorMap, GBool inlineImg) \n");}
+
+
+void dump_outline(gfxline_t*line)
+{
+    while(line) {
+       if(line->type == gfx_moveTo) {
+           msg("<debug> |     moveTo %.2f %.2f", line->x,line->y);
+       } else if(line->type == gfx_lineTo) {
+           msg("<debug> |     lineTo %.2f %.2f", line->x,line->y);
+       } else if(line->type == gfx_splineTo) {
+           msg("<debug> |     splineTo (%.2f %.2f) %.2f %.2f", line->sx,line->sy, line->x, line->y);
+       }
+       line = line->next;
+    }
+}
+
+gfxline_t* gfxPath_to_gfxline(GfxState*state, GfxPath*path, int closed, int user_movex, int user_movey)
+{
+    int num = path->getNumSubpaths();
+    int s,t;
+    int cpos = 0;
+    double lastx=0,lasty=0,posx=0,posy=0;
+    int needsfix=0;
+    if(!num) {
+       msg("<warning> empty path");
+       return 0;
+    }
+    gfxdrawer_t draw;
+    gfxdrawer_target_gfxline(&draw);
+
+    for(t = 0; t < num; t++) {
+       GfxSubpath *subpath = path->getSubpath(t);
+       int subnum = subpath->getNumPoints();
+       double bx=0,by=0,cx=0,cy=0;
+
+       for(s=0;s<subnum;s++) {
+          double x,y;
+          
+          state->transform(subpath->getX(s),subpath->getY(s),&x,&y);
+          x += user_movex;
+          y += user_movey;
+
+          if(s==0) {
+               if(closed && needsfix && (fabs(posx-lastx)+fabs(posy-lasty))>0.001) {
+                   draw.lineTo(&draw, lastx, lasty);
+               }
+               draw.moveTo(&draw, x,y);
+               posx = lastx = x; 
+               posy = lasty = y;
+               cpos = 0;
+               needsfix = 0;
+          } else if(subpath->getCurve(s) && cpos==0) {
+               bx = x;
+               by = y;
+               cpos = 1;
+          } else if(subpath->getCurve(s) && cpos==1) {
+               cx = x;
+               cy = y;
+               cpos = 2;
+          } else {
+               posx = x;
+               posy = y;
+               if(cpos==0) {
+                   draw.lineTo(&draw, x,y);
+               } else {
+                   gfxdraw_cubicTo(&draw, bx,by, cx,cy, x,y, 0.05);
+               }
+               needsfix = 1;
+               cpos = 0;
+          }
+       }
+    }
+    /* fix non-closed lines */
+    if(closed && needsfix && (fabs(posx-lastx)+fabs(posy-lasty))>0.001) {
+       draw.lineTo(&draw, lastx, lasty);
+    }
+    gfxline_t*result = (gfxline_t*)draw.result(&draw);
+    return result;
+}
+
+/*----------------------------------------------------------------------------
+ * Primitive Graphic routines
+ *----------------------------------------------------------------------------*/
+
+void GFXOutputDev::stroke(GfxState *state) 
+{
+    GfxPath * path = state->getPath();
+    gfxline_t*line= gfxPath_to_gfxline(state, path, 0, user_movex, user_movey);
+    strokeGfxline(state, line);
+    gfxline_free(line);
+}
+
+void GFXOutputDev::strokeGfxline(GfxState *state, gfxline_t*line)
+{
+    int lineCap = state->getLineCap(); // 0=butt, 1=round 2=square
+    int lineJoin = state->getLineJoin(); // 0=miter, 1=round 2=bevel
+    double miterLimit = state->getMiterLimit();
+    double width = state->getTransformedLineWidth();
+
+    GfxRGB rgb;
+    double opaq = state->getStrokeOpacity();
+    if(type3active)
+       state->getFillRGB(&rgb);
+    else
+       state->getStrokeRGB(&rgb);
+    gfxcolor_t col;
+    col.r = colToByte(rgb.r);
+    col.g = colToByte(rgb.g);
+    col.b = colToByte(rgb.b);
+    col.a = (unsigned char)(opaq*255);
+   
+    gfx_capType capType = gfx_capRound;
+    if(lineCap == 0) capType = gfx_capButt;
+    else if(lineCap == 1) capType = gfx_capRound;
+    else if(lineCap == 2) capType = gfx_capSquare;
+
+    gfx_joinType joinType = gfx_joinRound;
+    if(lineJoin == 0) joinType = gfx_joinMiter;
+    else if(lineJoin == 1) joinType = gfx_joinRound;
+    else if(lineJoin == 2) joinType = gfx_joinBevel;
+
+    int dashnum = 0;
+    double dashphase = 0;
+    double * ldash = 0;
+    state->getLineDash(&ldash, &dashnum, &dashphase);
+
+    gfxline_t*line2 = 0;
+
+    if(dashnum && ldash) {
+       float * dash = (float*)malloc(sizeof(float)*(dashnum+1));
+       int t;
+       double cut = 0;
+       int fixzero = 0;
+       msg("<trace> %d dashes", dashnum);
+       msg("<trace> |  phase: %f", dashphase);
+       for(t=0;t<dashnum;t++) {
+           dash[t] = ldash[t];
+           msg("<trace> |  d%-3d: %f", t, ldash[t]);
+       }
+       dash[dashnum] = -1;
+       if(getLogLevel() >= LOGLEVEL_TRACE) {
+           dump_outline(line);
+       }
+
+       line2 = gfxtool_dash_line(line, dash, dashphase);
+       line = line2;
+       free(dash);
+       msg("<trace> After dashing:");
+    }
+    
+    if(getLogLevel() >= LOGLEVEL_TRACE)  {
+        msg("<trace> stroke width=%f join=%s cap=%s dashes=%d color=%02x%02x%02x%02x\n",
+               width,
+               lineJoin==0?"miter": (lineJoin==1?"round":"bevel"),
+               lineCap==0?"butt": (lineJoin==1?"round":"square"),
+               dashnum,
+               col.r,col.g,col.b,col.a
+               );
+        dump_outline(line);
+    }
+   
+    //swfoutput_drawgfxline(output, line, width, &col, capType, joinType, miterLimit);
+    device->stroke(device, line, width, &col, capType, joinType, miterLimit);
+    
+    if(line2)
+       gfxline_free(line2);
+}
+
+void convertRGB()
+{
+}
+
+gfxcolor_t getFillColor(GfxState * state)
+{
+    GfxRGB rgb;
+    double opaq = state->getFillOpacity();
+    state->getFillRGB(&rgb);
+    gfxcolor_t col;
+    col.r = colToByte(rgb.r);
+    col.g = colToByte(rgb.g);
+    col.b = colToByte(rgb.b);
+    col.a = (unsigned char)(opaq*255);
+    return col;
+}
+
+void GFXOutputDev::fillGfxLine(GfxState *state, gfxline_t*line) 
+{
+    gfxcolor_t col = getFillColor(state);
+
+    if(getLogLevel() >= LOGLEVEL_TRACE)  {
+        msg("<trace> fill %02x%02x%02x%02x\n", col.r, col.g, col.b, col.a);
+        dump_outline(line);
+    }
+    device->fill(device, line, &col);
+}
+void GFXOutputDev::fill(GfxState *state) 
+{
+    GfxPath * path = state->getPath();
+    gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex, user_movey);
+    fillGfxLine(state, line);
+    gfxline_free(line);
+}
+void GFXOutputDev::eoFill(GfxState *state) 
+{
+    GfxPath * path = state->getPath();
+    gfxcolor_t col = getFillColor(state);
+
+    gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex, user_movey);
+
+    if(getLogLevel() >= LOGLEVEL_TRACE)  {
+        msg("<trace> eofill\n");
+        dump_outline(line);
+    }
+
+    device->fill(device, line, &col);
+    gfxline_free(line);
+}
+
+void GFXOutputDev::clip(GfxState *state) 
+{
+    GfxPath * path = state->getPath();
+    gfxline_t*line = gfxPath_to_gfxline(state, path, 1, user_movex, user_movey);
+    clipToGfxLine(state, line);
+    gfxline_free(line);
+}
+
+void GFXOutputDev::clipToGfxLine(GfxState *state, gfxline_t*line) 
+{
+    if(getLogLevel() >= LOGLEVEL_TRACE)  {
+        msg("<trace> clip\n");
+        dump_outline(line);
+    }
+
+    device->startclip(device, line);
+    states[statepos].clipping++;
+}
+void GFXOutputDev::eoClip(GfxState *state) 
+{
+    GfxPath * path = state->getPath();
+    gfxline_t*line = gfxPath_to_gfxline(state, path, 1, user_movex, user_movey);
+
+    if(getLogLevel() >= LOGLEVEL_TRACE)  {
+        msg("<trace> eoclip\n");
+        dump_outline(line);
+    }
+
+    device->startclip(device, line);
+    states[statepos].clipping++;
+    gfxline_free(line);
+}
+
+void GFXOutputDev::endframe()
+{
+    if(outer_clip_box) {
+       device->endclip(device);
+       outer_clip_box = 0;
+    }
+
+    device->endpage(device);
+}
+
+void GFXOutputDev::finish()
+{
+    if(outer_clip_box) {
+       if(device) {
+           device->endclip(device);
+       }
+       outer_clip_box = 0;
+    }
+}
+
+GFXOutputDev::~GFXOutputDev() 
+{
+    finish();
+
+    if(this->pages) {
+       free(this->pages); this->pages = 0;
+    }
+
+    fontlist_t*l = this->fontlist;
+    while(l) {
+       fontlist_t*next = l->next;
+       l->next = 0;
+       gfxfont_free(l->font);
+       free(l->id);l->id=0;
+       free(l->filename);l->filename=0;
+       free(l);
+       l = next;
+    }
+    this->fontlist = 0;
+};
+GBool GFXOutputDev::upsideDown() 
+{
+    return gTrue;
+};
+GBool GFXOutputDev::useDrawChar() 
+{
+    return gTrue;
+}
+
+char*renderModeDesc[]= {"fill", "stroke", "fill+stroke", "invisible",
+                      "clip+fill", "stroke+clip", "fill+stroke+clip", "clip"};
+
+#define RENDER_FILL 0
+#define RENDER_STROKE 1
+#define RENDER_FILLSTROKE 2
+#define RENDER_INVISIBLE 3
+#define RENDER_CLIP 4
+
+static char tmp_printstr[4096];
+char* makeStringPrintable(char*str)
+{
+    int len = strlen(str);
+    int dots = 0;
+    if(len>=80) {
+       len = 80;
+       dots = 1;
+    }
+    int t;
+    for(t=0;t<len;t++) {
+       char c = str[t];
+       if(c<32 || c>124) {
+           c = '.';
+       }
+       tmp_printstr[t] = c;
+    }
+    if(dots) {
+       tmp_printstr[len++] = '.';
+       tmp_printstr[len++] = '.';
+       tmp_printstr[len++] = '.';
+    }
+    tmp_printstr[len] = 0;
+    return tmp_printstr;
+}
+
+
+int getGfxCharID(gfxfont_t*font, int charnr, char *charname, int u)
+{
+    char*uniname = 0;
+    if(u>0) {
+       int t;
+       /* find out char name from unicode index 
+          TODO: should be precomputed
+        */
+       for(t=0;t<sizeof(nameToUnicodeTab)/sizeof(nameToUnicodeTab[0]);t++) {
+           if(nameToUnicodeTab[t].u == u) {
+               uniname = nameToUnicodeTab[t].name;
+               break;
+           }
+       }
+    }
+
+    if(charname) {
+       int t;
+       for(t=0;t<font->num_glyphs;t++) {
+           if(font->glyphs[t].name && !strcmp(font->glyphs[t].name,charname)) {
+               msg("<debug> Char [%d,>%s<,%d] maps to %d\n", charnr, charname, u, t);
+               return t;
+           }
+       }
+       /* if we didn't find the character, maybe
+          we can find the capitalized version */
+       for(t=0;t<font->num_glyphs;t++) {
+           if(font->glyphs[t].name && !strcasecmp(font->glyphs[t].name,charname)) {
+               msg("<debug> Char [%d,>>%s<<,%d] maps to %d\n", charnr, charname, u, t);
+               return t;
+           }
+       }
+    }
+
+    if(uniname) {
+       int t;
+       for(t=0;t<font->num_glyphs;t++) {
+           if(font->glyphs[t].name && !strcmp(font->glyphs[t].name,uniname)) {
+               msg("<debug> Char [%d,%s,>%d(%s)<] maps to %d\n", charnr, charname, u, uniname, t);
+               return t;
+           }
+       }
+       /* if we didn't find the character, maybe
+          we can find the capitalized version */
+       for(t=0;t<font->num_glyphs;t++) {
+           if(font->glyphs[t].name && !strcasecmp(font->glyphs[t].name,uniname)) {
+               msg("<debug> Char [%d,%s,>>%d(%s)<<] maps to %d\n", charnr, charname, u, uniname, t);
+               return t;
+           }
+       }
+    }
+
+    /* try to use the unicode id */
+    if(u>=0 && u<font->max_unicode && font->unicode2glyph[u]>=0) {
+       msg("<debug> Char [%d,%s,>%d<] maps to %d\n", charnr, charname, u, font->unicode2glyph[u]);
+       return font->unicode2glyph[u];
+    }
+
+    if(charnr>=0 && charnr<font->num_glyphs) {
+       msg("<debug> Char [>%d<,%s,%d] maps to %d\n", charnr, charname, u, charnr);
+       return charnr;
+    }
+    
+    return -1;
+}
+
+
+void GFXOutputDev::beginString(GfxState *state, GString *s) 
+{ 
+    int render = state->getRender();
+    if(current_text_stroke) {
+       msg("<error> Error: Incompatible change of text rendering to %d while inside cliptext", render);
+    }
+
+    msg("<trace> beginString(%s) render=%d", makeStringPrintable(s->getCString()), render);
+    double m11,m21,m12,m22;
+//    msg("<debug> %s beginstring \"%s\"\n", gfxstate2str(state), s->getCString());
+    state->getFontTransMat(&m11, &m12, &m21, &m22);
+    m11 *= state->getHorizScaling();
+    m21 *= state->getHorizScaling();
+
+    this->current_font_matrix.m00 = m11 / 1024.0;
+    this->current_font_matrix.m01 = m12 / 1024.0;
+    this->current_font_matrix.m10 = -m21 / 1024.0;
+    this->current_font_matrix.m11 = -m22 / 1024.0;
+    this->current_font_matrix.tx = 0;
+    this->current_font_matrix.ty = 0;
+
+    gfxmatrix_t m = this->current_font_matrix;
+
+    /*if(render != 3 && render != 0)
+       msg("<warning> Text rendering mode %d (%s) not fully supported yet (for text \"%s\")", render, renderModeDesc[render&7], makeStringPrintable(s->getCString()));*/
+    states[statepos].textRender = render;
+}
+
+void GFXOutputDev::drawChar(GfxState *state, double x, double y,
+                       double dx, double dy,
+                       double originX, double originY,
+                       CharCode c, int nBytes, Unicode *_u, int uLen)
+{
+    int render = state->getRender();
+    // check for invisible text -- this is used by Acrobat Capture
+    if (render == 3) {
+       msg("<debug> Ignoring invisible text: char %d at %f,%f", c, x, y);
+       return;
+    }
+
+    if(states[statepos].textRender != render)
+       msg("<error> Internal error: drawChar.render!=beginString.render");
+
+    gfxcolor_t col = getFillColor(state);
+
+    Gushort *CIDToGIDMap = 0;
+    GfxFont*font = state->getFont();
+
+    if(font->getType() == fontType3) {
+       /* type 3 chars are passed as graphics */
+       msg("<debug> type3 char at %f/%f", x, y);
+       return;
+    }
+    
+    Unicode u=0;
+    char*name=0;
+
+    if(uLen)
+       u = _u[0];
+
+    if(font->isCIDFont()) {
+       GfxCIDFont*cfont = (GfxCIDFont*)font;
+
+       if(font->getType() == fontCIDType2)
+           CIDToGIDMap = cfont->getCIDToGID();
+    } else {
+       Gfx8BitFont*font8;
+       font8 = (Gfx8BitFont*)font;
+       char**enc=font8->getEncoding();
+       name = enc[c];
+    }
+    if (CIDToGIDMap) {
+       msg("<debug> drawChar(%f, %f, c='%c' (%d), GID=%d, u=%d <%d>) CID=%d name=\"%s\" render=%d\n", x, y, (c&127)>=32?c:'?', c, CIDToGIDMap[c], u, uLen, font->isCIDFont(), FIXNULL(name), render);
+       c = CIDToGIDMap[c];
+    } else {
+       msg("<debug> drawChar(%f,%f,c='%c' (%d), u=%d <%d>) CID=%d name=\"%s\" render=%d\n",x,y,(c&127)>=32?c:'?',c,u, uLen, font->isCIDFont(), FIXNULL(name), render);
+    }
+
+    int charid = -1;
+   
+    if(uLen<=1) {
+       charid = getGfxCharID(current_gfxfont, c, name, u);
+    } else {
+       charid = getGfxCharID(current_gfxfont, c, name, -1);
+
+       if(charid < 0) {
+           /* multiple unicodes- should usually map to a ligature.
+              if the ligature doesn't exist, we need to draw
+              the characters one-by-one. */
+           int t;
+           msg("<warning> ligature %d missing in font %s\n", c, current_font_id);
+           for(t=0;t<uLen;t++) {
+               drawChar(state, x, y, dx, dy, originX, originY, c, nBytes, _u+t, 1);
+           }
+           return;
+       }
+    }
+    if(charid<0) {
+       msg("<warning> Didn't find character '%s' (c=%d,u=%d) in current charset (%s, %d characters)", 
+               FIXNULL(name),c, u, FIXNULL((char*)current_font_id), current_gfxfont->num_glyphs);
+       return;
+    }
+
+    gfxmatrix_t m = this->current_font_matrix;
+    state->transform(x, y, &m.tx, &m.ty);
+    m.tx += user_movex;
+    m.ty += user_movey;
+
+    if(render == RENDER_FILL) {
+       device->drawchar(device, current_font_id, charid, &col, &m);
+    } else {
+       msg("<debug> Drawing glyph %d as shape", charid);
+       if(!textmodeinfo) {
+           msg("<notice> Some texts will be rendered as shape");
+           textmodeinfo = 1;
+       }
+       gfxline_t*glyph = current_gfxfont->glyphs[charid].line;
+       gfxline_t*tglyph = gfxline_clone(glyph);
+       gfxline_transform(tglyph, &m);
+       if((render&3) != RENDER_INVISIBLE) {
+           gfxline_t*add = gfxline_clone(tglyph);
+           current_text_stroke = gfxline_append(current_text_stroke, add);
+       }
+       if(render&RENDER_CLIP) {
+           gfxline_t*add = gfxline_clone(tglyph);
+           current_text_clip = gfxline_append(current_text_clip, add);
+       }
+       gfxline_free(tglyph);
+    }
+}
+
+void GFXOutputDev::endString(GfxState *state) 
+{ 
+    int render = state->getRender();
+    msg("<trace> endString() render=%d textstroke=%08x", render, current_text_stroke);
+    if(states[statepos].textRender != render)
+       msg("<error> Internal error: drawChar.render!=beginString.render");
+    
+    if(current_text_stroke) {
+       /* fillstroke and stroke text rendering objects we can process right
+          now (as there may be texts of other rendering modes in this
+          text object)- clipping objects have to wait until endTextObject,
+          however */
+       device->setparameter(device, "mark","TXT");
+       if((render&3) == RENDER_FILL) {
+           fillGfxLine(state, current_text_stroke);
+           gfxline_free(current_text_stroke);
+           current_text_stroke = 0;
+       } else if((render&3) == RENDER_FILLSTROKE) {
+           fillGfxLine(state, current_text_stroke);
+           strokeGfxline(state, current_text_stroke);
+           gfxline_free(current_text_stroke);
+           current_text_stroke = 0;
+       } else if((render&3) == RENDER_STROKE) {
+           strokeGfxline(state, current_text_stroke);
+           gfxline_free(current_text_stroke);
+           current_text_stroke = 0;
+       }
+       device->setparameter(device, "mark","");
+    }
+}    
+
+void GFXOutputDev::endTextObject(GfxState *state)
+{
+    int render = state->getRender();
+    msg("<trace> endTextObject() render=%d textstroke=%08x clipstroke=%08x", render, current_text_stroke, current_text_clip);
+    if(states[statepos].textRender != render)
+       msg("<error> Internal error: drawChar.render!=beginString.render");
+    
+    if(current_text_clip) {
+       device->setparameter(device, "mark","TXT");
+       clipToGfxLine(state, current_text_clip);
+       device->setparameter(device, "mark","");
+       gfxline_free(current_text_clip);
+       current_text_clip = 0;
+    }
+}
+
+/* the logic seems to be as following:
+   first, beginType3Char is called, with the charcode and the coordinates.
+   if this function returns true, it already knew about the char and has now drawn it.
+   if the function returns false, it's a new char, and type3D1 is called with some parameters-
+   the all draw operations until endType3Char are part of the char (which in this moment is
+   at the position first passed to beginType3Char). the char ends with endType3Char.
+
+   The drawing operations between beginType3Char and endType3Char are somewhat different to
+   the normal ones. For example, the fillcolor equals the stroke color.
+*/
+
+GBool GFXOutputDev::beginType3Char(GfxState *state, double x, double y, double dx, double dy, CharCode code, Unicode *u, int uLen)
+{
+    msg("<debug> beginType3Char %d, %08x, %d", code, *u, uLen);
+    type3active = 1;
+    /* the character itself is going to be passed using the draw functions */
+    return gFalse; /* gTrue= is_in_cache? */
+}
+
+void GFXOutputDev::type3D0(GfxState *state, double wx, double wy) {
+    msg("<debug> type3D0 width=%f height=%f", wx, wy);
+}
+void GFXOutputDev::type3D1(GfxState *state, double wx, double wy, double llx, double lly, double urx, double ury) {
+    msg("<debug> type3D1 width=%f height=%f bbox=(%f,%f,%f,%f)", wx, wy,
+           llx,lly,urx,ury);
+}
+
+void GFXOutputDev::endType3Char(GfxState *state)
+{
+    type3active = 0;
+    msg("<debug> endType3Char");
+}
+
+void GFXOutputDev::startFrame(int width, int height) 
+{
+    device->startpage(device, width, height);
+}
+
+void GFXOutputDev::startPage(int pageNum, GfxState *state, double crop_x1, double crop_y1, double crop_x2, double crop_y2) 
+{
+    this->currentpage = pageNum;
+    double x1,y1,x2,y2;
+    int rot = doc->getPageRotate(1);
+    gfxcolor_t white;
+    laststate = state;
+    gfxline_t clippath[5];
+
+    white.r = white.g = white.b = white.a = 255;
+
+    /* state->transform(state->getX1(),state->getY1(),&x1,&y1);
+    state->transform(state->getX2(),state->getY2(),&x2,&y2);
+    Use CropBox, not MediaBox, as page size
+    */
+    
+    /*x1 = crop_x1;
+    y1 = crop_y1;
+    x2 = crop_x2;
+    y2 = crop_y2;*/
+    state->transform(crop_x1,crop_y1,&x1,&y1); //x1 += user_movex; y1 += user_movey;
+    state->transform(crop_x2,crop_y2,&x2,&y2); //x2 += user_movex; y2 += user_movey;
+
+    if(x2<x1) {double x3=x1;x1=x2;x2=x3;}
+    if(y2<y1) {double y3=y1;y1=y2;y2=y3;}
+
+
+    /* apply user clip box */
+    if(user_clipx1|user_clipy1|user_clipx2|user_clipy2) {
+        /*if(user_clipx1 > x1)*/ x1 = user_clipx1;
+        /*if(user_clipx2 < x2)*/ x2 = user_clipx2;
+        /*if(user_clipy1 > y1)*/ y1 = user_clipy1;
+        /*if(user_clipy2 < y2)*/ y2 = user_clipy2;
+    }
+
+    //msg("<verbose> Bounding box is (%f,%f)-(%f,%f) [shifted by %d/%d]", x1,y1,x2,y2, user_movex, user_movey);
+    
+    if(outer_clip_box) {
+       device->endclip(device);
+       outer_clip_box = 0;
+    }
+
+    msg("<notice> processing PDF page %d (%dx%d:%d:%d) (move:%d:%d)", pageNum, (int)x2-(int)x1,(int)y2-(int)y1, (int)x1, (int)y1, user_movex, user_movey);
+    if(rot!=0)
+        msg("<verbose> page is rotated %d degrees\n", rot);
+
+    clippath[0].type = gfx_moveTo;clippath[0].x = x1; clippath[0].y = y1; clippath[0].next = &clippath[1];
+    clippath[1].type = gfx_lineTo;clippath[1].x = x2; clippath[1].y = y1; clippath[1].next = &clippath[2];
+    clippath[2].type = gfx_lineTo;clippath[2].x = x2; clippath[2].y = y2; clippath[2].next = &clippath[3];
+    clippath[3].type = gfx_lineTo;clippath[3].x = x1; clippath[3].y = y2; clippath[3].next = &clippath[4];
+    clippath[4].type = gfx_lineTo;clippath[4].x = x1; clippath[4].y = y1; clippath[4].next = 0;
+    device->startclip(device, clippath); outer_clip_box = 1;
+    device->fill(device, clippath, &white);
+}
+
+void GFXOutputDev::drawLink(Link *link, Catalog *catalog) 
+{
+    double x1, y1, x2, y2, w;
+    gfxline_t points[5];
+    int x, y;
+    
+    msg("<debug> drawlink\n");
+
+    link->getRect(&x1, &y1, &x2, &y2);
+    cvtUserToDev(x1, y1, &x, &y);
+    points[0].type = gfx_moveTo;
+    points[0].x = points[4].x = x + user_movex;
+    points[0].y = points[4].y = y + user_movey;
+    points[0].next = &points[1];
+    cvtUserToDev(x2, y1, &x, &y);
+    points[1].type = gfx_lineTo;
+    points[1].x = x + user_movex;
+    points[1].y = y + user_movey;
+    points[1].next = &points[2];
+    cvtUserToDev(x2, y2, &x, &y);
+    points[2].type = gfx_lineTo;
+    points[2].x = x + user_movex;
+    points[2].y = y + user_movey;
+    points[2].next = &points[3];
+    cvtUserToDev(x1, y2, &x, &y);
+    points[3].type = gfx_lineTo;
+    points[3].x = x + user_movex;
+    points[3].y = y + user_movey;
+    points[3].next = &points[4];
+    cvtUserToDev(x1, y1, &x, &y);
+    points[4].type = gfx_lineTo;
+    points[4].x = x + user_movex;
+    points[4].y = y + user_movey;
+    points[4].next = 0;
+    
+    msg("<trace> drawlink %.2f/%.2f %.2f/%.2f %.2f/%.2f %.2f/%.2f\n",
+           points[0].x, points[0].y,
+           points[1].x, points[1].y,
+           points[2].x, points[2].y,
+           points[3].x, points[3].y); 
+
+    LinkAction*action=link->getAction();
+    char buf[128];
+    char*s = 0;
+    char*type = "-?-";
+    char*named = 0;
+    int page = -1;
+    msg("<trace> drawlink action=%d\n", action->getKind());
+    switch(action->getKind())
+    {
+        case actionGoTo: {
+            type = "GoTo";
+            LinkGoTo *ha=(LinkGoTo *)link->getAction();
+            LinkDest *dest=NULL;
+            if (ha->getDest()==NULL) 
+                dest=catalog->findDest(ha->getNamedDest());
+            else dest=ha->getDest();
+            if (dest){ 
+              if (dest->isPageRef()){
+                Ref pageref=dest->getPageRef();
+                page=catalog->findPage(pageref.num,pageref.gen);
+              }
+              else  page=dest->getPageNum();
+              sprintf(buf, "%d", page);
+              s = strdup(buf);
+            }
+        }
+        break;
+        case actionGoToR: {
+            type = "GoToR";
+            LinkGoToR*l = (LinkGoToR*)action;
+            GString*g = l->getNamedDest();
+            if(g)
+             s = strdup(g->getCString());
+        }
+        break;
+        case actionNamed: {
+            type = "Named";
+            LinkNamed*l = (LinkNamed*)action;
+            GString*name = l->getName();
+            if(name) {
+                s = strdup(name->lowerCase()->getCString());
+                named = name->getCString();
+                if(!strchr(s,':')) 
+                {
+                    if(strstr(s, "next") || strstr(s, "forward"))
+                    {
+                        page = currentpage + 1;
+                    }
+                    else if(strstr(s, "prev") || strstr(s, "back"))
+                    {
+                        page = currentpage - 1;
+                    }
+                    else if(strstr(s, "last") || strstr(s, "end"))
+                    {
+                       if(pages && pagepos>0)
+                           page = pages[pagepos-1];
+                    }
+                    else if(strstr(s, "first") || strstr(s, "top"))
+                    {
+                        page = 1;
+                    }
+                }
+            }
+        }
+        break;
+        case actionLaunch: {
+            type = "Launch";
+            LinkLaunch*l = (LinkLaunch*)action;
+            GString * str = new GString(l->getFileName());
+           GString * params = l->getParams();
+           if(params)
+               str->append(params);
+            s = strdup(str->getCString());
+            delete str;
+        }
+        break;
+        case actionURI: {
+           char*url = 0;
+            type = "URI";
+            LinkURI*l = (LinkURI*)action;
+            GString*g = l->getURI();
+            if(g) {
+             url = g->getCString();
+             s = strdup(url);
+            }
+        }
+        break;
+        case actionUnknown: {
+            type = "Unknown";
+            LinkUnknown*l = (LinkUnknown*)action;
+            s = strdup("");
+        }
+        break;
+        default: {
+            msg("<error> Unknown link type!\n");
+            break;
+        }
+    }
+
+    if(!s) s = strdup("-?-");
+    
+    msg("<trace> drawlink s=%s\n", s);
+
+    if(!linkinfo && (page || s))
+    {
+        msg("<notice> File contains links");
+        linkinfo = 1;
+    }
+    
+    if(page>0)
+    {
+        int t;
+       int lpage = -1;
+        for(t=1;t<=pagepos;t++) {
+            if(pages[t]==page) {
+               lpage = t;
+                break;
+           }
+       }
+        if(lpage<0) {
+           lpage = page;
+       }
+       char buf[80];
+       sprintf(buf, "page%d", lpage);
+       device->drawlink(device, points, buf);
+    }
+    else if(s)
+    {
+        device->drawlink(device, points, s);
+    }
+
+    msg("<verbose> \"%s\" link to \"%s\" (%d)\n", type, FIXNULL(s), page);
+    free(s);s=0;
+}
+
+void GFXOutputDev::saveState(GfxState *state) {
+  msg("<trace> saveState\n");
+  updateAll(state);
+  if(statepos>=64) {
+    msg("<error> Too many nested states in pdf.");
+    return;
+  }
+  statepos ++;
+  states[statepos].clipping = 0; //? shouldn't this be the current value?
+  states[statepos].textRender = states[statepos-1].textRender;
+};
+
+void GFXOutputDev::restoreState(GfxState *state) {
+  msg("<trace> restoreState\n");
+  updateAll(state);
+  while(states[statepos].clipping) {
+      device->endclip(device);
+      states[statepos].clipping--;
+  }
+  statepos--;
+}
+
+char* GFXOutputDev::searchFont(char*name) 
+{      
+    int i;
+    char*filename=0;
+    int is_standard_font = 0;
+       
+    msg("<verbose> SearchFont(%s)", name);
+
+    /* see if it is a pdf standard font */
+    for(i=0;i<sizeof(pdf2t1map)/sizeof(mapping);i++) 
+    {
+       if(!strcmp(name, pdf2t1map[i].pdffont))
+       {
+           name = pdf2t1map[i].filename;
+           is_standard_font = 1;
+           break;
+       }
+    }
+    /* look in all font files */
+    for(i=0;i<fontnum;i++) 
+    {
+       if(strstr(fonts[i].filename, name))
+       {
+           if(!fonts[i].used) {
+
+               fonts[i].used = 1;
+               if(!is_standard_font)
+                   msg("<notice> Using %s for %s", fonts[i].filename, name);
+           }
+           return strdup(fonts[i].filename);
+       }
+    }
+    return 0;
+}
+
+void GFXOutputDev::updateLineWidth(GfxState *state)
+{
+    double width = state->getTransformedLineWidth();
+    //swfoutput_setlinewidth(&device, width);
+}
+
+void GFXOutputDev::updateLineCap(GfxState *state)
+{
+    int c = state->getLineCap();
+}
+
+void GFXOutputDev::updateLineJoin(GfxState *state)
+{
+    int j = state->getLineJoin();
+}
+
+void GFXOutputDev::updateFillColor(GfxState *state) 
+{
+    GfxRGB rgb;
+    double opaq = state->getFillOpacity();
+    state->getFillRGB(&rgb);
+
+    //swfoutput_setfillcolor(&device, (char)(rgb.r*255), (char)(rgb.g*255), (char)(rgb.b*255), (char)(opaq*255));
+}
+
+void GFXOutputDev::updateStrokeColor(GfxState *state) 
+{
+    GfxRGB rgb;
+    double opaq = state->getStrokeOpacity();
+    state->getStrokeRGB(&rgb);
+    //swfoutput_setstrokecolor(&device, (char)(rgb.r*255), (char)(rgb.g*255), (char)(rgb.b*255), (char)(opaq*255));
+}
+
+void FoFiWrite(void *stream, char *data, int len)
+{
+   fwrite(data, len, 1, (FILE*)stream);
+}
+
+char*GFXOutputDev::writeEmbeddedFontToFile(XRef*ref, GfxFont*font)
+{
+    char*tmpFileName = NULL;
+    FILE *f;
+    int c;
+    char *fontBuf;
+    int fontLen;
+    Ref embRef;
+    Object refObj, strObj;
+    char namebuf[512];
+    tmpFileName = mktmpname(namebuf);
+    int ret;
+
+    ret = font->getEmbeddedFontID(&embRef);
+    if(!ret) {
+       msg("<verbose> Didn't get embedded font id");
+       /* not embedded- the caller should now search the font
+          directories for this font */
+       return 0;
+    }
+
+    f = fopen(tmpFileName, "wb");
+    if (!f) {
+      msg("<error> Couldn't create temporary Type 1 font file");
+       return 0;
+    }
+
+    /*if(font->isCIDFont()) {
+       GfxCIDFont* cidFont = (GfxCIDFont *)font;
+       GString c = cidFont->getCollection();
+       msg("<notice> Collection: %s", c.getCString());
+    }*/
+
+    //if (font->getType() == fontType1C) {
+    if (0) { //font->getType() == fontType1C) {
+      if (!(fontBuf = font->readEmbFontFile(xref, &fontLen))) {
+       fclose(f);
+       msg("<error> Couldn't read embedded font file");
+       return 0;
+      }
+      FoFiType1C *cvt = FoFiType1C::make(fontBuf, fontLen);
+      if(!cvt) return 0;
+      cvt->convertToType1(NULL, gTrue, FoFiWrite, f);
+      //cvt->convertToCIDType0("test", f);
+      //cvt->convertToType0("test", f);
+      delete cvt;
+      gfree(fontBuf);
+    } else if(font->getType() == fontTrueType) {
+      msg("<verbose> writing font using TrueTypeFontFile::writeTTF");
+      if (!(fontBuf = font->readEmbFontFile(xref, &fontLen))) {
+       fclose(f);
+       msg("<error> Couldn't read embedded font file");
+       return 0;
+      }
+      FoFiTrueType *cvt = FoFiTrueType::make(fontBuf, fontLen);
+      cvt->writeTTF(FoFiWrite, f);
+      delete cvt;
+      gfree(fontBuf);
+    } else {
+      font->getEmbeddedFontID(&embRef);
+      refObj.initRef(embRef.num, embRef.gen);
+      refObj.fetch(ref, &strObj);
+      refObj.free();
+      strObj.streamReset();
+      int f4[4];
+      char f4c[4];
+      int t;
+      for(t=0;t<4;t++) {
+         f4[t] = strObj.streamGetChar();
+         f4c[t] = (char)f4[t];
+         if(f4[t] == EOF)
+             break;
+      }
+      if(t==4) {
+         if(!strncmp(f4c, "true", 4)) {
+             /* some weird TTF fonts don't start with 0,1,0,0 but with "true".
+                Change this on the fly */
+             f4[0] = f4[2] = f4[3] = 0;
+             f4[1] = 1;
+         }
+         fputc(f4[0], f);
+         fputc(f4[1], f);
+         fputc(f4[2], f);
+         fputc(f4[3], f);
+
+         while ((c = strObj.streamGetChar()) != EOF) {
+           fputc(c, f);
+         }
+      }
+      strObj.streamClose();
+      strObj.free();
+    }
+    fclose(f);
+
+    return strdup(tmpFileName);
+}
+    
+char* GFXOutputDev::searchForSuitableFont(GfxFont*gfxFont)
+{
+    char*name = getFontName(gfxFont);
+    char*fontname = 0;
+    char*filename = 0;
+
+    if(!this->config_use_fontconfig)
+        return 0;
+    
+#ifdef HAVE_FONTCONFIG
+    FcPattern *pattern, *match;
+    FcResult result;
+    FcChar8 *v;
+
+    static int fcinitcalled = false; 
+        
+    msg("<debug> searchForSuitableFont(%s)", name);
+    
+    // call init ony once
+    if (!fcinitcalled) {
+        msg("<debug> Initializing FontConfig...");
+        fcinitcalled = true;
+       if(!FcInit()) {
+            msg("<debug> FontConfig Initialization failed. Disabling.");
+            config_use_fontconfig = 0;
+            return 0;
+        }
+        msg("<debug> ...initialized FontConfig");
+    }
+   
+    msg("<debug> FontConfig: Create \"%s\" Family Pattern", name);
+    pattern = FcPatternBuild(NULL, FC_FAMILY, FcTypeString, name, NULL);
+    if (gfxFont->isItalic()) // check for italic
+        msg("<debug> FontConfig: Adding Italic Slant");
+       FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ITALIC);
+    if (gfxFont->isBold()) // check for bold
+        msg("<debug> FontConfig: Adding Bold Weight");
+        FcPatternAddInteger(pattern, FC_WEIGHT, FC_WEIGHT_BOLD);
+
+    msg("<debug> FontConfig: Try to match...");
+    // configure and match using the original font name 
+    FcConfigSubstitute(0, pattern, FcMatchPattern); 
+    FcDefaultSubstitute(pattern);
+    match = FcFontMatch(0, pattern, &result);
+    
+    if (FcPatternGetString(match, "family", 0, &v) == FcResultMatch) {
+        msg("<debug> FontConfig: family=%s", (char*)v);
+        // if we get an exact match
+        if (strcmp((char *)v, name) == 0) {
+           if (FcPatternGetString(match, "file", 0, &v) == FcResultMatch) {
+               filename = strdup((char*)v); // mem leak
+               char *nfn = strrchr(filename, '/');
+               if(nfn) fontname = strdup(nfn+1);
+               else    fontname = filename;
+            }
+            msg("<debug> FontConfig: Returning \"%s\"", fontname);
+        } else {
+            // initialize patterns
+            FcPatternDestroy(pattern);
+           FcPatternDestroy(match);
+
+            // now match against serif etc.
+           if (gfxFont->isSerif()) {
+                msg("<debug> FontConfig: Create Serif Family Pattern");
+                pattern = FcPatternBuild (NULL, FC_FAMILY, FcTypeString, "serif", NULL);
+            } else if (gfxFont->isFixedWidth()) {
+                msg("<debug> FontConfig: Create Monospace Family Pattern");
+                pattern = FcPatternBuild (NULL, FC_FAMILY, FcTypeString, "monospace", NULL);
+            } else {
+                msg("<debug> FontConfig: Create Sans Family Pattern");
+                pattern = FcPatternBuild (NULL, FC_FAMILY, FcTypeString, "sans", NULL);
+            }
+
+            // check for italic
+            if (gfxFont->isItalic()) {
+                msg("<debug> FontConfig: Adding Italic Slant");
+                int bb = FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ITALIC);
+            }
+            // check for bold
+            if (gfxFont->isBold()) {
+                msg("<debug> FontConfig: Adding Bold Weight");
+                int bb = FcPatternAddInteger(pattern, FC_WEIGHT, FC_WEIGHT_BOLD);
+            }
+
+            msg("<debug> FontConfig: Try to match... (2)");
+            // configure and match using serif etc
+           FcConfigSubstitute (0, pattern, FcMatchPattern);
+            FcDefaultSubstitute (pattern);
+            match = FcFontMatch (0, pattern, &result);
+            
+            if (FcPatternGetString(match, "file", 0, &v) == FcResultMatch) {
+               filename = strdup((char*)v); // mem leak
+               char *nfn = strrchr(filename, '/');
+               if(nfn) fontname = strdup(nfn+1);
+               else    fontname = filename;
+           }
+            msg("<debug> FontConfig: Returning \"%s\"", fontname);
+        }        
+    }
+
+    //printf("FONTCONFIG: pattern");
+    //FcPatternPrint(pattern);
+    //printf("FONTCONFIG: match");
+    //FcPatternPrint(match);
+    FcPatternDestroy(pattern);
+    FcPatternDestroy(match);
+
+    pdfswf_addfont(filename);
+    return fontname;
+#else
+    return 0;
+#endif
+}
+
+char* GFXOutputDev::substituteFont(GfxFont*gfxFont, char* oldname)
+{
+    char*fontname = 0, *filename = 0;
+    msg("<notice> substituteFont(%s)", oldname);
+
+    if(!(fontname = searchForSuitableFont(gfxFont))) {
+       fontname = "Times-Roman";
+    }
+    filename = searchFont(fontname);
+    if(!filename) {
+       msg("<error> Couldn't find font %s- did you install the default fonts?", fontname);
+       return 0;
+    }
+
+    if(substitutepos>=sizeof(substitutesource)/sizeof(char*)) {
+       msg("<fatal> Too many fonts in file.");
+       exit(1);
+    }
+    if(oldname) {
+       substitutesource[substitutepos] = strdup(oldname); //mem leak
+       substitutetarget[substitutepos] = fontname;
+       msg("<notice> substituting %s -> %s", FIXNULL(oldname), FIXNULL(fontname));
+       substitutepos ++;
+    }
+    return strdup(filename); //mem leak
+}
+
+void unlinkfont(char* filename)
+{
+    int l;
+    if(!filename)
+       return;
+    l=strlen(filename);
+    unlink(filename);
+    if(!strncmp(&filename[l-4],".afm",4)) {
+       memcpy(&filename[l-4],".pfb",4);
+       unlink(filename);
+       memcpy(&filename[l-4],".pfa",4);
+       unlink(filename);
+       memcpy(&filename[l-4],".afm",4);
+       return;
+    } else 
+    if(!strncmp(&filename[l-4],".pfa",4)) {
+       memcpy(&filename[l-4],".afm",4);
+       unlink(filename);
+       memcpy(&filename[l-4],".pfa",4);
+       return;
+    } else 
+    if(!strncmp(&filename[l-4],".pfb",4)) {
+       memcpy(&filename[l-4],".afm",4);
+       unlink(filename);
+       memcpy(&filename[l-4],".pfb",4);
+       return;
+    }
+}
+
+void GFXOutputDev::setXRef(PDFDoc*doc, XRef *xref) 
+{
+    this->doc = doc;
+    this->xref = xref;
+}
+
+int GFXOutputDev::setGfxFont(char*id, char*name, char*filename, double maxSize)
+{
+    gfxfont_t*font = 0;
+    fontlist_t*last=0,*l = this->fontlist;
+
+    /* TODO: should this be part of the state? */
+    while(l) {
+       last = l;
+       if(!strcmp(l->id, id)) {
+           current_font_id = l->id;
+           current_gfxfont = l->font;
+           font = l->font;
+           device->addfont(device, id, current_gfxfont);
+           return 1;
+       }
+       l = l->next;
+    }
+    if(!filename) return 0;
+
+    /* A font size of e.g. 9 means the font will be scaled down by
+       1024 and scaled up by 9. So to have a maximum error of 1/20px,
+       we have to divide 0.05 by (fontsize/1024)
+     */
+    double quality = (1024 * 0.05) / maxSize;
+   
+    msg("<verbose> Loading %s...", filename);
+    font = gfxfont_load(filename, quality);
+    msg("<verbose> Font %s loaded successfully", filename);
+
+    l = new fontlist_t;
+    l->font = font;
+    l->filename = strdup(filename);
+    l->id = strdup(id);
+    l->next = 0;
+    current_font_id = l->id;
+    current_gfxfont = l->font;
+    if(last) {
+       last->next = l;
+    } else {
+       this->fontlist = l;
+    }
+    device->addfont(device, id, current_gfxfont);
+    return 1;
+}
+
+void GFXOutputDev::updateFont(GfxState *state) 
+{
+    GfxFont*gfxFont = state->getFont();
+      
+    if (!gfxFont) {
+       return;
+    }  
+    
+    char * fontid = getFontID(gfxFont);
+    char * fontname = getFontName(gfxFont);
+
+    double maxSize = 1.0;
+
+    if(this->info) {
+       maxSize = this->info->getMaximumFontSize(fontid);
+    }
+    
+    int t;
+    /* first, look if we substituted this font before-
+       this way, we don't initialize the T1 Fonts
+       too often */
+    for(t=0;t<substitutepos;t++) {
+       if(!strcmp(fontid, substitutesource[t])) {
+           free(fontid);fontid=0;
+           fontid = strdup(substitutetarget[t]);
+           break;
+       }
+    }
+
+    /* second, see if this is a font which was used before-
+       if so, we are done */
+    if(setGfxFont(fontid, fontname, 0, 0)) {
+       free(fontid);
+       free(fontname);
+       return;
+    }
+/*    if(swfoutput_queryfont(&device, fontid))
+       swfoutput_setfont(&device, fontid, 0);
+       
+       msg("<debug> updateFont(%s) [cached]", fontid);
+       return;
+    }*/
+
+    // look for Type 3 font
+    if (gfxFont->getType() == fontType3) {
+       if(!type3Warning) {
+           type3Warning = gTrue;
+           showFontError(gfxFont, 2);
+       }
+       free(fontid);
+       free(fontname);
+       return;
+    }
+
+    /* now either load the font, or find a substitution */
+
+    Ref embRef;
+    GBool embedded = gfxFont->getEmbeddedFontID(&embRef);
+
+    char*fileName = 0;
+    int del = 0;
+    if(embedded &&
+       (gfxFont->getType() == fontType1 ||
+       gfxFont->getType() == fontType1C ||
+       (gfxFont->getType() == fontCIDType0C && forceType0Fonts) ||
+       gfxFont->getType() == fontTrueType ||
+       gfxFont->getType() == fontCIDType2
+       ))
+    {
+      fileName = writeEmbeddedFontToFile(xref, gfxFont);
+      if(!fileName) showFontError(gfxFont,0);
+      else del = 1;
+    } else {
+      fileName = searchFont(fontname);
+      if(!fileName) showFontError(gfxFont,0);
+    }
+    if(!fileName) {
+       char * fontname = getFontName(gfxFont);
+       msg("<warning> Font %s %scould not be loaded.", fontname, embedded?"":"(not embedded) ");
+       
+       if(lastfontdir)
+           msg("<warning> Try putting a TTF version of that font (named \"%s.ttf\") into %s", fontname, lastfontdir);
+       else
+           msg("<warning> Try specifying one or more font directories");
+
+       fileName = substituteFont(gfxFont, fontid);
+       if(!fileName)
+           exit(1);
+       if(fontid) { free(fontid);fontid = strdup(substitutetarget[substitutepos-1]); /*ugly hack*/};
+       msg("<notice> Font is now %s (%s)", fontid, fileName);
+    }
+
+    if(!fileName) {
+       msg("<error> Couldn't set font %s\n", fontid);
+       free(fontid);
+       free(fontname);
+       return;
+    }
+       
+    msg("<verbose> updateFont(%s) -> %s (max size: %f)", fontid, fileName, maxSize);
+    dumpFontInfo("<verbose>", gfxFont);
+
+    //swfoutput_setfont(&device, fontid, fileName);
+    
+    if(!setGfxFont(fontid, fontname, 0, 0)) {
+       setGfxFont(fontid, fontname, fileName, maxSize);
+    }
+   
+    if(fileName && del)
+       unlinkfont(fileName);
+
+    if(fileName)
+        free(fileName);
+    free(fontid);
+    free(fontname);
+
+    msg("<verbose> |");
+}
+
+#define SQR(x) ((x)*(x))
+
+unsigned char* antialize(unsigned char*data, int width, int height, int newwidth, int newheight, int palettesize)
+{
+    if((newwidth<2 || newheight<2) ||
+       (width<=newwidth || height<=newheight))
+       return 0;
+    unsigned char*newdata;
+    int x,y;
+    newdata= (unsigned char*)malloc(newwidth*newheight);
+    int t;
+    double fx = (double)(width)/newwidth;
+    double fy = (double)(height)/newheight;
+    double px = 0;
+    int blocksize = (int)(8192/(fx*fy));
+    int r = 8192*256/palettesize;
+    for(x=0;x<newwidth;x++) {
+       double ex = px + fx;
+       int fromx = (int)px;
+       int tox = (int)ex;
+       int xweight1 = (int)(((fromx+1)-px)*256);
+       int xweight2 = (int)((ex-tox)*256);
+       double py =0;
+       for(y=0;y<newheight;y++) {
+           double ey = py + fy;
+           int fromy = (int)py;
+           int toy = (int)ey;
+           int yweight1 = (int)(((fromy+1)-py)*256);
+           int yweight2 = (int)((ey-toy)*256);
+           int a = 0;
+           int xx,yy;
+           for(xx=fromx;xx<=tox;xx++)
+           for(yy=fromy;yy<=toy;yy++) {
+               int b = 1-data[width*yy+xx];
+               int weight=256;
+               if(xx==fromx) weight = (weight*xweight1)/256;
+               if(xx==tox) weight = (weight*xweight2)/256;
+               if(yy==fromy) weight = (weight*yweight1)/256;
+               if(yy==toy) weight = (weight*yweight2)/256;
+               a+=b*weight;
+           }
+           //if(a) a=(palettesize-1)*r/blocksize;
+           newdata[y*newwidth+x] = (a*blocksize)/r;
+           py = ey;
+       }
+       px = ex;
+    }
+    return newdata;
+}
+
+#define IMAGE_TYPE_JPEG 0
+#define IMAGE_TYPE_LOSSLESS 1
+
+static void drawimage(gfxdevice_t*dev, gfxcolor_t* data, int sizex,int sizey, 
+        double x1,double y1,
+        double x2,double y2,
+        double x3,double y3,
+        double x4,double y4, int type)
+{
+    gfxcolor_t*newpic=0;
+    
+    double l1 = sqrt((x4-x1)*(x4-x1) + (y4-y1)*(y4-y1));
+    double l2 = sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1));
+   
+    gfxline_t p1,p2,p3,p4,p5;
+    p1.type=gfx_moveTo;p1.x=x1; p1.y=y1;p1.next=&p2;
+    p2.type=gfx_lineTo;p2.x=x2; p2.y=y2;p2.next=&p3;
+    p3.type=gfx_lineTo;p3.x=x3; p3.y=y3;p3.next=&p4;
+    p4.type=gfx_lineTo;p4.x=x4; p4.y=y4;p4.next=&p5;
+    p5.type=gfx_lineTo;p5.x=x1; p5.y=y1;p5.next=0;
+
+    {p1.x = (int)(p1.x*20)/20.0;
+     p1.y = (int)(p1.y*20)/20.0;
+     p2.x = (int)(p2.x*20)/20.0;
+     p2.y = (int)(p2.y*20)/20.0;
+     p3.x = (int)(p3.x*20)/20.0;
+     p3.y = (int)(p3.y*20)/20.0;
+     p4.x = (int)(p4.x*20)/20.0;
+     p4.y = (int)(p4.y*20)/20.0;
+     p5.x = (int)(p5.x*20)/20.0;
+     p5.y = (int)(p5.y*20)/20.0;
+    }
+    
+    float m00,m10,tx;
+    float m01,m11,ty;
+    
+    gfxmatrix_t m;
+    m.m00 = (p4.x-p1.x)/sizex; m.m10 = (p2.x-p1.x)/sizey;
+    m.m01 = (p4.y-p1.y)/sizex; m.m11 = (p2.y-p1.y)/sizey;
+    m.tx = p1.x - 0.5;
+    m.ty = p1.y - 0.5;
+
+    gfximage_t img;
+    img.data = (gfxcolor_t*)data;
+    img.width = sizex;
+    img.height = sizey;
+  
+    if(type == IMAGE_TYPE_JPEG)
+       /* TODO: pass image_dpi to device instead */
+       dev->setparameter(dev, "next_bitmap_is_jpeg", "1");
+
+    dev->fillbitmap(dev, &p1, &img, &m, 0);
+}
+
+void drawimagejpeg(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey, 
+        double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4)
+{
+    drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_JPEG);
+}
+
+void drawimagelossless(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey, 
+        double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4)
+{
+    drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_LOSSLESS);
+}
+
+
+void GFXOutputDev::drawGeneralImage(GfxState *state, Object *ref, Stream *str,
+                                  int width, int height, GfxImageColorMap*colorMap, GBool invert,
+                                  GBool inlineImg, int mask, int*maskColors,
+                                  Stream *maskStr, int maskWidth, int maskHeight, GBool maskInvert, GfxImageColorMap*maskColorMap)
+{
+  double x1,y1,x2,y2,x3,y3,x4,y4;
+  ImageStream *imgStr;
+  Guchar pixBuf[4];
+  GfxRGB rgb;
+  int ncomps = 1;
+  int bits = 1;
+  unsigned char* maskbitmap = 0;
+                                
+  if(colorMap) {
+    ncomps = colorMap->getNumPixelComps();
+    bits = colorMap->getBits();
+  }
+      
+  if(maskStr) {
+      int x,y;
+      unsigned char buf[8];
+      maskbitmap = (unsigned char*)malloc(maskHeight*maskWidth);
+      if(maskColorMap) {
+         ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, maskColorMap->getNumPixelComps(), maskColorMap->getBits());
+         imgMaskStr->reset();
+         unsigned char pal[256];
+         int n = 1 << colorMap->getBits();
+         int t;
+         for(t=0;t<n;t++) {
+             GfxGray gray;
+             pixBuf[0] = t;
+             maskColorMap->getGray(pixBuf, &gray);
+             pal[t] = colToByte(gray);
+         }
+         for (y = 0; y < maskHeight; y++) {
+             for (x = 0; x < maskWidth; x++) {
+                 imgMaskStr->getPixel(buf);
+                 maskbitmap[y*maskWidth+x] = pal[buf[0]];
+             }
+         }
+         delete imgMaskStr;
+      } else {
+         ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, 1, 1);
+         imgMaskStr->reset();
+         for (y = 0; y < maskHeight; y++) {
+             for (x = 0; x < maskWidth; x++) {
+                 imgMaskStr->getPixel(buf);
+                 buf[0]^=maskInvert;
+                 maskbitmap[y*maskWidth+x] = (buf[0]^1)*255;
+             }
+         }
+         delete imgMaskStr;
+      }
+      maskStr->close();
+  }
+  
+  imgStr = new ImageStream(str, width, ncomps,bits);
+  imgStr->reset();
+
+  if(!width || !height || (height<=1 && width<=1))
+  {
+      msg("<verbose> Ignoring %d by %d image", width, height);
+      unsigned char buf[8];
+      int x,y;
+      for (y = 0; y < height; ++y)
+      for (x = 0; x < width; ++x) {
+         imgStr->getPixel(buf);
+      }
+      delete imgStr;
+      if(maskbitmap)
+         free(maskbitmap);
+      return;
+  }
+
+  state->transform(0, 1, &x1, &y1); x1 += user_movex; y1 += user_movey;
+  state->transform(0, 0, &x2, &y2); x2 += user_movex; y2 += user_movey;
+  state->transform(1, 0, &x3, &y3); x3 += user_movex; y3 += user_movey;
+  state->transform(1, 1, &x4, &y4); x4 += user_movex; y4 += user_movey;
+
+
+  if(!pbminfo && !(str->getKind()==strDCT)) {
+      if(!type3active) {
+         msg("<notice> file contains pbm pictures %s",mask?"(masked)":"");
+         pbminfo = 1;
+      }
+      if(mask)
+      msg("<verbose> drawing %d by %d masked picture\n", width, height);
+  }
+  if(!jpeginfo && (str->getKind()==strDCT)) {
+      msg("<notice> file contains jpeg pictures");
+      jpeginfo = 1;
+  }
+
+  if(mask) {
+      int i,j;
+      unsigned char buf[8];
+      int x,y;
+      unsigned char*pic = new unsigned char[width*height];
+      gfxcolor_t pal[256];
+      GfxRGB rgb;
+      state->getFillRGB(&rgb);
+
+      memset(pal,255,sizeof(pal));
+      pal[0].r = (int)(colToByte(rgb.r)); pal[1].r = 0;
+      pal[0].g = (int)(colToByte(rgb.g)); pal[1].g = 0;
+      pal[0].b = (int)(colToByte(rgb.b)); pal[1].b = 0;
+      pal[0].a = 255;              pal[1].a = 0;
+    
+      int numpalette = 2;
+      int realwidth = (int)sqrt(SQR(x2-x3) + SQR(y2-y3));
+      int realheight = (int)sqrt(SQR(x1-x2) + SQR(y1-y2));
+      for (y = 0; y < height; ++y)
+      for (x = 0; x < width; ++x)
+      {
+           imgStr->getPixel(buf);
+           if(invert) 
+               buf[0]=1-buf[0];
+           pic[width*y+x] = buf[0];
+      }
+      
+      /* the size of the drawn image is added to the identifier
+        as the same image may require different bitmaps if displayed
+        at different sizes (due to antialiasing): */
+      int t,found = -1;
+      if(type3active) {
+         unsigned char*pic2 = 0;
+         numpalette = 16;
+         
+         pic2 = antialize(pic,width,height,realwidth,realheight,numpalette);
+
+         if(!pic2) {
+           delete pic;
+           delete imgStr;
+           return;
+         }
+
+         width = realwidth;
+         height = realheight;
+         free(pic);
+         pic = pic2;
+         
+         /* make a black/white palette */
+
+         float r = 255/(numpalette-1);
+         int t;
+         for(t=0;t<numpalette;t++) {
+             pal[t].r = colToByte(rgb.r);
+             pal[t].g = colToByte(rgb.g);
+             pal[t].b = colToByte(rgb.b);
+             pal[t].a = (unsigned char)(t*r);
+         }
+      }
+
+      gfxcolor_t*pic2 = new gfxcolor_t[width*height];
+      for (y = 0; y < height; ++y) {
+       for (x = 0; x < width; ++x) {
+         pic2[width*y+x] = pal[pic[y*width+x]];
+       }
+      }
+      drawimagelossless(device, pic2, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
+      free(pic2);
+      free(pic);
+      delete imgStr;
+      if(maskbitmap) free(maskbitmap);
+      return;
+  }
+
+  int x,y;
+
+  if(colorMap->getNumPixelComps()!=1 || str->getKind()==strDCT) {
+      gfxcolor_t*pic=new gfxcolor_t[width*height];
+      for (y = 0; y < height; ++y) {
+       for (x = 0; x < width; ++x) {
+         imgStr->getPixel(pixBuf);
+         colorMap->getRGB(pixBuf, &rgb);
+         pic[width*y+x].r = (unsigned char)(colToByte(rgb.r));
+         pic[width*y+x].g = (unsigned char)(colToByte(rgb.g));
+         pic[width*y+x].b = (unsigned char)(colToByte(rgb.b));
+         pic[width*y+x].a = 255;//(U8)(rgb.a * 255 + 0.5);
+         if(maskbitmap) {
+             pic[width*y+x].a = maskbitmap[(y*maskHeight/height)*maskWidth+(x*maskWidth/width)];
+         }
+       }
+      }
+      if(str->getKind()==strDCT)
+         drawimagejpeg(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
+      else
+         drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
+      delete pic;
+      delete imgStr;
+      if(maskbitmap) free(maskbitmap);
+      return;
+  } else {
+      gfxcolor_t*pic=new gfxcolor_t[width*height];
+      gfxcolor_t pal[256];
+      int n = 1 << colorMap->getBits();
+      int t;
+      for(t=0;t<256;t++) {
+         pixBuf[0] = t;
+         colorMap->getRGB(pixBuf, &rgb);
+
+         {/*if(maskColors && *maskColors==t) {
+             msg("<notice> Color %d is transparent", t);
+             if (imgData->maskColors) {
+               *alpha = 0;
+               for (i = 0; i < imgData->colorMap->getNumPixelComps(); ++i) {
+                 if (pix[i] < imgData->maskColors[2*i] ||
+                     pix[i] > imgData->maskColors[2*i+1]) {
+                   *alpha = 1;
+                   break;
+                 }
+               }
+             } else {
+               *alpha = 1;
+             }
+             if(!*alpha) {
+                   pal[t].r = 0;
+                   pal[t].g = 0;
+                   pal[t].b = 0;
+                   pal[t].a = 0;
+             }
+         } else {*/
+             pal[t].r = (unsigned char)(colToByte(rgb.r));
+             pal[t].g = (unsigned char)(colToByte(rgb.g));
+             pal[t].b = (unsigned char)(colToByte(rgb.b));
+             pal[t].a = 255;//(U8)(rgb.b * 255 + 0.5);
+         }
+      }
+      for (y = 0; y < height; ++y) {
+       for (x = 0; x < width; ++x) {
+         imgStr->getPixel(pixBuf);
+         pic[width*y+x] = pal[pixBuf[0]];
+         if(maskbitmap) {
+             pic[width*y+x].a = maskbitmap[(y*maskHeight/height)*maskWidth+(x*maskWidth/width)];
+         }
+       }
+      }
+      drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
+
+      delete pic;
+      delete imgStr;
+      if(maskbitmap) free(maskbitmap);
+      return;
+  }
+}
+
+void GFXOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
+                                  int width, int height, GBool invert,
+                                  GBool inlineImg) 
+{
+  if(states[statepos].textRender & 4) //clipped
+      return;
+  msg("<verbose> drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
+  drawGeneralImage(state,ref,str,width,height,0,invert,inlineImg,1, 0, 0,0,0,0, 0);
+}
+
+void GFXOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
+                        int width, int height, GfxImageColorMap *colorMap,
+                        int *maskColors, GBool inlineImg)
+{
+  if(states[statepos].textRender & 4) //clipped
+      return;
+
+  msg("<verbose> drawImage %dx%d, %s, %s, inline=%d", width, height, 
+         colorMap?"colorMap":"no colorMap", 
+         maskColors?"maskColors":"no maskColors",
+         inlineImg);
+  if(colorMap)
+      msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d\n", colorMap->getNumPixelComps(),
+             colorMap->getBits(),colorMap->getColorSpace()->getMode());
+  drawGeneralImage(state,ref,str,width,height,colorMap,0,inlineImg,0,maskColors, 0,0,0,0, 0);
+}
+  
+void GFXOutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str,
+                              int width, int height,
+                              GfxImageColorMap *colorMap,
+                              Stream *maskStr, int maskWidth, int maskHeight,
+                              GBool maskInvert)
+{
+  if(states[statepos].textRender & 4) //clipped
+      return;
+
+  msg("<verbose> drawMaskedImage %dx%d, %s, %dx%d mask", width, height, 
+         colorMap?"colorMap":"no colorMap", 
+         maskWidth, maskHeight);
+  if(colorMap)
+      msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d\n", colorMap->getNumPixelComps(),
+             colorMap->getBits(),colorMap->getColorSpace()->getMode());
+  drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, maskInvert, 0);
+}
+
+void GFXOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str,
+                                  int width, int height,
+                                  GfxImageColorMap *colorMap,
+                                  Stream *maskStr,
+                                  int maskWidth, int maskHeight,
+                                  GfxImageColorMap *maskColorMap)
+{
+  if(states[statepos].textRender & 4) //clipped
+      return;
+
+  msg("<verbose> drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height, 
+         colorMap?"colorMap":"no colorMap", 
+         maskWidth, maskHeight);
+  if(colorMap)
+      msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d\n", colorMap->getNumPixelComps(),
+             colorMap->getBits(),colorMap->getColorSpace()->getMode());
+  drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, 0, maskColorMap);
+}
+
+static char* dirseparator()
+{
+#ifdef WIN32
+    return "\\";
+#else
+    return "/";
+#endif
+}
+
+void addGlobalFont(char*filename)
+{
+    fontfile_t f;
+    memset(&f, 0, sizeof(fontfile_t));
+    f.filename = filename;
+    if(fontnum < sizeof(fonts)/sizeof(fonts[0])) {
+       msg("<verbose> Adding font \"%s\".", filename);
+       fonts[fontnum++] = f;
+    } else {
+       msg("<error> Too many external fonts. Not adding font file \"%s\".", filename);
+    }
+}
+
+void addGlobalLanguageDir(char*dir)
+{
+    if(!globalParams)
+        globalParams = new GlobalParams("");
+    
+    msg("<notice> Adding %s to language pack directories", dir);
+
+    int l;
+    FILE*fi = 0;
+    char* config_file = (char*)malloc(strlen(dir) + 1 + sizeof("add-to-xpdfrc") + 1);
+    strcpy(config_file, dir);
+    strcat(config_file, dirseparator());
+    strcat(config_file, "add-to-xpdfrc");
+
+    fi = fopen(config_file, "rb");
+    if(!fi) {
+        msg("<error> Could not open %s", config_file);
+        return;
+    }
+    globalParams->parseFile(new GString(config_file), fi);
+    fclose(fi);
+}
+
+void addGlobalFontDir(char*dirname)
+{
+#ifdef HAVE_DIRENT_H
+    msg("<notice> Adding %s to font directories", dirname);
+    lastfontdir = strdup(dirname);
+    DIR*dir = opendir(dirname);
+    if(!dir) {
+       msg("<warning> Couldn't open directory %s\n", dirname);
+       return;
+    }
+    struct dirent*ent;
+    while(1) {
+       ent = readdir (dir);
+       if (!ent) 
+           break;
+       int l;
+       char*name = ent->d_name;
+       char type = 0;
+       if(!name) continue;
+       l=strlen(name);
+       if(l<4)
+           continue;
+       if(!strncasecmp(&name[l-4], ".pfa", 4)) 
+           type=1;
+       if(!strncasecmp(&name[l-4], ".pfb", 4)) 
+           type=3;
+       if(!strncasecmp(&name[l-4], ".ttf", 4)) 
+           type=2;
+       if(type)
+       {
+           char*fontname = (char*)malloc(strlen(dirname)+strlen(name)+2);
+           strcpy(fontname, dirname);
+            strcat(fontname, dirseparator());
+           strcat(fontname, name);
+           addGlobalFont(fontname);
+       }
+    }
+    closedir(dir);
+#else
+    msg("<warning> No dirent.h- unable to add font dir %s", dir);
+#endif
+}
+
+void GFXOutputDev::preparePage(int pdfpage, int outputpage)
+{
+    if(pdfpage < 0)
+       return;
+
+    if(!this->pages) {
+       this->pagebuflen = 1024;
+       this->pages = (int*)malloc(this->pagebuflen*sizeof(int));
+       memset(this->pages, -1, this->pagebuflen*sizeof(int));
+    } else {
+       while(pdfpage >= this->pagebuflen)
+       {
+           int oldlen = this->pagebuflen;
+           this->pagebuflen+=1024;
+           this->pages = (int*)realloc(this->pages, this->pagebuflen*sizeof(int));
+           memset(&this->pages[oldlen], -1, (this->pagebuflen-oldlen)*sizeof(int));
+       }
+    }
+    this->pages[pdfpage] = outputpage;
+    if(pdfpage>this->pagepos)
+       this->pagepos = pdfpage;
+}
+
+/*class MemCheck
+{
+    public: ~MemCheck()
+    {
+        delete globalParams;globalParams=0;
+        Object::memCheck(stderr);
+        gMemReport(stderr);
+    }
+} myMemCheck;*/
+
diff --git a/lib/xpdf/GFXOutputDev.h b/lib/xpdf/GFXOutputDev.h
new file mode 100644 (file)
index 0000000..0439b95
--- /dev/null
@@ -0,0 +1,213 @@
+#ifndef __gfxoutputdev_h__
+#define __gfxoutputdev_h__
+
+#include "../gfxdevice.h"
+#include "../gfxsource.h"
+
+#include "InfoOutputDev.h"
+#include "PDFDoc.h"
+
+typedef struct _fontlist
+{
+    char*id;
+    char*filename;
+    gfxfont_t*font;
+    _fontlist*next;
+} fontlist_t;
+
+class GFXOutputState {
+    public:
+    int clipping;
+    int textRender;
+    GFXOutputState();
+};
+
+typedef struct _parameter
+{
+    char*name;
+    char*value;
+    struct _parameter*next;
+} parameter_t;
+
+void addGlobalFont(char*filename);
+void addGlobalLanguageDir(char*dir);
+void addGlobalFontDir(char*dirname);
+
+class GFXOutputDev:  public OutputDev {
+public:
+  gfxdevice_t* device;
+
+  // Constructor.
+  GFXOutputDev::GFXOutputDev(parameter_t*p);
+  void setDevice(gfxdevice_t*dev);
+
+  // Destructor.
+  virtual ~GFXOutputDev() ;
+
+  void setMove(int x,int y);
+  void setClip(int x1,int y1,int x2,int y2);
+
+  void setInfo(InfoOutputDev*info) {this->info = info;}
+  
+  // Start a page.
+  void startFrame(int width, int height);
+
+  virtual void startPage(int pageNum, GfxState *state, double x1, double y1, double x2, double y2) ;
+
+  void endframe();
+
+  //----- get info about output device
+
+  // Does this device use upside-down coordinates?
+  // (Upside-down means (0,0) is the top left corner of the page.)
+  virtual GBool upsideDown();
+
+  // Does this device use drawChar() or drawString()?
+  virtual GBool useDrawChar();
+  
+  virtual GBool interpretType3Chars();
+  
+  //virtual GBool useShadedFills() { return gTrue; }
+
+  //----- initialization and control
+
+  void setXRef(PDFDoc*doc, XRef *xref);
+
+  //----- link borders
+  virtual void drawLink(Link *link, Catalog *catalog) ;
+
+  //----- save/restore graphics state
+  virtual void saveState(GfxState *state) ;
+  virtual void restoreState(GfxState *state) ;
+
+  //----- update graphics state
+
+  virtual void updateFont(GfxState *state);
+  virtual void updateFillColor(GfxState *state);
+  virtual void updateStrokeColor(GfxState *state);
+  virtual void updateLineWidth(GfxState *state);
+  virtual void updateLineJoin(GfxState *state);
+  virtual void updateLineCap(GfxState *state);
+  
+  virtual void updateAll(GfxState *state) 
+  {
+      updateFont(state);
+      updateFillColor(state);
+      updateStrokeColor(state);
+      updateLineWidth(state);
+      updateLineJoin(state);
+      updateLineCap(state);
+  };
+
+  //----- path painting
+  virtual void stroke(GfxState *state) ;
+  virtual void fill(GfxState *state) ;
+  virtual void eoFill(GfxState *state) ;
+
+  //----- path clipping
+  virtual void clip(GfxState *state) ;
+  virtual void eoClip(GfxState *state) ;
+
+  //----- text drawing
+  virtual void beginString(GfxState *state, GString *s) ;
+  virtual void endString(GfxState *state) ;
+  virtual void endTextObject(GfxState *state);
+  virtual void drawChar(GfxState *state, double x, double y,
+                       double dx, double dy,
+                       double originX, double originY,
+                       CharCode code, int nBytes, Unicode *u, int uLen);
+
+  //----- image drawing
+  virtual void drawImageMask(GfxState *state, Object *ref, Stream *str,
+                            int width, int height, GBool invert,
+                            GBool inlineImg);
+  virtual void drawImage(GfxState *state, Object *ref, Stream *str,
+                        int width, int height, GfxImageColorMap *colorMap,
+                        int *maskColors, GBool inlineImg);
+  virtual void drawMaskedImage(GfxState *state, Object *ref, Stream *str,
+                              int width, int height,
+                              GfxImageColorMap *colorMap,
+                              Stream *maskStr, int maskWidth, int maskHeight,
+                              GBool maskInvert);
+  virtual void drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str,
+                                  int width, int height,
+                                  GfxImageColorMap *colorMap,
+                                  Stream *maskStr,
+                                  int maskWidth, int maskHeight,
+                                  GfxImageColorMap *maskColorMap);
+  
+  virtual GBool beginType3Char(GfxState *state, double x, double y, double dx, double dy, CharCode code, Unicode *u, int uLen);
+  virtual void endType3Char(GfxState *state);
+
+  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);
+
+  void preparePage(int pdfpage, int outputpage);
+
+  char* searchForSuitableFont(GfxFont*gfxFont);
+
+  void finish();
+
+  private:
+  void drawGeneralImage(GfxState *state, Object *ref, Stream *str,
+                                  int width, int height, GfxImageColorMap*colorMap, GBool invert,
+                                  GBool inlineImg, int mask, int *maskColors,
+                                  Stream *maskStr, int maskWidth, int maskHeight, GBool maskInvert, GfxImageColorMap*maskColorMap);
+  int setGfxFont(char*id, char*name, char*filename, double quality);
+  void strokeGfxline(GfxState *state, gfxline_t*line);
+  void clipToGfxLine(GfxState *state, gfxline_t*line);
+  void fillGfxLine(GfxState *state, gfxline_t*line);
+
+  char outer_clip_box; //whether the page clip box is still on
+
+  InfoOutputDev*info;
+  GFXOutputState states[64];
+  int statepos;
+
+  int currentpage;
+
+  PDFDoc*doc;
+  XRef*xref;
+
+  char* searchFont(char*name);
+  char* substituteFont(GfxFont*gfxFont, char*oldname);
+  char* writeEmbeddedFontToFile(XRef*ref, GfxFont*font);
+  int t1id;
+  int textmodeinfo; // did we write "Text will be rendered as polygon" yet?
+  int jpeginfo; // did we write "File contains jpegs" yet?
+  int pbminfo; // did we write "File contains jpegs" yet?
+  int linkinfo; // did we write "File contains links" yet?
+  int ttfinfo; // did we write "File contains TrueType Fonts" yet?
+  int gradientinfo; // did we write "File contains Gradients yet?
+
+  int type3active; // are we between beginType3()/endType3()?
+
+  GfxState *laststate;
+
+  char type3Warning;
+
+  char* substitutetarget[256];
+  char* substitutesource[256];
+  int substitutepos;
+
+  int user_movex,user_movey;
+  int user_clipx1,user_clipx2,user_clipy1,user_clipy2;
+
+  gfxline_t* current_text_stroke;
+  gfxline_t* current_text_clip;
+  char* current_font_id;
+  gfxfont_t* current_gfxfont;
+  gfxmatrix_t current_font_matrix;
+
+  fontlist_t* fontlist;
+
+  int*pages;
+  int pagebuflen;
+  int pagepos;
+
+  /* config */
+  int forceType0Fonts;
+  int config_use_fontconfig;
+};
+
+#endif //__gfxoutputdev_h__
diff --git a/lib/xpdf/GHash.cc b/lib/xpdf/GHash.cc
new file mode 100644 (file)
index 0000000..b51a764
--- /dev/null
@@ -0,0 +1,380 @@
+//========================================================================
+//
+// GHash.cc
+//
+// Copyright 2001-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include "gmem.h"
+#include "GString.h"
+#include "GHash.h"
+
+//------------------------------------------------------------------------
+
+struct GHashBucket {
+  GString *key;
+  union {
+    void *p;
+    int i;
+  } val;
+  GHashBucket *next;
+};
+
+struct GHashIter {
+  int h;
+  GHashBucket *p;
+};
+
+//------------------------------------------------------------------------
+
+GHash::GHash(GBool deleteKeysA) {
+  int h;
+
+  deleteKeys = deleteKeysA;
+  size = 7;
+  tab = (GHashBucket **)gmallocn(size, sizeof(GHashBucket *));
+  for (h = 0; h < size; ++h) {
+    tab[h] = NULL;
+  }
+  len = 0;
+}
+
+GHash::~GHash() {
+  GHashBucket *p;
+  int h;
+
+  for (h = 0; h < size; ++h) {
+    while (tab[h]) {
+      p = tab[h];
+      tab[h] = p->next;
+      if (deleteKeys) {
+       delete p->key;
+      }
+      delete p;
+    }
+  }
+  gfree(tab);
+}
+
+void GHash::add(GString *key, void *val) {
+  GHashBucket *p;
+  int h;
+
+  // expand the table if necessary
+  if (len >= size) {
+    expand();
+  }
+
+  // add the new symbol
+  p = new GHashBucket;
+  p->key = key;
+  p->val.p = val;
+  h = hash(key);
+  p->next = tab[h];
+  tab[h] = p;
+  ++len;
+}
+
+void GHash::add(GString *key, int val) {
+  GHashBucket *p;
+  int h;
+
+  // expand the table if necessary
+  if (len >= size) {
+    expand();
+  }
+
+  // add the new symbol
+  p = new GHashBucket;
+  p->key = key;
+  p->val.i = val;
+  h = hash(key);
+  p->next = tab[h];
+  tab[h] = p;
+  ++len;
+}
+
+void GHash::replace(GString *key, void *val) {
+  GHashBucket *p;
+  int h;
+
+  if ((p = find(key, &h))) {
+    p->val.p = val;
+    delete key;
+  } else {
+    add(key, val);
+  }
+}
+
+void GHash::replace(GString *key, int val) {
+  GHashBucket *p;
+  int h;
+
+  if ((p = find(key, &h))) {
+    p->val.i = val;
+    delete key;
+  } else {
+    add(key, val);
+  }
+}
+
+void *GHash::lookup(GString *key) {
+  GHashBucket *p;
+  int h;
+
+  if (!(p = find(key, &h))) {
+    return NULL;
+  }
+  return p->val.p;
+}
+
+int GHash::lookupInt(GString *key) {
+  GHashBucket *p;
+  int h;
+
+  if (!(p = find(key, &h))) {
+    return 0;
+  }
+  return p->val.i;
+}
+
+void *GHash::lookup(char *key) {
+  GHashBucket *p;
+  int h;
+
+  if (!(p = find(key, &h))) {
+    return NULL;
+  }
+  return p->val.p;
+}
+
+int GHash::lookupInt(char *key) {
+  GHashBucket *p;
+  int h;
+
+  if (!(p = find(key, &h))) {
+    return 0;
+  }
+  return p->val.i;
+}
+
+void *GHash::remove(GString *key) {
+  GHashBucket *p;
+  GHashBucket **q;
+  void *val;
+  int h;
+
+  if (!(p = find(key, &h))) {
+    return NULL;
+  }
+  q = &tab[h];
+  while (*q != p) {
+    q = &((*q)->next);
+  }
+  *q = p->next;
+  if (deleteKeys) {
+    delete p->key;
+  }
+  val = p->val.p;
+  delete p;
+  --len;
+  return val;
+}
+
+int GHash::removeInt(GString *key) {
+  GHashBucket *p;
+  GHashBucket **q;
+  int val;
+  int h;
+
+  if (!(p = find(key, &h))) {
+    return 0;
+  }
+  q = &tab[h];
+  while (*q != p) {
+    q = &((*q)->next);
+  }
+  *q = p->next;
+  if (deleteKeys) {
+    delete p->key;
+  }
+  val = p->val.i;
+  delete p;
+  --len;
+  return val;
+}
+
+void *GHash::remove(char *key) {
+  GHashBucket *p;
+  GHashBucket **q;
+  void *val;
+  int h;
+
+  if (!(p = find(key, &h))) {
+    return NULL;
+  }
+  q = &tab[h];
+  while (*q != p) {
+    q = &((*q)->next);
+  }
+  *q = p->next;
+  if (deleteKeys) {
+    delete p->key;
+  }
+  val = p->val.p;
+  delete p;
+  --len;
+  return val;
+}
+
+int GHash::removeInt(char *key) {
+  GHashBucket *p;
+  GHashBucket **q;
+  int val;
+  int h;
+
+  if (!(p = find(key, &h))) {
+    return 0;
+  }
+  q = &tab[h];
+  while (*q != p) {
+    q = &((*q)->next);
+  }
+  *q = p->next;
+  if (deleteKeys) {
+    delete p->key;
+  }
+  val = p->val.i;
+  delete p;
+  --len;
+  return val;
+}
+
+void GHash::startIter(GHashIter **iter) {
+  *iter = new GHashIter;
+  (*iter)->h = -1;
+  (*iter)->p = NULL;
+}
+
+GBool GHash::getNext(GHashIter **iter, GString **key, void **val) {
+  if (!*iter) {
+    return gFalse;
+  }
+  if ((*iter)->p) {
+    (*iter)->p = (*iter)->p->next;
+  }
+  while (!(*iter)->p) {
+    if (++(*iter)->h == size) {
+      delete *iter;
+      *iter = NULL;
+      return gFalse;
+    }
+    (*iter)->p = tab[(*iter)->h];
+  }
+  *key = (*iter)->p->key;
+  *val = (*iter)->p->val.p;
+  return gTrue;
+}
+
+GBool GHash::getNext(GHashIter **iter, GString **key, int *val) {
+  if (!*iter) {
+    return gFalse;
+  }
+  if ((*iter)->p) {
+    (*iter)->p = (*iter)->p->next;
+  }
+  while (!(*iter)->p) {
+    if (++(*iter)->h == size) {
+      delete *iter;
+      *iter = NULL;
+      return gFalse;
+    }
+    (*iter)->p = tab[(*iter)->h];
+  }
+  *key = (*iter)->p->key;
+  *val = (*iter)->p->val.i;
+  return gTrue;
+}
+
+void GHash::killIter(GHashIter **iter) {
+  delete *iter;
+  *iter = NULL;
+}
+
+void GHash::expand() {
+  GHashBucket **oldTab;
+  GHashBucket *p;
+  int oldSize, h, i;
+
+  oldSize = size;
+  oldTab = tab;
+  size = 2*size + 1;
+  tab = (GHashBucket **)gmallocn(size, sizeof(GHashBucket *));
+  for (h = 0; h < size; ++h) {
+    tab[h] = NULL;
+  }
+  for (i = 0; i < oldSize; ++i) {
+    while (oldTab[i]) {
+      p = oldTab[i];
+      oldTab[i] = oldTab[i]->next;
+      h = hash(p->key);
+      p->next = tab[h];
+      tab[h] = p;
+    }
+  }
+  gfree(oldTab);
+}
+
+GHashBucket *GHash::find(GString *key, int *h) {
+  GHashBucket *p;
+
+  *h = hash(key);
+  for (p = tab[*h]; p; p = p->next) {
+    if (!p->key->cmp(key)) {
+      return p;
+    }
+  }
+  return NULL;
+}
+
+GHashBucket *GHash::find(char *key, int *h) {
+  GHashBucket *p;
+
+  *h = hash(key);
+  for (p = tab[*h]; p; p = p->next) {
+    if (!p->key->cmp(key)) {
+      return p;
+    }
+  }
+  return NULL;
+}
+
+int GHash::hash(GString *key) {
+  char *p;
+  unsigned int h;
+  int i;
+
+  h = 0;
+  for (p = key->getCString(), i = 0; i < key->getLength(); ++p, ++i) {
+    h = 17 * h + (int)(*p & 0xff);
+  }
+  return (int)(h % size);
+}
+
+int GHash::hash(char *key) {
+  char *p;
+  unsigned int h;
+
+  h = 0;
+  for (p = key; *p; ++p) {
+    h = 17 * h + (int)(*p & 0xff);
+  }
+  return (int)(h % size);
+}
diff --git a/lib/xpdf/GHash.h b/lib/xpdf/GHash.h
new file mode 100644 (file)
index 0000000..31aba93
--- /dev/null
@@ -0,0 +1,78 @@
+//========================================================================
+//
+// GHash.h
+//
+// Copyright 2001-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef GHASH_H
+#define GHASH_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#include "gtypes.h"
+
+class GString;
+struct GHashBucket;
+struct GHashIter;
+
+//------------------------------------------------------------------------
+
+class GHash {
+public:
+
+  GHash(GBool deleteKeysA = gFalse);
+  ~GHash();
+  void add(GString *key, void *val);
+  void add(GString *key, int val);
+  void replace(GString *key, void *val);
+  void replace(GString *key, int val);
+  void *lookup(GString *key);
+  int lookupInt(GString *key);
+  void *lookup(char *key);
+  int lookupInt(char *key);
+  void *remove(GString *key);
+  int removeInt(GString *key);
+  void *remove(char *key);
+  int removeInt(char *key);
+  int getLength() { return len; }
+  void startIter(GHashIter **iter);
+  GBool getNext(GHashIter **iter, GString **key, void **val);
+  GBool getNext(GHashIter **iter, GString **key, int *val);
+  void killIter(GHashIter **iter);
+
+private:
+
+  void expand();
+  GHashBucket *find(GString *key, int *h);
+  GHashBucket *find(char *key, int *h);
+  int hash(GString *key);
+  int hash(char *key);
+
+  GBool deleteKeys;            // set if key strings should be deleted
+  int size;                    // number of buckets
+  int len;                     // number of entries
+  GHashBucket **tab;
+};
+
+#define deleteGHash(hash, T)                       \
+  do {                                             \
+    GHash *_hash = (hash);                         \
+    {                                              \
+      GHashIter *_iter;                            \
+      GString *_key;                               \
+      void *_p;                                    \
+      _hash->startIter(&_iter);                    \
+      while (_hash->getNext(&_iter, &_key, &_p)) { \
+        delete (T*)_p;                             \
+      }                                            \
+      delete _hash;                                \
+    }                                              \
+  } while(0)
+
+#endif
diff --git a/lib/xpdf/GList.cc b/lib/xpdf/GList.cc
new file mode 100644 (file)
index 0000000..fb5fd62
--- /dev/null
@@ -0,0 +1,97 @@
+//========================================================================
+//
+// GList.cc
+//
+// Copyright 2001-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+#include "gmem.h"
+#include "GList.h"
+
+//------------------------------------------------------------------------
+// GList
+//------------------------------------------------------------------------
+
+GList::GList() {
+  size = 8;
+  data = (void **)gmallocn(size, sizeof(void*));
+  length = 0;
+  inc = 0;
+}
+
+GList::GList(int sizeA) {
+  size = sizeA;
+  data = (void **)gmallocn(size, sizeof(void*));
+  length = 0;
+  inc = 0;
+}
+
+GList::~GList() {
+  gfree(data);
+}
+
+void GList::append(void *p) {
+  if (length >= size) {
+    expand();
+  }
+  data[length++] = p;
+}
+
+void GList::append(GList *list) {
+  int i;
+
+  while (length + list->length > size) {
+    expand();
+  }
+  for (i = 0; i < list->length; ++i) {
+    data[length++] = list->data[i];
+  }
+}
+
+void GList::insert(int i, void *p) {
+  if (length >= size) {
+    expand();
+  }
+  if (i < length) {
+    memmove(data+i+1, data+i, (length - i) * sizeof(void *));
+  }
+  data[i] = p;
+  ++length;
+}
+
+void *GList::del(int i) {
+  void *p;
+
+  p = data[i];
+  if (i < length - 1) {
+    memmove(data+i, data+i+1, (length - i - 1) * sizeof(void *));
+  }
+  --length;
+  if (size - length >= ((inc > 0) ? inc : size/2)) {
+    shrink();
+  }
+  return p;
+}
+
+void GList::sort(int (*cmp)(const void *obj1, const void *obj2)) {
+  qsort(data, length, sizeof(void *), cmp);
+}
+
+void GList::expand() {
+  size += (inc > 0) ? inc : size;
+  data = (void **)greallocn(data, size, sizeof(void*));
+}
+
+void GList::shrink() {
+  size -= (inc > 0) ? inc : size/2;
+  data = (void **)greallocn(data, size, sizeof(void*));
+}
diff --git a/lib/xpdf/GList.h b/lib/xpdf/GList.h
new file mode 100644 (file)
index 0000000..e4d8ff8
--- /dev/null
@@ -0,0 +1,96 @@
+//========================================================================
+//
+// GList.h
+//
+// Copyright 2001-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef GLIST_H
+#define GLIST_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#include "gtypes.h"
+
+//------------------------------------------------------------------------
+// GList
+//------------------------------------------------------------------------
+
+class GList {
+public:
+
+  // Create an empty list.
+  GList();
+
+  // Create an empty list with space for <size1> elements.
+  GList(int sizeA);
+
+  // Destructor - does not free pointed-to objects.
+  ~GList();
+
+  //----- general
+
+  // Get the number of elements.
+  int getLength() { return length; }
+
+  //----- ordered list support
+
+  // Return the <i>th element.
+  // Assumes 0 <= i < length.
+  void *get(int i) { return data[i]; }
+
+  // Append an element to the end of the list.
+  void append(void *p);
+
+  // Append another list to the end of this one.
+  void append(GList *list);
+
+  // Insert an element at index <i>.
+  // Assumes 0 <= i <= length.
+  void insert(int i, void *p);
+
+  // Deletes and returns the element at index <i>.
+  // Assumes 0 <= i < length.
+  void *del(int i);
+
+  // Sort the list accoring to the given comparison function.
+  // NB: this sorts an array of pointers, so the pointer args need to
+  // be double-dereferenced.
+  void sort(int (*cmp)(const void *ptr1, const void *ptr2));
+
+  //----- control
+
+  // Set allocation increment to <inc>.  If inc > 0, that many
+  // elements will be allocated every time the list is expanded.
+  // If inc <= 0, the list will be doubled in size.
+  void setAllocIncr(int incA) { inc = incA; }
+
+private:
+
+  void expand();
+  void shrink();
+
+  void **data;                 // the list elements
+  int size;                    // size of data array
+  int length;                  // number of elements on list
+  int inc;                     // allocation increment
+};
+
+#define deleteGList(list, T)                        \
+  do {                                              \
+    GList *_list = (list);                          \
+    {                                               \
+      int _i;                                       \
+      for (_i = 0; _i < _list->getLength(); ++_i) { \
+        delete (T*)_list->get(_i);                  \
+      }                                             \
+      delete _list;                                 \
+    }                                               \
+  } while (0)
+
+#endif
diff --git a/lib/xpdf/GMutex.h b/lib/xpdf/GMutex.h
new file mode 100644 (file)
index 0000000..7fa93d8
--- /dev/null
@@ -0,0 +1,49 @@
+//========================================================================
+//
+// GMutex.h
+//
+// Portable mutex macros.
+//
+// Copyright 2002-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef GMUTEX_H
+#define GMUTEX_H
+
+// Usage:
+//
+// GMutex m;
+// gInitMutex(&m);
+// ...
+// gLockMutex(&m);
+//   ... critical section ...
+// gUnlockMutex(&m);
+// ...
+// gDestroyMutex(&m);
+
+#ifdef WIN32
+
+#include <windows.h>
+
+typedef CRITICAL_SECTION GMutex;
+
+#define gInitMutex(m) InitializeCriticalSection(m)
+#define gDestroyMutex(m) DeleteCriticalSection(m)
+#define gLockMutex(m) EnterCriticalSection(m)
+#define gUnlockMutex(m) LeaveCriticalSection(m)
+
+#else // assume pthreads
+
+#include <pthread.h>
+
+typedef pthread_mutex_t GMutex;
+
+#define gInitMutex(m) pthread_mutex_init(m, NULL)
+#define gDestroyMutex(m) pthread_mutex_destroy(m)
+#define gLockMutex(m) pthread_mutex_lock(m)
+#define gUnlockMutex(m) pthread_mutex_unlock(m)
+
+#endif
+
+#endif
diff --git a/lib/xpdf/GString.cc b/lib/xpdf/GString.cc
new file mode 100644 (file)
index 0000000..049dcf3
--- /dev/null
@@ -0,0 +1,319 @@
+//========================================================================
+//
+// GString.cc
+//
+// Simple variable-length string type.
+//
+// Copyright 1996-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <stdlib.h>
+#include <stddef.h>
+#include <string.h>
+#include <ctype.h>
+#include "gtypes.h"
+#include "GString.h"
+
+static inline int size(int len) {
+  int delta;
+
+  delta = len < 256 ? 7 : 255;
+  return ((len + 1) + delta) & ~delta;
+}
+
+inline void GString::resize(int length1) {
+  char *s1;
+
+  if (!s) {
+    s = new char[size(length1)];
+  } else if (size(length1) != size(length)) {
+    s1 = new char[size(length1)];
+    if (length1 < length) {
+      memcpy(s1, s, length1);
+      s1[length1] = '\0';
+    } else {
+      memcpy(s1, s, length + 1);
+    }
+    delete[] s;
+    s = s1;
+  }
+}
+
+GString::GString() {
+  s = NULL;
+  resize(length = 0);
+  s[0] = '\0';
+}
+
+GString::GString(const char *sA) {
+  int n = strlen(sA);
+
+  s = NULL;
+  resize(length = n);
+  memcpy(s, sA, n + 1);
+}
+
+GString::GString(const char *sA, int lengthA) {
+  s = NULL;
+  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';
+}
+
+GString::GString(GString *str) {
+  s = NULL;
+  resize(length = str->getLength());
+  memcpy(s, str->getCString(), length + 1);
+}
+
+GString::GString(GString *str1, GString *str2) {
+  int n1 = str1->getLength();
+  int n2 = str2->getLength();
+
+  s = NULL;
+  resize(length = n1 + n2);
+  memcpy(s, str1->getCString(), n1);
+  memcpy(s + n1, str2->getCString(), n2 + 1);
+}
+
+GString *GString::fromInt(int x) {
+  char buf[24]; // enough space for 64-bit ints plus a little extra
+  GBool neg;
+  Guint y;
+  int i;
+
+  i = 24;
+  if (x == 0) {
+    buf[--i] = '0';
+  } else {
+    if ((neg = x < 0)) {
+      y = (Guint)-x;
+    } else {
+      y = (Guint)x;
+    }
+    while (i > 0 && y > 0) {
+      buf[--i] = '0' + y % 10;
+      y /= 10;
+    }
+    if (neg && i > 0) {
+      buf[--i] = '-';
+    }
+  }
+  return new GString(buf + i, 24 - i);
+}
+
+GString::~GString() {
+  delete[] s;
+}
+
+GString *GString::clear() {
+  s[length = 0] = '\0';
+  resize(0);
+  return this;
+}
+
+GString *GString::append(char c) {
+  resize(length + 1);
+  s[length++] = c;
+  s[length] = '\0';
+  return this;
+}
+
+GString *GString::append(GString *str) {
+  int n = str->getLength();
+
+  resize(length + n);
+  memcpy(s + length, str->getCString(), n + 1);
+  length += n;
+  return this;
+}
+
+GString *GString::append(const char *str) {
+  int n = strlen(str);
+
+  resize(length + n);
+  memcpy(s + length, str, n + 1);
+  length += n;
+  return this;
+}
+
+GString *GString::append(const char *str, int lengthA) {
+  resize(length + lengthA);
+  memcpy(s + length, str, lengthA);
+  length += lengthA;
+  s[length] = '\0';
+  return this;
+}
+
+GString *GString::insert(int i, char c) {
+  int j;
+
+  resize(length + 1);
+  for (j = length + 1; j > i; --j)
+    s[j] = s[j-1];
+  s[i] = c;
+  ++length;
+  return this;
+}
+
+GString *GString::insert(int i, GString *str) {
+  int n = str->getLength();
+  int j;
+
+  resize(length + n);
+  for (j = length; j >= i; --j)
+    s[j+n] = s[j];
+  memcpy(s+i, str->getCString(), n);
+  length += n;
+  return this;
+}
+
+GString *GString::insert(int i, const char *str) {
+  int n = strlen(str);
+  int j;
+
+  resize(length + n);
+  for (j = length; j >= i; --j)
+    s[j+n] = s[j];
+  memcpy(s+i, str, n);
+  length += n;
+  return this;
+}
+
+GString *GString::insert(int i, const char *str, int lengthA) {
+  int j;
+
+  resize(length + lengthA);
+  for (j = length; j >= i; --j)
+    s[j+lengthA] = s[j];
+  memcpy(s+i, str, lengthA);
+  length += lengthA;
+  return this;
+}
+
+GString *GString::del(int i, int n) {
+  int j;
+
+  if (n > 0) {
+    if (i + n > length) {
+      n = length - i;
+    }
+    for (j = i; j <= length - n; ++j) {
+      s[j] = s[j + n];
+    }
+    resize(length -= n);
+  }
+  return this;
+}
+
+GString *GString::upperCase() {
+  int i;
+
+  for (i = 0; i < length; ++i) {
+    if (islower(s[i]))
+      s[i] = toupper(s[i]);
+  }
+  return this;
+}
+
+GString *GString::lowerCase() {
+  int i;
+
+  for (i = 0; i < length; ++i) {
+    if (isupper(s[i]))
+      s[i] = tolower(s[i]);
+  }
+  return this;
+}
+
+int GString::cmp(GString *str) {
+  int n1, n2, i, x;
+  char *p1, *p2;
+
+  n1 = length;
+  n2 = str->length;
+  for (i = 0, p1 = s, p2 = str->s; i < n1 && i < n2; ++i, ++p1, ++p2) {
+    x = *p1 - *p2;
+    if (x != 0) {
+      return x;
+    }
+  }
+  return n1 - n2;
+}
+
+int GString::cmpN(GString *str, int n) {
+  int n1, n2, i, x;
+  char *p1, *p2;
+
+  n1 = length;
+  n2 = str->length;
+  for (i = 0, p1 = s, p2 = str->s;
+       i < n1 && i < n2 && i < n;
+       ++i, ++p1, ++p2) {
+    x = *p1 - *p2;
+    if (x != 0) {
+      return x;
+    }
+  }
+  if (i == n) {
+    return 0;
+  }
+  return n1 - n2;
+}
+
+int GString::cmp(const char *sA) {
+  int n1, i, x;
+  const char *p1, *p2;
+
+  n1 = length;
+  for (i = 0, p1 = s, p2 = sA; i < n1 && *p2; ++i, ++p1, ++p2) {
+    x = *p1 - *p2;
+    if (x != 0) {
+      return x;
+    }
+  }
+  if (i < n1) {
+    return 1;
+  }
+  if (*p2) {
+    return -1;
+  }
+  return 0;
+}
+
+int GString::cmpN(const char *sA, int n) {
+  int n1, i, x;
+  const char *p1, *p2;
+
+  n1 = length;
+  for (i = 0, p1 = s, p2 = sA; i < n1 && *p2 && i < n; ++i, ++p1, ++p2) {
+    x = *p1 - *p2;
+    if (x != 0) {
+      return x;
+    }
+  }
+  if (i == n) {
+    return 0;
+  }
+  if (i < n1) {
+    return 1;
+  }
+  if (*p2) {
+    return -1;
+  }
+  return 0;
+}
diff --git a/lib/xpdf/GString.h b/lib/xpdf/GString.h
new file mode 100644 (file)
index 0000000..f4ff7c6
--- /dev/null
@@ -0,0 +1,97 @@
+//========================================================================
+//
+// GString.h
+//
+// Simple variable-length string type.
+//
+// Copyright 1996-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef GSTRING_H
+#define GSTRING_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+class GString {
+public:
+
+  // Create an empty string.
+  GString();
+
+  // Create a string from a C string.
+  GString(const char *sA);
+
+  // Create a string from <lengthA> chars at <sA>.  This string
+  // can contain null characters.
+  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);
+  GString *copy() { return new GString(this); }
+
+  // Concatenate two strings.
+  GString(GString *str1, GString *str2);
+
+  // Convert an integer to a string.
+  static GString *fromInt(int x);
+
+  // Destructor.
+  ~GString();
+
+  // Get length.
+  int getLength() { return length; }
+
+  // Get C string.
+  char *getCString() { return s; }
+
+  // Get <i>th character.
+  char getChar(int i) { return s[i]; }
+
+  // Change <i>th character.
+  void setChar(int i, char c) { s[i] = c; }
+
+  // Clear string to zero length.
+  GString *clear();
+
+  // Append a character or string.
+  GString *append(char c);
+  GString *append(GString *str);
+  GString *append(const char *str);
+  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 lengthA);
+
+  // Delete a character or range of characters.
+  GString *del(int i, int n = 1);
+
+  // Convert string to all-upper/all-lower case.
+  GString *upperCase();
+  GString *lowerCase();
+
+  // Compare two strings:  -1:<  0:=  +1:>
+  int cmp(GString *str);
+  int cmpN(GString *str, int n);
+  int cmp(const char *sA);
+  int cmpN(const char *sA, int n);
+
+private:
+
+  int length;
+  char *s;
+
+  void resize(int length1);
+};
+
+#endif
diff --git a/lib/xpdf/Gfx.cc b/lib/xpdf/Gfx.cc
new file mode 100644 (file)
index 0000000..49e9407
--- /dev/null
@@ -0,0 +1,3596 @@
+//========================================================================
+//
+// Gfx.cc
+//
+// Copyright 1996-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <stddef.h>
+#include <string.h>
+#include <math.h>
+#include "gmem.h"
+#include "GlobalParams.h"
+#include "CharTypes.h"
+#include "Object.h"
+#include "Array.h"
+#include "Dict.h"
+#include "Stream.h"
+#include "Lexer.h"
+#include "Parser.h"
+#include "GfxFont.h"
+#include "GfxState.h"
+#include "OutputDev.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 recursive depth for a function shading fill.
+#define functionMaxDepth 6
+
+// Max delta allowed in any color component for a function shading fill.
+#define functionColorDelta (dblToCol(1 / 256.0))
+
+// Max number of splits along the t axis for an axial shading fill.
+#define axialMaxSplits 256
+
+// Max delta allowed in any color component for an axial shading fill.
+#define axialColorDelta (dblToCol(1 / 256.0))
+
+// Max number of splits along the t axis for a radial shading fill.
+#define radialMaxSplits 256
+
+// Max delta allowed in any color component for a radial shading fill.
+#define radialColorDelta (dblToCol(1 / 256.0))
+
+// Max recursive depth for a Gouraud triangle shading fill.
+#define gouraudMaxDepth 4
+
+// Max delta allowed in any color component for a Gouraud triangle
+// shading fill.
+#define gouraudColorDelta (dblToCol(1 / 256.0))
+
+// Max recursive depth for a patch mesh shading fill.
+#define patchMaxDepth 6
+
+// Max delta allowed in any color component for a patch mesh shading
+// fill.
+#define patchColorDelta (dblToCol(1 / 256.0))
+
+//------------------------------------------------------------------------
+// Operator table
+//------------------------------------------------------------------------
+
+#ifdef WIN32 // this works around a bug in the VC7 compiler
+#  pragma optimize("",off)
+#endif
+
+Operator Gfx::opTab[] = {
+  {"\"",  3, {tchkNum,    tchkNum,    tchkString},
+          &Gfx::opMoveSetShowText},
+  {"'",   1, {tchkString},
+          &Gfx::opMoveShowText},
+  {"B",   0, {tchkNone},
+          &Gfx::opFillStroke},
+  {"B*",  0, {tchkNone},
+          &Gfx::opEOFillStroke},
+  {"BDC", 2, {tchkName,   tchkProps},
+          &Gfx::opBeginMarkedContent},
+  {"BI",  0, {tchkNone},
+          &Gfx::opBeginImage},
+  {"BMC", 1, {tchkName},
+          &Gfx::opBeginMarkedContent},
+  {"BT",  0, {tchkNone},
+          &Gfx::opBeginText},
+  {"BX",  0, {tchkNone},
+          &Gfx::opBeginIgnoreUndef},
+  {"CS",  1, {tchkName},
+          &Gfx::opSetStrokeColorSpace},
+  {"DP",  2, {tchkName,   tchkProps},
+          &Gfx::opMarkPoint},
+  {"Do",  1, {tchkName},
+          &Gfx::opXObject},
+  {"EI",  0, {tchkNone},
+          &Gfx::opEndImage},
+  {"EMC", 0, {tchkNone},
+          &Gfx::opEndMarkedContent},
+  {"ET",  0, {tchkNone},
+          &Gfx::opEndText},
+  {"EX",  0, {tchkNone},
+          &Gfx::opEndIgnoreUndef},
+  {"F",   0, {tchkNone},
+          &Gfx::opFill},
+  {"G",   1, {tchkNum},
+          &Gfx::opSetStrokeGray},
+  {"ID",  0, {tchkNone},
+          &Gfx::opImageData},
+  {"J",   1, {tchkInt},
+          &Gfx::opSetLineCap},
+  {"K",   4, {tchkNum,    tchkNum,    tchkNum,    tchkNum},
+          &Gfx::opSetStrokeCMYKColor},
+  {"M",   1, {tchkNum},
+          &Gfx::opSetMiterLimit},
+  {"MP",  1, {tchkName},
+          &Gfx::opMarkPoint},
+  {"Q",   0, {tchkNone},
+          &Gfx::opRestore},
+  {"RG",  3, {tchkNum,    tchkNum,    tchkNum},
+          &Gfx::opSetStrokeRGBColor},
+  {"S",   0, {tchkNone},
+          &Gfx::opStroke},
+  {"SC",  -4, {tchkNum,   tchkNum,    tchkNum,    tchkNum},
+          &Gfx::opSetStrokeColor},
+  {"SCN", -5, {tchkSCN,   tchkSCN,    tchkSCN,    tchkSCN,
+              tchkSCN},
+          &Gfx::opSetStrokeColorN},
+  {"T*",  0, {tchkNone},
+          &Gfx::opTextNextLine},
+  {"TD",  2, {tchkNum,    tchkNum},
+          &Gfx::opTextMoveSet},
+  {"TJ",  1, {tchkArray},
+          &Gfx::opShowSpaceText},
+  {"TL",  1, {tchkNum},
+          &Gfx::opSetTextLeading},
+  {"Tc",  1, {tchkNum},
+          &Gfx::opSetCharSpacing},
+  {"Td",  2, {tchkNum,    tchkNum},
+          &Gfx::opTextMove},
+  {"Tf",  2, {tchkName,   tchkNum},
+          &Gfx::opSetFont},
+  {"Tj",  1, {tchkString},
+          &Gfx::opShowText},
+  {"Tm",  6, {tchkNum,    tchkNum,    tchkNum,    tchkNum,
+             tchkNum,    tchkNum},
+          &Gfx::opSetTextMatrix},
+  {"Tr",  1, {tchkInt},
+          &Gfx::opSetTextRender},
+  {"Ts",  1, {tchkNum},
+          &Gfx::opSetTextRise},
+  {"Tw",  1, {tchkNum},
+          &Gfx::opSetWordSpacing},
+  {"Tz",  1, {tchkNum},
+          &Gfx::opSetHorizScaling},
+  {"W",   0, {tchkNone},
+          &Gfx::opClip},
+  {"W*",  0, {tchkNone},
+          &Gfx::opEOClip},
+  {"b",   0, {tchkNone},
+          &Gfx::opCloseFillStroke},
+  {"b*",  0, {tchkNone},
+          &Gfx::opCloseEOFillStroke},
+  {"c",   6, {tchkNum,    tchkNum,    tchkNum,    tchkNum,
+             tchkNum,    tchkNum},
+          &Gfx::opCurveTo},
+  {"cm",  6, {tchkNum,    tchkNum,    tchkNum,    tchkNum,
+             tchkNum,    tchkNum},
+          &Gfx::opConcat},
+  {"cs",  1, {tchkName},
+          &Gfx::opSetFillColorSpace},
+  {"d",   2, {tchkArray,  tchkNum},
+          &Gfx::opSetDash},
+  {"d0",  2, {tchkNum,    tchkNum},
+          &Gfx::opSetCharWidth},
+  {"d1",  6, {tchkNum,    tchkNum,    tchkNum,    tchkNum,
+             tchkNum,    tchkNum},
+          &Gfx::opSetCacheDevice},
+  {"f",   0, {tchkNone},
+          &Gfx::opFill},
+  {"f*",  0, {tchkNone},
+          &Gfx::opEOFill},
+  {"g",   1, {tchkNum},
+          &Gfx::opSetFillGray},
+  {"gs",  1, {tchkName},
+          &Gfx::opSetExtGState},
+  {"h",   0, {tchkNone},
+          &Gfx::opClosePath},
+  {"i",   1, {tchkNum},
+          &Gfx::opSetFlat},
+  {"j",   1, {tchkInt},
+          &Gfx::opSetLineJoin},
+  {"k",   4, {tchkNum,    tchkNum,    tchkNum,    tchkNum},
+          &Gfx::opSetFillCMYKColor},
+  {"l",   2, {tchkNum,    tchkNum},
+          &Gfx::opLineTo},
+  {"m",   2, {tchkNum,    tchkNum},
+          &Gfx::opMoveTo},
+  {"n",   0, {tchkNone},
+          &Gfx::opEndPath},
+  {"q",   0, {tchkNone},
+          &Gfx::opSave},
+  {"re",  4, {tchkNum,    tchkNum,    tchkNum,    tchkNum},
+          &Gfx::opRectangle},
+  {"rg",  3, {tchkNum,    tchkNum,    tchkNum},
+          &Gfx::opSetFillRGBColor},
+  {"ri",  1, {tchkName},
+          &Gfx::opSetRenderingIntent},
+  {"s",   0, {tchkNone},
+          &Gfx::opCloseStroke},
+  {"sc",  -4, {tchkNum,   tchkNum,    tchkNum,    tchkNum},
+          &Gfx::opSetFillColor},
+  {"scn", -5, {tchkSCN,   tchkSCN,    tchkSCN,    tchkSCN,
+              tchkSCN},
+          &Gfx::opSetFillColorN},
+  {"sh",  1, {tchkName},
+          &Gfx::opShFill},
+  {"v",   4, {tchkNum,    tchkNum,    tchkNum,    tchkNum},
+          &Gfx::opCurveTo1},
+  {"w",   1, {tchkNum},
+          &Gfx::opSetLineWidth},
+  {"y",   4, {tchkNum,    tchkNum,    tchkNum,    tchkNum},
+          &Gfx::opCurveTo2},
+};
+
+#ifdef WIN32 // this works around a bug in the VC7 compiler
+#  pragma optimize("",on)
+#endif
+
+#define numOps (sizeof(opTab) / sizeof(Operator))
+
+//------------------------------------------------------------------------
+// GfxResources
+//------------------------------------------------------------------------
+
+GfxResources::GfxResources(XRef *xref, Dict *resDict, GfxResources *nextA) {
+  Object obj1, obj2;
+  Ref r;
+
+  if (resDict) {
+
+    // build font dictionary
+    fonts = NULL;
+    resDict->lookupNF("Font", &obj1);
+    if (obj1.isRef()) {
+      obj1.fetch(xref, &obj2);
+      if (obj2.isDict()) {
+       r = obj1.getRef();
+       fonts = new GfxFontDict(xref, &r, obj2.getDict());
+      }
+      obj2.free();
+    } else if (obj1.isDict()) {
+      fonts = new GfxFontDict(xref, NULL, obj1.getDict());
+    }
+    obj1.free();
+
+    // get XObject dictionary
+    resDict->lookup("XObject", &xObjDict);
+
+    // get color space dictionary
+    resDict->lookup("ColorSpace", &colorSpaceDict);
+
+    // get pattern dictionary
+    resDict->lookup("Pattern", &patternDict);
+
+    // get shading dictionary
+    resDict->lookup("Shading", &shadingDict);
+
+    // get graphics state parameter dictionary
+    resDict->lookup("ExtGState", &gStateDict);
+
+  } else {
+    fonts = NULL;
+    xObjDict.initNull();
+    colorSpaceDict.initNull();
+    patternDict.initNull();
+    shadingDict.initNull();
+    gStateDict.initNull();
+  }
+
+  next = nextA;
+}
+
+GfxResources::~GfxResources() {
+  if (fonts) {
+    delete fonts;
+  }
+  xObjDict.free();
+  colorSpaceDict.free();
+  patternDict.free();
+  shadingDict.free();
+  gStateDict.free();
+}
+
+GfxFont *GfxResources::lookupFont(char *name) {
+  GfxFont *font;
+  GfxResources *resPtr;
+
+  for (resPtr = this; resPtr; resPtr = resPtr->next) {
+    if (resPtr->fonts) {
+      if ((font = resPtr->fonts->lookup(name)))
+       return font;
+    }
+  }
+  error(-1, "Unknown font tag '%s'", name);
+  return NULL;
+}
+
+GBool GfxResources::lookupXObject(char *name, Object *obj) {
+  GfxResources *resPtr;
+
+  for (resPtr = this; resPtr; resPtr = resPtr->next) {
+    if (resPtr->xObjDict.isDict()) {
+      if (!resPtr->xObjDict.dictLookup(name, obj)->isNull())
+       return gTrue;
+      obj->free();
+    }
+  }
+  error(-1, "XObject '%s' is unknown", name);
+  return gFalse;
+}
+
+GBool GfxResources::lookupXObjectNF(char *name, Object *obj) {
+  GfxResources *resPtr;
+
+  for (resPtr = this; resPtr; resPtr = resPtr->next) {
+    if (resPtr->xObjDict.isDict()) {
+      if (!resPtr->xObjDict.dictLookupNF(name, obj)->isNull())
+       return gTrue;
+      obj->free();
+    }
+  }
+  error(-1, "XObject '%s' is unknown", name);
+  return gFalse;
+}
+
+void GfxResources::lookupColorSpace(char *name, Object *obj) {
+  GfxResources *resPtr;
+
+  for (resPtr = this; resPtr; resPtr = resPtr->next) {
+    if (resPtr->colorSpaceDict.isDict()) {
+      if (!resPtr->colorSpaceDict.dictLookup(name, obj)->isNull()) {
+       return;
+      }
+      obj->free();
+    }
+  }
+  obj->initNull();
+}
+
+GfxPattern *GfxResources::lookupPattern(char *name) {
+  GfxResources *resPtr;
+  GfxPattern *pattern;
+  Object obj;
+
+  for (resPtr = this; resPtr; resPtr = resPtr->next) {
+    if (resPtr->patternDict.isDict()) {
+      if (!resPtr->patternDict.dictLookup(name, &obj)->isNull()) {
+       pattern = GfxPattern::parse(&obj);
+       obj.free();
+       return pattern;
+      }
+      obj.free();
+    }
+  }
+  error(-1, "Unknown pattern '%s'", 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;
+
+  for (resPtr = this; resPtr; resPtr = resPtr->next) {
+    if (resPtr->gStateDict.isDict()) {
+      if (!resPtr->gStateDict.dictLookup(name, obj)->isNull()) {
+       return gTrue;
+      }
+      obj->free();
+    }
+  }
+  error(-1, "ExtGState '%s' is unknown", name);
+  return gFalse;
+}
+
+//------------------------------------------------------------------------
+// Gfx
+//------------------------------------------------------------------------
+
+Gfx::Gfx(XRef *xrefA, OutputDev *outA, int pageNum, Dict *resDict,
+        double hDPI, double vDPI, PDFRectangle *box,
+        PDFRectangle *cropBox, int rotate,
+        GBool (*abortCheckCbkA)(void *data),
+        void *abortCheckCbkDataA) {
+  int i;
+
+  xref = xrefA;
+  subPage = gFalse;
+  printCommands = globalParams->getPrintCommands();
+
+  // start the resource stack
+  res = new GfxResources(xref, resDict, NULL);
+
+  // initialize
+  out = outA;
+  state = new GfxState(hDPI, vDPI, box, rotate, out->upsideDown());
+  fontChanged = gFalse;
+  clip = clipNone;
+  ignoreUndef = 0;
+  out->startPage(pageNum, state, cropBox->x1,cropBox->y1,cropBox->x2,cropBox->y2);
+  out->setDefaultCTM(state->getCTM());
+  out->updateAll(state);
+  for (i = 0; i < 6; ++i) {
+    baseMatrix[i] = state->getCTM()[i];
+  }
+  formDepth = 0;
+  abortCheckCbk = abortCheckCbkA;
+  abortCheckCbkData = abortCheckCbkDataA;
+
+  // set crop box
+  /*if (cropBox) {
+    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(XRef *xrefA, OutputDev *outA, Dict *resDict,
+        PDFRectangle *box, PDFRectangle *cropBox,
+        GBool (*abortCheckCbkA)(void *data),
+        void *abortCheckCbkDataA) {
+  int i;
+
+  xref = xrefA;
+  subPage = gTrue;
+  printCommands = globalParams->getPrintCommands();
+
+  // start the resource stack
+  res = new GfxResources(xref, resDict, NULL);
+
+  // initialize
+  out = outA;
+  state = new GfxState(72, 72, box, 0, gFalse);
+  fontChanged = gFalse;
+  clip = clipNone;
+  ignoreUndef = 0;
+  for (i = 0; i < 6; ++i) {
+    baseMatrix[i] = state->getCTM()[i];
+  }
+  formDepth = 0;
+  abortCheckCbk = abortCheckCbkA;
+  abortCheckCbkData = abortCheckCbkDataA;
+
+  // set crop box
+  if (cropBox) {
+    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()) {
+    restoreState();
+  }
+  if (!subPage) {
+    out->endPage();
+  }
+  while (res) {
+    popResources();
+  }
+  if (state) {
+    delete state;
+  }
+}
+
+void Gfx::display(Object *obj, GBool topLevel) {
+  Object obj2;
+  int i;
+
+  if (obj->isArray()) {
+    for (i = 0; i < obj->arrayGetLength(); ++i) {
+      obj->arrayGet(i, &obj2);
+      if (!obj2.isStream()) {
+       error(-1, "Weird page contents");
+       obj2.free();
+       return;
+      }
+      obj2.free();
+    }
+  } else if (!obj->isStream()) {
+    error(-1, "Weird page contents");
+    return;
+  }
+  parser = new Parser(xref, new Lexer(xref, obj));
+  go(topLevel);
+  delete parser;
+  parser = NULL;
+}
+
+void Gfx::go(GBool topLevel) {
+  Object obj;
+  Object args[maxArgs];
+  int numArgs, i;
+  int lastAbortCheck;
+
+  // scan a sequence of objects
+  updateLevel = lastAbortCheck = 0;
+  numArgs = 0;
+  parser->getObj(&obj);
+  while (!obj.isEOF()) {
+
+    // got a command - execute it
+    if (obj.isCmd()) {
+      if (printCommands) {
+       obj.print(stdout);
+       for (i = 0; i < numArgs; ++i) {
+         printf(" ");
+         args[i].print(stdout);
+       }
+       printf("\n");
+       fflush(stdout);
+      }
+      execOp(&obj, args, numArgs);
+      obj.free();
+      for (i = 0; i < numArgs; ++i)
+       args[i].free();
+      numArgs = 0;
+
+      // periodically update display
+      if (++updateLevel >= 20000) {
+       out->dump();
+       updateLevel = 0;
+      }
+
+      // check for an abort
+      if (abortCheckCbk) {
+       if (updateLevel - lastAbortCheck > 10) {
+         if ((*abortCheckCbk)(abortCheckCbkData)) {
+           break;
+         }
+         lastAbortCheck = updateLevel;
+       }
+      }
+
+    // got an argument - save it
+    } else if (numArgs < maxArgs) {
+      args[numArgs++] = obj;
+
+    // too many arguments - something is wrong
+    } else {
+      error(getPos(), "Too many args in content stream");
+      if (printCommands) {
+       printf("throwing away arg: ");
+       obj.print(stdout);
+       printf("\n");
+       fflush(stdout);
+      }
+      obj.free();
+    }
+
+    // grab the next object
+    parser->getObj(&obj);
+  }
+  obj.free();
+
+  // args at end with no command
+  if (numArgs > 0) {
+    error(getPos(), "Leftover args in content stream");
+    if (printCommands) {
+      printf("%d leftovers:", numArgs);
+      for (i = 0; i < numArgs; ++i) {
+       printf(" ");
+       args[i].print(stdout);
+      }
+      printf("\n");
+      fflush(stdout);
+    }
+    for (i = 0; i < numArgs; ++i)
+      args[i].free();
+  }
+
+  // update display
+  if (topLevel && updateLevel > 0) {
+    out->dump();
+  }
+}
+
+void Gfx::execOp(Object *cmd, Object args[], int numArgs) {
+  Operator *op;
+  char *name;
+  Object *argPtr;
+  int i;
+
+  // find operator
+  name = cmd->getCmd();
+  if (!(op = findOp(name))) {
+    if (ignoreUndef == 0)
+      error(getPos(), "Unknown operator '%s'", name);
+    return;
+  }
+
+  // type check args
+  argPtr = args;
+  if (op->numArgs >= 0) {
+    if (numArgs < op->numArgs) {
+      error(getPos(), "Too few (%d) args to '%s' operator", numArgs, name);
+      return;
+    }
+    if (numArgs > op->numArgs) {
+#if 0
+      error(getPos(), "Too many (%d) args to '%s' operator", numArgs, name);
+#endif
+      argPtr += numArgs - op->numArgs;
+      numArgs = op->numArgs;
+    }
+  } else {
+    if (numArgs > -op->numArgs) {
+      error(getPos(), "Too many (%d) args to '%s' operator",
+           numArgs, name);
+      return;
+    }
+  }
+  for (i = 0; i < numArgs; ++i) {
+    if (!checkArg(&argPtr[i], op->tchk[i])) {
+      error(getPos(), "Arg #%d to '%s' operator is wrong type (%s)",
+           i, name, argPtr[i].getTypeName());
+      return;
+    }
+  }
+
+  // do it
+  (this->*op->func)(argPtr, numArgs);
+}
+
+Operator *Gfx::findOp(char *name) {
+  int a, b, m, cmp;
+
+  a = -1;
+  b = numOps;
+  // invariant: opTab[a] < name < opTab[b]
+  while (b - a > 1) {
+    m = (a + b) / 2;
+    cmp = strcmp(opTab[m].name, name);
+    if (cmp < 0)
+      a = m;
+    else if (cmp > 0)
+      b = m;
+    else
+      a = b = m;
+  }
+  if (cmp != 0)
+    return NULL;
+  return &opTab[a];
+}
+
+GBool Gfx::checkArg(Object *arg, TchkType type) {
+  switch (type) {
+  case tchkBool:   return arg->isBool();
+  case tchkInt:    return arg->isInt();
+  case tchkNum:    return arg->isNum();
+  case tchkString: return arg->isString();
+  case tchkName:   return arg->isName();
+  case tchkArray:  return arg->isArray();
+  case tchkProps:  return arg->isDict() || arg->isName();
+  case tchkSCN:    return arg->isNum() || arg->isName();
+  case tchkNone:   return gFalse;
+  }
+  return gFalse;
+}
+
+int Gfx::getPos() {
+  return parser ? parser->getPos() : -1;
+}
+
+//------------------------------------------------------------------------
+// graphics state operators
+//------------------------------------------------------------------------
+
+void Gfx::opSave(Object args[], int numArgs) {
+  saveState();
+}
+
+void Gfx::opRestore(Object args[], int numArgs) {
+  restoreState();
+}
+
+void Gfx::opConcat(Object args[], int numArgs) {
+  state->concatCTM(args[0].getNum(), args[1].getNum(),
+                  args[2].getNum(), args[3].getNum(),
+                  args[4].getNum(), args[5].getNum());
+  out->updateCTM(state, args[0].getNum(), args[1].getNum(),
+                args[2].getNum(), args[3].getNum(),
+                args[4].getNum(), args[5].getNum());
+  fontChanged = gTrue;
+}
+
+void Gfx::opSetDash(Object args[], int numArgs) {
+  Array *a;
+  int length;
+  Object obj;
+  double *dash;
+  int i;
+
+  a = args[0].getArray();
+  length = a->getLength();
+  if (length == 0) {
+    dash = NULL;
+  } else {
+    dash = (double *)gmallocn(length, sizeof(double));
+    for (i = 0; i < length; ++i) {
+      dash[i] = a->get(i, &obj)->getNum();
+      obj.free();
+    }
+  }
+  state->setLineDash(dash, length, args[1].getNum());
+  out->updateLineDash(state);
+}
+
+void Gfx::opSetFlat(Object args[], int numArgs) {
+  state->setFlatness((int)args[0].getNum());
+  out->updateFlatness(state);
+}
+
+void Gfx::opSetLineJoin(Object args[], int numArgs) {
+  state->setLineJoin(args[0].getInt());
+  out->updateLineJoin(state);
+}
+
+void Gfx::opSetLineCap(Object args[], int numArgs) {
+  state->setLineCap(args[0].getInt());
+  out->updateLineCap(state);
+}
+
+void Gfx::opSetMiterLimit(Object args[], int numArgs) {
+  state->setMiterLimit(args[0].getNum());
+  out->updateMiterLimit(state);
+}
+
+void Gfx::opSetLineWidth(Object args[], int numArgs) {
+  state->setLineWidth(args[0].getNum());
+  out->updateLineWidth(state);
+}
+
+void Gfx::opSetExtGState(Object args[], int numArgs) {
+  Object obj1, obj2;
+  GfxBlendMode mode;
+  GBool haveFillOP;
+
+  if (!res->lookupGState(args[0].getName(), &obj1)) {
+    return;
+  }
+  if (!obj1.isDict()) {
+    error(getPos(), "ExtGState '%s' is wrong type", args[0].getName());
+    obj1.free();
+    return;
+  }
+
+  // transparency support: blend mode, fill/stroke opacity
+  if (!obj1.dictLookup("BM", &obj2)->isNull()) {
+    if (state->parseBlendMode(&obj2, &mode)) {
+      state->setBlendMode(mode);
+      out->updateBlendMode(state);
+    } else {
+      error(getPos(), "Invalid blend mode in ExtGState");
+    }
+  }
+  obj2.free();
+  if (obj1.dictLookup("ca", &obj2)->isNum()) {
+    state->setFillOpacity(obj2.getNum());
+    out->updateFillOpacity(state);
+  }
+  obj2.free();
+  if (obj1.dictLookup("CA", &obj2)->isNum()) {
+    state->setStrokeOpacity(obj2.getNum());
+    out->updateStrokeOpacity(state);
+  }
+  obj2.free();
+
+  // fill/stroke overprint
+  if ((haveFillOP = (obj1.dictLookup("op", &obj2)->isBool()))) {
+    state->setFillOverprint(obj2.getBool());
+    out->updateFillOverprint(state);
+  }
+  obj2.free();
+  if (obj1.dictLookup("OP", &obj2)->isBool()) {
+    state->setStrokeOverprint(obj2.getBool());
+    out->updateStrokeOverprint(state);
+    if (!haveFillOP) {
+      state->setFillOverprint(obj2.getBool());
+      out->updateFillOverprint(state);
+    }
+  }
+  obj2.free();
+
+  obj1.free();
+}
+
+void Gfx::opSetRenderingIntent(Object args[], int numArgs) {
+}
+
+//------------------------------------------------------------------------
+// color operators
+//------------------------------------------------------------------------
+
+void Gfx::opSetFillGray(Object args[], int numArgs) {
+  GfxColor color;
+
+  state->setFillPattern(NULL);
+  state->setFillColorSpace(new GfxDeviceGrayColorSpace());
+  out->updateFillColorSpace(state);
+  color.c[0] = dblToCol(args[0].getNum());
+  state->setFillColor(&color);
+  out->updateFillColor(state);
+}
+
+void Gfx::opSetStrokeGray(Object args[], int numArgs) {
+  GfxColor color;
+
+  state->setStrokePattern(NULL);
+  state->setStrokeColorSpace(new GfxDeviceGrayColorSpace());
+  out->updateStrokeColorSpace(state);
+  color.c[0] = dblToCol(args[0].getNum());
+  state->setStrokeColor(&color);
+  out->updateStrokeColor(state);
+}
+
+void Gfx::opSetFillCMYKColor(Object args[], int numArgs) {
+  GfxColor color;
+  int i;
+
+  state->setFillPattern(NULL);
+  state->setFillColorSpace(new GfxDeviceCMYKColorSpace());
+  out->updateFillColorSpace(state);
+  for (i = 0; i < 4; ++i) {
+    color.c[i] = dblToCol(args[i].getNum());
+  }
+  state->setFillColor(&color);
+  out->updateFillColor(state);
+}
+
+void Gfx::opSetStrokeCMYKColor(Object args[], int numArgs) {
+  GfxColor color;
+  int i;
+
+  state->setStrokePattern(NULL);
+  state->setStrokeColorSpace(new GfxDeviceCMYKColorSpace());
+  out->updateStrokeColorSpace(state);
+  for (i = 0; i < 4; ++i) {
+    color.c[i] = dblToCol(args[i].getNum());
+  }
+  state->setStrokeColor(&color);
+  out->updateStrokeColor(state);
+}
+
+void Gfx::opSetFillRGBColor(Object args[], int numArgs) {
+  GfxColor color;
+  int i;
+
+  state->setFillPattern(NULL);
+  state->setFillColorSpace(new GfxDeviceRGBColorSpace());
+  out->updateFillColorSpace(state);
+  for (i = 0; i < 3; ++i) {
+    color.c[i] = dblToCol(args[i].getNum());
+  }
+  state->setFillColor(&color);
+  out->updateFillColor(state);
+}
+
+void Gfx::opSetStrokeRGBColor(Object args[], int numArgs) {
+  GfxColor color;
+  int i;
+
+  state->setStrokePattern(NULL);
+  state->setStrokeColorSpace(new GfxDeviceRGBColorSpace());
+  out->updateStrokeColorSpace(state);
+  for (i = 0; i < 3; ++i) {
+    color.c[i] = dblToCol(args[i].getNum());
+  }
+  state->setStrokeColor(&color);
+  out->updateStrokeColor(state);
+}
+
+void Gfx::opSetFillColorSpace(Object args[], int numArgs) {
+  Object obj;
+  GfxColorSpace *colorSpace;
+  GfxColor color;
+  int i;
+
+  state->setFillPattern(NULL);
+  res->lookupColorSpace(args[0].getName(), &obj);
+  if (obj.isNull()) {
+    colorSpace = GfxColorSpace::parse(&args[0]);
+  } else {
+    colorSpace = GfxColorSpace::parse(&obj);
+  }
+  obj.free();
+  if (colorSpace) {
+    state->setFillColorSpace(colorSpace);
+    out->updateFillColorSpace(state);
+  } else {
+    error(getPos(), "Bad color space (fill)");
+  }
+  for (i = 0; i < gfxColorMaxComps; ++i) {
+    color.c[i] = 0;
+  }
+  state->setFillColor(&color);
+  out->updateFillColor(state);
+}
+
+void Gfx::opSetStrokeColorSpace(Object args[], int numArgs) {
+  Object obj;
+  GfxColorSpace *colorSpace;
+  GfxColor color;
+  int i;
+
+  state->setStrokePattern(NULL);
+  res->lookupColorSpace(args[0].getName(), &obj);
+  if (obj.isNull()) {
+    colorSpace = GfxColorSpace::parse(&args[0]);
+  } else {
+    colorSpace = GfxColorSpace::parse(&obj);
+  }
+  obj.free();
+  if (colorSpace) {
+    state->setStrokeColorSpace(colorSpace);
+    out->updateStrokeColorSpace(state);
+  } else {
+    error(getPos(), "Bad color space (stroke)");
+  }
+  for (i = 0; i < gfxColorMaxComps; ++i) {
+    color.c[i] = 0;
+  }
+  state->setStrokeColor(&color);
+  out->updateStrokeColor(state);
+}
+
+void Gfx::opSetFillColor(Object args[], int numArgs) {
+  GfxColor color;
+  int i;
+
+  state->setFillPattern(NULL);
+  for (i = 0; i < numArgs; ++i) {
+    color.c[i] = dblToCol(args[i].getNum());
+  }
+  state->setFillColor(&color);
+  out->updateFillColor(state);
+}
+
+void Gfx::opSetStrokeColor(Object args[], int numArgs) {
+  GfxColor color;
+  int i;
+
+  state->setStrokePattern(NULL);
+  for (i = 0; i < numArgs; ++i) {
+    color.c[i] = dblToCol(args[i].getNum());
+  }
+  state->setStrokeColor(&color);
+  out->updateStrokeColor(state);
+}
+
+void Gfx::opSetFillColorN(Object args[], int numArgs) {
+  GfxColor color;
+  GfxPattern *pattern;
+  int i;
+
+  if (state->getFillColorSpace()->getMode() == csPattern) {
+    if (numArgs > 1) {
+      for (i = 0; i < numArgs && i < 4; ++i) {
+       if (args[i].isNum()) {
+         color.c[i] = dblToCol(args[i].getNum());
+       }
+      }
+      state->setFillColor(&color);
+      out->updateFillColor(state);
+    }
+    if (args[numArgs-1].isName() &&
+       (pattern = res->lookupPattern(args[numArgs-1].getName()))) {
+      state->setFillPattern(pattern);
+    }
+
+  } else {
+    state->setFillPattern(NULL);
+    for (i = 0; i < numArgs && i < 4; ++i) {
+      if (args[i].isNum()) {
+       color.c[i] = dblToCol(args[i].getNum());
+      }
+    }
+    state->setFillColor(&color);
+    out->updateFillColor(state);
+  }
+}
+
+void Gfx::opSetStrokeColorN(Object args[], int numArgs) {
+  GfxColor color;
+  GfxPattern *pattern;
+  int i;
+
+  if (state->getStrokeColorSpace()->getMode() == csPattern) {
+    if (numArgs > 1) {
+      for (i = 0; i < numArgs && i < 4; ++i) {
+       if (args[i].isNum()) {
+         color.c[i] = dblToCol(args[i].getNum());
+       }
+      }
+      state->setStrokeColor(&color);
+      out->updateStrokeColor(state);
+    }
+    if (args[numArgs-1].isName() &&
+       (pattern = res->lookupPattern(args[numArgs-1].getName()))) {
+      state->setStrokePattern(pattern);
+    }
+
+  } else {
+    state->setStrokePattern(NULL);
+    for (i = 0; i < numArgs && i < 4; ++i) {
+      if (args[i].isNum()) {
+       color.c[i] = dblToCol(args[i].getNum());
+      }
+    }
+    state->setStrokeColor(&color);
+    out->updateStrokeColor(state);
+  }
+}
+
+//------------------------------------------------------------------------
+// path segment operators
+//------------------------------------------------------------------------
+
+void Gfx::opMoveTo(Object args[], int numArgs) {
+  state->moveTo(args[0].getNum(), args[1].getNum());
+}
+
+void Gfx::opLineTo(Object args[], int numArgs) {
+  if (!state->isCurPt()) {
+    error(getPos(), "No current point in lineto");
+    return;
+  }
+  state->lineTo(args[0].getNum(), args[1].getNum());
+}
+
+void Gfx::opCurveTo(Object args[], int numArgs) {
+  double x1, y1, x2, y2, x3, y3;
+
+  if (!state->isCurPt()) {
+    error(getPos(), "No current point in curveto");
+    return;
+  }
+  x1 = args[0].getNum();
+  y1 = args[1].getNum();
+  x2 = args[2].getNum();
+  y2 = args[3].getNum();
+  x3 = args[4].getNum();
+  y3 = args[5].getNum();
+  state->curveTo(x1, y1, x2, y2, x3, y3);
+}
+
+void Gfx::opCurveTo1(Object args[], int numArgs) {
+  double x1, y1, x2, y2, x3, y3;
+
+  if (!state->isCurPt()) {
+    error(getPos(), "No current point in curveto1");
+    return;
+  }
+  x1 = state->getCurX();
+  y1 = state->getCurY();
+  x2 = args[0].getNum();
+  y2 = args[1].getNum();
+  x3 = args[2].getNum();
+  y3 = args[3].getNum();
+  state->curveTo(x1, y1, x2, y2, x3, y3);
+}
+
+void Gfx::opCurveTo2(Object args[], int numArgs) {
+  double x1, y1, x2, y2, x3, y3;
+
+  if (!state->isCurPt()) {
+    error(getPos(), "No current point in curveto2");
+    return;
+  }
+  x1 = args[0].getNum();
+  y1 = args[1].getNum();
+  x2 = args[2].getNum();
+  y2 = args[3].getNum();
+  x3 = x2;
+  y3 = y2;
+  state->curveTo(x1, y1, x2, y2, x3, y3);
+}
+
+void Gfx::opRectangle(Object args[], int numArgs) {
+  double x, y, w, h;
+
+  x = args[0].getNum();
+  y = args[1].getNum();
+  w = args[2].getNum();
+  h = args[3].getNum();
+  state->moveTo(x, y);
+  state->lineTo(x + w, y);
+  state->lineTo(x + w, y + h);
+  state->lineTo(x, y + h);
+  state->closePath();
+}
+
+void Gfx::opClosePath(Object args[], int numArgs) {
+  if (!state->isCurPt()) {
+    error(getPos(), "No current point in closepath");
+    return;
+  }
+  state->closePath();
+}
+
+//------------------------------------------------------------------------
+// path painting operators
+//------------------------------------------------------------------------
+
+void Gfx::opEndPath(Object args[], int numArgs) {
+  doEndPath();
+}
+
+void Gfx::opStroke(Object args[], int numArgs) {
+  if (!state->isCurPt()) {
+    //error(getPos(), "No path in stroke");
+    return;
+  }
+  if (state->isPath())
+    out->stroke(state);
+  doEndPath();
+}
+
+void Gfx::opCloseStroke(Object args[], int numArgs) {
+  if (!state->isCurPt()) {
+    //error(getPos(), "No path in closepath/stroke");
+    return;
+  }
+  if (state->isPath()) {
+    state->closePath();
+    out->stroke(state);
+  }
+  doEndPath();
+}
+
+void Gfx::opFill(Object args[], int numArgs) {
+  if (!state->isCurPt()) {
+    //error(getPos(), "No path in fill");
+    return;
+  }
+  if (state->isPath()) {
+    if (state->getFillColorSpace()->getMode() == csPattern) {
+      doPatternFill(gFalse);
+    } else {
+      out->fill(state);
+    }
+  }
+  doEndPath();
+}
+
+void Gfx::opEOFill(Object args[], int numArgs) {
+  if (!state->isCurPt()) {
+    //error(getPos(), "No path in eofill");
+    return;
+  }
+  if (state->isPath()) {
+    if (state->getFillColorSpace()->getMode() == csPattern) {
+      doPatternFill(gTrue);
+    } else {
+      out->eoFill(state);
+    }
+  }
+  doEndPath();
+}
+
+void Gfx::opFillStroke(Object args[], int numArgs) {
+  if (!state->isCurPt()) {
+    //error(getPos(), "No path in fill/stroke");
+    return;
+  }
+  if (state->isPath()) {
+    if (state->getFillColorSpace()->getMode() == csPattern) {
+      doPatternFill(gFalse);
+    } else {
+      out->fill(state);
+    }
+    out->stroke(state);
+  }
+  doEndPath();
+}
+
+void Gfx::opCloseFillStroke(Object args[], int numArgs) {
+  if (!state->isCurPt()) {
+    //error(getPos(), "No path in closepath/fill/stroke");
+    return;
+  }
+  if (state->isPath()) {
+    state->closePath();
+    if (state->getFillColorSpace()->getMode() == csPattern) {
+      doPatternFill(gFalse);
+    } else {
+      out->fill(state);
+    }
+    out->stroke(state);
+  }
+  doEndPath();
+}
+
+void Gfx::opEOFillStroke(Object args[], int numArgs) {
+  if (!state->isCurPt()) {
+    //error(getPos(), "No path in eofill/stroke");
+    return;
+  }
+  if (state->isPath()) {
+    if (state->getFillColorSpace()->getMode() == csPattern) {
+      doPatternFill(gTrue);
+    } else {
+      out->eoFill(state);
+    }
+    out->stroke(state);
+  }
+  doEndPath();
+}
+
+void Gfx::opCloseEOFillStroke(Object args[], int numArgs) {
+  if (!state->isCurPt()) {
+    //error(getPos(), "No path in closepath/eofill/stroke");
+    return;
+  }
+  if (state->isPath()) {
+    state->closePath();
+    if (state->getFillColorSpace()->getMode() == csPattern) {
+      doPatternFill(gTrue);
+    } else {
+      out->eoFill(state);
+    }
+    out->stroke(state);
+  }
+  doEndPath();
+}
+
+void Gfx::doPatternFill(GBool eoFill) {
+  GfxPattern *pattern;
+
+  // 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;
+  }
+
+  if (!(pattern = state->getFillPattern())) {
+    return;
+  }
+  switch (pattern->getType()) {
+  case 1:
+    doTilingPatternFill((GfxTilingPattern *)pattern, eoFill);
+    break;
+  case 2:
+    doShadingPatternFill((GfxShadingPattern *)pattern, eoFill);
+    break;
+  default:
+    error(getPos(), "Unimplemented pattern type (%d) in fill",
+         pattern->getType());
+    break;
+  }
+}
+
+void Gfx::doTilingPatternFill(GfxTilingPattern *tPat, GBool eoFill) {
+  GfxPatternColorSpace *patCS;
+  GfxColorSpace *cs;
+  GfxPath *savedPath;
+  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], imb[6];
+  double det;
+  double xstep, ystep;
+  int i;
+
+  // get color space
+  patCS = (GfxPatternColorSpace *)state->getFillColorSpace();
+
+  // construct a (pattern space) -> (current space) transform matrix
+  ctm = state->getCTM();
+  btm = baseMatrix;
+  ptm = tPat->getMatrix();
+  // iCTM = invert 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;
+  // m1 = PTM * BTM = PTM * base transform matrix
+  m1[0] = ptm[0] * btm[0] + ptm[1] * btm[2];
+  m1[1] = ptm[0] * btm[1] + ptm[1] * btm[3];
+  m1[2] = ptm[2] * btm[0] + ptm[3] * btm[2];
+  m1[3] = ptm[2] * btm[1] + ptm[3] * btm[3];
+  m1[4] = ptm[4] * btm[0] + ptm[5] * btm[2] + btm[4];
+  m1[5] = ptm[4] * btm[1] + ptm[5] * btm[3] + btm[5];
+  // m = m1 * iCTM = (PTM * BTM) * (iCTM)
+  m[0] = m1[0] * ictm[0] + m1[1] * ictm[2];
+  m[1] = m1[0] * ictm[1] + m1[1] * ictm[3];
+  m[2] = m1[2] * ictm[0] + m1[3] * ictm[2];
+  m[3] = m1[2] * ictm[1] + m1[3] * ictm[3];
+  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 (device space) -> (pattern space) transform matrix
+  det = 1 / (m1[0] * m1[3] - m1[1] * m1[2]);
+  imb[0] = m1[3] * det;
+  imb[1] = -m1[1] * det;
+  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
+  savedPath = state->getPath()->copy();
+  saveState();
+
+  // set underlying color space (for uncolored tiling patterns); set
+  // various other parameters (stroke color, line width) to match
+  // Adobe's behavior
+  if (tPat->getPaintType() == 2 && (cs = patCS->getUnder())) {
+    state->setFillColorSpace(cs->copy());
+    out->updateFillColorSpace(state);
+    state->setStrokeColorSpace(cs->copy());
+    out->updateStrokeColorSpace(state);
+    state->setStrokeColor(state->getFillColor());
+  } else {
+    state->setFillColorSpace(new GfxDeviceGrayColorSpace());
+    out->updateFillColorSpace(state);
+    state->setStrokeColorSpace(new GfxDeviceGrayColorSpace());
+    out->updateStrokeColorSpace(state);
+  }
+  state->setFillPattern(NULL);
+  out->updateFillColor(state);
+  state->setStrokePattern(NULL);
+  out->updateStrokeColor(state);
+  state->setLineWidth(0);
+  out->updateLineWidth(state);
+
+  // clip to current path
+  state->clip();
+  if (eoFill) {
+    out->eoClip(state);
+  } else {
+    out->clip(state);
+  }
+  state->clearPath();
+
+  // get the clip region, check for empty
+  state->getClipBBox(&cxMin, &cyMin, &cxMax, &cyMax);
+  if (cxMin > cxMax || cyMin > cyMax) {
+    goto err;
+  }
+
+  // transform clip region bbox to pattern space
+  xMin = xMax = cxMin * imb[0] + cyMin * imb[2] + imb[4];
+  yMin = yMax = cxMin * imb[1] + cyMin * imb[3] + imb[5];
+  x1 = cxMin * imb[0] + cyMax * imb[2] + imb[4];
+  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 (?)
+  xstep = fabs(tPat->getXStep());
+  ystep = fabs(tPat->getYStep());
+  xi0 = (int)floor((xMin - tPat->getBBox()[0]) / xstep);
+  xi1 = (int)ceil((xMax - tPat->getBBox()[0]) / xstep);
+  yi0 = (int)floor((yMin - tPat->getBBox()[1]) / ystep);
+  yi1 = (int)ceil((yMax - tPat->getBBox()[1]) / ystep);
+  for (i = 0; i < 4; ++i) {
+    m1[i] = m[i];
+  }
+  if (out->useTilingPatternFill()) {
+    m1[4] = m[4];
+    m1[5] = m[5];
+    out->tilingPatternFill(state, tPat->getContentStream(),
+                          tPat->getPaintType(), tPat->getResDict(),
+                          m1, tPat->getBBox(),
+                          xi0, yi0, xi1, yi1, xstep, ystep);
+  } else {
+    for (yi = yi0; yi < yi1; ++yi) {
+      for (xi = xi0; xi < xi1; ++xi) {
+       x = xi * xstep;
+       y = yi * ystep;
+       m1[4] = x * m[0] + y * m[2] + m[4];
+       m1[5] = x * m[1] + y * m[3] + m[5];
+       doForm1(tPat->getContentStream(), tPat->getResDict(),
+               m1, tPat->getBBox());
+      }
+    }
+  }
+
+  // restore graphics state
+ err:
+  restoreState();
+  state->setPath(savedPath);
+}
+
+void Gfx::doShadingPatternFill(GfxShadingPattern *sPat, GBool eoFill) {
+  GfxShading *shading;
+  GfxPath *savedPath;
+  double *ctm, *btm, *ptm;
+  double m[6], ictm[6], m1[6];
+  double xMin, yMin, xMax, yMax;
+  double det;
+
+  shading = sPat->getShading();
+
+  // save current graphics state
+  savedPath = state->getPath()->copy();
+  saveState();
+
+  // 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->setPath(savedPath->copy());
+  }
+
+  // clip to current path
+  state->clip();
+  if (eoFill) {
+    out->eoClip(state);
+  } else {
+    out->clip(state);
+  }
+
+  // set the color space
+  state->setFillColorSpace(shading->getColorSpace()->copy());
+  out->updateFillColorSpace(state);
+
+  // background color fill
+  if (shading->getHasBackground()) {
+    state->setFillColor(shading->getBackground());
+    out->updateFillColor(state);
+    out->fill(state);
+  }
+  state->clearPath();
+
+  // construct a (pattern space) -> (current space) transform matrix
+  ctm = state->getCTM();
+  btm = baseMatrix;
+  ptm = sPat->getMatrix();
+  // iCTM = invert 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;
+  // m1 = PTM * BTM = PTM * base transform matrix
+  m1[0] = ptm[0] * btm[0] + ptm[1] * btm[2];
+  m1[1] = ptm[0] * btm[1] + ptm[1] * btm[3];
+  m1[2] = ptm[2] * btm[0] + ptm[3] * btm[2];
+  m1[3] = ptm[2] * btm[1] + ptm[3] * btm[3];
+  m1[4] = ptm[4] * btm[0] + ptm[5] * btm[2] + btm[4];
+  m1[5] = ptm[4] * btm[1] + ptm[5] * btm[3] + btm[5];
+  // m = m1 * iCTM = (PTM * BTM) * (iCTM)
+  m[0] = m1[0] * ictm[0] + m1[1] * ictm[2];
+  m[1] = m1[0] * ictm[1] + m1[1] * ictm[3];
+  m[2] = m1[2] * ictm[0] + m1[3] * ictm[2];
+  m[3] = m1[2] * ictm[1] + m1[3] * ictm[3];
+  m[4] = m1[4] * ictm[0] + m1[5] * ictm[2] + ictm[4];
+  m[5] = m1[4] * ictm[1] + m1[5] * ictm[3] + ictm[5];
+
+  // set the new matrix
+  state->concatCTM(m[0], m[1], m[2], m[3], m[4], m[5]);
+  out->updateCTM(state, m[0], m[1], m[2], m[3], m[4], m[5]);
+
+  // do shading type-specific operations
+  switch (shading->getType()) {
+  case 1:
+    doFunctionShFill((GfxFunctionShading *)shading);
+    break;
+  case 2:
+    doAxialShFill((GfxAxialShading *)shading);
+    break;
+  case 3:
+    doRadialShFill((GfxRadialShading *)shading);
+    break;
+  case 4:
+  case 5:
+    doGouraudTriangleShFill((GfxGouraudTriangleShading *)shading);
+    break;
+  case 6:
+  case 7:
+    doPatchMeshShFill((GfxPatchMeshShading *)shading);
+    break;
+  }
+
+  // restore graphics state
+  restoreState();
+  state->setPath(savedPath);
+}
+
+void Gfx::opShFill(Object args[], int numArgs) {
+  GfxShading *shading;
+  GfxPath *savedPath;
+  double xMin, yMin, xMax, yMax;
+
+  if (!(shading = res->lookupShading(args[0].getName()))) {
+    return;
+  }
+
+  // save current graphics state
+  savedPath = state->getPath()->copy();
+  saveState();
+
+  // 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());
+  out->updateFillColorSpace(state);
+
+  // do shading type-specific operations
+  switch (shading->getType()) {
+  case 1:
+    doFunctionShFill((GfxFunctionShading *)shading);
+    break;
+  case 2:
+    doAxialShFill((GfxAxialShading *)shading);
+    break;
+  case 3:
+    doRadialShFill((GfxRadialShading *)shading);
+    break;
+  case 4:
+  case 5:
+    doGouraudTriangleShFill((GfxGouraudTriangleShading *)shading);
+    break;
+  case 6:
+  case 7:
+    doPatchMeshShFill((GfxPatchMeshShading *)shading);
+    break;
+  }
+
+  // restore graphics state
+  restoreState();
+  state->setPath(savedPath);
+
+  delete shading;
+}
+
+void Gfx::doFunctionShFill(GfxFunctionShading *shading) {
+  double x0, y0, x1, y1;
+  GfxColor colors[4];
+
+  if (out->useShadedFills()) {
+    out->functionShadedFill(state, shading);
+  } else {
+    shading->getDomain(&x0, &y0, &x1, &y1);
+    shading->getColor(x0, y0, &colors[0]);
+    shading->getColor(x0, y1, &colors[1]);
+    shading->getColor(x1, y0, &colors[2]);
+    shading->getColor(x1, y1, &colors[3]);
+    doFunctionShFill1(shading, x0, y0, x1, y1, colors, 0);
+  }
+}
+
+void Gfx::doFunctionShFill1(GfxFunctionShading *shading,
+                           double x0, double y0,
+                           double x1, double y1,
+                           GfxColor *colors, int depth) {
+  GfxColor fillColor;
+  GfxColor color0M, color1M, colorM0, colorM1, colorMM;
+  GfxColor colors2[4];
+  double *matrix;
+  double xM, yM;
+  int nComps, i, j;
+
+  nComps = shading->getColorSpace()->getNComps();
+  matrix = shading->getMatrix();
+
+  // compare the four corner colors
+  for (i = 0; i < 4; ++i) {
+    for (j = 0; j < nComps; ++j) {
+      if (abs(colors[i].c[j] - colors[(i+1)&3].c[j]) > functionColorDelta) {
+       break;
+      }
+    }
+    if (j < nComps) {
+      break;
+    }
+  }
+
+  // center of the rectangle
+  xM = 0.5 * (x0 + x1);
+  yM = 0.5 * (y0 + y1);
+
+  // the four corner colors are close (or we hit the recursive limit)
+  // -- fill the rectangle; but require at least one subdivision
+  // (depth==0) to avoid problems when the four outer corners of the
+  // shaded region are the same color
+  if ((i == 4 && depth > 0) || depth == functionMaxDepth) {
+
+    // use the center color
+    shading->getColor(xM, yM, &fillColor);
+    state->setFillColor(&fillColor);
+    out->updateFillColor(state);
+
+    // fill the rectangle
+    state->moveTo(x0 * matrix[0] + y0 * matrix[2] + matrix[4],
+                 x0 * matrix[1] + y0 * matrix[3] + matrix[5]);
+    state->lineTo(x1 * matrix[0] + y0 * matrix[2] + matrix[4],
+                 x1 * matrix[1] + y0 * matrix[3] + matrix[5]);
+    state->lineTo(x1 * matrix[0] + y1 * matrix[2] + matrix[4],
+                 x1 * matrix[1] + y1 * matrix[3] + matrix[5]);
+    state->lineTo(x0 * matrix[0] + y1 * matrix[2] + matrix[4],
+                 x0 * matrix[1] + y1 * matrix[3] + matrix[5]);
+    state->closePath();
+    out->fill(state);
+    state->clearPath();
+
+  // the four corner colors are not close enough -- subdivide the
+  // rectangle
+  } else {
+
+    // colors[0]       colorM0       colors[2]
+    //   (x0,y0)       (xM,y0)       (x1,y0)
+    //         +----------+----------+
+    //         |          |          |
+    //         |    UL    |    UR    |
+    // color0M |       colorMM       | color1M
+    // (x0,yM) +----------+----------+ (x1,yM)
+    //         |       (xM,yM)       |
+    //         |    LL    |    LR    |
+    //         |          |          |
+    //         +----------+----------+
+    // colors[1]       colorM1       colors[3]
+    //   (x0,y1)       (xM,y1)       (x1,y1)
+
+    shading->getColor(x0, yM, &color0M);
+    shading->getColor(x1, yM, &color1M);
+    shading->getColor(xM, y0, &colorM0);
+    shading->getColor(xM, y1, &colorM1);
+    shading->getColor(xM, yM, &colorMM);
+
+    // upper-left sub-rectangle
+    colors2[0] = colors[0];
+    colors2[1] = color0M;
+    colors2[2] = colorM0;
+    colors2[3] = colorMM;
+    doFunctionShFill1(shading, x0, y0, xM, yM, colors2, depth + 1);
+    
+    // lower-left sub-rectangle
+    colors2[0] = color0M;
+    colors2[1] = colors[1];
+    colors2[2] = colorMM;
+    colors2[3] = colorM1;
+    doFunctionShFill1(shading, x0, yM, xM, y1, colors2, depth + 1);
+    
+    // upper-right sub-rectangle
+    colors2[0] = colorM0;
+    colors2[1] = colorMM;
+    colors2[2] = colors[2];
+    colors2[3] = color1M;
+    doFunctionShFill1(shading, xM, y0, x1, yM, colors2, depth + 1);
+
+    // lower-right sub-rectangle
+    colors2[0] = colorMM;
+    colors2[1] = colorM1;
+    colors2[2] = color1M;
+    colors2[3] = colors[3];
+    doFunctionShFill1(shading, xM, yM, x1, y1, colors2, depth + 1);
+  }
+}
+
+void Gfx::doAxialShFill(GfxAxialShading *shading) {
+  double xMin, yMin, xMax, yMax;
+  double x0, y0, x1, y1;
+  double dx, dy, mul;
+  GBool dxZero, dyZero;
+  double tMin, tMax, t, tx, ty;
+  double s[4], sMin, sMax, tmp;
+  double ux0, uy0, ux1, uy1, vx0, vy0, vx1, vy1;
+  double t0, t1, tt;
+  double ta[axialMaxSplits + 1];
+  int next[axialMaxSplits + 1];
+  GfxColor color0, color1;
+  int nComps;
+  int i, j, k, kk;
+
+  if (out->useShadedFills()) {
+
+    out->axialShadedFill(state, shading);
+
+  } else {
+
+    // get the clip region bbox
+    state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax);
+
+    // compute min and max t values, based on the four corners of the
+    // clip region bbox
+    shading->getCoords(&x0, &y0, &x1, &y1);
+    dx = x1 - x0;
+    dy = y1 - y0;
+    dxZero = fabs(dx) < 0.001;
+    dyZero = fabs(dy) < 0.001;
+    mul = 1 / (dx * dx + dy * dy);
+    tMin = tMax = ((xMin - x0) * dx + (yMin - y0) * dy) * mul;
+    t = ((xMin - x0) * dx + (yMax - y0) * dy) * mul;
+    if (t < tMin) {
+      tMin = t;
+    } else if (t > tMax) {
+      tMax = t;
+    }
+    t = ((xMax - x0) * dx + (yMin - y0) * dy) * mul;
+    if (t < tMin) {
+      tMin = t;
+    } else if (t > tMax) {
+      tMax = t;
+    }
+    t = ((xMax - x0) * dx + (yMax - y0) * dy) * mul;
+    if (t < tMin) {
+      tMin = t;
+    } else if (t > tMax) {
+      tMax = t;
+    }
+    if (tMin < 0 && !shading->getExtend0()) {
+      tMin = 0;
+    }
+    if (tMax > 1 && !shading->getExtend1()) {
+      tMax = 1;
+    }
+
+    // get the function domain
+    t0 = shading->getDomain0();
+    t1 = shading->getDomain1();
+
+    // Traverse the t axis and do the shading.
+    //
+    // For each point (tx, ty) on the t axis, consider a line through
+    // that point perpendicular to the t axis:
+    //
+    //     x(s) = tx + s * -dy   -->   s = (x - tx) / -dy
+    //     y(s) = ty + s * dx    -->   s = (y - ty) / dx
+    //
+    // Then look at the intersection of this line with the bounding box
+    // (xMin, yMin, xMax, yMax).  In the general case, there are four
+    // intersection points:
+    //
+    //     s0 = (xMin - tx) / -dy
+    //     s1 = (xMax - tx) / -dy
+    //     s2 = (yMin - ty) / dx
+    //     s3 = (yMax - ty) / dx
+    //
+    // and we want the middle two s values.
+    //
+    // In the case where dx = 0, take s0 and s1; in the case where dy =
+    // 0, take s2 and s3.
+    //
+    // Each filled polygon is bounded by two of these line segments
+    // perpdendicular to the t axis.
+    //
+    // The t axis is bisected into smaller regions until the color
+    // difference across a region is small enough, and then the region
+    // is painted with a single color.
+
+    // set up: require at least one split to avoid problems when the two
+    // ends of the t axis have the same color
+    nComps = shading->getColorSpace()->getNComps();
+    ta[0] = tMin;
+    next[0] = axialMaxSplits / 2;
+    ta[axialMaxSplits / 2] = 0.5 * (tMin + tMax);
+    next[axialMaxSplits / 2] = axialMaxSplits;
+    ta[axialMaxSplits] = tMax;
+
+    // compute the color at t = tMin
+    if (tMin < 0) {
+      tt = t0;
+    } else if (tMin > 1) {
+      tt = t1;
+    } else {
+      tt = t0 + (t1 - t0) * tMin;
+    }
+    shading->getColor(tt, &color0);
+
+    // compute the coordinates of the point on the t axis at t = tMin;
+    // then compute the intersection of the perpendicular line with the
+    // bounding box
+    tx = x0 + tMin * dx;
+    ty = y0 + tMin * dy;
+    if (dxZero && dyZero) {
+      sMin = sMax = 0;
+    } if (dxZero) {
+      sMin = (xMin - tx) / -dy;
+      sMax = (xMax - tx) / -dy;
+      if (sMin > sMax) { tmp = sMin; sMin = sMax; sMax = tmp; }
+    } else if (dyZero) {
+      sMin = (yMin - ty) / dx;
+      sMax = (yMax - ty) / dx;
+      if (sMin > sMax) { tmp = sMin; sMin = sMax; sMax = tmp; }
+    } else {
+      s[0] = (yMin - ty) / dx;
+      s[1] = (yMax - ty) / dx;
+      s[2] = (xMin - tx) / -dy;
+      s[3] = (xMax - tx) / -dy;
+      for (j = 0; j < 3; ++j) {
+       kk = j;
+       for (k = j + 1; k < 4; ++k) {
+         if (s[k] < s[kk]) {
+           kk = k;
+         }
+       }
+       tmp = s[j]; s[j] = s[kk]; s[kk] = tmp;
+      }
+      sMin = s[1];
+      sMax = s[2];
+    }
+    ux0 = tx - sMin * dy;
+    uy0 = ty + sMin * dx;
+    vx0 = tx - sMax * dy;
+    vy0 = ty + sMax * dx;
+
+    i = 0;
+    while (i < axialMaxSplits) {
+
+      // bisect until color difference is small enough or we hit the
+      // bisection limit
+      j = next[i];
+      while (j > i + 1) {
+       if (ta[j] < 0) {
+         tt = t0;
+       } else if (ta[j] > 1) {
+         tt = t1;
+       } else {
+         tt = t0 + (t1 - t0) * ta[j];
+       }
+       shading->getColor(tt, &color1);
+       for (k = 0; k < nComps; ++k) {
+         if (abs(color1.c[k] - color0.c[k]) > axialColorDelta) {
+           break;
+         }
+       }
+       if (k == nComps) {
+         break;
+       }
+       k = (i + j) / 2;
+       ta[k] = 0.5 * (ta[i] + ta[j]);
+       next[i] = k;
+       next[k] = j;
+       j = k;
+      }
+
+      // use the average of the colors of the two sides of the region
+      for (k = 0; k < nComps; ++k) {
+       color0.c[k] = (color0.c[k] + color1.c[k]) / 2;
+      }
+
+      // compute the coordinates of the point on the t axis; then
+      // compute the intersection of the perpendicular line with the
+      // bounding box
+      tx = x0 + ta[j] * dx;
+      ty = y0 + ta[j] * dy;
+      if (dxZero && dyZero) {
+       sMin = sMax = 0;
+      } if (dxZero) {
+       sMin = (xMin - tx) / -dy;
+       sMax = (xMax - tx) / -dy;
+       if (sMin > sMax) { tmp = sMin; sMin = sMax; sMax = tmp; }
+      } else if (dyZero) {
+       sMin = (yMin - ty) / dx;
+       sMax = (yMax - ty) / dx;
+       if (sMin > sMax) { tmp = sMin; sMin = sMax; sMax = tmp; }
+      } else {
+       s[0] = (yMin - ty) / dx;
+       s[1] = (yMax - ty) / dx;
+       s[2] = (xMin - tx) / -dy;
+       s[3] = (xMax - tx) / -dy;
+       for (j = 0; j < 3; ++j) {
+         kk = j;
+         for (k = j + 1; k < 4; ++k) {
+           if (s[k] < s[kk]) {
+             kk = k;
+           }
+         }
+         tmp = s[j]; s[j] = s[kk]; s[kk] = tmp;
+       }
+       sMin = s[1];
+       sMax = s[2];
+      }
+      ux1 = tx - sMin * dy;
+      uy1 = ty + sMin * dx;
+      vx1 = tx - sMax * dy;
+      vy1 = ty + sMax * dx;
+
+      // set 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, d0, d1;
+
+  if (out->useShadedFills()) {
+
+    out->radialShadedFill(state, shading);
+
+  } else {
+
+    // get the shading info
+    shading->getCoords(&x0, &y0, &r0, &x1, &y1, &r1);
+    t0 = shading->getDomain0();
+    t1 = shading->getDomain1();
+    nComps = shading->getColorSpace()->getNComps();
+
+    // compute the (possibly extended) s range
+    sMin = 0;
+    sMax = 1;
+    if (shading->getExtend0()) {
+      if (r0 < r1) {
+       // extend the smaller end
+       sMin = -r0 / (r1 - r0);
+      } else {
+       // extend the larger end
+       state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax);
+       d0 = (x0 - xMin) * (x0 - xMin);
+       d1 = (x0 - xMax) * (x0 - xMax);
+       sMin = d0 > d1 ? d0 : d1;
+       d0 = (y0 - yMin) * (y0 - yMin);
+       d1 = (y0 - yMax) * (y0 - yMax);
+       sMin += d0 > d1 ? d0 : d1;
+       sMin = (sqrt(sMin) - r0) / (r1 - r0);
+       if (sMin > 0) {
+         sMin = 0;
+       } else if (sMin < -20) {
+         // sanity check
+         sMin = -20;
+       }
+      }
+    }
+    if (shading->getExtend1()) {
+      if (r1 < r0) {
+       // extend the smaller end
+       sMax = -r0 / (r1 - r0);
+      } else if (r1 > r0) {
+       // extend the larger end
+       state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax);
+       d0 = (x1 - xMin) * (x1 - xMin);
+       d1 = (x1 - xMax) * (x1 - xMax);
+       sMax = d0 > d1 ? d0 : d1;
+       d0 = (y1 - yMin) * (y1 - yMin);
+       d1 = (y1 - yMax) * (y1 - yMax);
+       sMax += d0 > d1 ? d0 : d1;
+       sMax = (sqrt(sMax) - r0) / (r1 - r0);
+       if (sMax < 1) {
+         sMax = 1;
+       } else if (sMax > 20) {
+         // sanity check
+         sMax = 20;
+       }
+      }
+    }
+
+    // compute the number of steps into which circles must be divided to
+    // achieve a curve flatness of 0.1 pixel in device space for the
+    // largest circle (note that "device space" is 72 dpi when generating
+    // PostScript, hence the relatively small 0.1 pixel accuracy)
+    ctm = state->getCTM();
+    t = fabs(ctm[0]);
+    if (fabs(ctm[1]) > t) {
+      t = fabs(ctm[1]);
+    }
+    if (fabs(ctm[2]) > t) {
+      t = fabs(ctm[2]);
+    }
+    if (fabs(ctm[3]) > t) {
+      t = fabs(ctm[3]);
+    }
+    if (r0 > r1) {
+      t *= r0;
+    } else {
+      t *= r1;
+    }
+    if (t < 1) {
+      n = 3;
+    } else {
+      n = (int)(M_PI / acos(1 - 0.1 / t));
+      if (n < 3) {
+       n = 3;
+      } else if (n > 200) {
+       n = 200;
+      }
+    }
+
+    // Traverse the t axis and do the shading.
+    //
+    // This generates and fills a series of rings.  Each ring is defined
+    // by two circles:
+    //   sa, ta, xa, ya, ra, colorA
+    //   sb, tb, xb, yb, rb, colorB
+    //
+    // The s/t axis is divided into radialMaxSplits parts; these parts
+    // are combined as much as possible while respecting the
+    // radialColorDelta parameter.
+
+    // setup for the start circle
+    ia = 0;
+    sa = sMin;
+    ta = t0 + sa * (t1 - t0);
+    xa = x0 + sa * (x1 - x0);
+    ya = y0 + sa * (y1 - y0);
+    ra = r0 + sa * (r1 - r0);
+    if (ta < t0) {
+      shading->getColor(t0, &colorA);
+    } else if (ta > t1) {
+      shading->getColor(t1, &colorA);
+    } else {
+      shading->getColor(ta, &colorA);
+    }
+
+    while (ia < radialMaxSplits) {
+
+      // go as far along the t axis (toward t1) as we can, such that the
+      // color difference is within the tolerance (radialColorDelta) --
+      // this uses bisection (between the current value, t, and t1),
+      // limited to radialMaxSplits points along the t axis; require at
+      // least one split to avoid problems when the innermost and
+      // outermost colors are the same
+      ib = radialMaxSplits;
+      sb = sMin + ((double)ib / (double)radialMaxSplits) * (sMax - sMin);
+      tb = t0 + sb * (t1 - t0);
+      if (tb < t0) {
+       shading->getColor(t0, &colorB);
+      } else if (tb > t1) {
+       shading->getColor(t1, &colorB);
+      } else {
+       shading->getColor(tb, &colorB);
+      }
+      while (ib - ia > 1) {
+       for (k = 0; k < nComps; ++k) {
+         if (abs(colorB.c[k] - colorA.c[k]) > radialColorDelta) {
+           break;
+         }
+       }
+       if (k == nComps && ib < radialMaxSplits) {
+         break;
+       }
+       ib = (ia + ib) / 2;
+       sb = sMin + ((double)ib / (double)radialMaxSplits) * (sMax - sMin);
+       tb = t0 + sb * (t1 - t0);
+       if (tb < t0) {
+         shading->getColor(t0, &colorB);
+       } else if (tb > t1) {
+         shading->getColor(t1, &colorB);
+       } else {
+         shading->getColor(tb, &colorB);
+       }
+      }
+
+      // compute center and radius of the circle
+      xb = x0 + sb * (x1 - x0);
+      yb = y0 + sb * (y1 - y0);
+      rb = r0 + sb * (r1 - r0);
+
+      // use the average of the colors at the two circles
+      for (k = 0; k < nComps; ++k) {
+       colorA.c[k] = (colorA.c[k] + colorB.c[k]) / 2;
+      }
+      state->setFillColor(&colorA);
+      out->updateFillColor(state);
+
+      // construct path for first circle
+      state->moveTo(xa + ra, ya);
+      for (k = 1; k < n; ++k) {
+       angle = ((double)k / (double)n) * 2 * M_PI;
+       state->lineTo(xa + ra * cos(angle), ya + ra * sin(angle));
+      }
+      state->closePath();
+
+      // 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::doGouraudTriangleShFill(GfxGouraudTriangleShading *shading) {
+  double x0, y0, x1, y1, x2, y2;
+  GfxColor color0, color1, color2;
+  int i;
+
+  for (i = 0; i < shading->getNTriangles(); ++i) {
+    shading->getTriangle(i, &x0, &y0, &color0,
+                        &x1, &y1, &color1,
+                        &x2, &y2, &color2);
+    gouraudFillTriangle(x0, y0, &color0, x1, y1, &color1, x2, y2, &color2,
+                       shading->getColorSpace()->getNComps(), 0);
+  }
+}
+
+void Gfx::gouraudFillTriangle(double x0, double y0, GfxColor *color0,
+                             double x1, double y1, GfxColor *color1,
+                             double x2, double y2, GfxColor *color2,
+                             int nComps, int depth) {
+  double x01, y01, x12, y12, x20, y20;
+  GfxColor color01, color12, color20;
+  int i;
+
+  for (i = 0; i < nComps; ++i) {
+    if (abs(color0->c[i] - color1->c[i]) > gouraudColorDelta ||
+       abs(color1->c[i] - color2->c[i]) > gouraudColorDelta) {
+      break;
+    }
+  }
+  if (i == nComps || depth == gouraudMaxDepth) {
+    state->setFillColor(color0);
+    out->updateFillColor(state);
+    state->moveTo(x0, y0);
+    state->lineTo(x1, y1);
+    state->lineTo(x2, y2);
+    state->closePath();
+    out->fill(state);
+    state->clearPath();
+  } else {
+    x01 = 0.5 * (x0 + x1);
+    y01 = 0.5 * (y0 + y1);
+    x12 = 0.5 * (x1 + x2);
+    y12 = 0.5 * (y1 + y2);
+    x20 = 0.5 * (x2 + x0);
+    y20 = 0.5 * (y2 + y0);
+    //~ if the shading has a Function, this should interpolate on the
+    //~ function parameter, not on the color components
+    for (i = 0; i < nComps; ++i) {
+      color01.c[i] = (color0->c[i] + color1->c[i]) / 2;
+      color12.c[i] = (color1->c[i] + color2->c[i]) / 2;
+      color20.c[i] = (color2->c[i] + color0->c[i]) / 2;
+    }
+    gouraudFillTriangle(x0, y0, color0, x01, y01, &color01,
+                       x20, y20, &color20, nComps, depth + 1);
+    gouraudFillTriangle(x01, y01, &color01, x1, y1, color1,
+                       x12, y12, &color12, nComps, depth + 1);
+    gouraudFillTriangle(x01, y01, &color01, x12, y12, &color12,
+                       x20, y20, &color20, nComps, depth + 1);
+    gouraudFillTriangle(x20, y20, &color20, x12, y12, &color12,
+                       x2, y2, color2, nComps, depth + 1);
+  }
+}
+
+void Gfx::doPatchMeshShFill(GfxPatchMeshShading *shading) {
+  int start, i;
+
+  if (shading->getNPatches() > 128) {
+    start = 3;
+  } else if (shading->getNPatches() > 64) {
+    start = 2;
+  } else if (shading->getNPatches() > 16) {
+    start = 1;
+  } else {
+    start = 0;
+  }
+  for (i = 0; i < shading->getNPatches(); ++i) {
+    fillPatch(shading->getPatch(i), shading->getColorSpace()->getNComps(),
+             start);
+  }
+}
+
+void Gfx::fillPatch(GfxPatch *patch, int nComps, int depth) {
+  GfxPatch patch00, patch01, patch10, patch11;
+  double xx[4][8], yy[4][8];
+  double xxm, yym;
+  int i;
+
+  for (i = 0; i < nComps; ++i) {
+    if (abs(patch->color[0][0].c[i] - patch->color[0][1].c[i])
+         > patchColorDelta ||
+       abs(patch->color[0][1].c[i] - patch->color[1][1].c[i])
+         > patchColorDelta ||
+       abs(patch->color[1][1].c[i] - patch->color[1][0].c[i])
+         > patchColorDelta ||
+       abs(patch->color[1][0].c[i] - patch->color[0][0].c[i])
+         > patchColorDelta) {
+      break;
+    }
+  }
+  if (i == nComps || depth == patchMaxDepth) {
+    state->setFillColor(&patch->color[0][0]);
+    out->updateFillColor(state);
+    state->moveTo(patch->x[0][0], patch->y[0][0]);
+    state->curveTo(patch->x[0][1], patch->y[0][1],
+                  patch->x[0][2], patch->y[0][2],
+                  patch->x[0][3], patch->y[0][3]);
+    state->curveTo(patch->x[1][3], patch->y[1][3],
+                  patch->x[2][3], patch->y[2][3],
+                  patch->x[3][3], patch->y[3][3]);
+    state->curveTo(patch->x[3][2], patch->y[3][2],
+                  patch->x[3][1], patch->y[3][1],
+                  patch->x[3][0], patch->y[3][0]);
+    state->curveTo(patch->x[2][0], patch->y[2][0],
+                  patch->x[1][0], patch->y[1][0],
+                  patch->x[0][0], patch->y[0][0]);
+    state->closePath();
+    out->fill(state);
+    state->clearPath();
+  } else {
+    for (i = 0; i < 4; ++i) {
+      xx[i][0] = patch->x[i][0];
+      yy[i][0] = patch->y[i][0];
+      xx[i][1] = 0.5 * (patch->x[i][0] + patch->x[i][1]);
+      yy[i][1] = 0.5 * (patch->y[i][0] + patch->y[i][1]);
+      xxm = 0.5 * (patch->x[i][1] + patch->x[i][2]);
+      yym = 0.5 * (patch->y[i][1] + patch->y[i][2]);
+      xx[i][6] = 0.5 * (patch->x[i][2] + patch->x[i][3]);
+      yy[i][6] = 0.5 * (patch->y[i][2] + patch->y[i][3]);
+      xx[i][2] = 0.5 * (xx[i][1] + xxm);
+      yy[i][2] = 0.5 * (yy[i][1] + yym);
+      xx[i][5] = 0.5 * (xxm + xx[i][6]);
+      yy[i][5] = 0.5 * (yym + yy[i][6]);
+      xx[i][3] = xx[i][4] = 0.5 * (xx[i][2] + xx[i][5]);
+      yy[i][3] = yy[i][4] = 0.5 * (yy[i][2] + yy[i][5]);
+      xx[i][7] = patch->x[i][3];
+      yy[i][7] = patch->y[i][3];
+    }
+    for (i = 0; i < 4; ++i) {
+      patch00.x[0][i] = xx[0][i];
+      patch00.y[0][i] = yy[0][i];
+      patch00.x[1][i] = 0.5 * (xx[0][i] + xx[1][i]);
+      patch00.y[1][i] = 0.5 * (yy[0][i] + yy[1][i]);
+      xxm = 0.5 * (xx[1][i] + xx[2][i]);
+      yym = 0.5 * (yy[1][i] + yy[2][i]);
+      patch10.x[2][i] = 0.5 * (xx[2][i] + xx[3][i]);
+      patch10.y[2][i] = 0.5 * (yy[2][i] + yy[3][i]);
+      patch00.x[2][i] = 0.5 * (patch00.x[1][i] + xxm);
+      patch00.y[2][i] = 0.5 * (patch00.y[1][i] + yym);
+      patch10.x[1][i] = 0.5 * (xxm + patch10.x[2][i]);
+      patch10.y[1][i] = 0.5 * (yym + patch10.y[2][i]);
+      patch00.x[3][i] = 0.5 * (patch00.x[2][i] + patch10.x[1][i]);
+      patch00.y[3][i] = 0.5 * (patch00.y[2][i] + patch10.y[1][i]);
+      patch10.x[0][i] = patch00.x[3][i];
+      patch10.y[0][i] = patch00.y[3][i];
+      patch10.x[3][i] = xx[3][i];
+      patch10.y[3][i] = yy[3][i];
+    }
+    for (i = 4; i < 8; ++i) {
+      patch01.x[0][i-4] = xx[0][i];
+      patch01.y[0][i-4] = yy[0][i];
+      patch01.x[1][i-4] = 0.5 * (xx[0][i] + xx[1][i]);
+      patch01.y[1][i-4] = 0.5 * (yy[0][i] + yy[1][i]);
+      xxm = 0.5 * (xx[1][i] + xx[2][i]);
+      yym = 0.5 * (yy[1][i] + yy[2][i]);
+      patch11.x[2][i-4] = 0.5 * (xx[2][i] + xx[3][i]);
+      patch11.y[2][i-4] = 0.5 * (yy[2][i] + yy[3][i]);
+      patch01.x[2][i-4] = 0.5 * (patch01.x[1][i-4] + xxm);
+      patch01.y[2][i-4] = 0.5 * (patch01.y[1][i-4] + yym);
+      patch11.x[1][i-4] = 0.5 * (xxm + patch11.x[2][i-4]);
+      patch11.y[1][i-4] = 0.5 * (yym + patch11.y[2][i-4]);
+      patch01.x[3][i-4] = 0.5 * (patch01.x[2][i-4] + patch11.x[1][i-4]);
+      patch01.y[3][i-4] = 0.5 * (patch01.y[2][i-4] + patch11.y[1][i-4]);
+      patch11.x[0][i-4] = patch01.x[3][i-4];
+      patch11.y[0][i-4] = patch01.y[3][i-4];
+      patch11.x[3][i-4] = xx[3][i];
+      patch11.y[3][i-4] = yy[3][i];
+    }
+    //~ if the shading has a Function, this should interpolate on the
+    //~ function parameter, not on the color components
+    for (i = 0; i < nComps; ++i) {
+      patch00.color[0][0].c[i] = patch->color[0][0].c[i];
+      patch00.color[0][1].c[i] = (patch->color[0][0].c[i] +
+                                 patch->color[0][1].c[i]) / 2;
+      patch01.color[0][0].c[i] = patch00.color[0][1].c[i];
+      patch01.color[0][1].c[i] = patch->color[0][1].c[i];
+      patch01.color[1][1].c[i] = (patch->color[0][1].c[i] +
+                                 patch->color[1][1].c[i]) / 2;
+      patch11.color[0][1].c[i] = patch01.color[1][1].c[i];
+      patch11.color[1][1].c[i] = patch->color[1][1].c[i];
+      patch11.color[1][0].c[i] = (patch->color[1][1].c[i] +
+                                 patch->color[1][0].c[i]) / 2;
+      patch10.color[1][1].c[i] = patch11.color[1][0].c[i];
+      patch10.color[1][0].c[i] = patch->color[1][0].c[i];
+      patch10.color[0][0].c[i] = (patch->color[1][0].c[i] +
+                                 patch->color[0][0].c[i]) / 2;
+      patch00.color[1][0].c[i] = patch10.color[0][0].c[i];
+      patch00.color[1][1].c[i] = (patch00.color[1][0].c[i] +
+                                 patch01.color[1][1].c[i]) / 2;
+      patch01.color[1][0].c[i] = patch00.color[1][1].c[i];
+      patch11.color[0][0].c[i] = patch00.color[1][1].c[i];
+      patch10.color[0][1].c[i] = patch00.color[1][1].c[i];
+    }
+    fillPatch(&patch00, nComps, depth + 1);
+    fillPatch(&patch10, nComps, depth + 1);
+    fillPatch(&patch01, nComps, depth + 1);
+    fillPatch(&patch11, nComps, depth + 1);
+  }
+}
+
+void Gfx::doEndPath() {
+  if (state->isCurPt() && clip != clipNone) {
+    state->clip();
+    if (clip == clipNormal) {
+      out->clip(state);
+    } else {
+      out->eoClip(state);
+    }
+  }
+  clip = clipNone;
+  state->clearPath();
+}
+
+//------------------------------------------------------------------------
+// path clipping operators
+//------------------------------------------------------------------------
+
+void Gfx::opClip(Object args[], int numArgs) {
+  clip = clipNormal;
+}
+
+void Gfx::opEOClip(Object args[], int numArgs) {
+  clip = clipEO;
+}
+
+//------------------------------------------------------------------------
+// text object operators
+//------------------------------------------------------------------------
+
+void Gfx::opBeginText(Object args[], int numArgs) {
+  state->setTextMat(1, 0, 0, 1, 0, 0);
+  state->textMoveTo(0, 0);
+  out->updateTextMat(state);
+  out->updateTextPos(state);
+  fontChanged = gTrue;
+}
+
+void Gfx::opEndText(Object args[], int numArgs) {
+  out->endTextObject(state);
+}
+
+//------------------------------------------------------------------------
+// text state operators
+//------------------------------------------------------------------------
+
+void Gfx::opSetCharSpacing(Object args[], int numArgs) {
+  state->setCharSpace(args[0].getNum());
+  out->updateCharSpace(state);
+}
+
+void Gfx::opSetFont(Object args[], int numArgs) {
+  GfxFont *font;
+
+  if (!(font = res->lookupFont(args[0].getName()))) {
+    return;
+  }
+  if (printCommands) {
+    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;
+}
+
+void Gfx::opSetTextLeading(Object args[], int numArgs) {
+  state->setLeading(args[0].getNum());
+}
+
+void Gfx::opSetTextRender(Object args[], int numArgs) {
+  state->setRender(args[0].getInt());
+  out->updateRender(state);
+}
+
+void Gfx::opSetTextRise(Object args[], int numArgs) {
+  state->setRise(args[0].getNum());
+  out->updateRise(state);
+}
+
+void Gfx::opSetWordSpacing(Object args[], int numArgs) {
+  state->setWordSpace(args[0].getNum());
+  out->updateWordSpace(state);
+}
+
+void Gfx::opSetHorizScaling(Object args[], int numArgs) {
+  state->setHorizScaling(args[0].getNum());
+  out->updateHorizScaling(state);
+  fontChanged = gTrue;
+}
+
+//------------------------------------------------------------------------
+// text positioning operators
+//------------------------------------------------------------------------
+
+void Gfx::opTextMove(Object args[], int numArgs) {
+  double tx, ty;
+
+  tx = state->getLineX() + args[0].getNum();
+  ty = state->getLineY() + args[1].getNum();
+  state->textMoveTo(tx, ty);
+  out->updateTextPos(state);
+}
+
+void Gfx::opTextMoveSet(Object args[], int numArgs) {
+  double tx, ty;
+
+  tx = state->getLineX() + args[0].getNum();
+  ty = args[1].getNum();
+  state->setLeading(-ty);
+  ty += state->getLineY();
+  state->textMoveTo(tx, ty);
+  out->updateTextPos(state);
+}
+
+void Gfx::opSetTextMatrix(Object args[], int numArgs) {
+  state->setTextMat(args[0].getNum(), args[1].getNum(),
+                   args[2].getNum(), args[3].getNum(),
+                   args[4].getNum(), args[5].getNum());
+  state->textMoveTo(0, 0);
+  out->updateTextMat(state);
+  out->updateTextPos(state);
+  fontChanged = gTrue;
+}
+
+void Gfx::opTextNextLine(Object args[], int numArgs) {
+  double tx, ty;
+
+  tx = state->getLineX();
+  ty = state->getLineY() - state->getLeading();
+  state->textMoveTo(tx, ty);
+  out->updateTextPos(state);
+}
+
+//------------------------------------------------------------------------
+// text string operators
+//------------------------------------------------------------------------
+
+void Gfx::opShowText(Object args[], int numArgs) {
+  if (!state->getFont()) {
+    error(getPos(), "No font in show");
+    return;
+  }
+  if (fontChanged) {
+    out->updateFont(state);
+    fontChanged = gFalse;
+  }
+  out->beginStringOp(state);
+  doShowText(args[0].getString());
+  out->endStringOp(state);
+}
+
+void Gfx::opMoveShowText(Object args[], int numArgs) {
+  double tx, ty;
+
+  if (!state->getFont()) {
+    error(getPos(), "No font in move/show");
+    return;
+  }
+  if (fontChanged) {
+    out->updateFont(state);
+    fontChanged = gFalse;
+  }
+  tx = state->getLineX();
+  ty = state->getLineY() - state->getLeading();
+  state->textMoveTo(tx, ty);
+  out->updateTextPos(state);
+  out->beginStringOp(state);
+  doShowText(args[0].getString());
+  out->endStringOp(state);
+}
+
+void Gfx::opMoveSetShowText(Object args[], int numArgs) {
+  double tx, ty;
+
+  if (!state->getFont()) {
+    error(getPos(), "No font in move/set/show");
+    return;
+  }
+  if (fontChanged) {
+    out->updateFont(state);
+    fontChanged = gFalse;
+  }
+  state->setWordSpace(args[0].getNum());
+  state->setCharSpace(args[1].getNum());
+  tx = state->getLineX();
+  ty = state->getLineY() - state->getLeading();
+  state->textMoveTo(tx, ty);
+  out->updateWordSpace(state);
+  out->updateCharSpace(state);
+  out->updateTextPos(state);
+  out->beginStringOp(state);
+  doShowText(args[2].getString());
+  out->endStringOp(state);
+}
+
+void Gfx::opShowSpaceText(Object args[], int numArgs) {
+  Array *a;
+  Object obj;
+  int wMode;
+  int i;
+
+  if (!state->getFont()) {
+    error(getPos(), "No font in show/space");
+    return;
+  }
+  if (fontChanged) {
+    out->updateFont(state);
+    fontChanged = gFalse;
+  }
+  out->beginStringOp(state);
+  wMode = state->getFont()->getWMode();
+  a = args[0].getArray();
+  for (i = 0; i < a->getLength(); ++i) {
+    a->get(i, &obj);
+    if (obj.isNum()) {
+      // this uses the absolute value of the font size to match
+      // Acrobat's behavior
+      if (wMode) {
+       state->textShift(0, -obj.getNum() * 0.001 *
+                           fabs(state->getFontSize()));
+      } else {
+       state->textShift(-obj.getNum() * 0.001 *
+                        fabs(state->getFontSize()), 0);
+      }
+      out->updateTextShift(state, obj.getNum());
+    } else if (obj.isString()) {
+      doShowText(obj.getString());
+    } else {
+      error(getPos(), "Element of show/space array must be number or string");
+    }
+    obj.free();
+  }
+  out->endStringOp(state);
+}
+
+void Gfx::doShowText(GString *s) {
+  GfxFont *font;
+  int wMode;
+  double riseX, riseY;
+  CharCode code;
+  Unicode u[8];
+  double x, y, dx, dy, dx2, dy2, curX, curY, tdx, tdy, lineX, lineY;
+  double originX, originY, tOriginX, tOriginY;
+  double oldCTM[6], newCTM[6];
+  double *mat;
+  Object charProc;
+  Dict *resDict;
+  Parser *oldParser;
+  char *p;
+  int len, n, uLen, nChars, nSpaces, i;
+
+  font = state->getFont();
+  wMode = font->getWMode();
+
+  if (out->useDrawChar()) {
+    out->beginString(state, s);
+  }
+
+  // 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[1] *= state->getFontSize();
+    newCTM[2] *= 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();
+    lineX = state->getLineX();
+    lineY = state->getLineY();
+    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();
+      }
+      dx *= state->getHorizScaling();
+      dy *= state->getFontSize();
+      state->textTransformDelta(dx, dy, &tdx, &tdy);
+      state->transform(curX + riseX, curY + riseY, &x, &y);
+      saveState();
+      state->setCTM(newCTM[0], newCTM[1], newCTM[2], newCTM[3], x, y);
+      //~ out->updateCTM(???)
+      if (!out->beginType3Char(state, curX + riseX, curY + riseY, tdx, tdy,
+                              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");
+       }
+       out->endType3Char(state);
+       if (resDict) {
+         popResources();
+       }
+       charProc.free();
+      }
+      restoreState();
+      // GfxState::restore() does *not* restore the current position,
+      // so we deal with it here using (curX, curY) and (lineX, lineY)
+      curX += tdx;
+      curY += tdy;
+      state->moveTo(curX, curY);
+      state->textSetPos(lineX, lineY);
+      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();
+       }
+       dx *= state->getHorizScaling();
+       dy *= state->getFontSize();
+      }
+      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, n, 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;
+      }
+      ++nChars;
+      p += n;
+      len -= n;
+    }
+    if (wMode) {
+      dx *= state->getFontSize();
+      dy = dy * state->getFontSize()
+          + nChars * state->getCharSpace()
+          + nSpaces * state->getWordSpace();
+    } else {
+      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);
+  }
+
+  if (out->useDrawChar()) {
+    out->endString(state);
+  }
+
+  updateLevel += 10 * s->getLength();
+}
+
+//------------------------------------------------------------------------
+// XObject operators
+//------------------------------------------------------------------------
+
+void Gfx::opXObject(Object args[], int numArgs) {
+  Object obj1, obj2, obj3, refObj;
+#if OPI_SUPPORT
+  Object opiDict;
+#endif
+
+  if (!res->lookupXObject(args[0].getName(), &obj1)) {
+    return;
+  }
+  if (!obj1.isStream()) {
+    error(getPos(), "XObject '%s' is wrong type", args[0].getName());
+    obj1.free();
+    return;
+  }
+#if OPI_SUPPORT
+  obj1.streamGetDict()->lookup("OPI", &opiDict);
+  if (opiDict.isDict()) {
+    out->opiBegin(state, opiDict.getDict());
+  }
+#endif
+  obj1.streamGetDict()->lookup("Subtype", &obj2);
+  if (obj2.isName("Image")) {
+    if (out->needNonText()) {
+      res->lookupXObjectNF(args[0].getName(), &refObj);
+      doImage(&refObj, obj1.getStream(), gFalse);
+      refObj.free();
+    }
+  } else if (obj2.isName("Form")) {
+    doForm(&obj1);
+  } else if (obj2.isName("PS")) {
+    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 {
+    error(getPos(), "XObject subtype is missing or wrong type");
+  }
+  obj2.free();
+#if OPI_SUPPORT
+  if (opiDict.isDict()) {
+    out->opiEnd(state, opiDict.getDict());
+  }
+  opiDict.free();
+#endif
+  obj1.free();
+}
+
+void Gfx::doImage(Object *ref, Stream *str, GBool inlineImg) {
+  Dict *dict, *maskDict;
+  int width, height;
+  int bits, maskBits;
+  StreamColorSpaceMode csMode;
+  GBool mask;
+  GBool invert;
+  GfxColorSpace *colorSpace, *maskColorSpace;
+  GfxImageColorMap *colorMap, *maskColorMap;
+  Object maskObj, smaskObj;
+  GBool haveColorKeyMask, haveExplicitMask, haveSoftMask;
+  int maskColors[2*gfxColorMaxComps];
+  int maskWidth, maskHeight;
+  GBool maskInvert;
+  Stream *maskStr;
+  Object obj1, obj2;
+  int i;
+
+  // get info from the stream
+  bits = 0;
+  csMode = streamCSNone;
+  str->getImageParams(&bits, &csMode);
+
+  // get stream dict
+  dict = str->getDict();
+
+  // get size
+  dict->lookup("Width", &obj1);
+  if (obj1.isNull()) {
+    obj1.free();
+    dict->lookup("W", &obj1);
+  }
+  if (!obj1.isInt())
+    goto err2;
+  width = obj1.getInt();
+  obj1.free();
+  dict->lookup("Height", &obj1);
+  if (obj1.isNull()) {
+    obj1.free();
+    dict->lookup("H", &obj1);
+  }
+  if (!obj1.isInt())
+    goto err2;
+  height = obj1.getInt();
+  obj1.free();
+
+  // image or mask?
+  dict->lookup("ImageMask", &obj1);
+  if (obj1.isNull()) {
+    obj1.free();
+    dict->lookup("IM", &obj1);
+  }
+  mask = gFalse;
+  if (obj1.isBool())
+    mask = obj1.getBool();
+  else if (!obj1.isNull())
+    goto err2;
+  obj1.free();
+
+  // bit depth
+  if (bits == 0) {
+    dict->lookup("BitsPerComponent", &obj1);
+    if (obj1.isNull()) {
+      obj1.free();
+      dict->lookup("BPC", &obj1);
+    }
+    if (obj1.isInt()) {
+      bits = obj1.getInt();
+    } else if (mask) {
+      bits = 1;
+    } else {
+      goto err2;
+    }
+    obj1.free();
+  }
+
+  // display a mask
+  if (mask) {
+
+    // check for inverted mask
+    if (bits != 1)
+      goto err1;
+    invert = gFalse;
+    dict->lookup("Decode", &obj1);
+    if (obj1.isNull()) {
+      obj1.free();
+      dict->lookup("D", &obj1);
+    }
+    if (obj1.isArray()) {
+      obj1.arrayGet(0, &obj2);
+      if (obj2.isInt() && obj2.getInt() == 1)
+       invert = gTrue;
+      obj2.free();
+    } else if (!obj1.isNull()) {
+      goto err2;
+    }
+    obj1.free();
+
+    // draw it
+    out->drawImageMask(state, ref, str, width, height, invert, inlineImg);
+
+  } else {
+
+    // get color space and color map
+    dict->lookup("ColorSpace", &obj1);
+    if (obj1.isNull()) {
+      obj1.free();
+      dict->lookup("CS", &obj1);
+    }
+    if (obj1.isName()) {
+      res->lookupColorSpace(obj1.getName(), &obj2);
+      if (!obj2.isNull()) {
+       obj1.free();
+       obj1 = obj2;
+      } else {
+       obj2.free();
+      }
+    }
+    if (!obj1.isNull()) {
+      colorSpace = GfxColorSpace::parse(&obj1);
+    } else if (csMode == streamCSDeviceGray) {
+      colorSpace = new GfxDeviceGrayColorSpace();
+    } else if (csMode == streamCSDeviceRGB) {
+      colorSpace = new GfxDeviceRGBColorSpace();
+    } else if (csMode == streamCSDeviceCMYK) {
+      colorSpace = new GfxDeviceCMYKColorSpace();
+    } else {
+      colorSpace = NULL;
+    }
+    obj1.free();
+    if (!colorSpace) {
+      goto err1;
+    }
+    dict->lookup("Decode", &obj1);
+    if (obj1.isNull()) {
+      obj1.free();
+      dict->lookup("D", &obj1);
+    }
+    colorMap = new GfxImageColorMap(bits, &obj1, colorSpace);
+    obj1.free();
+    if (!colorMap->isOk()) {
+      delete colorMap;
+      goto err1;
+    }
+
+    // get the mask
+    haveColorKeyMask = haveExplicitMask = haveSoftMask = gFalse;
+    maskStr = NULL; // make gcc happy
+    maskWidth = maskHeight = 0; // make gcc happy
+    maskInvert = gFalse; // make gcc happy
+    maskColorMap = NULL; // make gcc happy
+    dict->lookup("Mask", &maskObj);
+    dict->lookup("SMask", &smaskObj);
+    if (smaskObj.isStream()) {
+      // soft mask
+      if (inlineImg) {
+       goto err1;
+      }
+      maskStr = smaskObj.getStream();
+      maskDict = smaskObj.streamGetDict();
+      maskDict->lookup("Width", &obj1);
+      if (obj1.isNull()) {
+       obj1.free();
+       maskDict->lookup("W", &obj1);
+      }
+      if (!obj1.isInt()) {
+       goto err2;
+      }
+      maskWidth = obj1.getInt();
+      obj1.free();
+      maskDict->lookup("Height", &obj1);
+      if (obj1.isNull()) {
+       obj1.free();
+       maskDict->lookup("H", &obj1);
+      }
+      if (!obj1.isInt()) {
+       goto err2;
+      }
+      maskHeight = obj1.getInt();
+      obj1.free();
+      maskDict->lookup("BitsPerComponent", &obj1);
+      if (obj1.isNull()) {
+       obj1.free();
+       maskDict->lookup("BPC", &obj1);
+      }
+      if (!obj1.isInt()) {
+       goto err2;
+      }
+      maskBits = obj1.getInt();
+      obj1.free();
+      maskDict->lookup("ColorSpace", &obj1);
+      if (obj1.isNull()) {
+       obj1.free();
+       maskDict->lookup("CS", &obj1);
+      }
+      if (obj1.isName()) {
+       res->lookupColorSpace(obj1.getName(), &obj2);
+       if (!obj2.isNull()) {
+         obj1.free();
+         obj1 = obj2;
+       } else {
+         obj2.free();
+       }
+      }
+      maskColorSpace = GfxColorSpace::parse(&obj1);
+      obj1.free();
+      if (!maskColorSpace || maskColorSpace->getMode() != csDeviceGray) {
+       goto err1;
+      }
+      maskDict->lookup("Decode", &obj1);
+      if (obj1.isNull()) {
+       obj1.free();
+       maskDict->lookup("D", &obj1);
+      }
+      maskColorMap = new GfxImageColorMap(maskBits, &obj1, maskColorSpace);
+      obj1.free();
+      if (!maskColorMap->isOk()) {
+       delete maskColorMap;
+       goto err1;
+      }
+      //~ handle the Matte entry
+      haveSoftMask = gTrue;
+    } else if (maskObj.isArray()) {
+      // color key mask
+      for (i = 0;
+          i < maskObj.arrayGetLength() && i < 2*gfxColorMaxComps;
+          ++i) {
+       maskObj.arrayGet(i, &obj1);
+       maskColors[i] = obj1.getInt();
+       obj1.free();
+      }
+      haveColorKeyMask = gTrue;
+    } else if (maskObj.isStream()) {
+      // explicit mask
+      if (inlineImg) {
+       goto err1;
+      }
+      maskStr = maskObj.getStream();
+      maskDict = maskObj.streamGetDict();
+      maskDict->lookup("Width", &obj1);
+      if (obj1.isNull()) {
+       obj1.free();
+       maskDict->lookup("W", &obj1);
+      }
+      if (!obj1.isInt()) {
+       goto err2;
+      }
+      maskWidth = obj1.getInt();
+      obj1.free();
+      maskDict->lookup("Height", &obj1);
+      if (obj1.isNull()) {
+       obj1.free();
+       maskDict->lookup("H", &obj1);
+      }
+      if (!obj1.isInt()) {
+       goto err2;
+      }
+      maskHeight = obj1.getInt();
+      obj1.free();
+      maskDict->lookup("ImageMask", &obj1);
+      if (obj1.isNull()) {
+       obj1.free();
+       maskDict->lookup("IM", &obj1);
+      }
+      if (!obj1.isBool() || !obj1.getBool()) {
+       goto err2;
+      }
+      obj1.free();
+      maskInvert = gFalse;
+      maskDict->lookup("Decode", &obj1);
+      if (obj1.isNull()) {
+       obj1.free();
+       maskDict->lookup("D", &obj1);
+      }
+      if (obj1.isArray()) {
+       obj1.arrayGet(0, &obj2);
+       if (obj2.isInt() && obj2.getInt() == 1) {
+         maskInvert = gTrue;
+       }
+       obj2.free();
+      } else if (!obj1.isNull()) {
+       goto err2;
+      }
+      obj1.free();
+      haveExplicitMask = gTrue;
+    }
+
+    // draw it
+    if (haveSoftMask) {
+      out->drawSoftMaskedImage(state, ref, str, width, height, colorMap,
+                              maskStr, maskWidth, maskHeight, maskColorMap);
+      delete maskColorMap;
+    } else if (haveExplicitMask) {
+      out->drawMaskedImage(state, ref, str, width, height, colorMap,
+                          maskStr, maskWidth, maskHeight, maskInvert);
+    } else {
+      out->drawImage(state, ref, str, width, height, colorMap,
+                    haveColorKeyMask ? maskColors : (int *)NULL, inlineImg);
+    }
+    delete colorMap;
+
+    maskObj.free();
+    smaskObj.free();
+  }
+
+  if ((i = width * height) > 1000) {
+    i = 1000;
+  }
+  updateLevel += i;
+
+  return;
+
+ err2:
+  obj1.free();
+ err1:
+  error(getPos(), "Bad image parameters");
+}
+
+void Gfx::doForm(Object *str) {
+  Dict *dict;
+  Object matrixObj, bboxObj;
+  double m[6], bbox[6];
+  Object resObj;
+  Dict *resDict;
+  Object obj1;
+  int i;
+
+  // check for excessive recursion
+  if (formDepth > 20) {
+    return;
+  }
+
+  // get stream dict
+  dict = str->streamGetDict();
+
+  // check form type
+  dict->lookup("FormType", &obj1);
+  if (!(obj1.isNull() || (obj1.isInt() && obj1.getInt() == 1))) {
+    error(getPos(), "Unknown form type");
+  }
+  obj1.free();
+
+  // get bounding box
+  dict->lookup("BBox", &bboxObj);
+  if (!bboxObj.isArray()) {
+    matrixObj.free();
+    bboxObj.free();
+    error(getPos(), "Bad form bounding box");
+    return;
+  }
+  for (i = 0; i < 4; ++i) {
+    bboxObj.arrayGet(i, &obj1);
+    bbox[i] = obj1.getNum();
+    obj1.free();
+  }
+  bboxObj.free();
+
+  // get matrix
+  dict->lookup("Matrix", &matrixObj);
+  if (matrixObj.isArray()) {
+    for (i = 0; i < 6; ++i) {
+      matrixObj.arrayGet(i, &obj1);
+      m[i] = obj1.getNum();
+      obj1.free();
+    }
+  } else {
+    m[0] = 1; m[1] = 0;
+    m[2] = 0; m[3] = 1;
+    m[4] = 0; m[5] = 0;
+  }
+  matrixObj.free();
+
+  // get resources
+  dict->lookup("Resources", &resObj);
+  resDict = resObj.isDict() ? resObj.getDict() : (Dict *)NULL;
+
+  // draw it
+  ++formDepth;
+  doForm1(str, resDict, m, bbox);
+  --formDepth;
+
+  resObj.free();
+}
+
+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], 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 the form bounding box
+  dict->lookup("BBox", &bboxObj);
+  if (!bboxObj.isArray()) {
+    bboxObj.free();
+    error(getPos(), "Bad form bounding box");
+    return;
+  }
+  for (i = 0; i < 4; ++i) {
+    bboxObj.arrayGet(i, &obj1);
+    bbox[i] = obj1.getNum();
+    obj1.free();
+  }
+  bboxObj.free();
+
+  // get the form matrix
+  dict->lookup("Matrix", &matrixObj);
+  if (matrixObj.isArray()) {
+    for (i = 0; i < 6; ++i) {
+      matrixObj.arrayGet(i, &obj1);
+      m[i] = obj1.getNum();
+      obj1.free();
+    }
+  } else {
+    m[0] = 1; m[1] = 0;
+    m[2] = 0; m[3] = 1;
+    m[4] = 0; m[5] = 0;
+  }
+  matrixObj.free();
+
+  // 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;
+  }
+
+  // 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() : (Dict *)NULL;
+
+  // draw it
+  doForm1(str, resDict, m, bbox);
+
+  resObj.free();
+  bboxObj.free();
+}
+
+void Gfx::doForm1(Object *str, Dict *resDict, double *matrix, double *bbox) {
+  Parser *oldParser;
+  double oldBaseMatrix[6];
+  int i;
+
+  // push new resources on stack
+  pushResources(resDict);
+
+  // save current graphics state
+  saveState();
+
+  // kill any pre-existing path
+  state->clearPath();
+
+  // save current parser
+  oldParser = parser;
+
+  // set form transformation matrix
+  state->concatCTM(matrix[0], matrix[1], matrix[2],
+                  matrix[3], matrix[4], matrix[5]);
+  out->updateCTM(state, matrix[0], matrix[1], matrix[2],
+                matrix[3], matrix[4], matrix[5]);
+
+  // set new base matrix
+  for (i = 0; i < 6; ++i) {
+    oldBaseMatrix[i] = baseMatrix[i];
+    baseMatrix[i] = state->getCTM()[i];
+  }
+
+  // set form bounding box
+  state->moveTo(bbox[0], bbox[1]);
+  state->lineTo(bbox[2], bbox[1]);
+  state->lineTo(bbox[2], bbox[3]);
+  state->lineTo(bbox[0], bbox[3]);
+  state->closePath();
+  state->clip();
+  out->clip(state);
+  state->clearPath();
+
+  // draw the form
+  display(str, gFalse);
+
+  // restore base matrix
+  for (i = 0; i < 6; ++i) {
+    baseMatrix[i] = oldBaseMatrix[i];
+  }
+
+  // restore parser
+  parser = oldParser;
+
+  // restore graphics state
+  restoreState();
+
+  // pop resource stack
+  popResources();
+
+  return;
+}
+
+//------------------------------------------------------------------------
+// in-line image operators
+//------------------------------------------------------------------------
+
+void Gfx::opBeginImage(Object args[], int numArgs) {
+  Stream *str;
+  int c1, c2;
+
+  // build dict/stream
+  str = buildImageStream();
+
+  // display the image
+  if (str) {
+    doImage(NULL, str, gTrue);
+  
+    // skip 'EI' tag
+    c1 = str->getBaseStream()->getChar();
+    c2 = str->getBaseStream()->getChar();
+    while (!(c1 == 'E' && c2 == 'I') && c2 != EOF) {
+      c1 = c2;
+      c2 = str->getBaseStream()->getChar();
+    }
+    delete str;
+  }
+}
+
+Stream *Gfx::buildImageStream() {
+  Object dict;
+  Object obj;
+  char *key;
+  Stream *str;
+
+  // build dictionary
+  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();
+    } else {
+      key = copyString(obj.getName());
+      obj.free();
+      parser->getObj(&obj);
+      if (obj.isEOF() || obj.isError()) {
+       gfree(key);
+       break;
+      }
+      dict.dictAdd(key, &obj);
+    }
+    parser->getObj(&obj);
+  }
+  if (obj.isEOF()) {
+    error(getPos(), "End of file in inline image");
+    obj.free();
+    dict.free();
+    return NULL;
+  }
+  obj.free();
+
+  // make stream
+  str = new EmbedStream(parser->getStream(), &dict, gFalse, 0);
+  str = str->addFilters(&dict);
+
+  return str;
+}
+
+void Gfx::opImageData(Object args[], int numArgs) {
+  error(getPos(), "Internal: got 'ID' operator");
+}
+
+void Gfx::opEndImage(Object args[], int numArgs) {
+  error(getPos(), "Internal: got 'EI' operator");
+}
+
+//------------------------------------------------------------------------
+// type 3 font operators
+//------------------------------------------------------------------------
+
+void Gfx::opSetCharWidth(Object args[], int numArgs) {
+  out->type3D0(state, args[0].getNum(), args[1].getNum());
+}
+
+void Gfx::opSetCacheDevice(Object args[], int numArgs) {
+  out->type3D1(state, args[0].getNum(), args[1].getNum(),
+              args[2].getNum(), args[3].getNum(),
+              args[4].getNum(), args[5].getNum());
+}
+
+//------------------------------------------------------------------------
+// compatibility operators
+//------------------------------------------------------------------------
+
+void Gfx::opBeginIgnoreUndef(Object args[], int numArgs) {
+  ++ignoreUndef;
+}
+
+void Gfx::opEndIgnoreUndef(Object args[], int numArgs) {
+  if (ignoreUndef > 0)
+    --ignoreUndef;
+}
+
+//------------------------------------------------------------------------
+// marked content operators
+//------------------------------------------------------------------------
+
+void Gfx::opBeginMarkedContent(Object args[], int numArgs) {
+  if (printCommands) {
+    printf("  marked content: %s ", args[0].getName());
+    if (numArgs == 2)
+      args[2].print(stdout);
+    printf("\n");
+    fflush(stdout);
+  }
+}
+
+void Gfx::opEndMarkedContent(Object args[], int numArgs) {
+}
+
+void Gfx::opMarkPoint(Object args[], int numArgs) {
+  if (printCommands) {
+    printf("  mark point: %s ", args[0].getName());
+    if (numArgs == 2)
+      args[2].print(stdout);
+    printf("\n");
+    fflush(stdout);
+  }
+}
+
+//------------------------------------------------------------------------
+// misc
+//------------------------------------------------------------------------
+
+void Gfx::saveState() {
+  out->saveState(state);
+  state = state->save();
+}
+
+void Gfx::restoreState() {
+  state = state->restore();
+  out->restoreState(state);
+}
+
+void Gfx::pushResources(Dict *resDict) {
+  res = new GfxResources(xref, resDict, res);
+}
+
+void Gfx::popResources() {
+  GfxResources *resPtr;
+
+  resPtr = res->getNext();
+  delete res;
+  res = resPtr;
+}
diff --git a/lib/xpdf/Gfx.h b/lib/xpdf/Gfx.h
new file mode 100644 (file)
index 0000000..168206d
--- /dev/null
@@ -0,0 +1,293 @@
+//========================================================================
+//
+// Gfx.h
+//
+// Copyright 1996-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef GFX_H
+#define GFX_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#include "gtypes.h"
+
+class GString;
+class XRef;
+class Array;
+class Stream;
+class Parser;
+class Dict;
+class OutputDev;
+class GfxFontDict;
+class GfxFont;
+class GfxPattern;
+class GfxTilingPattern;
+class GfxShadingPattern;
+class GfxShading;
+class GfxFunctionShading;
+class GfxAxialShading;
+class GfxRadialShading;
+class GfxGouraudTriangleShading;
+class GfxPatchMeshShading;
+struct GfxPatch;
+class GfxState;
+struct GfxColor;
+class Gfx;
+class PDFRectangle;
+
+//------------------------------------------------------------------------
+// Gfx
+//------------------------------------------------------------------------
+
+enum GfxClipType {
+  clipNone,
+  clipNormal,
+  clipEO
+};
+
+enum TchkType {
+  tchkBool,                    // boolean
+  tchkInt,                     // integer
+  tchkNum,                     // number (integer or real)
+  tchkString,                  // string
+  tchkName,                    // name
+  tchkArray,                   // array
+  tchkProps,                   // properties (dictionary or name)
+  tchkSCN,                     // scn/SCN args (number of name)
+  tchkNone                     // used to avoid empty initializer lists
+};
+
+#define maxArgs 8
+
+struct Operator {
+  char name[4];
+  int numArgs;
+  TchkType tchk[maxArgs];
+  void (Gfx::*func)(Object args[], int numArgs);
+};
+
+class GfxResources {
+public:
+
+  GfxResources(XRef *xref, Dict *resDict, GfxResources *nextA);
+  ~GfxResources();
+
+  GfxFont *lookupFont(char *name);
+  GBool lookupXObject(char *name, Object *obj);
+  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; }
+
+private:
+
+  GfxFontDict *fonts;
+  Object xObjDict;
+  Object colorSpaceDict;
+  Object patternDict;
+  Object shadingDict;
+  Object gStateDict;
+  GfxResources *next;
+};
+
+class Gfx {
+public:
+
+  // Constructor for regular output.
+  Gfx(XRef *xrefA, OutputDev *outA, int pageNum, Dict *resDict,
+      double hDPI, double vDPI, PDFRectangle *box,
+      PDFRectangle *cropBox, int rotate,
+      GBool (*abortCheckCbkA)(void *data) = NULL,
+      void *abortCheckCbkDataA = NULL);
+
+  // Constructor for a sub-page object.
+  Gfx(XRef *xrefA, OutputDev *outA, Dict *resDict,
+      PDFRectangle *box, PDFRectangle *cropBox,
+      GBool (*abortCheckCbkA)(void *data) = NULL,
+      void *abortCheckCbkDataA = NULL);
+
+  ~Gfx();
+
+  // Interpret a stream or array of streams.
+  void display(Object *obj, GBool topLevel = gTrue);
+
+  // 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);
+
+  // Save graphics state.
+  void saveState();
+
+  // Restore graphics state.
+  void restoreState();
+
+  // Get the current graphics state object.
+  GfxState *getState() { return state; }
+
+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
+  GfxClipType clip;            // do a clip?
+  int ignoreUndef;             // current BX/EX nesting level
+  double baseMatrix[6];                // default matrix for most recent
+                               //   page/form/pattern
+  int formDepth;
+
+  Parser *parser;              // parser for page content stream(s)
+
+  GBool                                // callback to check for an abort
+    (*abortCheckCbk)(void *data);
+  void *abortCheckCbkData;
+
+  static Operator opTab[];     // table of operators
+
+  void go(GBool topLevel);
+  void execOp(Object *cmd, Object args[], int numArgs);
+  Operator *findOp(char *name);
+  GBool checkArg(Object *arg, TchkType type);
+  int getPos();
+
+  // graphics state operators
+  void opSave(Object args[], int numArgs);
+  void opRestore(Object args[], int numArgs);
+  void opConcat(Object args[], int numArgs);
+  void opSetDash(Object args[], int numArgs);
+  void opSetFlat(Object args[], int numArgs);
+  void opSetLineJoin(Object args[], int numArgs);
+  void opSetLineCap(Object args[], int numArgs);
+  void opSetMiterLimit(Object args[], int numArgs);
+  void opSetLineWidth(Object args[], int numArgs);
+  void opSetExtGState(Object args[], int numArgs);
+  void opSetRenderingIntent(Object args[], int numArgs);
+
+  // color operators
+  void opSetFillGray(Object args[], int numArgs);
+  void opSetStrokeGray(Object args[], int numArgs);
+  void opSetFillCMYKColor(Object args[], int numArgs);
+  void opSetStrokeCMYKColor(Object args[], int numArgs);
+  void opSetFillRGBColor(Object args[], int numArgs);
+  void opSetStrokeRGBColor(Object args[], int numArgs);
+  void opSetFillColorSpace(Object args[], int numArgs);
+  void opSetStrokeColorSpace(Object args[], int numArgs);
+  void opSetFillColor(Object args[], int numArgs);
+  void opSetStrokeColor(Object args[], int numArgs);
+  void opSetFillColorN(Object args[], int numArgs);
+  void opSetStrokeColorN(Object args[], int numArgs);
+
+  // path segment operators
+  void opMoveTo(Object args[], int numArgs);
+  void opLineTo(Object args[], int numArgs);
+  void opCurveTo(Object args[], int numArgs);
+  void opCurveTo1(Object args[], int numArgs);
+  void opCurveTo2(Object args[], int numArgs);
+  void opRectangle(Object args[], int numArgs);
+  void opClosePath(Object args[], int numArgs);
+
+  // path painting operators
+  void opEndPath(Object args[], int numArgs);
+  void opStroke(Object args[], int numArgs);
+  void opCloseStroke(Object args[], int numArgs);
+  void opFill(Object args[], int numArgs);
+  void opEOFill(Object args[], int numArgs);
+  void opFillStroke(Object args[], int numArgs);
+  void opCloseFillStroke(Object args[], int numArgs);
+  void opEOFillStroke(Object args[], int numArgs);
+  void opCloseEOFillStroke(Object args[], int numArgs);
+  void doPatternFill(GBool eoFill);
+  void doTilingPatternFill(GfxTilingPattern *tPat, GBool eoFill);
+  void doShadingPatternFill(GfxShadingPattern *sPat, GBool eoFill);
+  void opShFill(Object args[], int numArgs);
+  void doFunctionShFill(GfxFunctionShading *shading);
+  void doFunctionShFill1(GfxFunctionShading *shading,
+                        double x0, double y0,
+                        double x1, double y1,
+                        GfxColor *colors, int depth);
+  void doAxialShFill(GfxAxialShading *shading);
+  void doRadialShFill(GfxRadialShading *shading);
+  void doGouraudTriangleShFill(GfxGouraudTriangleShading *shading);
+  void gouraudFillTriangle(double x0, double y0, GfxColor *color0,
+                          double x1, double y1, GfxColor *color1,
+                          double x2, double y2, GfxColor *color2,
+                          int nComps, int depth);
+  void doPatchMeshShFill(GfxPatchMeshShading *shading);
+  void fillPatch(GfxPatch *patch, int nComps, int depth);
+  void doEndPath();
+
+  // path clipping operators
+  void opClip(Object args[], int numArgs);
+  void opEOClip(Object args[], int numArgs);
+
+  // text object operators
+  void opBeginText(Object args[], int numArgs);
+  void opEndText(Object args[], int numArgs);
+
+  // text state operators
+  void opSetCharSpacing(Object args[], int numArgs);
+  void opSetFont(Object args[], int numArgs);
+  void opSetTextLeading(Object args[], int numArgs);
+  void opSetTextRender(Object args[], int numArgs);
+  void opSetTextRise(Object args[], int numArgs);
+  void opSetWordSpacing(Object args[], int numArgs);
+  void opSetHorizScaling(Object args[], int numArgs);
+
+  // text positioning operators
+  void opTextMove(Object args[], int numArgs);
+  void opTextMoveSet(Object args[], int numArgs);
+  void opSetTextMatrix(Object args[], int numArgs);
+  void opTextNextLine(Object args[], int numArgs);
+
+  // text string operators
+  void opShowText(Object args[], int numArgs);
+  void opMoveShowText(Object args[], int numArgs);
+  void opMoveSetShowText(Object args[], int numArgs);
+  void opShowSpaceText(Object args[], int numArgs);
+  void doShowText(GString *s);
+
+  // XObject operators
+  void opXObject(Object args[], int numArgs);
+  void doImage(Object *ref, Stream *str, GBool inlineImg);
+  void doForm(Object *str);
+  void doForm1(Object *str, Dict *resDict, double *matrix, double *bbox);
+
+  // in-line image operators
+  void opBeginImage(Object args[], int numArgs);
+  Stream *buildImageStream();
+  void opImageData(Object args[], int numArgs);
+  void opEndImage(Object args[], int numArgs);
+
+  // type 3 font operators
+  void opSetCharWidth(Object args[], int numArgs);
+  void opSetCacheDevice(Object args[], int numArgs);
+
+  // compatibility operators
+  void opBeginIgnoreUndef(Object args[], int numArgs);
+  void opEndIgnoreUndef(Object args[], int numArgs);
+
+  // marked content operators
+  void opBeginMarkedContent(Object args[], int numArgs);
+  void opEndMarkedContent(Object args[], int numArgs);
+  void opMarkPoint(Object args[], int numArgs);
+
+  void pushResources(Dict *resDict);
+  void popResources();
+};
+
+#endif
diff --git a/lib/xpdf/GfxFont.cc b/lib/xpdf/GfxFont.cc
new file mode 100644 (file)
index 0000000..a4581c0
--- /dev/null
@@ -0,0 +1,1546 @@
+//========================================================================
+//
+// GfxFont.cc
+//
+// Copyright 1996-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include "gmem.h"
+#include "Error.h"
+#include "Object.h"
+#include "Dict.h"
+#include "GlobalParams.h"
+#include "CMap.h"
+#include "CharCodeToUnicode.h"
+#include "FontEncodingTables.h"
+#include "BuiltinFontTables.h"
+#include "FoFiType1.h"
+#include "FoFiType1C.h"
+#include "FoFiTrueType.h"
+#include "GfxFont.h"
+
+//------------------------------------------------------------------------
+
+struct StdFontMapEntry {
+  char *altName;
+  char *properName;
+};
+
+// 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, with some additions based on empirical
+// evidence.
+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,BoldItalic",           "Courier-BoldOblique" },
+  { "Courier,Italic",               "Courier-Oblique" },
+  { "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" },
+  { "Symbol,Bold",                  "Symbol" },
+  { "Symbol,BoldItalic",            "Symbol" },
+  { "Symbol,Italic",                "Symbol" },
+  { "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" },
+  { "TimesNewRomanPSMT,Bold",       "Times-Bold" },
+  { "TimesNewRomanPSMT,BoldItalic", "Times-BoldItalic" },
+  { "TimesNewRomanPSMT,Italic",     "Times-Italic" }
+};
+
+//------------------------------------------------------------------------
+// GfxFont
+//------------------------------------------------------------------------
+
+GfxFont *GfxFont::makeFont(XRef *xref, char *tagA, Ref idA, Dict *fontDict) {
+  GString *nameA;
+  GfxFont *font;
+  Object obj1;
+
+  // get base font name
+  nameA = NULL;
+  fontDict->lookup("BaseFont", &obj1);
+  if (obj1.isName()) {
+    nameA = new GString(obj1.getName());
+  }
+  obj1.free();
+
+  // get font type
+  font = NULL;
+  fontDict->lookup("Subtype", &obj1);
+  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();
+
+  return font;
+}
+
+GfxFont::GfxFont(char *tagA, Ref idA, GString *nameA) {
+  ok = gFalse;
+  tag = new GString(tagA);
+  id = idA;
+  name = nameA;
+  origName = nameA;
+  embFontName = NULL;
+  extFontFile = NULL;
+}
+
+GfxFont::~GfxFont() {
+  delete tag;
+  if (origName && origName != name) {
+    delete origName;
+  }
+  if (name) {
+    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;
+
+  embFontID.num = -1;
+  embFontID.gen = -1;
+  missingWidth = 0;
+
+  if (fontDict->lookup("FontDescriptor", &obj1)->isDict()) {
+
+    // get flags
+    if (obj1.dictLookup("Flags", &obj2)->isInt()) {
+      flags = obj2.getInt();
+    }
+    obj2.free();
+
+    // get name
+    obj1.dictLookup("FontName", &obj2);
+    if (obj2.isName()) {
+      embFontName = new GString(obj2.getName());
+    }
+    obj2.free();
+
+    // look for embedded font file
+    if (obj1.dictLookupNF("FontFile", &obj2)->isRef()) {
+      embFontID = obj2.getRef();
+      if (type != fontType1) {
+       error(-1, "Mismatch between font type and embedded font file");
+       type = fontType1;
+      }
+    }
+    obj2.free();
+    if (embFontID.num == -1 &&
+       obj1.dictLookupNF("FontFile2", &obj2)->isRef()) {
+      embFontID = obj2.getRef();
+      if (type != fontTrueType && type != fontCIDType2) {
+       error(-1, "Mismatch between font type and embedded font file");
+       type = type == fontCIDType0 ? fontCIDType2 : fontTrueType;
+      }
+    }
+    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")) {
+         embFontID = obj2.getRef();
+         if (type != fontType1) {
+           error(-1, "Mismatch between font type and embedded font file");
+           type = fontType1;
+         }
+       } else if (obj4.isName("Type1C")) {
+         embFontID = obj2.getRef();
+         if (type != fontType1 && type != fontType1C) {
+           error(-1, "Mismatch between font type and embedded font file");
+         }
+         type = fontType1C;
+       } else if (obj4.isName("TrueType")) {
+         embFontID = obj2.getRef();
+         if (type != fontTrueType) {
+           error(-1, "Mismatch between font type and embedded font file");
+           type = fontTrueType;
+         }
+       } else if (obj4.isName("CIDFontType0C")) {
+         embFontID = obj2.getRef();
+         if (type != fontCIDType0) {
+           error(-1, "Mismatch between font type and embedded font file");
+         }
+         type = fontCIDType0C;
+       } else {
+         error(-1, "Unknown embedded font type '%s'",
+               obj4.isName() ? obj4.getName() : "???");
+       }
+       obj4.free();
+      }
+      obj3.free();
+    }
+    obj2.free();
+
+    // look for MissingWidth
+    obj1.dictLookup("MissingWidth", &obj2);
+    if (obj2.isNum()) {
+      missingWidth = obj2.getNum();
+    }
+    obj2.free();
+
+    // 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();
+    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;
+      }
+      // some broken font descriptors specify a positive descent
+      if (descent > 0) {
+       descent = -descent;
+      }
+    }
+    obj2.free();
+
+    // 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;
+
+  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();
+  if (ctu) {
+    ctu->mergeCMap(buf, nBits);
+  } else {
+    ctu = CharCodeToUnicode::parseCMap(buf, nBits);
+  }
+  delete buf;
+  return ctu;
+}
+
+void GfxFont::findExtFontFile() {
+  static char *type1Exts[] = { ".pfa", ".pfb", ".ps", "", NULL };
+  static char *ttExts[] = { ".ttf", NULL };
+
+  if (name) {
+    if (type == fontType1) {
+      extFontFile = globalParams->findFontFile(name, type1Exts);
+    } else if (type == fontTrueType) {
+      extFontFile = globalParams->findFontFile(name, ttExts);
+    }
+  }
+}
+
+char *GfxFont::readExtFontFile(int *len) {
+  FILE *f;
+  char *buf;
+
+  if (!(f = fopen(extFontFile->getCString(), "rb"))) {
+    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) {
+    error(-1, "Error reading external font file '%s'",
+         extFontFile->getCString());
+  }
+  fclose(f);
+  return buf;
+}
+
+char *GfxFont::readEmbFontFile(XRef *xref, int *len) {
+  char *buf;
+  Object obj1, obj2;
+  Stream *str;
+  int c;
+  int size, i;
+
+  obj1.initRef(embFontID.num, embFontID.gen);
+  obj1.fetch(xref, &obj2);
+  if (!obj2.isStream()) {
+    error(-1, "Embedded font file is not a stream");
+    obj2.free();
+    obj1.free();
+    embFontID.num = -1;
+    return NULL;
+  }
+  str = obj2.getStream();
+
+  buf = NULL;
+  i = size = 0;
+  str->reset();
+  while ((c = str->getChar()) != EOF) {
+    if (i == size) {
+      size += 4096;
+      buf = (char *)grealloc(buf, size);
+    }
+    buf[i++] = c;
+  }
+  *len = i;
+  str->close();
+
+  obj2.free();
+  obj1.free();
+
+  return buf;
+}
+
+//------------------------------------------------------------------------
+// Gfx8BitFont
+//------------------------------------------------------------------------
+
+Gfx8BitFont::Gfx8BitFont(XRef *xref, char *tagA, Ref idA, GString *nameA,
+                        GfxFontType typeA, Dict *fontDict):
+  GfxFont(tagA, idA, nameA)
+{
+  GString *name2;
+  BuiltinFont *builtinFont;
+  char **baseEnc;
+  GBool baseEncFromFontFile;
+  char *buf;
+  int len;
+  FoFiType1 *ffT1;
+  FoFiType1C *ffT1C;
+  int code, code2;
+  char *charName;
+  GBool missing, hex;
+  Unicode toUnicode[256];
+  CharCodeToUnicode *utu, *ctu2;
+  Unicode uBuf[8];
+  double mul;
+  int firstChar, lastChar;
+  Gushort w;
+  Object obj1, obj2, obj3;
+  int n, i, a, b, m;
+
+  type = typeA;
+  ctu = NULL;
+
+  // do font name substitution for various aliases of the Base 14 font
+  // names
+  if (name) {
+    name2 = name->copy();
+    i = 0;
+    while (i < name2->getLength()) {
+      if (name2->getChar(i) == ' ') {
+       name2->del(i);
+      } else {
+       ++i;
+      }
+    }
+    a = 0;
+    b = sizeof(stdFontMap) / sizeof(StdFontMapEntry);
+    // invariant: stdFontMap[a].altName <= name2 < stdFontMap[b].altName
+    while (b - a > 1) {
+      m = (a + b) / 2;
+      if (name2->cmp(stdFontMap[m].altName) >= 0) {
+       a = m;
+      } else {
+       b = m;
+      }
+    }
+    if (!name2->cmp(stdFontMap[a].altName)) {
+      name = new GString(stdFontMap[a].properName);
+    }
+    delete name2;
+  }
+
+  // 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 {
+    ascent = 0.95;
+    descent = -0.35;
+    fontBBox[0] = fontBBox[1] = fontBBox[2] = fontBBox[3] = 0;
+  }
+
+  // get info from font descriptor
+  readFontDescriptor(xref, fontDict);
+
+  // for non-embedded fonts, don't trust the ascent/descent/bbox
+  // values from the font descriptor
+  if (builtinFont && embFontID.num < 0) {
+    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];
+  }
+
+  // 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 --> WinAnsiEncoding
+  //        - others --> StandardEncoding
+  // and then add a list of differences (if any) from
+  // FontDict.Encoding.Differences.
+
+  // check FontDict for base encoding
+  hasEncoding = gFalse;
+  usesMacRomanEnc = gFalse;
+  baseEnc = NULL;
+  baseEncFromFontFile = gFalse;
+  fontDict->lookup("Encoding", &obj1);
+  if (obj1.isDict()) {
+    obj1.dictLookup("BaseEncoding", &obj2);
+    if (obj2.isName("MacRomanEncoding")) {
+      hasEncoding = gTrue;
+      usesMacRomanEnc = gTrue;
+      baseEnc = macRomanEncoding;
+    } else if (obj2.isName("MacExpertEncoding")) {
+      hasEncoding = gTrue;
+      baseEnc = macExpertEncoding;
+    } else if (obj2.isName("WinAnsiEncoding")) {
+      hasEncoding = gTrue;
+      baseEnc = winAnsiEncoding;
+    }
+    obj2.free();
+  } else if (obj1.isName("MacRomanEncoding")) {
+    hasEncoding = gTrue;
+    usesMacRomanEnc = gTrue;
+    baseEnc = macRomanEncoding;
+  } else if (obj1.isName("MacExpertEncoding")) {
+    hasEncoding = gTrue;
+    baseEnc = macExpertEncoding;
+  } else if (obj1.isName("WinAnsiEncoding")) {
+    hasEncoding = gTrue;
+    baseEnc = winAnsiEncoding;
+  }
+
+  // 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)
+  ffT1 = NULL;
+  ffT1C = NULL;
+  buf = NULL;
+  if (type == fontType1 && (extFontFile || embFontID.num >= 0)) {
+    if (extFontFile) {
+      ffT1 = FoFiType1::load(extFontFile->getCString());
+    } else {
+      buf = readEmbFontFile(xref, &len);
+      ffT1 = FoFiType1::make(buf, len);
+    }
+    if (ffT1) {
+      if (ffT1->getName()) {
+       if (embFontName) {
+         delete embFontName;
+       }
+       embFontName = new GString(ffT1->getName());
+      }
+      if (!baseEnc) {
+       baseEnc = ffT1->getEncoding();
+       baseEncFromFontFile = gTrue;
+      }
+    }
+  } else if (type == fontType1C && (extFontFile || embFontID.num >= 0)) {
+    if (extFontFile) {
+      ffT1C = FoFiType1C::load(extFontFile->getCString());
+    } else {
+      buf = readEmbFontFile(xref, &len);
+      ffT1C = FoFiType1C::make(buf, len);
+    }
+    if (ffT1C) {
+      if (ffT1C->getName()) {
+       if (embFontName) {
+         delete embFontName;
+       }
+       embFontName = new GString(ffT1C->getName());
+      }
+      if (!baseEnc) {
+       baseEnc = ffT1C->getEncoding();
+       baseEncFromFontFile = gTrue;
+      }
+    }
+  }
+  if (buf) {
+    gfree(buf);
+  }
+
+  // get default base encoding
+  if (!baseEnc) {
+    if (builtinFont && embFontID.num < 0) {
+      baseEnc = builtinFont->defaultBaseEnc;
+      hasEncoding = gTrue;
+    } else if (type == fontTrueType) {
+      baseEnc = winAnsiEncoding;
+    } else {
+      baseEnc = standardEncoding;
+    }
+  }
+
+  // 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]);
+    }
+  }
+
+  // some Type 1C font files have empty encodings, which can break the
+  // T1C->T1 conversion (since the 'seac' operator depends on having
+  // the accents in the encoding), so we fill in any gaps from
+  // StandardEncoding
+  if (type == fontType1C && (extFontFile || embFontID.num >= 0) &&
+      baseEncFromFontFile) {
+    for (i = 0; i < 256; ++i) {
+      if (!enc[i] && standardEncoding[i]) {
+       enc[i] = standardEncoding[i];
+       encFree[i] = gFalse;
+      }
+    }
+  }
+
+  // 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 >= 0 && 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 (ffT1) {
+    delete ffT1;
+  }
+  if (ffT1C) {
+    delete ffT1C;
+  }
+
+  //----- build the mapping to Unicode -----
+
+  // 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 = 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;
+       }
+      }
+    }
+  }
+
+  // construct the char code -> Unicode mapping object
+  ctu = CharCodeToUnicode::make8BitToUnicode(toUnicode);
+
+  // merge in a ToUnicode CMap, if there is one -- this overwrites
+  // existing entries in ctu, i.e., the ToUnicode CMap takes
+  // precedence, but the other encoding info is allowed to fill in any
+  // holes
+  readToUnicodeCMap(fontDict, 8, ctu);
+
+  // look for a Unicode-to-Unicode mapping
+  if (name && (utu = globalParams->getUnicodeToUnicode(name))) {
+    for (i = 0; i < 256; ++i) {
+      toUnicode[i] = 0;
+    }
+    ctu2 = CharCodeToUnicode::make8BitToUnicode(toUnicode);
+    for (i = 0; i < 256; ++i) {
+      n = ctu->mapToUnicode((CharCode)i, uBuf, 8);
+      if (n >= 1) {
+       n = utu->mapToUnicode((CharCode)uBuf[0], uBuf, 8);
+       if (n >= 1) {
+         ctu2->setMapping((CharCode)i, uBuf, n);
+       }
+      }
+    }
+    utu->decRefCnt();
+    delete ctu;
+    ctu = ctu2;
+  }
+
+  //----- 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();
+  if (firstChar < 0 || firstChar > 255) {
+    firstChar = 0;
+  }
+  fontDict->lookup("LastChar", &obj1);
+  lastChar = obj1.isInt() ? obj1.getInt() : 255;
+  obj1.free();
+  if (lastChar < 0 || lastChar > 255) {
+    lastChar = 255;
+  }
+  mul = (type == fontType3) ? fontMat[0] : 0.001;
+  fontDict->lookup("Widths", &obj1);
+  if (obj1.isArray()) {
+    flags |= fontFixedWidth;
+    if (obj1.arrayGetLength() < lastChar - firstChar + 1) {
+      lastChar = firstChar + obj1.arrayGetLength() - 1;
+    }
+    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();
+  }
+}
+
+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;
+
+  *code = c = (CharCode)(*s & 0xff);
+  *uLen = ctu->mapToUnicode(c, u, uSize);
+  *dx = widths[c];
+  *dy = *ox = *oy = 0;
+  return 1;
+}
+
+CharCodeToUnicode *Gfx8BitFont::getToUnicode() {
+  ctu->incRefCnt();
+  return ctu;
+}
+
+Gushort *Gfx8BitFont::getCodeToGIDMap(FoFiTrueType *ff) {
+  Gushort *map;
+  int cmapPlatform, cmapEncoding;
+  int unicodeCmap, macRomanCmap, msSymbolCmap, cmap;
+  GBool useMacRoman, useUnicode;
+  char *charName;
+  Unicode u;
+  int code, i, n;
+
+  map = (Gushort *)gmallocn(256, sizeof(Gushort));
+  for (i = 0; i < 256; ++i) {
+    map[i] = 0;
+  }
+
+  // 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 PDF font specified MacRomanEncoding and the
+  //        TrueType font has a Macintosh Roman cmap, use it, and
+  //        reverse map the char names through MacRomanEncoding to
+  //        get char codes.
+  //    1b. If the TrueType font has a Microsoft Unicode cmap or a
+  //        non-Microsoft Unicode cmap, use it, and use the Unicode
+  //        indexes, not the char codes.
+  //    1c. If the PDF font is symbolic and the TrueType font has a
+  //        Microsoft Symbol cmap, use it, and use char codes
+  //        directly (possibly with an offset of 0xf000).
+  //    1d. If the TrueType font has a Macintosh Roman cmap, use it,
+  //        as in case 1a.
+  // 2. If the PDF font does not have an encoding or the PDF font is
+  //    symbolic:
+  //    2a. If the TrueType font has a Macintosh Roman cmap, use it,
+  //        and use char codes directly (possibly with an offset of
+  //        0xf000).
+  //    2b. If the TrueType font has a Microsoft Symbol cmap, use it,
+  //        and use char codes directly (possible with an offset of
+  //        0xf000).
+  // 3. If none of these rules apply, use the first cmap and hope for
+  //    the best (this shouldn't happen).
+  unicodeCmap = macRomanCmap = msSymbolCmap = -1;
+  for (i = 0; i < ff->getNumCmaps(); ++i) {
+    cmapPlatform = ff->getCmapPlatform(i);
+    cmapEncoding = ff->getCmapEncoding(i);
+    if ((cmapPlatform == 3 && cmapEncoding == 1) ||
+       cmapPlatform == 0) {
+      unicodeCmap = i;
+    } else if (cmapPlatform == 1 && cmapEncoding == 0) {
+      macRomanCmap = i;
+    } else if (cmapPlatform == 3 && cmapEncoding == 0) {
+      msSymbolCmap = i;
+    }
+  }
+  cmap = 0;
+  useMacRoman = gFalse;
+  useUnicode = gFalse;
+  if (hasEncoding) {
+    if (usesMacRomanEnc && macRomanCmap >= 0) {
+      cmap = macRomanCmap;
+      useMacRoman = gTrue;
+    } else if (unicodeCmap >= 0) {
+      cmap = unicodeCmap;
+      useUnicode = gTrue;
+    } else if ((flags & fontSymbolic) && msSymbolCmap >= 0) {
+      cmap = msSymbolCmap;
+    } else if ((flags & fontSymbolic) && macRomanCmap >= 0) {
+      cmap = macRomanCmap;
+    } else if (macRomanCmap >= 0) {
+      cmap = macRomanCmap;
+      useMacRoman = gTrue;
+    }
+  } else {
+    if (macRomanCmap >= 0) {
+      cmap = macRomanCmap;
+    } else if (msSymbolCmap >= 0) {
+      cmap = msSymbolCmap;
+    }
+  }
+
+  // reverse map the char names through MacRomanEncoding, then map the
+  // char codes through the cmap
+  if (useMacRoman) {
+    for (i = 0; i < 256; ++i) {
+      if ((charName = enc[i])) {
+       if ((code = globalParams->getMacRomanCharCode(charName))) {
+         map[i] = ff->mapCodeToGID(cmap, code);
+       }
+      }
+    }
+
+  // map Unicode through the cmap
+  } else if (useUnicode) {
+    for (i = 0; i < 256; ++i) {
+      if (((charName = enc[i]) &&
+          (u = globalParams->mapNameToUnicode(charName))) ||
+         (n = ctu->mapToUnicode((CharCode)i, &u, 1))) {
+       map[i] = ff->mapCodeToGID(cmap, u);
+      }
+    }
+
+  // map the char codes through the cmap, possibly with an offset of
+  // 0xf000
+  } else {
+    for (i = 0; i < 256; ++i) {
+      if (!(map[i] = ff->mapCodeToGID(cmap, i))) {
+       map[i] = ff->mapCodeToGID(cmap, 0xf000 + i);
+      }
+    }
+  }
+
+  // try the TrueType 'post' table to handle any unmapped characters
+  for (i = 0; i < 256; ++i) {
+    if (!map[i] && (charName = enc[i])) {
+      map[i] = (Gushort)(int)ff->mapNameToGID(charName);
+    }
+  }
+
+  return map;
+}
+
+Dict *Gfx8BitFont::getCharProcs() {
+  return charProcs.isDict() ? charProcs.getDict() : (Dict *)NULL;
+}
+
+Object *Gfx8BitFont::getCharProc(int code, Object *proc) {
+  if (enc[code] && 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 CDECL cmpWidthExcep(const void *w1, const void *w2) {
+  return ((GfxFontCIDWidthExcep *)w1)->first -
+         ((GfxFontCIDWidthExcep *)w2)->first;
+}
+
+static int CDECL 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;
+  CharCodeToUnicode *utu;
+  CharCode c;
+  Unicode uBuf[8];
+  int c1, c2;
+  int excepsSize, i, j, k, n;
+
+  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;
+  }
+  if (!obj1.arrayGet(0, &desFontDictObj)->isDict()) {
+    error(-1, "Bad descendant font in Type 0 font");
+    goto err3;
+  }
+  obj1.free();
+  desFontDict = desFontDictObj.getDict();
+
+  // font type
+  if (!desFontDict->lookup("Subtype", &obj1)) {
+    error(-1, "Missing Subtype entry in Type 0 descendant font");
+    goto err3;
+  }
+  if (obj1.isName("CIDFontType0")) {
+    type = fontCIDType0;
+  } else if (obj1.isName("CIDFontType2")) {
+    type = fontCIDType2;
+  } else {
+    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;
+  }
+  collection = obj2.getString()->copy()->append('-')->append(obj3.getString());
+  obj3.free();
+  obj2.free();
+  obj1.free();
+
+  // look for a ToUnicode CMap
+  if (!(ctu = readToUnicodeCMap(fontDict, 16, NULL))) {
+
+    // the "Adobe-Identity" and "Adobe-UCS" collections don't have
+    // cidToUnicode files
+    if (collection->cmp("Adobe-Identity") &&
+       collection->cmp("Adobe-UCS")) {
+
+      // look for a user-supplied .cidToUnicode file
+      if (!(ctu = globalParams->getCIDToUnicode(collection))) {
+       error(-1, "Unknown character collection '%s'",
+             collection->getCString());
+       // fall-through, assuming the Identity mapping -- this appears
+       // to match Adobe's behavior
+      }
+    }
+  }
+
+  // look for a Unicode-to-Unicode mapping
+  if (name && (utu = globalParams->getUnicodeToUnicode(name))) {
+    if (ctu) {
+      for (c = 0; c < ctu->getLength(); ++c) {
+       n = ctu->mapToUnicode(c, uBuf, 8);
+       if (n >= 1) {
+         n = utu->mapToUnicode((CharCode)uBuf[0], uBuf, 8);
+         if (n >= 1) {
+           ctu->setMapping(c, uBuf, n);
+         }
+       }
+      }
+      utu->decRefCnt();
+    } else {
+      ctu = utu;
+    }
+  }
+
+  // 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) {
+    desFontDict->lookup("CIDToGIDMap", &obj1);
+    if (obj1.isStream()) {
+      cidToGIDLen = 0;
+      i = 64;
+      cidToGID = (Gushort *)gmallocn(i, sizeof(Gushort));
+      obj1.streamReset();
+      while ((c1 = obj1.streamGetChar()) != EOF &&
+            (c2 = obj1.streamGetChar()) != EOF) {
+       if (cidToGIDLen == i) {
+         i *= 2;
+         cidToGID = (Gushort *)greallocn(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");
+    }
+    obj1.free();
+  }
+
+  //----- 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;
+    i = 0;
+    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 *)
+             greallocn(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");
+       }
+       obj4.free();
+       i += 3;
+      } else if (obj2.isInt() && obj3.isArray()) {
+       if (widths.nExceps + obj3.arrayGetLength() > excepsSize) {
+         excepsSize = (widths.nExceps + obj3.arrayGetLength() + 15) & ~15;
+         widths.exceps = (GfxFontCIDWidthExcep *)
+           greallocn(widths.exceps,
+                     excepsSize, sizeof(GfxFontCIDWidthExcep));
+       }
+       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");
+         }
+         obj4.free();
+       }
+       i += 2;
+      } else {
+       error(-1, "Bad widths array in Type 0 font");
+       ++i;
+      }
+      obj3.free();
+      obj2.free();
+    }
+    qsort(widths.exceps, widths.nExceps, sizeof(GfxFontCIDWidthExcep),
+         &cmpWidthExcep);
+  }
+  obj1.free();
+
+  // default metrics for vertical font
+  if (desFontDict->lookup("DW2", &obj1)->isArray() &&
+      obj1.arrayGetLength() == 2) {
+    if (obj1.arrayGet(0, &obj2)->isNum()) {
+      widths.defVY = obj2.getNum() * 0.001;
+    }
+    obj2.free();
+    if (obj1.arrayGet(1, &obj2)->isNum()) {
+      widths.defHeight = obj2.getNum() * 0.001;
+    }
+    obj2.free();
+  }
+  obj1.free();
+
+  // char metric exceptions for vertical font
+  if (desFontDict->lookup("W2", &obj1)->isArray()) {
+    excepsSize = 0;
+    i = 0;
+    while (i + 1 < obj1.arrayGetLength()) {
+      obj1.arrayGet(i, &obj2);
+      obj1.arrayGet(i+ 1, &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 *)
+             greallocn(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");
+       }
+       obj6.free();
+       obj5.free();
+       obj4.free();
+       i += 5;
+      } else if (obj2.isInt() && obj3.isArray()) {
+       if (widths.nExcepsV + obj3.arrayGetLength() / 3 > excepsSize) {
+         excepsSize =
+           (widths.nExcepsV + obj3.arrayGetLength() / 3 + 15) & ~15;
+         widths.excepsV = (GfxFontCIDWidthExcepV *)
+           greallocn(widths.excepsV,
+                     excepsSize, sizeof(GfxFontCIDWidthExcepV));
+       }
+       j = obj2.getInt();
+       for (k = 0; k < obj3.arrayGetLength(); k += 3) {
+         if (obj3.arrayGet(k, &obj4)->isNum() &&
+             obj3.arrayGet(k+1, &obj5)->isNum() &&
+             obj3.arrayGet(k+2, &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();
+         obj5.free();
+         obj4.free();
+       }
+       i += 2;
+      } else {
+       error(-1, "Bad widths (W2) array in Type 0 font");
+       ++i;
+      }
+      obj3.free();
+      obj2.free();
+    }
+    qsort(widths.excepsV, widths.nExcepsV, sizeof(GfxFontCIDWidthExcepV),
+         &cmpWidthExcepV);
+  }
+  obj1.free();
+
+  desFontDictObj.free();
+  ok = gTrue;
+  return;
+
+ err4:
+  obj3.free();
+  obj2.free();
+ err3:
+  obj1.free();
+ err2:
+  desFontDictObj.free();
+ err1:;
+}
+
+GfxCIDFont::~GfxCIDFont() {
+  if (cMap) {
+    cMap->decRefCnt();
+  }
+  if (ctu) {
+    ctu->decRefCnt();
+  }
+  gfree(widths.exceps);
+  gfree(widths.excepsV);
+  if (cidToGID) {
+    gfree(cidToGID);
+  }
+}
+
+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;
+      }
+    }
+
+  // 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;
+      }
+    }
+  }
+
+  *dx = w;
+  *dy = h;
+  *ox = vx;
+  *oy = vy;
+
+  return n;
+}
+
+int GfxCIDFont::getWMode() {
+  return cMap ? cMap->getWMode() : 0;
+}
+
+CharCodeToUnicode *GfxCIDFont::getToUnicode() {
+  if (ctu) {
+    ctu->incRefCnt();
+  }
+  return ctu;
+}
+
+GString *GfxCIDFont::getCollection() {
+  return cMap ? cMap->getCollection() : (GString *)NULL;
+}
+
+//------------------------------------------------------------------------
+// GfxFontDict
+//------------------------------------------------------------------------
+
+GfxFontDict::GfxFontDict(XRef *xref, Ref *fontDictRef, Dict *fontDict) {
+  int i;
+  Object obj1, obj2;
+  Ref r;
+
+  numFonts = fontDict->getLength();
+  fonts = (GfxFont **)gmallocn(numFonts, sizeof(GfxFont *));
+  for (i = 0; i < numFonts; ++i) {
+    fontDict->getValNF(i, &obj1);
+    obj1.fetch(xref, &obj2);
+    if (obj2.isDict()) {
+      if (obj1.isRef()) {
+       r = obj1.getRef();
+      } else {
+       // no indirect reference for this font, so invent a unique one
+       // (legal generation numbers are five digits, so any 6-digit
+       // number would be safe)
+       r.num = i;
+       if (fontDictRef) {
+         r.gen = 100000 + fontDictRef->num;
+       } else {
+         r.gen = 999999;
+       }
+      }
+      fonts[i] = GfxFont::makeFont(xref, fontDict->getKey(i),
+                                  r, obj2.getDict());
+      if (fonts[i] && !fonts[i]->isOk()) {
+       delete fonts[i];
+       fonts[i] = NULL;
+      }
+    } else {
+      error(-1, "font resource is not a dictionary");
+      fonts[i] = NULL;
+    }
+    obj1.free();
+    obj2.free();
+  }
+}
+
+GfxFontDict::~GfxFontDict() {
+  int i;
+
+  for (i = 0; i < numFonts; ++i) {
+    if (fonts[i]) {
+      delete fonts[i];
+    }
+  }
+  gfree(fonts);
+}
+
+GfxFont *GfxFontDict::lookup(char *tag) {
+  int i;
+
+  for (i = 0; i < numFonts; ++i) {
+    if (fonts[i] && fonts[i]->matches(tag)) {
+      return fonts[i];
+    }
+  }
+  return NULL;
+}
diff --git a/lib/xpdf/GfxFont.h b/lib/xpdf/GfxFont.h
new file mode 100644 (file)
index 0000000..df1e00c
--- /dev/null
@@ -0,0 +1,315 @@
+//========================================================================
+//
+// GfxFont.h
+//
+// Copyright 1996-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef GFXFONT_H
+#define GFXFONT_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#include "gtypes.h"
+#include "GString.h"
+#include "Object.h"
+#include "CharTypes.h"
+
+class Dict;
+class CMap;
+class CharCodeToUnicode;
+class FoFiTrueType;
+struct GfxFontCIDWidths;
+
+//------------------------------------------------------------------------
+// GfxFontType
+//------------------------------------------------------------------------
+
+enum GfxFontType {
+  //----- Gfx8BitFont
+  fontUnknownType,
+  fontType1,
+  fontType1C,
+  fontType3,
+  fontTrueType,
+  //----- GfxCIDFont
+  fontCIDType0,
+  fontCIDType0C,
+  fontCIDType2
+};
+
+//------------------------------------------------------------------------
+// GfxFontCIDWidths
+//------------------------------------------------------------------------
+
+struct GfxFontCIDWidthExcep {
+  CID first;                   // this record applies to
+  CID last;                    //   CIDs <first>..<last>
+  double width;                        // char width
+};
+
+struct GfxFontCIDWidthExcepV {
+  CID first;                   // this record applies to
+  CID last;                    //   CIDs <first>..<last>
+  double height;               // char height
+  double vx, vy;               // origin position
+};
+
+struct GfxFontCIDWidths {
+  double defWidth;             // default char width
+  double defHeight;            // default char height
+  double defVY;                        // default origin position
+  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
+};
+
+//------------------------------------------------------------------------
+// GfxFont
+//------------------------------------------------------------------------
+
+#define fontFixedWidth (1 << 0)
+#define fontSerif      (1 << 1)
+#define fontSymbolic   (1 << 2)
+#define fontItalic     (1 << 6)
+#define fontBold       (1 << 18)
+
+class GfxFont {
+public:
+
+  // Build a GfxFont object.
+  static GfxFont *makeFont(XRef *xref, char *tagA, Ref idA, Dict *fontDict);
+
+  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; }
+
+  // Does this font match the tag?
+  GBool matches(char *tagA) { return !tag->cmp(tagA); }
+
+  // Get base font name.
+  GString *getName() { return name; }
+
+  // Get the original font name (ignornig any munging that might have
+  // been done to map to a canonical Base-14 font name).
+  GString *getOrigName() { return origName; }
+
+  // Get font type.
+  GfxFontType getType() { return type; }
+  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.
+  GBool getEmbeddedFontID(Ref *embID)
+    { *embID = embFontID; return embFontID.num >= 0; }
+
+  // Get the PostScript font name for the embedded font.  Returns
+  // NULL if there is no embedded font.
+  GString *getEmbeddedFontName() { return embFontName; }
+
+  // Get the name of the external font file.  Returns NULL if there
+  // is no external font file.
+  GString *getExtFontFile() { return extFontFile; }
+
+  // Get font descriptor flags.
+  GBool isFixedWidth() { return flags & fontFixedWidth; }
+  GBool isSerif() { return flags & fontSerif; }
+  GBool isSymbolic() { return flags & fontSymbolic; }
+  GBool isItalic() { return flags & fontItalic; }
+  GBool isBold() { return flags & fontBold; }
+
+  // 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,
+                                      CharCodeToUnicode *ctu);
+  void findExtFontFile();
+
+  GString *tag;                        // PDF font tag
+  Ref id;                      // reference (used as unique ID)
+  GString *name;               // font name
+  GString *origName;           // original 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
+//------------------------------------------------------------------------
+
+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.
+  char **getEncoding() { return enc; }
+
+  // Return the Unicode map.
+  CharCodeToUnicode *getToUnicode();
+
+  // Return the character name associated with <code>.
+  char *getCharName(int code) { return code>=256?0:enc[code]; }
+
+  // Returns true if the PDF font specified an encoding.
+  GBool getHasEncoding() { return hasEncoding; }
+
+  // Returns true if the PDF font specified MacRomanEncoding.
+  GBool getUsesMacRomanEnc() { return usesMacRomanEnc; }
+
+  // Get width of a character.
+  double getWidth(Guchar c) { return widths[c]; }
+
+  // Return a char code-to-GID mapping for the provided font file.
+  // (This is only useful for TrueType fonts.)
+  Gushort *getCodeToGIDMap(FoFiTrueType *ff);
+
+  // 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 Type 3 Resources dictionary, or NULL if none.
+  Dict *getResources();
+
+private:
+
+  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;
+  GBool usesMacRomanEnc;
+  double widths[256];          // character widths
+  Object charProcs;            // Type 3 CharProcs dictionary
+  Object resources;            // Type 3 Resources dictionary
+};
+
+//------------------------------------------------------------------------
+// GfxCIDFont
+//------------------------------------------------------------------------
+
+class GfxCIDFont: public GfxFont {
+public:
+
+  GfxCIDFont(XRef *xref, char *tagA, Ref idA, GString *nameA,
+            Dict *fontDict);
+
+  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;
+};
+
+//------------------------------------------------------------------------
+// GfxFontDict
+//------------------------------------------------------------------------
+
+class GfxFontDict {
+public:
+
+  // Build the font dictionary, given the PDF font dictionary.
+  GfxFontDict(XRef *xref, Ref *fontDictRef, Dict *fontDict);
+
+  // Destructor.
+  ~GfxFontDict();
+
+  // Get the specified font.
+  GfxFont *lookup(char *tag);
+
+  // Iterative access.
+  int getNumFonts() { return numFonts; }
+  GfxFont *getFont(int i) { return fonts[i]; }
+
+private:
+
+  GfxFont **fonts;             // list of fonts
+  int numFonts;                        // number of fonts
+};
+
+#endif
diff --git a/lib/xpdf/GfxState.cc b/lib/xpdf/GfxState.cc
new file mode 100644 (file)
index 0000000..6e38d55
--- /dev/null
@@ -0,0 +1,3961 @@
+//========================================================================
+//
+// GfxState.cc
+//
+// Copyright 1996-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <stddef.h>
+#include <math.h>
+#include <string.h>
+#include "gmem.h"
+#include "Error.h"
+#include "Object.h"
+#include "Array.h"
+#include "Page.h"
+#include "GfxState.h"
+#include "cmyk.h"
+
+//------------------------------------------------------------------------
+
+static inline GfxColorComp clip01(GfxColorComp x) {
+  return (x < 0) ? 0 : (x > gfxColorComp1) ? gfxColorComp1 : x;
+}
+
+static inline double clip01(double x) {
+  return (x < 0) ? 0 : (x > 1) ? 1 : x;
+}
+
+//------------------------------------------------------------------------
+
+static struct {
+  char *name;
+  GfxBlendMode mode;
+} gfxBlendModeNames[] = {
+  { "Normal",     gfxBlendNormal },
+  { "Compatible", gfxBlendNormal },
+  { "Multiply",   gfxBlendMultiply },
+  { "Screen",     gfxBlendScreen },
+  { "Overlay",    gfxBlendOverlay },
+  { "Darken",     gfxBlendDarken },
+  { "Lighten",    gfxBlendLighten },
+  { "ColorDodge", gfxBlendColorDodge },
+  { "ColorBurn",  gfxBlendColorBurn },
+  { "HardLight",  gfxBlendHardLight },
+  { "SoftLight",  gfxBlendSoftLight },
+  { "Difference", gfxBlendDifference },
+  { "Exclusion",  gfxBlendExclusion },
+  { "Hue",        gfxBlendHue },
+  { "Saturation", gfxBlendSaturation },
+  { "Color",      gfxBlendColor },
+  { "Luminosity", gfxBlendLuminosity }
+};
+
+#define nGfxBlendModeNames \
+          ((int)((sizeof(gfxBlendModeNames) / sizeof(char *))))
+
+//------------------------------------------------------------------------
+
+// NB: This must match the GfxColorSpaceMode enum defined in
+// GfxState.h
+static char *gfxColorSpaceModeNames[] = {
+  "DeviceGray",
+  "CalGray",
+  "DeviceRGB",
+  "CalRGB",
+  "DeviceCMYK",
+  "Lab",
+  "ICCBased",
+  "Indexed",
+  "Separation",
+  "DeviceN",
+  "Pattern"
+};
+
+#define nGfxColorSpaceModes ((sizeof(gfxColorSpaceModeNames) / sizeof(char *)))
+
+//------------------------------------------------------------------------
+// GfxColorSpace
+//------------------------------------------------------------------------
+
+GfxColorSpace::GfxColorSpace() {
+}
+
+GfxColorSpace::~GfxColorSpace() {
+}
+
+GfxColorSpace *GfxColorSpace::parse(Object *csObj) {
+  GfxColorSpace *cs;
+  Object obj1;
+
+  cs = NULL;
+  if (csObj->isName()) {
+    if (csObj->isName("DeviceGray") || csObj->isName("G")) {
+      cs = new GfxDeviceGrayColorSpace();
+    } else if (csObj->isName("DeviceRGB") || csObj->isName("RGB")) {
+      cs = new GfxDeviceRGBColorSpace();
+    } else if (csObj->isName("DeviceCMYK") || csObj->isName("CMYK")) {
+      cs = new GfxDeviceCMYKColorSpace();
+    } else if (csObj->isName("Pattern")) {
+      cs = new GfxPatternColorSpace(NULL);
+    } else {
+      error(-1, "Bad color space '%s'", csObj->getName());
+    }
+  } else if (csObj->isArray()) {
+    csObj->arrayGet(0, &obj1);
+    if (obj1.isName("DeviceGray") || obj1.isName("G")) {
+      cs = new GfxDeviceGrayColorSpace();
+    } else if (obj1.isName("DeviceRGB") || obj1.isName("RGB")) {
+      cs = new GfxDeviceRGBColorSpace();
+    } else if (obj1.isName("DeviceCMYK") || obj1.isName("CMYK")) {
+      cs = new GfxDeviceCMYKColorSpace();
+    } else if (obj1.isName("CalGray")) {
+      cs = GfxCalGrayColorSpace::parse(csObj->getArray());
+    } else if (obj1.isName("CalRGB")) {
+      cs = GfxCalRGBColorSpace::parse(csObj->getArray());
+    } else if (obj1.isName("Lab")) {
+      cs = GfxLabColorSpace::parse(csObj->getArray());
+    } else if (obj1.isName("ICCBased")) {
+      cs = GfxICCBasedColorSpace::parse(csObj->getArray());
+    } else if (obj1.isName("Indexed") || obj1.isName("I")) {
+      cs = GfxIndexedColorSpace::parse(csObj->getArray());
+    } else if (obj1.isName("Separation")) {
+      cs = GfxSeparationColorSpace::parse(csObj->getArray());
+    } else if (obj1.isName("DeviceN")) {
+      cs = GfxDeviceNColorSpace::parse(csObj->getArray());
+    } else if (obj1.isName("Pattern")) {
+      cs = GfxPatternColorSpace::parse(csObj->getArray());
+    } else {
+      error(-1, "Bad color space");
+    }
+    obj1.free();
+  } else {
+    error(-1, "Bad color space - expected name or array");
+  }
+  return cs;
+}
+
+void GfxColorSpace::getDefaultRanges(double *decodeLow, double *decodeRange,
+                                    int maxImgPixel) {
+  int i;
+
+  for (i = 0; i < getNComps(); ++i) {
+    decodeLow[i] = 0;
+    decodeRange[i] = 1;
+  }
+}
+
+int GfxColorSpace::getNumColorSpaceModes() {
+  return nGfxColorSpaceModes;
+}
+
+char *GfxColorSpace::getColorSpaceModeName(int idx) {
+  return gfxColorSpaceModeNames[idx];
+}
+
+//------------------------------------------------------------------------
+// GfxDeviceGrayColorSpace
+//------------------------------------------------------------------------
+
+GfxDeviceGrayColorSpace::GfxDeviceGrayColorSpace() {
+}
+
+GfxDeviceGrayColorSpace::~GfxDeviceGrayColorSpace() {
+}
+
+GfxColorSpace *GfxDeviceGrayColorSpace::copy() {
+  return new GfxDeviceGrayColorSpace();
+}
+
+void GfxDeviceGrayColorSpace::getGray(GfxColor *color, GfxGray *gray) {
+  *gray = clip01(color->c[0]);
+}
+
+void GfxDeviceGrayColorSpace::getRGB(GfxColor *color, GfxRGB *rgb) {
+  rgb->r = rgb->g = rgb->b = clip01(color->c[0]);
+}
+
+void GfxDeviceGrayColorSpace::getCMYK(GfxColor *color, GfxCMYK *cmyk) {
+  cmyk->c = cmyk->m = cmyk->y = 0;
+  cmyk->k = clip01(gfxColorComp1 - color->c[0]);
+}
+
+//------------------------------------------------------------------------
+// GfxCalGrayColorSpace
+//------------------------------------------------------------------------
+
+GfxCalGrayColorSpace::GfxCalGrayColorSpace() {
+  whiteX = whiteY = whiteZ = 1;
+  blackX = blackY = blackZ = 0;
+  gamma = 1;
+}
+
+GfxCalGrayColorSpace::~GfxCalGrayColorSpace() {
+}
+
+GfxColorSpace *GfxCalGrayColorSpace::copy() {
+  GfxCalGrayColorSpace *cs;
+
+  cs = new GfxCalGrayColorSpace();
+  cs->whiteX = whiteX;
+  cs->whiteY = whiteY;
+  cs->whiteZ = whiteZ;
+  cs->blackX = blackX;
+  cs->blackY = blackY;
+  cs->blackZ = blackZ;
+  cs->gamma = gamma;
+  return cs;
+}
+
+GfxColorSpace *GfxCalGrayColorSpace::parse(Array *arr) {
+  GfxCalGrayColorSpace *cs;
+  Object obj1, obj2, obj3;
+
+  arr->get(1, &obj1);
+  if (!obj1.isDict()) {
+    error(-1, "Bad CalGray color space");
+    obj1.free();
+    return NULL;
+  }
+  cs = new GfxCalGrayColorSpace();
+  if (obj1.dictLookup("WhitePoint", &obj2)->isArray() &&
+      obj2.arrayGetLength() == 3) {
+    obj2.arrayGet(0, &obj3);
+    cs->whiteX = obj3.getNum();
+    obj3.free();
+    obj2.arrayGet(1, &obj3);
+    cs->whiteY = obj3.getNum();
+    obj3.free();
+    obj2.arrayGet(2, &obj3);
+    cs->whiteZ = obj3.getNum();
+    obj3.free();
+  }
+  obj2.free();
+  if (obj1.dictLookup("BlackPoint", &obj2)->isArray() &&
+      obj2.arrayGetLength() == 3) {
+    obj2.arrayGet(0, &obj3);
+    cs->blackX = obj3.getNum();
+    obj3.free();
+    obj2.arrayGet(1, &obj3);
+    cs->blackY = obj3.getNum();
+    obj3.free();
+    obj2.arrayGet(2, &obj3);
+    cs->blackZ = obj3.getNum();
+    obj3.free();
+  }
+  obj2.free();
+  if (obj1.dictLookup("Gamma", &obj2)->isNum()) {
+    cs->gamma = obj2.getNum();
+  }
+  obj2.free();
+  obj1.free();
+  return cs;
+}
+
+void GfxCalGrayColorSpace::getGray(GfxColor *color, GfxGray *gray) {
+  *gray = clip01(color->c[0]);
+}
+
+void GfxCalGrayColorSpace::getRGB(GfxColor *color, GfxRGB *rgb) {
+  rgb->r = rgb->g = rgb->b = clip01(color->c[0]);
+}
+
+void GfxCalGrayColorSpace::getCMYK(GfxColor *color, GfxCMYK *cmyk) {
+  cmyk->c = cmyk->m = cmyk->y = 0;
+  cmyk->k = clip01(gfxColorComp1 - color->c[0]);
+}
+
+//------------------------------------------------------------------------
+// GfxDeviceRGBColorSpace
+//------------------------------------------------------------------------
+
+GfxDeviceRGBColorSpace::GfxDeviceRGBColorSpace() {
+}
+
+GfxDeviceRGBColorSpace::~GfxDeviceRGBColorSpace() {
+}
+
+GfxColorSpace *GfxDeviceRGBColorSpace::copy() {
+  return new GfxDeviceRGBColorSpace();
+}
+
+void GfxDeviceRGBColorSpace::getGray(GfxColor *color, GfxGray *gray) {
+  *gray = clip01((GfxColorComp)(0.3  * color->c[0] +
+                               0.59 * color->c[1] +
+                               0.11 * color->c[2] + 0.5));
+}
+
+void GfxDeviceRGBColorSpace::getRGB(GfxColor *color, GfxRGB *rgb) {
+  rgb->r = clip01(color->c[0]);
+  rgb->g = clip01(color->c[1]);
+  rgb->b = clip01(color->c[2]);
+}
+
+void GfxDeviceRGBColorSpace::getCMYK(GfxColor *color, GfxCMYK *cmyk) {
+  GfxColorComp c, m, y, k;
+
+  c = clip01(gfxColorComp1 - color->c[0]);
+  m = clip01(gfxColorComp1 - color->c[1]);
+  y = clip01(gfxColorComp1 - color->c[2]);
+  k = c;
+  if (m < k) {
+    k = m;
+  }
+  if (y < k) {
+    k = y;
+  }
+  cmyk->c = c - k;
+  cmyk->m = m - k;
+  cmyk->y = y - k;
+  cmyk->k = k;
+}
+
+//------------------------------------------------------------------------
+// GfxCalRGBColorSpace
+//------------------------------------------------------------------------
+
+GfxCalRGBColorSpace::GfxCalRGBColorSpace() {
+  whiteX = whiteY = whiteZ = 1;
+  blackX = blackY = blackZ = 0;
+  gammaR = gammaG = gammaB = 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() {
+}
+
+GfxColorSpace *GfxCalRGBColorSpace::copy() {
+  GfxCalRGBColorSpace *cs;
+  int i;
+
+  cs = new GfxCalRGBColorSpace();
+  cs->whiteX = whiteX;
+  cs->whiteY = whiteY;
+  cs->whiteZ = whiteZ;
+  cs->blackX = blackX;
+  cs->blackY = blackY;
+  cs->blackZ = blackZ;
+  cs->gammaR = gammaR;
+  cs->gammaG = gammaG;
+  cs->gammaB = gammaB;
+  for (i = 0; i < 9; ++i) {
+    cs->mat[i] = mat[i];
+  }
+  return cs;
+}
+
+GfxColorSpace *GfxCalRGBColorSpace::parse(Array *arr) {
+  GfxCalRGBColorSpace *cs;
+  Object obj1, obj2, obj3;
+  int i;
+
+  arr->get(1, &obj1);
+  if (!obj1.isDict()) {
+    error(-1, "Bad CalRGB color space");
+    obj1.free();
+    return NULL;
+  }
+  cs = new GfxCalRGBColorSpace();
+  if (obj1.dictLookup("WhitePoint", &obj2)->isArray() &&
+      obj2.arrayGetLength() == 3) {
+    obj2.arrayGet(0, &obj3);
+    cs->whiteX = obj3.getNum();
+    obj3.free();
+    obj2.arrayGet(1, &obj3);
+    cs->whiteY = obj3.getNum();
+    obj3.free();
+    obj2.arrayGet(2, &obj3);
+    cs->whiteZ = obj3.getNum();
+    obj3.free();
+  }
+  obj2.free();
+  if (obj1.dictLookup("BlackPoint", &obj2)->isArray() &&
+      obj2.arrayGetLength() == 3) {
+    obj2.arrayGet(0, &obj3);
+    cs->blackX = obj3.getNum();
+    obj3.free();
+    obj2.arrayGet(1, &obj3);
+    cs->blackY = obj3.getNum();
+    obj3.free();
+    obj2.arrayGet(2, &obj3);
+    cs->blackZ = obj3.getNum();
+    obj3.free();
+  }
+  obj2.free();
+  if (obj1.dictLookup("Gamma", &obj2)->isArray() &&
+      obj2.arrayGetLength() == 3) {
+    obj2.arrayGet(0, &obj3);
+    cs->gammaR = obj3.getNum();
+    obj3.free();
+    obj2.arrayGet(1, &obj3);
+    cs->gammaG = obj3.getNum();
+    obj3.free();
+    obj2.arrayGet(2, &obj3);
+    cs->gammaB = obj3.getNum();
+    obj3.free();
+  }
+  obj2.free();
+  if (obj1.dictLookup("Matrix", &obj2)->isArray() &&
+      obj2.arrayGetLength() == 9) {
+    for (i = 0; i < 9; ++i) {
+      obj2.arrayGet(i, &obj3);
+      cs->mat[i] = obj3.getNum();
+      obj3.free();
+    }
+  }
+  obj2.free();
+  obj1.free();
+  return cs;
+}
+
+void GfxCalRGBColorSpace::getGray(GfxColor *color, GfxGray *gray) {
+  *gray = clip01((GfxColorComp)(0.299 * color->c[0] +
+                               0.587 * color->c[1] +
+                               0.114 * color->c[2] + 0.5));
+}
+
+void GfxCalRGBColorSpace::getRGB(GfxColor *color, GfxRGB *rgb) {
+  rgb->r = clip01(color->c[0]);
+  rgb->g = clip01(color->c[1]);
+  rgb->b = clip01(color->c[2]);
+}
+
+void GfxCalRGBColorSpace::getCMYK(GfxColor *color, GfxCMYK *cmyk) {
+  GfxColorComp c, m, y, k;
+
+  c = clip01(gfxColorComp1 - color->c[0]);
+  m = clip01(gfxColorComp1 - color->c[1]);
+  y = clip01(gfxColorComp1 - color->c[2]);
+  k = c;
+  if (m < k) {
+    k = m;
+  }
+  if (y < k) {
+    k = y;
+  }
+  cmyk->c = c - k;
+  cmyk->m = m - k;
+  cmyk->y = y - k;
+  cmyk->k = k;
+}
+
+//------------------------------------------------------------------------
+// GfxDeviceCMYKColorSpace
+//------------------------------------------------------------------------
+
+GfxDeviceCMYKColorSpace::GfxDeviceCMYKColorSpace() {
+}
+
+GfxDeviceCMYKColorSpace::~GfxDeviceCMYKColorSpace() {
+}
+
+GfxColorSpace *GfxDeviceCMYKColorSpace::copy() {
+  return new GfxDeviceCMYKColorSpace();
+}
+
+void GfxDeviceCMYKColorSpace::getGray(GfxColor *color, GfxGray *gray) {
+  *gray = clip01((GfxColorComp)(gfxColorComp1 - color->c[3]
+                               - 0.3  * color->c[0]
+                               - 0.59 * color->c[1]
+                               - 0.11 * color->c[2] + 0.5));
+}
+
+/*void GfxDeviceCMYKColorSpace::getRGB(GfxColor *color, GfxRGB *rgb) {
+    unsigned char r,g,b;
+    float c = color->c[0];
+    float m = color->c[1];
+    float y = color->c[2];
+    float k = color->c[3];
+    convert_cmyk2rgb(c,m,y,k, &r,&g,&b);
+    rgb->r = r/255.0;
+    rgb->g = g/255.0;
+    rgb->b = b/255.0;
+}*/
+
+void GfxDeviceCMYKColorSpace::getRGB(GfxColor *color, GfxRGB *rgb) {
+  double c, m, y, k, c1, m1, y1, k1, r, g, b, x;
+
+  c = colToDbl(color->c[0]);
+  m = colToDbl(color->c[1]);
+  y = colToDbl(color->c[2]);
+  k = colToDbl(color->c[3]);
+  c1 = 1 - c;
+  m1 = 1 - m;
+  y1 = 1 - y;
+  k1 = 1 - k;
+  // this is a matrix multiplication, unrolled for performance
+  //                        C M Y K
+  x = c1 * m1 * y1 * k1; // 0 0 0 0
+  r = g = b = x;
+  x = c1 * m1 * y1 * k;  // 0 0 0 1
+  r += 0.1373 * x;
+  g += 0.1216 * x;
+  b += 0.1255 * x;
+  x = c1 * m1 * y  * k1; // 0 0 1 0
+  r += x;
+  g += 0.9490 * x;
+  x = c1 * m1 * y  * k;  // 0 0 1 1
+  r += 0.1098 * x;
+  g += 0.1020 * x;
+  x = c1 * m  * y1 * k1; // 0 1 0 0
+  r += 0.9255 * x;
+  b += 0.5490 * x;
+  x = c1 * m  * y1 * k;  // 0 1 0 1
+  r += 0.1412 * x;
+  x = c1 * m  * y  * k1; // 0 1 1 0
+  r += 0.9294 * x;
+  g += 0.1098 * x;
+  b += 0.1412 * x;
+  x = c1 * m  * y  * k;  // 0 1 1 1
+  r += 0.1333 * x;
+  x = c  * m1 * y1 * k1; // 1 0 0 0
+  g += 0.6784 * x;
+  b += 0.9373 * x;
+  x = c  * m1 * y1 * k;  // 1 0 0 1
+  g += 0.0588 * x;
+  b += 0.1412 * x;
+  x = c  * m1 * y  * k1; // 1 0 1 0
+  g += 0.6510 * x;
+  b += 0.3137 * x;
+  x = c  * m1 * y  * k;  // 1 0 1 1
+  g += 0.0745 * x;
+  x = c  * m  * y1 * k1; // 1 1 0 0
+  r += 0.1804 * x;
+  g += 0.1922 * x;
+  b += 0.5725 * x;
+  x = c  * m  * y1 * k;  // 1 1 0 1
+  b += 0.0078 * x;
+  x = c  * m  * y  * k1; // 1 1 1 0
+  r += 0.2118 * x;
+  g += 0.2119 * x;
+  b += 0.2235 * x;
+  rgb->r = clip01(dblToCol(r));
+  rgb->g = clip01(dblToCol(g));
+  rgb->b = clip01(dblToCol(b));
+}
+
+void GfxDeviceCMYKColorSpace::getCMYK(GfxColor *color, GfxCMYK *cmyk) {
+  cmyk->c = clip01(color->c[0]);
+  cmyk->m = clip01(color->c[1]);
+  cmyk->y = clip01(color->c[2]);
+  cmyk->k = clip01(color->c[3]);
+}
+
+//------------------------------------------------------------------------
+// GfxLabColorSpace
+//------------------------------------------------------------------------
+
+// This is the inverse of MatrixLMN in Example 4.10 from the PostScript
+// Language Reference, Third Edition.
+static double xyzrgb[3][3] = {
+  {  3.240449, -1.537136, -0.498531 },
+  { -0.969265,  1.876011,  0.041556 },
+  {  0.055643, -0.204026,  1.057229 }
+};
+
+GfxLabColorSpace::GfxLabColorSpace() {
+  whiteX = whiteY = whiteZ = 1;
+  blackX = blackY = blackZ = 0;
+  aMin = bMin = -100;
+  aMax = bMax = 100;
+}
+
+GfxLabColorSpace::~GfxLabColorSpace() {
+}
+
+GfxColorSpace *GfxLabColorSpace::copy() {
+  GfxLabColorSpace *cs;
+
+  cs = new GfxLabColorSpace();
+  cs->whiteX = whiteX;
+  cs->whiteY = whiteY;
+  cs->whiteZ = whiteZ;
+  cs->blackX = blackX;
+  cs->blackY = blackY;
+  cs->blackZ = blackZ;
+  cs->aMin = aMin;
+  cs->aMax = aMax;
+  cs->bMin = bMin;
+  cs->bMax = bMax;
+  cs->kr = kr;
+  cs->kg = kg;
+  cs->kb = kb;
+  return cs;
+}
+
+GfxColorSpace *GfxLabColorSpace::parse(Array *arr) {
+  GfxLabColorSpace *cs;
+  Object obj1, obj2, obj3;
+
+  arr->get(1, &obj1);
+  if (!obj1.isDict()) {
+    error(-1, "Bad Lab color space");
+    obj1.free();
+    return NULL;
+  }
+  cs = new GfxLabColorSpace();
+  if (obj1.dictLookup("WhitePoint", &obj2)->isArray() &&
+      obj2.arrayGetLength() == 3) {
+    obj2.arrayGet(0, &obj3);
+    cs->whiteX = obj3.getNum();
+    obj3.free();
+    obj2.arrayGet(1, &obj3);
+    cs->whiteY = obj3.getNum();
+    obj3.free();
+    obj2.arrayGet(2, &obj3);
+    cs->whiteZ = obj3.getNum();
+    obj3.free();
+  }
+  obj2.free();
+  if (obj1.dictLookup("BlackPoint", &obj2)->isArray() &&
+      obj2.arrayGetLength() == 3) {
+    obj2.arrayGet(0, &obj3);
+    cs->blackX = obj3.getNum();
+    obj3.free();
+    obj2.arrayGet(1, &obj3);
+    cs->blackY = obj3.getNum();
+    obj3.free();
+    obj2.arrayGet(2, &obj3);
+    cs->blackZ = obj3.getNum();
+    obj3.free();
+  }
+  obj2.free();
+  if (obj1.dictLookup("Range", &obj2)->isArray() &&
+      obj2.arrayGetLength() == 4) {
+    obj2.arrayGet(0, &obj3);
+    cs->aMin = obj3.getNum();
+    obj3.free();
+    obj2.arrayGet(1, &obj3);
+    cs->aMax = obj3.getNum();
+    obj3.free();
+    obj2.arrayGet(2, &obj3);
+    cs->bMin = obj3.getNum();
+    obj3.free();
+    obj2.arrayGet(3, &obj3);
+    cs->bMax = obj3.getNum();
+    obj3.free();
+  }
+  obj2.free();
+  obj1.free();
+
+  cs->kr = 1 / (xyzrgb[0][0] * cs->whiteX +
+               xyzrgb[0][1] * cs->whiteY +
+               xyzrgb[0][2] * cs->whiteZ);
+  cs->kg = 1 / (xyzrgb[1][0] * cs->whiteX +
+               xyzrgb[1][1] * cs->whiteY +
+               xyzrgb[1][2] * cs->whiteZ);
+  cs->kb = 1 / (xyzrgb[2][0] * cs->whiteX +
+               xyzrgb[2][1] * cs->whiteY +
+               xyzrgb[2][2] * cs->whiteZ);
+
+  return cs;
+}
+
+void GfxLabColorSpace::getGray(GfxColor *color, GfxGray *gray) {
+  GfxRGB rgb;
+
+  getRGB(color, &rgb);
+  *gray = clip01((GfxColorComp)(0.299 * rgb.r +
+                               0.587 * rgb.g +
+                               0.114 * rgb.b + 0.5));
+}
+
+void GfxLabColorSpace::getRGB(GfxColor *color, GfxRGB *rgb) {
+  double X, Y, Z;
+  double t1, t2;
+  double r, g, b;
+
+  // convert L*a*b* to CIE 1931 XYZ color space
+  t1 = (colToDbl(color->c[0]) + 16) / 116;
+  t2 = t1 + colToDbl(color->c[1]) / 500;
+  if (t2 >= (6.0 / 29.0)) {
+    X = t2 * t2 * t2;
+  } else {
+    X = (108.0 / 841.0) * (t2 - (4.0 / 29.0));
+  }
+  X *= whiteX;
+  if (t1 >= (6.0 / 29.0)) {
+    Y = t1 * t1 * t1;
+  } else {
+    Y = (108.0 / 841.0) * (t1 - (4.0 / 29.0));
+  }
+  Y *= whiteY;
+  t2 = t1 - colToDbl(color->c[2]) / 200;
+  if (t2 >= (6.0 / 29.0)) {
+    Z = t2 * t2 * t2;
+  } else {
+    Z = (108.0 / 841.0) * (t2 - (4.0 / 29.0));
+  }
+  Z *= whiteZ;
+
+  // convert XYZ to RGB, including gamut mapping and gamma correction
+  r = xyzrgb[0][0] * X + xyzrgb[0][1] * Y + xyzrgb[0][2] * Z;
+  g = xyzrgb[1][0] * X + xyzrgb[1][1] * Y + xyzrgb[1][2] * Z;
+  b = xyzrgb[2][0] * X + xyzrgb[2][1] * Y + xyzrgb[2][2] * Z;
+  rgb->r = dblToCol(pow(clip01(r * kr), 0.5));
+  rgb->g = dblToCol(pow(clip01(g * kg), 0.5));
+  rgb->b = dblToCol(pow(clip01(b * kb), 0.5));
+}
+
+void GfxLabColorSpace::getCMYK(GfxColor *color, GfxCMYK *cmyk) {
+  GfxRGB rgb;
+  GfxColorComp c, m, y, k;
+
+  getRGB(color, &rgb);
+  c = clip01(gfxColorComp1 - rgb.r);
+  m = clip01(gfxColorComp1 - rgb.g);
+  y = clip01(gfxColorComp1 - rgb.b);
+  k = c;
+  if (m < k) {
+    k = m;
+  }
+  if (y < k) {
+    k = y;
+  }
+  cmyk->c = c - k;
+  cmyk->m = m - k;
+  cmyk->y = y - k;
+  cmyk->k = k;
+}
+
+void GfxLabColorSpace::getDefaultRanges(double *decodeLow, double *decodeRange,
+                                       int maxImgPixel) {
+  decodeLow[0] = 0;
+  decodeRange[0] = 100;
+  decodeLow[1] = aMin;
+  decodeRange[1] = aMax - aMin;
+  decodeLow[2] = bMin;
+  decodeRange[2] = bMax - bMin;
+}
+
+//------------------------------------------------------------------------
+// GfxICCBasedColorSpace
+//------------------------------------------------------------------------
+
+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;
+}
+
+GfxICCBasedColorSpace::~GfxICCBasedColorSpace() {
+  delete alt;
+}
+
+GfxColorSpace *GfxICCBasedColorSpace::copy() {
+  GfxICCBasedColorSpace *cs;
+  int i;
+
+  cs = new GfxICCBasedColorSpace(nComps, alt->copy(), &iccProfileStream);
+  for (i = 0; i < 4; ++i) {
+    cs->rangeMin[i] = rangeMin[i];
+    cs->rangeMax[i] = rangeMax[i];
+  }
+  return cs;
+}
+
+GfxColorSpace *GfxICCBasedColorSpace::parse(Array *arr) {
+  GfxICCBasedColorSpace *cs;
+  Ref iccProfileStreamA;
+  int nCompsA;
+  GfxColorSpace *altA;
+  Dict *dict;
+  Object obj1, obj2, obj3;
+  int i;
+
+  arr->getNF(1, &obj1);
+  if (obj1.isRef()) {
+    iccProfileStreamA = obj1.getRef();
+  } else {
+    iccProfileStreamA.num = 0;
+    iccProfileStreamA.gen = 0;
+  }
+  obj1.free();
+  arr->get(1, &obj1);
+  if (!obj1.isStream()) {
+    error(-1, "Bad ICCBased color space (stream)");
+    obj1.free();
+    return NULL;
+  }
+  dict = obj1.streamGetDict();
+  if (!dict->lookup("N", &obj2)->isInt()) {
+    error(-1, "Bad ICCBased color space (N)");
+    obj2.free();
+    obj1.free();
+    return NULL;
+  }
+  nCompsA = obj2.getInt();
+  obj2.free();
+  if (nCompsA > gfxColorMaxComps) {
+    error(-1, "ICCBased color space with too many (%d > %d) components",
+         nCompsA, gfxColorMaxComps);
+    nCompsA = gfxColorMaxComps;
+  }
+  if (dict->lookup("Alternate", &obj2)->isNull() ||
+      !(altA = GfxColorSpace::parse(&obj2))) {
+    switch (nCompsA) {
+    case 1:
+      altA = new GfxDeviceGrayColorSpace();
+      break;
+    case 3:
+      altA = new GfxDeviceRGBColorSpace();
+      break;
+    case 4:
+      altA = new GfxDeviceCMYKColorSpace();
+      break;
+    default:
+      error(-1, "Bad ICCBased color space - invalid N");
+      obj2.free();
+      obj1.free();
+      return NULL;
+    }
+  }
+  obj2.free();
+  cs = new GfxICCBasedColorSpace(nCompsA, altA, &iccProfileStreamA);
+  if (dict->lookup("Range", &obj2)->isArray() &&
+      obj2.arrayGetLength() == 2 * nCompsA) {
+    for (i = 0; i < nCompsA; ++i) {
+      obj2.arrayGet(2*i, &obj3);
+      cs->rangeMin[i] = obj3.getNum();
+      obj3.free();
+      obj2.arrayGet(2*i+1, &obj3);
+      cs->rangeMax[i] = obj3.getNum();
+      obj3.free();
+    }
+  }
+  obj2.free();
+  obj1.free();
+  return cs;
+}
+
+void GfxICCBasedColorSpace::getGray(GfxColor *color, GfxGray *gray) {
+  alt->getGray(color, gray);
+}
+
+void GfxICCBasedColorSpace::getRGB(GfxColor *color, GfxRGB *rgb) {
+  alt->getRGB(color, rgb);
+}
+
+void GfxICCBasedColorSpace::getCMYK(GfxColor *color, GfxCMYK *cmyk) {
+  alt->getCMYK(color, cmyk);
+}
+
+void GfxICCBasedColorSpace::getDefaultRanges(double *decodeLow,
+                                            double *decodeRange,
+                                            int maxImgPixel) {
+  alt->getDefaultRanges(decodeLow, decodeRange, maxImgPixel);
+
+#if 0
+  // this is nominally correct, but some PDF files don't set the
+  // correct ranges in the ICCBased dict
+  int i;
+
+  for (i = 0; i < nComps; ++i) {
+    decodeLow[i] = rangeMin[i];
+    decodeRange[i] = rangeMax[i] - rangeMin[i];
+  }
+#endif
+}
+
+//------------------------------------------------------------------------
+// GfxIndexedColorSpace
+//------------------------------------------------------------------------
+
+GfxIndexedColorSpace::GfxIndexedColorSpace(GfxColorSpace *baseA,
+                                          int indexHighA) {
+  base = baseA;
+  indexHigh = indexHighA;
+  lookup = (Guchar *)gmallocn((indexHigh + 1) * base->getNComps(),
+                             sizeof(Guchar));
+}
+
+GfxIndexedColorSpace::~GfxIndexedColorSpace() {
+  delete base;
+  gfree(lookup);
+}
+
+GfxColorSpace *GfxIndexedColorSpace::copy() {
+  GfxIndexedColorSpace *cs;
+
+  cs = new GfxIndexedColorSpace(base->copy(), indexHigh);
+  memcpy(cs->lookup, lookup,
+        (indexHigh + 1) * base->getNComps() * sizeof(Guchar));
+  return cs;
+}
+
+GfxColorSpace *GfxIndexedColorSpace::parse(Array *arr) {
+  GfxIndexedColorSpace *cs;
+  GfxColorSpace *baseA;
+  int indexHighA;
+  Object obj1;
+  int x;
+  char *s;
+  int n, i, j;
+
+  if (arr->getLength() != 4) {
+    error(-1, "Bad Indexed color space");
+    goto err1;
+  }
+  arr->get(1, &obj1);
+  if (!(baseA = GfxColorSpace::parse(&obj1))) {
+    error(-1, "Bad Indexed color space (base color space)");
+    goto err2;
+  }
+  obj1.free();
+  if (!arr->get(2, &obj1)->isInt()) {
+    error(-1, "Bad Indexed color space (hival)");
+    delete baseA;
+    goto err2;
+  }
+  indexHighA = obj1.getInt();
+  if (indexHighA < 0 || indexHighA > 255) {
+    // the PDF spec requires indexHigh to be in [0,255] -- allowing
+    // values larger than 255 creates a security hole: if nComps *
+    // indexHigh is greater than 2^31, the loop below may overwrite
+    // past the end of the array
+    error(-1, "Bad Indexed color space (invalid indexHigh value)");
+    delete baseA;
+    goto err2;
+  }
+  obj1.free();
+  cs = new GfxIndexedColorSpace(baseA, indexHighA);
+  arr->get(3, &obj1);
+  n = baseA->getNComps();
+  if (obj1.isStream()) {
+    obj1.streamReset();
+    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)");
+         goto err3;
+       }
+       cs->lookup[i*n + j] = (Guchar)x;
+      }
+    }
+    obj1.streamClose();
+  } else if (obj1.isString()) {
+    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 <= indexHighA; ++i) {
+      for (j = 0; j < n; ++j) {
+       cs->lookup[i*n + j] = (Guchar)*s++;
+      }
+    }
+  } else {
+    error(-1, "Bad Indexed color space (lookup table)");
+    goto err3;
+  }
+  obj1.free();
+  return cs;
+
+ err3:
+  delete cs;
+ err2:
+  obj1.free();
+ err1:
+  return NULL;
+}
+
+GfxColor *GfxIndexedColorSpace::mapColorToBase(GfxColor *color,
+                                              GfxColor *baseColor) {
+  Guchar *p;
+  double low[gfxColorMaxComps], range[gfxColorMaxComps];
+  int n, i;
+
+  n = base->getNComps();
+  base->getDefaultRanges(low, range, indexHigh);
+  p = &lookup[(int)(colToDbl(color->c[0]) + 0.5) * n];
+  for (i = 0; i < n; ++i) {
+    baseColor->c[i] = dblToCol(low[i] + (p[i] / 255.0) * range[i]);
+  }
+  return baseColor;
+}
+
+void GfxIndexedColorSpace::getGray(GfxColor *color, GfxGray *gray) {
+  GfxColor color2;
+
+  base->getGray(mapColorToBase(color, &color2), gray);
+}
+
+void GfxIndexedColorSpace::getRGB(GfxColor *color, GfxRGB *rgb) {
+  GfxColor color2;
+
+  base->getRGB(mapColorToBase(color, &color2), rgb);
+}
+
+void GfxIndexedColorSpace::getCMYK(GfxColor *color, GfxCMYK *cmyk) {
+  GfxColor color2;
+
+  base->getCMYK(mapColorToBase(color, &color2), cmyk);
+}
+
+void GfxIndexedColorSpace::getDefaultRanges(double *decodeLow,
+                                           double *decodeRange,
+                                           int maxImgPixel) {
+  decodeLow[0] = 0;
+  decodeRange[0] = maxImgPixel;
+}
+
+//------------------------------------------------------------------------
+// GfxSeparationColorSpace
+//------------------------------------------------------------------------
+
+GfxSeparationColorSpace::GfxSeparationColorSpace(GString *nameA,
+                                                GfxColorSpace *altA,
+                                                Function *funcA) {
+  name = nameA;
+  alt = altA;
+  func = funcA;
+}
+
+GfxSeparationColorSpace::~GfxSeparationColorSpace() {
+  delete name;
+  delete alt;
+  delete func;
+}
+
+GfxColorSpace *GfxSeparationColorSpace::copy() {
+  return new GfxSeparationColorSpace(name->copy(), alt->copy(), func->copy());
+}
+
+//~ handle the 'All' and 'None' colorants
+GfxColorSpace *GfxSeparationColorSpace::parse(Array *arr) {
+  GfxSeparationColorSpace *cs;
+  GString *nameA;
+  GfxColorSpace *altA;
+  Function *funcA;
+  Object obj1;
+
+  if (arr->getLength() != 4) {
+    error(-1, "Bad Separation color space");
+    goto err1;
+  }
+  if (!arr->get(1, &obj1)->isName()) {
+    error(-1, "Bad Separation color space (name)");
+    goto err2;
+  }
+  nameA = new GString(obj1.getName());
+  obj1.free();
+  arr->get(2, &obj1);
+  if (!(altA = GfxColorSpace::parse(&obj1))) {
+    error(-1, "Bad Separation color space (alternate color space)");
+    goto err3;
+  }
+  obj1.free();
+  arr->get(3, &obj1);
+  if (!(funcA = Function::parse(&obj1))) {
+    goto err4;
+  }
+  obj1.free();
+  cs = new GfxSeparationColorSpace(nameA, altA, funcA);
+  return cs;
+
+ err4:
+  delete altA;
+ err3:
+  delete nameA;
+ err2:
+  obj1.free();
+ err1:
+  return NULL;
+}
+
+void GfxSeparationColorSpace::getGray(GfxColor *color, GfxGray *gray) {
+  double x;
+  double c[gfxColorMaxComps];
+  GfxColor color2;
+  int i;
+
+  x = colToDbl(color->c[0]);
+  func->transform(&x, c);
+  for (i = 0; i < alt->getNComps(); ++i) {
+    color2.c[i] = dblToCol(c[i]);
+  }
+  alt->getGray(&color2, gray);
+}
+
+void GfxSeparationColorSpace::getRGB(GfxColor *color, GfxRGB *rgb) {
+  double x;
+  double c[gfxColorMaxComps];
+  GfxColor color2;
+  int i;
+
+  x = colToDbl(color->c[0]);
+  func->transform(&x, c);
+  for (i = 0; i < alt->getNComps(); ++i) {
+    color2.c[i] = dblToCol(c[i]);
+  }
+  alt->getRGB(&color2, rgb);
+}
+
+void GfxSeparationColorSpace::getCMYK(GfxColor *color, GfxCMYK *cmyk) {
+  double x;
+  double c[gfxColorMaxComps];
+  GfxColor color2;
+  int i;
+
+  x = colToDbl(color->c[0]);
+  func->transform(&x, c);
+  for (i = 0; i < alt->getNComps(); ++i) {
+    color2.c[i] = dblToCol(c[i]);
+  }
+  alt->getCMYK(&color2, cmyk);
+}
+
+//------------------------------------------------------------------------
+// GfxDeviceNColorSpace
+//------------------------------------------------------------------------
+
+GfxDeviceNColorSpace::GfxDeviceNColorSpace(int nCompsA,
+                                          GfxColorSpace *altA,
+                                          Function *funcA) {
+  nComps = nCompsA;
+  alt = altA;
+  func = funcA;
+}
+
+GfxDeviceNColorSpace::~GfxDeviceNColorSpace() {
+  int i;
+
+  for (i = 0; i < nComps; ++i) {
+    delete names[i];
+  }
+  delete alt;
+  delete func;
+}
+
+GfxColorSpace *GfxDeviceNColorSpace::copy() {
+  GfxDeviceNColorSpace *cs;
+  int i;
+
+  cs = new GfxDeviceNColorSpace(nComps, alt->copy(), func->copy());
+  for (i = 0; i < nComps; ++i) {
+    cs->names[i] = names[i]->copy();
+  }
+  return cs;
+}
+
+//~ handle the 'None' colorant
+GfxColorSpace *GfxDeviceNColorSpace::parse(Array *arr) {
+  GfxDeviceNColorSpace *cs;
+  int nCompsA;
+  GString *namesA[gfxColorMaxComps];
+  GfxColorSpace *altA;
+  Function *funcA;
+  Object obj1, obj2;
+  int i;
+
+  if (arr->getLength() != 4 && arr->getLength() != 5) {
+    error(-1, "Bad DeviceN color space");
+    goto err1;
+  }
+  if (!arr->get(1, &obj1)->isArray()) {
+    error(-1, "Bad DeviceN color space (names)");
+    goto err2;
+  }
+  nCompsA = obj1.arrayGetLength();
+  if (nCompsA > gfxColorMaxComps) {
+    error(-1, "DeviceN color space with too many (%d > %d) components",
+         nCompsA, gfxColorMaxComps);
+    nCompsA = gfxColorMaxComps;
+  }
+  for (i = 0; i < nCompsA; ++i) {
+    if (!obj1.arrayGet(i, &obj2)->isName()) {
+      error(-1, "Bad DeviceN color space (names)");
+      obj2.free();
+      goto err2;
+    }
+    namesA[i] = new GString(obj2.getName());
+    obj2.free();
+  }
+  obj1.free();
+  arr->get(2, &obj1);
+  if (!(altA = GfxColorSpace::parse(&obj1))) {
+    error(-1, "Bad DeviceN color space (alternate color space)");
+    goto err3;
+  }
+  obj1.free();
+  arr->get(3, &obj1);
+  if (!(funcA = Function::parse(&obj1))) {
+    goto err4;
+  }
+  obj1.free();
+  cs = new GfxDeviceNColorSpace(nCompsA, altA, funcA);
+  for (i = 0; i < nCompsA; ++i) {
+    cs->names[i] = namesA[i];
+  }
+  return cs;
+
+ err4:
+  delete altA;
+ err3:
+  for (i = 0; i < nCompsA; ++i) {
+    delete namesA[i];
+  }
+ err2:
+  obj1.free();
+ err1:
+  return NULL;
+}
+
+void GfxDeviceNColorSpace::getGray(GfxColor *color, GfxGray *gray) {
+  double x[gfxColorMaxComps], c[gfxColorMaxComps];
+  GfxColor color2;
+  int i;
+
+  for (i = 0; i < nComps; ++i) {
+    x[i] = colToDbl(color->c[i]);
+  }
+  func->transform(x, c);
+  for (i = 0; i < alt->getNComps(); ++i) {
+    color2.c[i] = dblToCol(c[i]);
+  }
+  alt->getGray(&color2, gray);
+}
+
+void GfxDeviceNColorSpace::getRGB(GfxColor *color, GfxRGB *rgb) {
+  double x[gfxColorMaxComps], c[gfxColorMaxComps];
+  GfxColor color2;
+  int i;
+
+  for (i = 0; i < nComps; ++i) {
+    x[i] = colToDbl(color->c[i]);
+  }
+  func->transform(x, c);
+  for (i = 0; i < alt->getNComps(); ++i) {
+    color2.c[i] = dblToCol(c[i]);
+  }
+  alt->getRGB(&color2, rgb);
+}
+
+void GfxDeviceNColorSpace::getCMYK(GfxColor *color, GfxCMYK *cmyk) {
+  double x[gfxColorMaxComps], c[gfxColorMaxComps];
+  GfxColor color2;
+  int i;
+
+  for (i = 0; i < nComps; ++i) {
+    x[i] = colToDbl(color->c[i]);
+  }
+  func->transform(x, c);
+  for (i = 0; i < alt->getNComps(); ++i) {
+    color2.c[i] = dblToCol(c[i]);
+  }
+  alt->getCMYK(&color2, cmyk);
+}
+
+//------------------------------------------------------------------------
+// GfxPatternColorSpace
+//------------------------------------------------------------------------
+
+GfxPatternColorSpace::GfxPatternColorSpace(GfxColorSpace *underA) {
+  under = underA;
+}
+
+GfxPatternColorSpace::~GfxPatternColorSpace() {
+  if (under) {
+    delete under;
+  }
+}
+
+GfxColorSpace *GfxPatternColorSpace::copy() {
+  return new GfxPatternColorSpace(under ? under->copy() :
+                                         (GfxColorSpace *)NULL);
+}
+
+GfxColorSpace *GfxPatternColorSpace::parse(Array *arr) {
+  GfxPatternColorSpace *cs;
+  GfxColorSpace *underA;
+  Object obj1;
+
+  if (arr->getLength() != 1 && arr->getLength() != 2) {
+    error(-1, "Bad Pattern color space");
+    return NULL;
+  }
+  underA = NULL;
+  if (arr->getLength() == 2) {
+    arr->get(1, &obj1);
+    if (!(underA = GfxColorSpace::parse(&obj1))) {
+      error(-1, "Bad Pattern color space (underlying color space)");
+      obj1.free();
+      return NULL;
+    }
+    obj1.free();
+  }
+  cs = new GfxPatternColorSpace(underA);
+  return cs;
+}
+
+void GfxPatternColorSpace::getGray(GfxColor *color, GfxGray *gray) {
+  *gray = 0;
+}
+
+void GfxPatternColorSpace::getRGB(GfxColor *color, GfxRGB *rgb) {
+  rgb->r = rgb->g = rgb->b = 0;
+}
+
+void GfxPatternColorSpace::getCMYK(GfxColor *color, GfxCMYK *cmyk) {
+  cmyk->c = cmyk->m = cmyk->y = 0;
+  cmyk->k = 1;
+}
+
+//------------------------------------------------------------------------
+// Pattern
+//------------------------------------------------------------------------
+
+GfxPattern::GfxPattern(int typeA) {
+  type = typeA;
+}
+
+GfxPattern::~GfxPattern() {
+}
+
+GfxPattern *GfxPattern::parse(Object *obj) {
+  GfxPattern *pattern;
+  Object obj1;
+
+  if (obj->isDict()) {
+    obj->dictLookup("PatternType", &obj1);
+  } else if (obj->isStream()) {
+    obj->streamGetDict()->lookup("PatternType", &obj1);
+  } else {
+    return NULL;
+  }
+  pattern = NULL;
+  if (obj1.isInt() && obj1.getInt() == 1) {
+    pattern = GfxTilingPattern::parse(obj);
+  } else if (obj1.isInt() && obj1.getInt() == 2) {
+    pattern = GfxShadingPattern::parse(obj);
+  }
+  obj1.free();
+  return pattern;
+}
+
+//------------------------------------------------------------------------
+// GfxTilingPattern
+//------------------------------------------------------------------------
+
+GfxTilingPattern *GfxTilingPattern::parse(Object *patObj) {
+  GfxTilingPattern *pat;
+  Dict *dict;
+  int paintTypeA, tilingTypeA;
+  double bboxA[4], matrixA[6];
+  double xStepA, yStepA;
+  Object resDictA;
+  Object obj1, obj2;
+  int i;
+
+  if (!patObj->isStream()) {
+    return NULL;
+  }
+  dict = patObj->streamGetDict();
+
+  if (dict->lookup("PaintType", &obj1)->isInt()) {
+    paintTypeA = obj1.getInt();
+  } else {
+    paintTypeA = 1;
+    error(-1, "Invalid or missing PaintType in pattern");
+  }
+  obj1.free();
+  if (dict->lookup("TilingType", &obj1)->isInt()) {
+    tilingTypeA = obj1.getInt();
+  } else {
+    tilingTypeA = 1;
+    error(-1, "Invalid or missing TilingType in pattern");
+  }
+  obj1.free();
+  bboxA[0] = bboxA[1] = 0;
+  bboxA[2] = bboxA[3] = 1;
+  if (dict->lookup("BBox", &obj1)->isArray() &&
+      obj1.arrayGetLength() == 4) {
+    for (i = 0; i < 4; ++i) {
+      if (obj1.arrayGet(i, &obj2)->isNum()) {
+       bboxA[i] = obj2.getNum();
+      }
+      obj2.free();
+    }
+  } else {
+    error(-1, "Invalid or missing BBox in pattern");
+  }
+  obj1.free();
+  if (dict->lookup("XStep", &obj1)->isNum()) {
+    xStepA = obj1.getNum();
+  } else {
+    xStepA = 1;
+    error(-1, "Invalid or missing XStep in pattern");
+  }
+  obj1.free();
+  if (dict->lookup("YStep", &obj1)->isNum()) {
+    yStepA = obj1.getNum();
+  } else {
+    yStepA = 1;
+    error(-1, "Invalid or missing YStep in pattern");
+  }
+  obj1.free();
+  if (!dict->lookup("Resources", &resDictA)->isDict()) {
+    resDictA.free();
+    resDictA.initNull();
+    error(-1, "Invalid or missing Resources in pattern");
+  }
+  matrixA[0] = 1; matrixA[1] = 0;
+  matrixA[2] = 0; matrixA[3] = 1;
+  matrixA[4] = 0; matrixA[5] = 0;
+  if (dict->lookup("Matrix", &obj1)->isArray() &&
+      obj1.arrayGetLength() == 6) {
+    for (i = 0; i < 6; ++i) {
+      if (obj1.arrayGet(i, &obj2)->isNum()) {
+       matrixA[i] = obj2.getNum();
+      }
+      obj2.free();
+    }
+  }
+  obj1.free();
+
+  pat = new GfxTilingPattern(paintTypeA, tilingTypeA, bboxA, xStepA, yStepA,
+                            &resDictA, matrixA, patObj);
+  resDictA.free();
+  return pat;
+}
+
+GfxTilingPattern::GfxTilingPattern(int paintTypeA, int tilingTypeA,
+                                  double *bboxA, double xStepA, double yStepA,
+                                  Object *resDictA, double *matrixA,
+                                  Object *contentStreamA):
+  GfxPattern(1)
+{
+  int i;
+
+  paintType = paintTypeA;
+  tilingType = tilingTypeA;
+  for (i = 0; i < 4; ++i) {
+    bbox[i] = bboxA[i];
+  }
+  xStep = xStepA;
+  yStep = yStepA;
+  resDictA->copy(&resDict);
+  for (i = 0; i < 6; ++i) {
+    matrix[i] = matrixA[i];
+  }
+  contentStreamA->copy(&contentStream);
+}
+
+GfxTilingPattern::~GfxTilingPattern() {
+  resDict.free();
+  contentStream.free();
+}
+
+GfxPattern *GfxTilingPattern::copy() {
+  return new GfxTilingPattern(paintType, tilingType, bbox, xStep, yStep,
+                             &resDict, matrix, &contentStream);
+}
+
+//------------------------------------------------------------------------
+// GfxShadingPattern
+//------------------------------------------------------------------------
+
+GfxShadingPattern *GfxShadingPattern::parse(Object *patObj) {
+  Dict *dict;
+  GfxShading *shadingA;
+  double matrixA[6];
+  Object obj1, obj2;
+  int i;
+
+  if (!patObj->isDict()) {
+    return NULL;
+  }
+  dict = patObj->getDict();
+
+  dict->lookup("Shading", &obj1);
+  shadingA = GfxShading::parse(&obj1);
+  obj1.free();
+  if (!shadingA) {
+    return NULL;
+  }
+
+  matrixA[0] = 1; matrixA[1] = 0;
+  matrixA[2] = 0; matrixA[3] = 1;
+  matrixA[4] = 0; matrixA[5] = 0;
+  if (dict->lookup("Matrix", &obj1)->isArray() &&
+      obj1.arrayGetLength() == 6) {
+    for (i = 0; i < 6; ++i) {
+      if (obj1.arrayGet(i, &obj2)->isNum()) {
+       matrixA[i] = obj2.getNum();
+      }
+      obj2.free();
+    }
+  }
+  obj1.free();
+
+  return new GfxShadingPattern(shadingA, matrixA);
+}
+
+GfxShadingPattern::GfxShadingPattern(GfxShading *shadingA, double *matrixA):
+  GfxPattern(2)
+{
+  int i;
+
+  shading = shadingA;
+  for (i = 0; i < 6; ++i) {
+    matrix[i] = matrixA[i];
+  }
+}
+
+GfxShadingPattern::~GfxShadingPattern() {
+  delete shading;
+}
+
+GfxPattern *GfxShadingPattern::copy() {
+  return new GfxShadingPattern(shading->copy(), matrix);
+}
+
+//------------------------------------------------------------------------
+// GfxShading
+//------------------------------------------------------------------------
+
+GfxShading::GfxShading(int typeA) {
+  type = typeA;
+  colorSpace = NULL;
+}
+
+GfxShading::GfxShading(GfxShading *shading) {
+  int i;
+
+  type = shading->type;
+  colorSpace = shading->colorSpace->copy();
+  for (i = 0; i < gfxColorMaxComps; ++i) {
+    background.c[i] = shading->background.c[i];
+  }
+  hasBackground = shading->hasBackground;
+  xMin = shading->xMin;
+  yMin = shading->yMin;
+  xMax = shading->xMax;
+  yMax = shading->yMax;
+  hasBBox = shading->hasBBox;
+}
+
+GfxShading::~GfxShading() {
+  if (colorSpace) {
+    delete colorSpace;
+  }
+}
+
+GfxShading *GfxShading::parse(Object *obj) {
+  GfxShading *shading;
+  Dict *dict;
+  int typeA;
+  Object obj1;
+
+  if (obj->isDict()) {
+    dict = obj->getDict();
+  } else if (obj->isStream()) {
+    dict = obj->streamGetDict();
+  } else {
+    return NULL;
+  }
+
+  if (!dict->lookup("ShadingType", &obj1)->isInt()) {
+    error(-1, "Invalid ShadingType in shading dictionary");
+    obj1.free();
+    return NULL;
+  }
+  typeA = obj1.getInt();
+  obj1.free();
+
+  switch (typeA) {
+  case 1:
+    shading = GfxFunctionShading::parse(dict);
+    break;
+  case 2:
+    shading = GfxAxialShading::parse(dict);
+    break;
+  case 3:
+    shading = GfxRadialShading::parse(dict);
+    break;
+  case 4:
+    if (obj->isStream()) {
+      shading = GfxGouraudTriangleShading::parse(4, dict, obj->getStream());
+    } else {
+      error(-1, "Invalid Type 4 shading object");
+      goto err1;
+    }
+    break;
+  case 5:
+    if (obj->isStream()) {
+      shading = GfxGouraudTriangleShading::parse(5, dict, obj->getStream());
+    } else {
+      error(-1, "Invalid Type 5 shading object");
+      goto err1;
+    }
+    break;
+  case 6:
+    if (obj->isStream()) {
+      shading = GfxPatchMeshShading::parse(6, dict, obj->getStream());
+    } else {
+      error(-1, "Invalid Type 6 shading object");
+      goto err1;
+    }
+    break;
+  case 7:
+    if (obj->isStream()) {
+      shading = GfxPatchMeshShading::parse(7, dict, obj->getStream());
+    } else {
+      error(-1, "Invalid Type 7 shading object");
+      goto err1;
+    }
+    break;
+  default:
+    error(-1, "Unimplemented shading type %d", typeA);
+    goto err1;
+  }
+
+  return shading;
+
+ err1:
+  return NULL;
+}
+
+GBool GfxShading::init(Dict *dict) {
+  Object obj1, obj2;
+  int i;
+
+  dict->lookup("ColorSpace", &obj1);
+  if (!(colorSpace = GfxColorSpace::parse(&obj1))) {
+    error(-1, "Bad color space in shading dictionary");
+    obj1.free();
+    return gFalse;
+  }
+  obj1.free();
+
+  for (i = 0; i < gfxColorMaxComps; ++i) {
+    background.c[i] = 0;
+  }
+  hasBackground = gFalse;
+  if (dict->lookup("Background", &obj1)->isArray()) {
+    if (obj1.arrayGetLength() == colorSpace->getNComps()) {
+      hasBackground = gTrue;
+      for (i = 0; i < colorSpace->getNComps(); ++i) {
+       background.c[i] = dblToCol(obj1.arrayGet(i, &obj2)->getNum());
+       obj2.free();
+      }
+    } else {
+      error(-1, "Bad Background in shading dictionary");
+    }
+  }
+  obj1.free();
+
+  xMin = yMin = xMax = yMax = 0;
+  hasBBox = gFalse;
+  if (dict->lookup("BBox", &obj1)->isArray()) {
+    if (obj1.arrayGetLength() == 4) {
+      hasBBox = gTrue;
+      xMin = obj1.arrayGet(0, &obj2)->getNum();
+      obj2.free();
+      yMin = obj1.arrayGet(1, &obj2)->getNum();
+      obj2.free();
+      xMax = obj1.arrayGet(2, &obj2)->getNum();
+      obj2.free();
+      yMax = obj1.arrayGet(3, &obj2)->getNum();
+      obj2.free();
+    } else {
+      error(-1, "Bad BBox in shading dictionary");
+    }
+  }
+  obj1.free();
+
+  return gTrue;
+}
+
+//------------------------------------------------------------------------
+// GfxFunctionShading
+//------------------------------------------------------------------------
+
+GfxFunctionShading::GfxFunctionShading(double x0A, double y0A,
+                                      double x1A, double y1A,
+                                      double *matrixA,
+                                      Function **funcsA, int nFuncsA):
+  GfxShading(1)
+{
+  int i;
+
+  x0 = x0A;
+  y0 = y0A;
+  x1 = x1A;
+  y1 = y1A;
+  for (i = 0; i < 6; ++i) {
+    matrix[i] = matrixA[i];
+  }
+  nFuncs = nFuncsA;
+  for (i = 0; i < nFuncs; ++i) {
+    funcs[i] = funcsA[i];
+  }
+}
+
+GfxFunctionShading::GfxFunctionShading(GfxFunctionShading *shading):
+  GfxShading(shading)
+{
+  int i;
+
+  x0 = shading->x0;
+  y0 = shading->y0;
+  x1 = shading->x1;
+  y1 = shading->y1;
+  for (i = 0; i < 6; ++i) {
+    matrix[i] = shading->matrix[i];
+  }
+  nFuncs = shading->nFuncs;
+  for (i = 0; i < nFuncs; ++i) {
+    funcs[i] = shading->funcs[i]->copy();
+  }
+}
+
+GfxFunctionShading::~GfxFunctionShading() {
+  int i;
+
+  for (i = 0; i < nFuncs; ++i) {
+    delete funcs[i];
+  }
+}
+
+GfxFunctionShading *GfxFunctionShading::parse(Dict *dict) {
+  GfxFunctionShading *shading;
+  double x0A, y0A, x1A, y1A;
+  double matrixA[6];
+  Function *funcsA[gfxColorMaxComps];
+  int nFuncsA;
+  Object obj1, obj2;
+  int i;
+
+  x0A = y0A = 0;
+  x1A = y1A = 1;
+  if (dict->lookup("Domain", &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();
+  }
+  obj1.free();
+
+  matrixA[0] = 1; matrixA[1] = 0;
+  matrixA[2] = 0; matrixA[3] = 1;
+  matrixA[4] = 0; matrixA[5] = 0;
+  if (dict->lookup("Matrix", &obj1)->isArray() &&
+      obj1.arrayGetLength() == 6) {
+    matrixA[0] = obj1.arrayGet(0, &obj2)->getNum();
+    obj2.free();
+    matrixA[1] = obj1.arrayGet(1, &obj2)->getNum();
+    obj2.free();
+    matrixA[2] = obj1.arrayGet(2, &obj2)->getNum();
+    obj2.free();
+    matrixA[3] = obj1.arrayGet(3, &obj2)->getNum();
+    obj2.free();
+    matrixA[4] = obj1.arrayGet(4, &obj2)->getNum();
+    obj2.free();
+    matrixA[5] = obj1.arrayGet(5, &obj2)->getNum();
+    obj2.free();
+  }
+  obj1.free();
+
+  dict->lookup("Function", &obj1);
+  if (obj1.isArray()) {
+    nFuncsA = obj1.arrayGetLength();
+    if (nFuncsA > gfxColorMaxComps) {
+      error(-1, "Invalid Function array in shading dictionary");
+      goto err1;
+    }
+    for (i = 0; i < nFuncsA; ++i) {
+      obj1.arrayGet(i, &obj2);
+      if (!(funcsA[i] = Function::parse(&obj2))) {
+       goto err2;
+      }
+      obj2.free();
+    }
+  } else {
+    nFuncsA = 1;
+    if (!(funcsA[0] = Function::parse(&obj1))) {
+      goto err1;
+    }
+  }
+  obj1.free();
+
+  shading = new GfxFunctionShading(x0A, y0A, x1A, y1A, matrixA,
+                                  funcsA, nFuncsA);
+  if (!shading->init(dict)) {
+    delete shading;
+    return NULL;
+  }
+  return shading;
+
+ err2:
+  obj2.free();
+ err1:
+  obj1.free();
+  return NULL;
+}
+
+GfxShading *GfxFunctionShading::copy() {
+  return new GfxFunctionShading(this);
+}
+
+void GfxFunctionShading::getColor(double x, double y, GfxColor *color) {
+  double in[2], out[gfxColorMaxComps];
+  int i;
+
+  // NB: there can be one function with n outputs or n functions with
+  // one output each (where n = number of color components)
+  for (i = 0; i < gfxColorMaxComps; ++i) {
+    out[i] = 0;
+  }
+  in[0] = x;
+  in[1] = y;
+  for (i = 0; i < nFuncs; ++i) {
+    funcs[i]->transform(in, &out[i]);
+  }
+  for (i = 0; i < gfxColorMaxComps; ++i) {
+    color->c[i] = dblToCol(out[i]);
+  }
+}
+
+//------------------------------------------------------------------------
+// GfxAxialShading
+//------------------------------------------------------------------------
+
+GfxAxialShading::GfxAxialShading(double x0A, double y0A,
+                                double x1A, double y1A,
+                                double t0A, double t1A,
+                                Function **funcsA, int nFuncsA,
+                                GBool extend0A, GBool extend1A):
+  GfxShading(2)
+{
+  int i;
+
+  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;
+}
+
+GfxAxialShading::GfxAxialShading(GfxAxialShading *shading):
+  GfxShading(shading)
+{
+  int i;
+
+  x0 = shading->x0;
+  y0 = shading->y0;
+  x1 = shading->x1;
+  y1 = shading->y1;
+  t0 = shading->t0;
+  y1 = shading->t1;
+  nFuncs = shading->nFuncs;
+  for (i = 0; i < nFuncs; ++i) {
+    funcs[i] = shading->funcs[i]->copy();
+  }
+  extend0 = shading->extend0;
+  extend1 = shading->extend1;
+}
+
+GfxAxialShading::~GfxAxialShading() {
+  int i;
+
+  for (i = 0; i < nFuncs; ++i) {
+    delete funcs[i];
+  }
+}
+
+GfxAxialShading *GfxAxialShading::parse(Dict *dict) {
+  GfxAxialShading *shading;
+  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();
+
+  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();
+
+  dict->lookup("Function", &obj1);
+  if (obj1.isArray()) {
+    nFuncsA = obj1.arrayGetLength();
+    if (nFuncsA > gfxColorMaxComps) {
+      error(-1, "Invalid Function array in shading dictionary");
+      goto err1;
+    }
+    for (i = 0; i < nFuncsA; ++i) {
+      obj1.arrayGet(i, &obj2);
+      if (!(funcsA[i] = Function::parse(&obj2))) {
+       obj1.free();
+       obj2.free();
+       goto err1;
+      }
+      obj2.free();
+    }
+  } else {
+    nFuncsA = 1;
+    if (!(funcsA[0] = Function::parse(&obj1))) {
+      obj1.free();
+      goto err1;
+    }
+  }
+  obj1.free();
+
+  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();
+
+  shading = new GfxAxialShading(x0A, y0A, x1A, y1A, t0A, t1A,
+                               funcsA, nFuncsA, extend0A, extend1A);
+  if (!shading->init(dict)) {
+    delete shading;
+    return NULL;
+  }
+  return shading;
+
+ err1:
+  return NULL;
+}
+
+GfxShading *GfxAxialShading::copy() {
+  return new GfxAxialShading(this);
+}
+
+void GfxAxialShading::getColor(double t, GfxColor *color) {
+  double out[gfxColorMaxComps];
+  int i;
+
+  // NB: there can be one function with n outputs or n functions with
+  // one output each (where n = number of color components)
+  for (i = 0; i < gfxColorMaxComps; ++i) {
+    out[i] = 0;
+  }
+  for (i = 0; i < nFuncs; ++i) {
+    funcs[i]->transform(&t, &out[i]);
+  }
+  for (i = 0; i < gfxColorMaxComps; ++i) {
+    color->c[i] = dblToCol(out[i]);
+  }
+}
+
+//------------------------------------------------------------------------
+// GfxRadialShading
+//------------------------------------------------------------------------
+
+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):
+  GfxShading(3)
+{
+  int 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];
+  }
+  extend0 = extend0A;
+  extend1 = extend1A;
+}
+
+GfxRadialShading::GfxRadialShading(GfxRadialShading *shading):
+  GfxShading(shading)
+{
+  int i;
+
+  x0 = shading->x0;
+  y0 = shading->y0;
+  r0 = shading->r0;
+  x1 = shading->x1;
+  y1 = shading->y1;
+  r1 = shading->r1;
+  t0 = shading->t0;
+  y1 = shading->t1;
+  nFuncs = shading->nFuncs;
+  for (i = 0; i < nFuncs; ++i) {
+    funcs[i] = shading->funcs[i]->copy();
+  }
+  extend0 = shading->extend0;
+  extend1 = shading->extend1;
+}
+
+GfxRadialShading::~GfxRadialShading() {
+  int i;
+
+  for (i = 0; i < nFuncs; ++i) {
+    delete funcs[i];
+  }
+}
+
+GfxRadialShading *GfxRadialShading::parse(Dict *dict) {
+  GfxRadialShading *shading;
+  double x0A, y0A, r0A, x1A, y1A, r1A;
+  double t0A, t1A;
+  Function *funcsA[gfxColorMaxComps];
+  int nFuncsA;
+  GBool extend0A, extend1A;
+  Object obj1, obj2;
+  int i;
+
+  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();
+
+  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();
+
+  dict->lookup("Function", &obj1);
+  if (obj1.isArray()) {
+    nFuncsA = obj1.arrayGetLength();
+    if (nFuncsA > gfxColorMaxComps) {
+      error(-1, "Invalid Function array in shading dictionary");
+      goto err1;
+    }
+    for (i = 0; i < nFuncsA; ++i) {
+      obj1.arrayGet(i, &obj2);
+      if (!(funcsA[i] = Function::parse(&obj2))) {
+       obj1.free();
+       obj2.free();
+       goto err1;
+      }
+      obj2.free();
+    }
+  } else {
+    nFuncsA = 1;
+    if (!(funcsA[0] = Function::parse(&obj1))) {
+      obj1.free();
+      goto err1;
+    }
+  }
+  obj1.free();
+
+  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();
+
+  shading = new GfxRadialShading(x0A, y0A, r0A, x1A, y1A, r1A, t0A, t1A,
+                                funcsA, nFuncsA, extend0A, extend1A);
+  if (!shading->init(dict)) {
+    delete shading;
+    return NULL;
+  }
+  return shading;
+
+ err1:
+  return NULL;
+}
+
+GfxShading *GfxRadialShading::copy() {
+  return new GfxRadialShading(this);
+}
+
+void GfxRadialShading::getColor(double t, GfxColor *color) {
+  double out[gfxColorMaxComps];
+  int i;
+
+  // NB: there can be one function with n outputs or n functions with
+  // one output each (where n = number of color components)
+  for (i = 0; i < gfxColorMaxComps; ++i) {
+    out[i] = 0;
+  }
+  for (i = 0; i < nFuncs; ++i) {
+    funcs[i]->transform(&t, &out[i]);
+  }
+  for (i = 0; i < gfxColorMaxComps; ++i) {
+    color->c[i] = dblToCol(out[i]);
+  }
+}
+
+//------------------------------------------------------------------------
+// GfxShadingBitBuf
+//------------------------------------------------------------------------
+
+class GfxShadingBitBuf {
+public:
+
+  GfxShadingBitBuf(Stream *strA);
+  ~GfxShadingBitBuf();
+  GBool getBits(int n, Guint *val);
+  void flushBits();
+
+private:
+
+  Stream *str;
+  int bitBuf;
+  int nBits;
+};
+
+GfxShadingBitBuf::GfxShadingBitBuf(Stream *strA) {
+  str = strA;
+  str->reset();
+  bitBuf = 0;
+  nBits = 0;
+}
+
+GfxShadingBitBuf::~GfxShadingBitBuf() {
+  str->close();
+}
+
+GBool GfxShadingBitBuf::getBits(int n, Guint *val) {
+  int x;
+
+  if (nBits >= n) {
+    x = (bitBuf >> (nBits - n)) & ((1 << n) - 1);
+    nBits -= n;
+  } else {
+    x = 0;
+    if (nBits > 0) {
+      x = bitBuf & ((1 << nBits) - 1);
+      n -= nBits;
+      nBits = 0;
+    }
+    while (n > 0) {
+      if ((bitBuf = str->getChar()) == EOF) {
+       nBits = 0;
+       return gFalse;
+      }
+      if (n >= 8) {
+       x = (x << 8) | bitBuf;
+       n -= 8;
+      } else {
+       x = (x << n) | (bitBuf >> (8 - n));
+       nBits = 8 - n;
+       n = 0;
+      }
+    }
+  }
+  *val = x;
+  return gTrue;
+}
+
+void GfxShadingBitBuf::flushBits() {
+  bitBuf = 0;
+  nBits = 0;
+}
+
+//------------------------------------------------------------------------
+// GfxGouraudTriangleShading
+//------------------------------------------------------------------------
+
+GfxGouraudTriangleShading::GfxGouraudTriangleShading(
+                              int typeA,
+                              GfxGouraudVertex *verticesA, int nVerticesA,
+                              int (*trianglesA)[3], int nTrianglesA,
+                              Function **funcsA, int nFuncsA):
+  GfxShading(typeA)
+{
+  int i;
+
+  vertices = verticesA;
+  nVertices = nVerticesA;
+  triangles = trianglesA;
+  nTriangles = nTrianglesA;
+  nFuncs = nFuncsA;
+  for (i = 0; i < nFuncs; ++i) {
+    funcs[i] = funcsA[i];
+  }
+}
+
+GfxGouraudTriangleShading::GfxGouraudTriangleShading(
+                              GfxGouraudTriangleShading *shading):
+  GfxShading(shading)
+{
+  int i;
+
+  nVertices = shading->nVertices;
+  vertices = (GfxGouraudVertex *)gmallocn(nVertices, sizeof(GfxGouraudVertex));
+  memcpy(vertices, shading->vertices, nVertices * sizeof(GfxGouraudVertex));
+  nTriangles = shading->nTriangles;
+  triangles = (int (*)[3])gmallocn(nTriangles * 3, sizeof(int));
+  memcpy(triangles, shading->triangles, nTriangles * 3 * sizeof(int));
+  nFuncs = shading->nFuncs;
+  for (i = 0; i < nFuncs; ++i) {
+    funcs[i] = shading->funcs[i]->copy();
+  }
+}
+
+GfxGouraudTriangleShading::~GfxGouraudTriangleShading() {
+  int i;
+
+  gfree(vertices);
+  gfree(triangles);
+  for (i = 0; i < nFuncs; ++i) {
+    delete funcs[i];
+  }
+}
+
+GfxGouraudTriangleShading *GfxGouraudTriangleShading::parse(int typeA,
+                                                           Dict *dict,
+                                                           Stream *str) {
+  GfxGouraudTriangleShading *shading;
+  Function *funcsA[gfxColorMaxComps];
+  int nFuncsA;
+  int coordBits, compBits, flagBits, vertsPerRow, nRows;
+  double xMin, xMax, yMin, yMax;
+  double cMin[gfxColorMaxComps], cMax[gfxColorMaxComps];
+  double xMul, yMul;
+  double cMul[gfxColorMaxComps];
+  GfxGouraudVertex *verticesA;
+  int (*trianglesA)[3];
+  int nComps, nVerticesA, nTrianglesA, vertSize, triSize;
+  Guint x, y, flag;
+  Guint c[gfxColorMaxComps];
+  GfxShadingBitBuf *bitBuf;
+  Object obj1, obj2;
+  int i, j, k, state;
+
+  if (dict->lookup("BitsPerCoordinate", &obj1)->isInt()) {
+    coordBits = obj1.getInt();
+  } else {
+    error(-1, "Missing or invalid BitsPerCoordinate in shading dictionary");
+    goto err2;
+  }
+  obj1.free();
+  if (dict->lookup("BitsPerComponent", &obj1)->isInt()) {
+    compBits = obj1.getInt();
+  } else {
+    error(-1, "Missing or invalid BitsPerComponent in shading dictionary");
+    goto err2;
+  }
+  obj1.free();
+  flagBits = vertsPerRow = 0; // make gcc happy
+  if (typeA == 4) {
+    if (dict->lookup("BitsPerFlag", &obj1)->isInt()) {
+      flagBits = obj1.getInt();
+    } else {
+      error(-1, "Missing or invalid BitsPerFlag in shading dictionary");
+      goto err2;
+    }
+    obj1.free();
+  } else {
+    if (dict->lookup("VerticesPerRow", &obj1)->isInt()) {
+      vertsPerRow = obj1.getInt();
+    } else {
+      error(-1, "Missing or invalid VerticesPerRow in shading dictionary");
+      goto err2;
+    }
+    obj1.free();
+  }
+  if (dict->lookup("Decode", &obj1)->isArray() &&
+      obj1.arrayGetLength() >= 6) {
+    xMin = obj1.arrayGet(0, &obj2)->getNum();
+    obj2.free();
+    xMax = obj1.arrayGet(1, &obj2)->getNum();
+    obj2.free();
+    xMul = (xMax - xMin) / (pow(2.0, coordBits) - 1);
+    yMin = obj1.arrayGet(2, &obj2)->getNum();
+    obj2.free();
+    yMax = obj1.arrayGet(3, &obj2)->getNum();
+    obj2.free();
+    yMul = (yMax - yMin) / (pow(2.0, coordBits) - 1);
+    for (i = 0; 5 + 2*i < obj1.arrayGetLength() && i < gfxColorMaxComps; ++i) {
+      cMin[i] = obj1.arrayGet(4 + 2*i, &obj2)->getNum();
+      obj2.free();
+      cMax[i] = obj1.arrayGet(5 + 2*i, &obj2)->getNum();
+      obj2.free();
+      cMul[i] = (cMax[i] - cMin[i]) / (double)((1 << compBits) - 1);
+    }
+    nComps = i;
+  } else {
+    error(-1, "Missing or invalid Decode array in shading dictionary");
+    goto err2;
+  }
+  obj1.free();
+
+  if (!dict->lookup("Function", &obj1)->isNull()) {
+    if (obj1.isArray()) {
+      nFuncsA = obj1.arrayGetLength();
+      if (nFuncsA > gfxColorMaxComps) {
+       error(-1, "Invalid Function array in shading dictionary");
+       goto err1;
+      }
+      for (i = 0; i < nFuncsA; ++i) {
+       obj1.arrayGet(i, &obj2);
+       if (!(funcsA[i] = Function::parse(&obj2))) {
+         obj1.free();
+         obj2.free();
+         goto err1;
+       }
+       obj2.free();
+      }
+    } else {
+      nFuncsA = 1;
+      if (!(funcsA[0] = Function::parse(&obj1))) {
+       obj1.free();
+       goto err1;
+      }
+    }
+  } else {
+    nFuncsA = 0;
+  }
+  obj1.free();
+
+  nVerticesA = nTrianglesA = 0;
+  verticesA = NULL;
+  trianglesA = NULL;
+  vertSize = triSize = 0;
+  state = 0;
+  flag = 0; // make gcc happy
+  bitBuf = new GfxShadingBitBuf(str);
+  while (1) {
+    if (typeA == 4) {
+      if (!bitBuf->getBits(flagBits, &flag)) {
+       break;
+      }
+    }
+    if (!bitBuf->getBits(coordBits, &x) ||
+       !bitBuf->getBits(coordBits, &y)) {
+      break;
+    }
+    for (i = 0; i < nComps; ++i) {
+      if (!bitBuf->getBits(compBits, &c[i])) {
+       break;
+      }
+    }
+    if (i < nComps) {
+      break;
+    }
+    if (nVerticesA == vertSize) {
+      vertSize = (vertSize == 0) ? 16 : 2 * vertSize;
+      verticesA = (GfxGouraudVertex *)
+                     greallocn(verticesA, vertSize, sizeof(GfxGouraudVertex));
+    }
+    verticesA[nVerticesA].x = xMin + xMul * (double)x;
+    verticesA[nVerticesA].y = yMin + yMul * (double)y;
+    for (i = 0; i < nComps; ++i) {
+      verticesA[nVerticesA].color.c[i] =
+         dblToCol(cMin[i] + cMul[i] * (double)c[i]);
+    }
+    ++nVerticesA;
+    bitBuf->flushBits();
+    if (typeA == 4) {
+      if (state == 0 || state == 1) {
+       ++state;
+      } else if (state == 2 || flag > 0) {
+       if (nTrianglesA == triSize) {
+         triSize = (triSize == 0) ? 16 : 2 * triSize;
+         trianglesA = (int (*)[3])
+                          greallocn(trianglesA, triSize * 3, sizeof(int));
+       }
+       if (state == 2) {
+         trianglesA[nTrianglesA][0] = nVerticesA - 3;
+         trianglesA[nTrianglesA][1] = nVerticesA - 2;
+         trianglesA[nTrianglesA][2] = nVerticesA - 1;
+         ++state;
+       } else if (flag == 1) {
+         trianglesA[nTrianglesA][0] = trianglesA[nTrianglesA - 1][1];
+         trianglesA[nTrianglesA][1] = trianglesA[nTrianglesA - 1][2];
+         trianglesA[nTrianglesA][2] = nVerticesA - 1;
+       } else { // flag == 2
+         trianglesA[nTrianglesA][0] = trianglesA[nTrianglesA - 1][0];
+         trianglesA[nTrianglesA][1] = trianglesA[nTrianglesA - 1][2];
+         trianglesA[nTrianglesA][2] = nVerticesA - 1;
+       }
+       ++nTrianglesA;
+      } else { // state == 3 && flag == 0
+       state = 1;
+      }
+    }
+  }
+  delete bitBuf;
+  if (typeA == 5) {
+    nRows = nVerticesA / vertsPerRow;
+    nTrianglesA = (nRows - 1) * 2 * (vertsPerRow - 1);
+    trianglesA = (int (*)[3])gmallocn(nTrianglesA * 3, sizeof(int));
+    k = 0;
+    for (i = 0; i < nRows - 1; ++i) {
+      for (j = 0; j < vertsPerRow - 1; ++j) {
+       trianglesA[k][0] = i * vertsPerRow + j;
+       trianglesA[k][1] = i * vertsPerRow + j+1;
+       trianglesA[k][2] = (i+1) * vertsPerRow + j;
+       ++k;
+       trianglesA[k][0] = i * vertsPerRow + j+1;
+       trianglesA[k][1] = (i+1) * vertsPerRow + j;
+       trianglesA[k][2] = (i+1) * vertsPerRow + j+1;
+       ++k;
+      }
+    }
+  }
+
+  shading = new GfxGouraudTriangleShading(typeA, verticesA, nVerticesA,
+                                         trianglesA, nTrianglesA,
+                                         funcsA, nFuncsA);
+  if (!shading->init(dict)) {
+    delete shading;
+    return NULL;
+  }
+  return shading;
+
+ err2:
+  obj1.free();
+ err1:
+  return NULL;
+}
+
+GfxShading *GfxGouraudTriangleShading::copy() {
+  return new GfxGouraudTriangleShading(this);
+}
+
+void GfxGouraudTriangleShading::getTriangle(
+                                   int i,
+                                   double *x0, double *y0, GfxColor *color0,
+                                   double *x1, double *y1, GfxColor *color1,
+                                   double *x2, double *y2, GfxColor *color2) {
+  double in;
+  double out[gfxColorMaxComps];
+  int v, j;
+
+  v = triangles[i][0];
+  *x0 = vertices[v].x;
+  *y0 = vertices[v].y;
+  if (nFuncs > 0) {
+    in = colToDbl(vertices[v].color.c[0]);
+    for (j = 0; j < nFuncs; ++j) {
+      funcs[j]->transform(&in, &out[j]);
+    }
+    for (j = 0; j < gfxColorMaxComps; ++j) {
+      color0->c[j] = dblToCol(out[j]);
+    }
+  } else {
+    *color0 = vertices[v].color;
+  }
+  v = triangles[i][1];
+  *x1 = vertices[v].x;
+  *y1 = vertices[v].y;
+  if (nFuncs > 0) {
+    in = colToDbl(vertices[v].color.c[0]);
+    for (j = 0; j < nFuncs; ++j) {
+      funcs[j]->transform(&in, &out[j]);
+    }
+    for (j = 0; j < gfxColorMaxComps; ++j) {
+      color1->c[j] = dblToCol(out[j]);
+    }
+  } else {
+    *color1 = vertices[v].color;
+  }
+  v = triangles[i][2];
+  *x2 = vertices[v].x;
+  *y2 = vertices[v].y;
+  if (nFuncs > 0) {
+    in = colToDbl(vertices[v].color.c[0]);
+    for (j = 0; j < nFuncs; ++j) {
+      funcs[j]->transform(&in, &out[j]);
+    }
+    for (j = 0; j < gfxColorMaxComps; ++j) {
+      color2->c[j] = dblToCol(out[j]);
+    }
+  } else {
+    *color2 = vertices[v].color;
+  }
+}
+
+//------------------------------------------------------------------------
+// GfxPatchMeshShading
+//------------------------------------------------------------------------
+
+GfxPatchMeshShading::GfxPatchMeshShading(int typeA,
+                                        GfxPatch *patchesA, int nPatchesA,
+                                        Function **funcsA, int nFuncsA):
+  GfxShading(typeA)
+{
+  int i;
+
+  patches = patchesA;
+  nPatches = nPatchesA;
+  nFuncs = nFuncsA;
+  for (i = 0; i < nFuncs; ++i) {
+    funcs[i] = funcsA[i];
+  }
+}
+
+GfxPatchMeshShading::GfxPatchMeshShading(GfxPatchMeshShading *shading):
+  GfxShading(shading)
+{
+  int i;
+
+  nPatches = shading->nPatches;
+  patches = (GfxPatch *)gmallocn(nPatches, sizeof(GfxPatch));
+  memcpy(patches, shading->patches, nPatches * sizeof(GfxPatch));
+  nFuncs = shading->nFuncs;
+  for (i = 0; i < nFuncs; ++i) {
+    funcs[i] = shading->funcs[i]->copy();
+  }
+}
+
+GfxPatchMeshShading::~GfxPatchMeshShading() {
+  int i;
+
+  gfree(patches);
+  for (i = 0; i < nFuncs; ++i) {
+    delete funcs[i];
+  }
+}
+
+GfxPatchMeshShading *GfxPatchMeshShading::parse(int typeA, Dict *dict,
+                                               Stream *str) {
+  GfxPatchMeshShading *shading;
+  Function *funcsA[gfxColorMaxComps];
+  int nFuncsA;
+  int coordBits, compBits, flagBits;
+  double xMin, xMax, yMin, yMax;
+  double cMin[gfxColorMaxComps], cMax[gfxColorMaxComps];
+  double xMul, yMul;
+  double cMul[gfxColorMaxComps];
+  GfxPatch *patchesA, *p;
+  int nComps, nPatchesA, patchesSize, nPts, nColors;
+  Guint flag;
+  double x[16], y[16];
+  Guint xi, yi;
+  GfxColorComp c[4][gfxColorMaxComps];
+  Guint ci[4];
+  GfxShadingBitBuf *bitBuf;
+  Object obj1, obj2;
+  int i, j;
+
+  if (dict->lookup("BitsPerCoordinate", &obj1)->isInt()) {
+    coordBits = obj1.getInt();
+  } else {
+    error(-1, "Missing or invalid BitsPerCoordinate in shading dictionary");
+    goto err2;
+  }
+  obj1.free();
+  if (dict->lookup("BitsPerComponent", &obj1)->isInt()) {
+    compBits = obj1.getInt();
+  } else {
+    error(-1, "Missing or invalid BitsPerComponent in shading dictionary");
+    goto err2;
+  }
+  obj1.free();
+  if (dict->lookup("BitsPerFlag", &obj1)->isInt()) {
+    flagBits = obj1.getInt();
+  } else {
+    error(-1, "Missing or invalid BitsPerFlag in shading dictionary");
+    goto err2;
+  }
+  obj1.free();
+  if (dict->lookup("Decode", &obj1)->isArray() &&
+      obj1.arrayGetLength() >= 6) {
+    xMin = obj1.arrayGet(0, &obj2)->getNum();
+    obj2.free();
+    xMax = obj1.arrayGet(1, &obj2)->getNum();
+    obj2.free();
+    xMul = (xMax - xMin) / (pow(2.0, coordBits) - 1);
+    yMin = obj1.arrayGet(2, &obj2)->getNum();
+    obj2.free();
+    yMax = obj1.arrayGet(3, &obj2)->getNum();
+    obj2.free();
+    yMul = (yMax - yMin) / (pow(2.0, coordBits) - 1);
+    for (i = 0; 5 + 2*i < obj1.arrayGetLength() && i < gfxColorMaxComps; ++i) {
+      cMin[i] = obj1.arrayGet(4 + 2*i, &obj2)->getNum();
+      obj2.free();
+      cMax[i] = obj1.arrayGet(5 + 2*i, &obj2)->getNum();
+      obj2.free();
+      cMul[i] = (cMax[i] - cMin[i]) / (double)((1 << compBits) - 1);
+    }
+    nComps = i;
+  } else {
+    error(-1, "Missing or invalid Decode array in shading dictionary");
+    goto err2;
+  }
+  obj1.free();
+
+  if (!dict->lookup("Function", &obj1)->isNull()) {
+    if (obj1.isArray()) {
+      nFuncsA = obj1.arrayGetLength();
+      if (nFuncsA > gfxColorMaxComps) {
+       error(-1, "Invalid Function array in shading dictionary");
+       goto err1;
+      }
+      for (i = 0; i < nFuncsA; ++i) {
+       obj1.arrayGet(i, &obj2);
+       if (!(funcsA[i] = Function::parse(&obj2))) {
+         obj1.free();
+         obj2.free();
+         goto err1;
+       }
+       obj2.free();
+      }
+    } else {
+      nFuncsA = 1;
+      if (!(funcsA[0] = Function::parse(&obj1))) {
+       obj1.free();
+       goto err1;
+      }
+    }
+  } else {
+    nFuncsA = 0;
+  }
+  obj1.free();
+
+  nPatchesA = 0;
+  patchesA = NULL;
+  patchesSize = 0;
+  bitBuf = new GfxShadingBitBuf(str);
+  while (1) {
+    if (!bitBuf->getBits(flagBits, &flag)) {
+      break;
+    }
+    if (typeA == 6) {
+      switch (flag) {
+      case 0: nPts = 12; nColors = 4; break;
+      case 1:
+      case 2:
+      case 3:
+      default: nPts =  8; nColors = 2; break;
+      }
+    } else {
+      switch (flag) {
+      case 0: nPts = 16; nColors = 4; break;
+      case 1:
+      case 2:
+      case 3:
+      default: nPts = 12; nColors = 2; break;
+      }
+    }
+    for (i = 0; i < nPts; ++i) {
+      if (!bitBuf->getBits(coordBits, &xi) ||
+         !bitBuf->getBits(coordBits, &yi)) {
+       break;
+      }
+      x[i] = xMin + xMul * (double)xi;
+      y[i] = yMin + yMul * (double)yi;
+    }
+    if (i < nPts) {
+      break;
+    }
+    for (i = 0; i < nColors; ++i) {
+      for (j = 0; j < nComps; ++j) {
+       if (!bitBuf->getBits(compBits, &ci[j])) {
+         break;
+       }
+       c[i][j] = dblToCol(cMin[j] + cMul[j] * (double)ci[j]);
+      }
+      if (j < nComps) {
+       break;
+      }
+    }
+    if (i < nColors) {
+      break;
+    }
+    if (nPatchesA == patchesSize) {
+      patchesSize = (patchesSize == 0) ? 16 : 2 * patchesSize;
+      patchesA = (GfxPatch *)greallocn(patchesA,
+                                      patchesSize, sizeof(GfxPatch));
+    }
+    p = &patchesA[nPatchesA];
+    if (typeA == 6) {
+      switch (flag) {
+      case 0:
+       p->x[0][0] = x[0];
+       p->y[0][0] = y[0];
+       p->x[0][1] = x[1];
+       p->y[0][1] = y[1];
+       p->x[0][2] = x[2];
+       p->y[0][2] = y[2];
+       p->x[0][3] = x[3];
+       p->y[0][3] = y[3];
+       p->x[1][3] = x[4];
+       p->y[1][3] = y[4];
+       p->x[2][3] = x[5];
+       p->y[2][3] = y[5];
+       p->x[3][3] = x[6];
+       p->y[3][3] = y[6];
+       p->x[3][2] = x[7];
+       p->y[3][2] = y[7];
+       p->x[3][1] = x[8];
+       p->y[3][1] = y[8];
+       p->x[3][0] = x[9];
+       p->y[3][0] = y[9];
+       p->x[2][0] = x[10];
+       p->y[2][0] = y[10];
+       p->x[1][0] = x[11];
+       p->y[1][0] = y[11];
+       for (j = 0; j < nComps; ++j) {
+         p->color[0][0].c[j] = c[0][j];
+         p->color[0][1].c[j] = c[1][j];
+         p->color[1][1].c[j] = c[2][j];
+         p->color[1][0].c[j] = c[3][j];
+       }
+       break;
+      case 1:
+       p->x[0][0] = patchesA[nPatchesA-1].x[0][3];
+       p->y[0][0] = patchesA[nPatchesA-1].y[0][3];
+       p->x[0][1] = patchesA[nPatchesA-1].x[1][3];
+       p->y[0][1] = patchesA[nPatchesA-1].y[1][3];
+       p->x[0][2] = patchesA[nPatchesA-1].x[2][3];
+       p->y[0][2] = patchesA[nPatchesA-1].y[2][3];
+       p->x[0][3] = patchesA[nPatchesA-1].x[3][3];
+       p->y[0][3] = patchesA[nPatchesA-1].y[3][3];
+       p->x[1][3] = x[0];
+       p->y[1][3] = y[0];
+       p->x[2][3] = x[1];
+       p->y[2][3] = y[1];
+       p->x[3][3] = x[2];
+       p->y[3][3] = y[2];
+       p->x[3][2] = x[3];
+       p->y[3][2] = y[3];
+       p->x[3][1] = x[4];
+       p->y[3][1] = y[4];
+       p->x[3][0] = x[5];
+       p->y[3][0] = y[5];
+       p->x[2][0] = x[6];
+       p->y[2][0] = y[6];
+       p->x[1][0] = x[7];
+       p->y[1][0] = y[7];
+       for (j = 0; j < nComps; ++j) {
+         p->color[0][0].c[j] = patchesA[nPatchesA-1].color[0][1].c[j];
+         p->color[0][1].c[j] = patchesA[nPatchesA-1].color[1][1].c[j];
+         p->color[1][1].c[j] = c[0][j];
+         p->color[1][0].c[j] = c[1][j];
+       }
+       break;
+      case 2:
+       p->x[0][0] = patchesA[nPatchesA-1].x[3][3];
+       p->y[0][0] = patchesA[nPatchesA-1].y[3][3];
+       p->x[0][1] = patchesA[nPatchesA-1].x[3][2];
+       p->y[0][1] = patchesA[nPatchesA-1].y[3][2];
+       p->x[0][2] = patchesA[nPatchesA-1].x[3][1];
+       p->y[0][2] = patchesA[nPatchesA-1].y[3][1];
+       p->x[0][3] = patchesA[nPatchesA-1].x[3][0];
+       p->y[0][3] = patchesA[nPatchesA-1].y[3][0];
+       p->x[1][3] = x[0];
+       p->y[1][3] = y[0];
+       p->x[2][3] = x[1];
+       p->y[2][3] = y[1];
+       p->x[3][3] = x[2];
+       p->y[3][3] = y[2];
+       p->x[3][2] = x[3];
+       p->y[3][2] = y[3];
+       p->x[3][1] = x[4];
+       p->y[3][1] = y[4];
+       p->x[3][0] = x[5];
+       p->y[3][0] = y[5];
+       p->x[2][0] = x[6];
+       p->y[2][0] = y[6];
+       p->x[1][0] = x[7];
+       p->y[1][0] = y[7];
+       for (j = 0; j < nComps; ++j) {
+         p->color[0][0].c[j] = patchesA[nPatchesA-1].color[1][1].c[j];
+         p->color[0][1].c[j] = patchesA[nPatchesA-1].color[1][0].c[j];
+         p->color[1][1].c[j] = c[0][j];
+         p->color[1][0].c[j] = c[1][j];
+       }
+       break;
+      case 3:
+       p->x[0][0] = patchesA[nPatchesA-1].x[3][0];
+       p->y[0][0] = patchesA[nPatchesA-1].y[3][0];
+       p->x[0][1] = patchesA[nPatchesA-1].x[2][0];
+       p->y[0][1] = patchesA[nPatchesA-1].y[2][0];
+       p->x[0][2] = patchesA[nPatchesA-1].x[1][0];
+       p->y[0][2] = patchesA[nPatchesA-1].y[1][0];
+       p->x[0][3] = patchesA[nPatchesA-1].x[0][0];
+       p->y[0][3] = patchesA[nPatchesA-1].y[0][0];
+       p->x[1][3] = x[0];
+       p->y[1][3] = y[0];
+       p->x[2][3] = x[1];
+       p->y[2][3] = y[1];
+       p->x[3][3] = x[2];
+       p->y[3][3] = y[2];
+       p->x[3][2] = x[3];
+       p->y[3][2] = y[3];
+       p->x[3][1] = x[4];
+       p->y[3][1] = y[4];
+       p->x[3][0] = x[5];
+       p->y[3][0] = y[5];
+       p->x[2][0] = x[6];
+       p->y[2][0] = y[6];
+       p->x[1][0] = x[7];
+       p->y[1][0] = y[7];
+       for (j = 0; j < nComps; ++j) {
+         p->color[0][1].c[j] = patchesA[nPatchesA-1].color[1][0].c[j];
+         p->color[0][1].c[j] = patchesA[nPatchesA-1].color[0][0].c[j];
+         p->color[1][1].c[j] = c[0][j];
+         p->color[1][0].c[j] = c[1][j];
+       }
+       break;
+      }
+    } else {
+      switch (flag) {
+      case 0:
+       p->x[0][0] = x[0];
+       p->y[0][0] = y[0];
+       p->x[0][1] = x[1];
+       p->y[0][1] = y[1];
+       p->x[0][2] = x[2];
+       p->y[0][2] = y[2];
+       p->x[0][3] = x[3];
+       p->y[0][3] = y[3];
+       p->x[1][3] = x[4];
+       p->y[1][3] = y[4];
+       p->x[2][3] = x[5];
+       p->y[2][3] = y[5];
+       p->x[3][3] = x[6];
+       p->y[3][3] = y[6];
+       p->x[3][2] = x[7];
+       p->y[3][2] = y[7];
+       p->x[3][1] = x[8];
+       p->y[3][1] = y[8];
+       p->x[3][0] = x[9];
+       p->y[3][0] = y[9];
+       p->x[2][0] = x[10];
+       p->y[2][0] = y[10];
+       p->x[1][0] = x[11];
+       p->y[1][0] = y[11];
+       p->x[1][1] = x[12];
+       p->y[1][1] = y[12];
+       p->x[1][2] = x[13];
+       p->y[1][2] = y[13];
+       p->x[2][2] = x[14];
+       p->y[2][2] = y[14];
+       p->x[2][1] = x[15];
+       p->y[2][1] = y[15];
+       for (j = 0; j < nComps; ++j) {
+         p->color[0][0].c[j] = c[0][j];
+         p->color[0][1].c[j] = c[1][j];
+         p->color[1][1].c[j] = c[2][j];
+         p->color[1][0].c[j] = c[3][j];
+       }
+       break;
+      case 1:
+       p->x[0][0] = patchesA[nPatchesA-1].x[0][3];
+       p->y[0][0] = patchesA[nPatchesA-1].y[0][3];
+       p->x[0][1] = patchesA[nPatchesA-1].x[1][3];
+       p->y[0][1] = patchesA[nPatchesA-1].y[1][3];
+       p->x[0][2] = patchesA[nPatchesA-1].x[2][3];
+       p->y[0][2] = patchesA[nPatchesA-1].y[2][3];
+       p->x[0][3] = patchesA[nPatchesA-1].x[3][3];
+       p->y[0][3] = patchesA[nPatchesA-1].y[3][3];
+       p->x[1][3] = x[0];
+       p->y[1][3] = y[0];
+       p->x[2][3] = x[1];
+       p->y[2][3] = y[1];
+       p->x[3][3] = x[2];
+       p->y[3][3] = y[2];
+       p->x[3][2] = x[3];
+       p->y[3][2] = y[3];
+       p->x[3][1] = x[4];
+       p->y[3][1] = y[4];
+       p->x[3][0] = x[5];
+       p->y[3][0] = y[5];
+       p->x[2][0] = x[6];
+       p->y[2][0] = y[6];
+       p->x[1][0] = x[7];
+       p->y[1][0] = y[7];
+       p->x[1][1] = x[8];
+       p->y[1][1] = y[8];
+       p->x[1][2] = x[9];
+       p->y[1][2] = y[9];
+       p->x[2][2] = x[10];
+       p->y[2][2] = y[10];
+       p->x[2][1] = x[11];
+       p->y[2][1] = y[11];
+       for (j = 0; j < nComps; ++j) {
+         p->color[0][0].c[j] = patchesA[nPatchesA-1].color[0][1].c[j];
+         p->color[0][1].c[j] = patchesA[nPatchesA-1].color[1][1].c[j];
+         p->color[1][1].c[j] = c[0][j];
+         p->color[1][0].c[j] = c[1][j];
+       }
+       break;
+      case 2:
+       p->x[0][0] = patchesA[nPatchesA-1].x[3][3];
+       p->y[0][0] = patchesA[nPatchesA-1].y[3][3];
+       p->x[0][1] = patchesA[nPatchesA-1].x[3][2];
+       p->y[0][1] = patchesA[nPatchesA-1].y[3][2];
+       p->x[0][2] = patchesA[nPatchesA-1].x[3][1];
+       p->y[0][2] = patchesA[nPatchesA-1].y[3][1];
+       p->x[0][3] = patchesA[nPatchesA-1].x[3][0];
+       p->y[0][3] = patchesA[nPatchesA-1].y[3][0];
+       p->x[1][3] = x[0];
+       p->y[1][3] = y[0];
+       p->x[2][3] = x[1];
+       p->y[2][3] = y[1];
+       p->x[3][3] = x[2];
+       p->y[3][3] = y[2];
+       p->x[3][2] = x[3];
+       p->y[3][2] = y[3];
+       p->x[3][1] = x[4];
+       p->y[3][1] = y[4];
+       p->x[3][0] = x[5];
+       p->y[3][0] = y[5];
+       p->x[2][0] = x[6];
+       p->y[2][0] = y[6];
+       p->x[1][0] = x[7];
+       p->y[1][0] = y[7];
+       p->x[1][1] = x[8];
+       p->y[1][1] = y[8];
+       p->x[1][2] = x[9];
+       p->y[1][2] = y[9];
+       p->x[2][2] = x[10];
+       p->y[2][2] = y[10];
+       p->x[2][1] = x[11];
+       p->y[2][1] = y[11];
+       for (j = 0; j < nComps; ++j) {
+         p->color[0][0].c[j] = patchesA[nPatchesA-1].color[1][1].c[j];
+         p->color[0][1].c[j] = patchesA[nPatchesA-1].color[1][0].c[j];
+         p->color[1][1].c[j] = c[0][j];
+         p->color[1][0].c[j] = c[1][j];
+       }
+       break;
+      case 3:
+       p->x[0][0] = patchesA[nPatchesA-1].x[3][0];
+       p->y[0][0] = patchesA[nPatchesA-1].y[3][0];
+       p->x[0][1] = patchesA[nPatchesA-1].x[2][0];
+       p->y[0][1] = patchesA[nPatchesA-1].y[2][0];
+       p->x[0][2] = patchesA[nPatchesA-1].x[1][0];
+       p->y[0][2] = patchesA[nPatchesA-1].y[1][0];
+       p->x[0][3] = patchesA[nPatchesA-1].x[0][0];
+       p->y[0][3] = patchesA[nPatchesA-1].y[0][0];
+       p->x[1][3] = x[0];
+       p->y[1][3] = y[0];
+       p->x[2][3] = x[1];
+       p->y[2][3] = y[1];
+       p->x[3][3] = x[2];
+       p->y[3][3] = y[2];
+       p->x[3][2] = x[3];
+       p->y[3][2] = y[3];
+       p->x[3][1] = x[4];
+       p->y[3][1] = y[4];
+       p->x[3][0] = x[5];
+       p->y[3][0] = y[5];
+       p->x[2][0] = x[6];
+       p->y[2][0] = y[6];
+       p->x[1][0] = x[7];
+       p->y[1][0] = y[7];
+       p->x[1][1] = x[8];
+       p->y[1][1] = y[8];
+       p->x[1][2] = x[9];
+       p->y[1][2] = y[9];
+       p->x[2][2] = x[10];
+       p->y[2][2] = y[10];
+       p->x[2][1] = x[11];
+       p->y[2][1] = y[11];
+       for (j = 0; j < nComps; ++j) {
+         p->color[0][0].c[j] = patchesA[nPatchesA-1].color[1][0].c[j];
+         p->color[0][1].c[j] = patchesA[nPatchesA-1].color[0][0].c[j];
+         p->color[1][1].c[j] = c[0][j];
+         p->color[1][0].c[j] = c[1][j];
+       }
+       break;
+      }
+    }
+    ++nPatchesA;
+    bitBuf->flushBits();
+  }
+  delete bitBuf;
+
+  if (typeA == 6) {
+    for (i = 0; i < nPatchesA; ++i) {
+      p = &patchesA[i];
+      p->x[1][1] = (-4 * p->x[0][0]
+                   +6 * (p->x[0][1] + p->x[1][0])
+                   -2 * (p->x[0][3] + p->x[3][0])
+                   +3 * (p->x[3][1] + p->x[1][3])
+                   - p->x[3][3]) / 9;
+      p->y[1][1] = (-4 * p->y[0][0]
+                   +6 * (p->y[0][1] + p->y[1][0])
+                   -2 * (p->y[0][3] + p->y[3][0])
+                   +3 * (p->y[3][1] + p->y[1][3])
+                   - p->y[3][3]) / 9;
+      p->x[1][2] = (-4 * p->x[0][3]
+                   +6 * (p->x[0][2] + p->x[1][3])
+                   -2 * (p->x[0][0] + p->x[3][3])
+                   +3 * (p->x[3][2] + p->x[1][0])
+                   - p->x[3][0]) / 9;
+      p->y[1][2] = (-4 * p->y[0][3]
+                   +6 * (p->y[0][2] + p->y[1][3])
+                   -2 * (p->y[0][0] + p->y[3][3])
+                   +3 * (p->y[3][2] + p->y[1][0])
+                   - p->y[3][0]) / 9;
+      p->x[2][1] = (-4 * p->x[3][0]
+                   +6 * (p->x[3][1] + p->x[2][0])
+                   -2 * (p->x[3][3] + p->x[0][0])
+                   +3 * (p->x[0][1] + p->x[2][3])
+                   - p->x[0][3]) / 9;
+      p->y[2][1] = (-4 * p->y[3][0]
+                   +6 * (p->y[3][1] + p->y[2][0])
+                   -2 * (p->y[3][3] + p->y[0][0])
+                   +3 * (p->y[0][1] + p->y[2][3])
+                   - p->y[0][3]) / 9;
+      p->x[2][2] = (-4 * p->x[3][3]
+                   +6 * (p->x[3][2] + p->x[2][3])
+                   -2 * (p->x[3][0] + p->x[0][3])
+                   +3 * (p->x[0][2] + p->x[2][0])
+                   - p->x[0][0]) / 9;
+      p->y[2][2] = (-4 * p->y[3][3]
+                   +6 * (p->y[3][2] + p->y[2][3])
+                   -2 * (p->y[3][0] + p->y[0][3])
+                   +3 * (p->y[0][2] + p->y[2][0])
+                   - p->y[0][0]) / 9;
+    }
+  }
+
+  shading = new GfxPatchMeshShading(typeA, patchesA, nPatchesA,
+                                   funcsA, nFuncsA);
+  if (!shading->init(dict)) {
+    delete shading;
+    return NULL;
+  }
+  return shading;
+
+ err2:
+  obj1.free();
+ err1:
+  return NULL;
+}
+
+GfxShading *GfxPatchMeshShading::copy() {
+  return new GfxPatchMeshShading(this);
+}
+
+//------------------------------------------------------------------------
+// GfxImageColorMap
+//------------------------------------------------------------------------
+
+GfxImageColorMap::GfxImageColorMap(int bitsA, Object *decode,
+                                  GfxColorSpace *colorSpaceA) {
+  GfxIndexedColorSpace *indexedCS;
+  GfxSeparationColorSpace *sepCS;
+  int maxPixel, indexHigh;
+  int maxPixelForAlloc;
+  Guchar *lookup2;
+  Function *sepFunc;
+  Object obj;
+  double x[gfxColorMaxComps];
+  double y[gfxColorMaxComps];
+  int i, j, k;
+
+  ok = gTrue;
+
+  // bits per component and color space
+  bits = bitsA;
+  maxPixel = (1 << bits) - 1;
+  maxPixelForAlloc = (1 << (bits>8?bits:8));
+  colorSpace = colorSpaceA;
+
+  // get decode map
+  if (decode->isNull()) {
+    nComps = colorSpace->getNComps();
+    colorSpace->getDefaultRanges(decodeLow, decodeRange, maxPixel);
+  } else if (decode->isArray()) {
+    nComps = decode->arrayGetLength() / 2;
+    if (nComps != colorSpace->getNComps()) {
+      goto err1;
+    }
+    for (i = 0; i < nComps; ++i) {
+      decode->arrayGet(2*i, &obj);
+      if (!obj.isNum()) {
+       goto err2;
+      }
+      decodeLow[i] = obj.getNum();
+      obj.free();
+      decode->arrayGet(2*i+1, &obj);
+      if (!obj.isNum()) {
+       goto err2;
+      }
+      decodeRange[i] = obj.getNum() - decodeLow[i];
+      obj.free();
+    }
+  } else {
+    goto err1;
+  }
+
+  // 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.
+  //
+  // Optimization: for Indexed and Separation color spaces (which have
+  // only one component), we store color values in the lookup table
+  // rather than component values.
+  for (k = 0; k < gfxColorMaxComps; ++k) {
+    lookup[k] = NULL;
+  }
+  colorSpace2 = NULL;
+  nComps2 = 0;
+  if (colorSpace->getMode() == csIndexed) {
+    // Note that indexHigh may not be the same as maxPixel --
+    // Distiller will remove unused palette entries, resulting in
+    // indexHigh < maxPixel.
+    indexedCS = (GfxIndexedColorSpace *)colorSpace;
+    colorSpace2 = indexedCS->getBase();
+    indexHigh = indexedCS->getIndexHigh();
+    nComps2 = colorSpace2->getNComps();
+    lookup2 = indexedCS->getLookup();
+    colorSpace2->getDefaultRanges(x, y, indexHigh);
+    for (k = 0; k < nComps2; ++k) {
+      lookup[k] = (GfxColorComp *)gmallocn(maxPixelForAlloc + 1,
+                                          sizeof(GfxColorComp));
+      for (i = 0; i <= maxPixel; ++i) {
+       j = (int)(decodeLow[0] + (i * decodeRange[0]) / maxPixel + 0.5);
+       if (j < 0) {
+         j = 0;
+       } else if (j > indexHigh) {
+         j = indexHigh;
+       }
+       lookup[k][i] =
+           dblToCol(x[k] + (lookup2[j*nComps2 + k] / 255.0) * y[k]);
+      }
+    }
+  } else if (colorSpace->getMode() == csSeparation) {
+    sepCS = (GfxSeparationColorSpace *)colorSpace;
+    colorSpace2 = sepCS->getAlt();
+    nComps2 = colorSpace2->getNComps();
+    sepFunc = sepCS->getFunc();
+    for (k = 0; k < nComps2; ++k) {
+      lookup[k] = (GfxColorComp *)gmallocn(maxPixelForAlloc + 1,
+                                          sizeof(GfxColorComp));
+      for (i = 0; i <= maxPixel; ++i) {
+       x[0] = decodeLow[0] + (i * decodeRange[0]) / maxPixel;
+       sepFunc->transform(x, y);
+       lookup[k][i] = dblToCol(y[k]);
+      }
+    }
+  } else {
+    for (k = 0; k < nComps; ++k) {
+      lookup[k] = (GfxColorComp *)gmallocn(maxPixelForAlloc + 1,
+                                          sizeof(GfxColorComp));
+      for (i = 0; i <= maxPixel; ++i) {
+       lookup[k][i] = dblToCol(decodeLow[k] +
+                               (i * decodeRange[k]) / maxPixel);
+      }
+    }
+  }
+
+  return;
+
+ err2:
+  obj.free();
+ err1:
+  ok = gFalse;
+}
+
+GfxImageColorMap::GfxImageColorMap(GfxImageColorMap *colorMap) {
+  int n, i, k;
+
+  colorSpace = colorMap->colorSpace->copy();
+  bits = colorMap->bits;
+  nComps = colorMap->nComps;
+  nComps2 = colorMap->nComps2;
+  colorSpace2 = NULL;
+  for (k = 0; k < gfxColorMaxComps; ++k) {
+    lookup[k] = NULL;
+  }
+  n = 1 << bits;
+  if (colorSpace->getMode() == csIndexed) {
+    colorSpace2 = ((GfxIndexedColorSpace *)colorSpace)->getBase();
+    for (k = 0; k < nComps2; ++k) {
+      lookup[k] = (GfxColorComp *)gmallocn(n, sizeof(GfxColorComp));
+      memcpy(lookup[k], colorMap->lookup[k], n * sizeof(GfxColorComp));
+    }
+  } else if (colorSpace->getMode() == csSeparation) {
+    colorSpace2 = ((GfxSeparationColorSpace *)colorSpace)->getAlt();
+    for (k = 0; k < nComps2; ++k) {
+      lookup[k] = (GfxColorComp *)gmallocn(n, sizeof(GfxColorComp));
+      memcpy(lookup[k], colorMap->lookup[k], n * sizeof(GfxColorComp));
+    }
+  } else {
+    for (k = 0; k < nComps; ++k) {
+      lookup[k] = (GfxColorComp *)gmallocn(n, sizeof(GfxColorComp));
+      memcpy(lookup[k], colorMap->lookup[k], n * sizeof(GfxColorComp));
+    }
+  }
+  for (i = 0; i < nComps; ++i) {
+    decodeLow[i] = colorMap->decodeLow[i];
+    decodeRange[i] = colorMap->decodeRange[i];
+  }
+  ok = gTrue;
+}
+
+GfxImageColorMap::~GfxImageColorMap() {
+  int i;
+
+  delete colorSpace;
+  for (i = 0; i < gfxColorMaxComps; ++i) {
+    gfree(lookup[i]);
+  }
+}
+
+void GfxImageColorMap::getGray(Guchar *x, GfxGray *gray) {
+  GfxColor color;
+  int i;
+
+  if (colorSpace2) {
+    for (i = 0; i < nComps2; ++i) {
+      color.c[i] = lookup[i][x[0]];
+    }
+    colorSpace2->getGray(&color, gray);
+  } else {
+    for (i = 0; i < nComps; ++i) {
+      color.c[i] = lookup[i][x[i]];
+    }
+    colorSpace->getGray(&color, gray);
+  }
+}
+
+void GfxImageColorMap::getRGB(Guchar *x, GfxRGB *rgb) {
+  GfxColor color;
+  int i;
+
+  if (colorSpace2) {
+    for (i = 0; i < nComps2; ++i) {
+      color.c[i] = lookup[i][x[0]];
+    }
+    colorSpace2->getRGB(&color, rgb);
+  } else {
+    for (i = 0; i < nComps; ++i) {
+      color.c[i] = lookup[i][x[i]];
+    }
+    colorSpace->getRGB(&color, rgb);
+  }
+}
+
+void GfxImageColorMap::getCMYK(Guchar *x, GfxCMYK *cmyk) {
+  GfxColor color;
+  int i;
+
+  if (colorSpace2) {
+    for (i = 0; i < nComps2; ++i) {
+      color.c[i] = lookup[i][x[0]];
+    }
+    colorSpace2->getCMYK(&color, cmyk);
+  } else {
+    for (i = 0; i < nComps; ++i) {
+      color.c[i] = lookup[i][x[i]];
+    }
+    colorSpace->getCMYK(&color, cmyk);
+  }
+}
+
+void GfxImageColorMap::getColor(Guchar *x, GfxColor *color) {
+  int maxPixel, i;
+
+  maxPixel = (1 << bits) - 1;
+  for (i = 0; i < nComps; ++i) {
+    color->c[i] = dblToCol(decodeLow[i] + (x[i] * decodeRange[i]) / maxPixel);
+  }
+}
+
+//------------------------------------------------------------------------
+// GfxSubpath and GfxPath
+//------------------------------------------------------------------------
+
+GfxSubpath::GfxSubpath(double x1, double y1) {
+  size = 16;
+  x = (double *)gmallocn(size, sizeof(double));
+  y = (double *)gmallocn(size, sizeof(double));
+  curve = (GBool *)gmallocn(size, sizeof(GBool));
+  n = 1;
+  x[0] = x1;
+  y[0] = y1;
+  curve[0] = gFalse;
+  closed = gFalse;
+}
+
+GfxSubpath::~GfxSubpath() {
+  gfree(x);
+  gfree(y);
+  gfree(curve);
+}
+
+// Used for copy().
+GfxSubpath::GfxSubpath(GfxSubpath *subpath) {
+  size = subpath->size;
+  n = subpath->n;
+  x = (double *)gmallocn(size, sizeof(double));
+  y = (double *)gmallocn(size, sizeof(double));
+  curve = (GBool *)gmallocn(size, sizeof(GBool));
+  memcpy(x, subpath->x, n * sizeof(double));
+  memcpy(y, subpath->y, n * sizeof(double));
+  memcpy(curve, subpath->curve, n * sizeof(GBool));
+  closed = subpath->closed;
+}
+
+void GfxSubpath::lineTo(double x1, double y1) {
+  if (n >= size) {
+    size += 16;
+    x = (double *)greallocn(x, size, sizeof(double));
+    y = (double *)greallocn(y, size, sizeof(double));
+    curve = (GBool *)greallocn(curve, size, sizeof(GBool));
+  }
+  x[n] = x1;
+  y[n] = y1;
+  curve[n] = gFalse;
+  ++n;
+}
+
+void GfxSubpath::curveTo(double x1, double y1, double x2, double y2,
+                        double x3, double y3) {
+  if (n+3 > size) {
+    size += 16;
+    x = (double *)greallocn(x, size, sizeof(double));
+    y = (double *)greallocn(y, size, sizeof(double));
+    curve = (GBool *)greallocn(curve, size, sizeof(GBool));
+  }
+  x[n] = x1;
+  y[n] = y1;
+  x[n+1] = x2;
+  y[n+1] = y2;
+  x[n+2] = x3;
+  y[n+2] = y3;
+  curve[n] = curve[n+1] = gTrue;
+  curve[n+2] = gFalse;
+  n += 3;
+}
+
+void GfxSubpath::close() {
+  if (x[n-1] != x[0] || y[n-1] != y[0]) {
+    lineTo(x[0], y[0]);
+  }
+  closed = gTrue;
+}
+
+void GfxSubpath::offset(double dx, double dy) {
+  int i;
+
+  for (i = 0; i < n; ++i) {
+    x[i] += dx;
+    y[i] += dy;
+  }
+}
+
+GfxPath::GfxPath() {
+  justMoved = gFalse;
+  size = 16;
+  n = 0;
+  firstX = firstY = 0;
+  subpaths = (GfxSubpath **)gmallocn(size, sizeof(GfxSubpath *));
+}
+
+GfxPath::~GfxPath() {
+  int i;
+
+  for (i = 0; i < n; ++i)
+    delete subpaths[i];
+  gfree(subpaths);
+}
+
+// Used for copy().
+GfxPath::GfxPath(GBool justMoved1, double firstX1, double firstY1,
+                GfxSubpath **subpaths1, int n1, int size1) {
+  int i;
+
+  justMoved = justMoved1;
+  firstX = firstX1;
+  firstY = firstY1;
+  size = size1;
+  n = n1;
+  subpaths = (GfxSubpath **)gmallocn(size, sizeof(GfxSubpath *));
+  for (i = 0; i < n; ++i)
+    subpaths[i] = subpaths1[i]->copy();
+}
+
+void GfxPath::moveTo(double x, double y) {
+  justMoved = gTrue;
+  firstX = x;
+  firstY = y;
+}
+
+void GfxPath::lineTo(double x, double y) {
+  if (justMoved) {
+    if (n >= size) {
+      size += 16;
+      subpaths = (GfxSubpath **)
+                  greallocn(subpaths, size, sizeof(GfxSubpath *));
+    }
+    subpaths[n] = new GfxSubpath(firstX, firstY);
+    ++n;
+    justMoved = gFalse;
+  }
+  subpaths[n-1]->lineTo(x, y);
+}
+
+void GfxPath::curveTo(double x1, double y1, double x2, double y2,
+            double x3, double y3) {
+  if (justMoved) {
+    if (n >= size) {
+      size += 16;
+      subpaths = (GfxSubpath **)
+                  greallocn(subpaths, size, sizeof(GfxSubpath *));
+    }
+    subpaths[n] = new GfxSubpath(firstX, firstY);
+    ++n;
+    justMoved = gFalse;
+  }
+  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 **)
+                  greallocn(subpaths, size, sizeof(GfxSubpath *));
+    }
+    subpaths[n] = new GfxSubpath(firstX, firstY);
+    ++n;
+    justMoved = gFalse;
+  }
+  subpaths[n-1]->close();
+}
+
+void GfxPath::append(GfxPath *path) {
+  int i;
+
+  if (n + path->n > size) {
+    size = n + path->n;
+    subpaths = (GfxSubpath **)
+                 greallocn(subpaths, size, sizeof(GfxSubpath *));
+  }
+  for (i = 0; i < path->n; ++i) {
+    subpaths[n++] = path->subpaths[i]->copy();
+  }
+  justMoved = gFalse;
+}
+
+void GfxPath::offset(double dx, double dy) {
+  int i;
+
+  for (i = 0; i < n; ++i) {
+    subpaths[i]->offset(dx, dy);
+  }
+}
+
+//------------------------------------------------------------------------
+// GfxState
+//------------------------------------------------------------------------
+
+GfxState::GfxState(double hDPI, double vDPI, PDFRectangle *pageBox,
+                  int rotateA, GBool upsideDown) {
+  double kx, ky;
+
+  rotate = rotateA;
+  px1 = pageBox->x1;
+  py1 = pageBox->y1;
+  px2 = pageBox->x2;
+  py2 = pageBox->y2;
+  kx = hDPI / 72.0;
+  ky = vDPI / 72.0;
+  if (rotate == 90) {
+    ctm[0] = 0;
+    ctm[1] = upsideDown ? ky : -ky;
+    ctm[2] = kx;
+    ctm[3] = 0;
+    ctm[4] = -kx * py1;
+    ctm[5] = ky * (upsideDown ? -px1 : px2);
+    pageWidth = kx * (py2 - py1);
+    pageHeight = ky * (px2 - px1);
+  } else if (rotate == 180) {
+    ctm[0] = -kx;
+    ctm[1] = 0;
+    ctm[2] = 0;
+    ctm[3] = upsideDown ? ky : -ky;
+    ctm[4] = kx * px2;
+    ctm[5] = ky * (upsideDown ? -py1 : py2);
+    pageWidth = kx * (px2 - px1);
+    pageHeight = ky * (py2 - py1);
+  } else if (rotate == 270) {
+    ctm[0] = 0;
+    ctm[1] = upsideDown ? -ky : ky;
+    ctm[2] = -kx;
+    ctm[3] = 0;
+    ctm[4] = kx * py2;
+    ctm[5] = ky * (upsideDown ? px2 : -px1);
+    pageWidth = kx * (py2 - py1);
+    pageHeight = ky * (px2 - px1);
+  } else {
+    ctm[0] = kx;
+    ctm[1] = 0;
+    ctm[2] = 0;
+    ctm[3] = upsideDown ? -ky : ky;
+    ctm[4] = -kx * px1;
+    ctm[5] = ky * (upsideDown ? py2 : -py1);
+    pageWidth = kx * (px2 - px1);
+    pageHeight = ky * (py2 - py1);
+  }
+
+  fillColorSpace = new GfxDeviceGrayColorSpace();
+  strokeColorSpace = new GfxDeviceGrayColorSpace();
+  fillColor.c[0] = 0;
+  strokeColor.c[0] = 0;
+  fillPattern = NULL;
+  strokePattern = NULL;
+  blendMode = gfxBlendNormal;
+  fillOpacity = 1;
+  strokeOpacity = 1;
+  fillOverprint = gFalse;
+  strokeOverprint = gFalse;
+
+  lineWidth = 1;
+  lineDash = NULL;
+  lineDashLength = 0;
+  lineDashStart = 0;
+  flatness = 1;
+  lineJoin = 0;
+  lineCap = 0;
+  miterLimit = 10;
+
+  font = NULL;
+  fontSize = 0;
+  textMat[0] = 1; textMat[1] = 0;
+  textMat[2] = 0; textMat[3] = 1;
+  textMat[4] = 0; textMat[5] = 0;
+  charSpace = 0;
+  wordSpace = 0;
+  horizScaling = 1;
+  leading = 0;
+  rise = 0;
+  render = 0;
+
+  path = new GfxPath();
+  curX = curY = 0;
+  lineX = lineY = 0;
+
+  clipXMin = 0;
+  clipYMin = 0;
+  clipXMax = pageWidth;
+  clipYMax = pageHeight;
+
+  saved = NULL;
+}
+
+GfxState::~GfxState() {
+  if (fillColorSpace) {
+    delete fillColorSpace;
+  }
+  if (strokeColorSpace) {
+    delete strokeColorSpace;
+  }
+  if (fillPattern) {
+    delete fillPattern;
+  }
+  if (strokePattern) {
+    delete strokePattern;
+  }
+  gfree(lineDash);
+  if (path) {
+    // this gets set to NULL by restore()
+    delete path;
+  }
+  if (saved) {
+    delete saved;
+  }
+}
+
+// Used for copy();
+GfxState::GfxState(GfxState *state) {
+  memcpy(this, state, sizeof(GfxState));
+  if (fillColorSpace) {
+    fillColorSpace = state->fillColorSpace->copy();
+  }
+  if (strokeColorSpace) {
+    strokeColorSpace = state->strokeColorSpace->copy();
+  }
+  if (fillPattern) {
+    fillPattern = state->fillPattern->copy();
+  }
+  if (strokePattern) {
+    strokePattern = state->strokePattern->copy();
+  }
+  if (lineDashLength > 0) {
+    lineDash = (double *)gmallocn(lineDashLength, sizeof(double));
+    memcpy(lineDash, state->lineDash, lineDashLength * sizeof(double));
+  }
+  saved = NULL;
+}
+
+void GfxState::setPath(GfxPath *pathA) {
+  delete path;
+  path = pathA;
+}
+
+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;
+
+  x = ctm[0] + ctm[2];
+  y = ctm[1] + ctm[3];
+  return w * sqrt(0.5 * (x * x + y * y));
+}
+
+double GfxState::getTransformedFontSize() {
+  double x1, y1, x2, y2;
+
+  x1 = textMat[2] * fontSize;
+  y1 = textMat[3] * fontSize;
+  x2 = ctm[0] * x1 + ctm[2] * y1;
+  y2 = ctm[1] * x1 + ctm[3] * y1;
+  return sqrt(x2 * x2 + y2 * y2);
+}
+
+void GfxState::getFontTransMat(double *m11, double *m12,
+                              double *m21, double *m22) {
+  *m11 = (textMat[0] * ctm[0] + textMat[1] * ctm[2]) * fontSize;
+  *m12 = (textMat[0] * ctm[1] + textMat[1] * ctm[3]) * fontSize;
+  *m21 = (textMat[2] * ctm[0] + textMat[3] * ctm[2]) * fontSize;
+  *m22 = (textMat[2] * ctm[1] + textMat[3] * ctm[3]) * fontSize;
+}
+
+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,
+                        double d, double e, double f) {
+  double a1 = ctm[0];
+  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;
+  ctm[2] = c * a1 + d * c1;
+  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) {
+  if (fillColorSpace) {
+    delete fillColorSpace;
+  }
+  fillColorSpace = colorSpace;
+}
+
+void GfxState::setStrokeColorSpace(GfxColorSpace *colorSpace) {
+  if (strokeColorSpace) {
+    delete strokeColorSpace;
+  }
+  strokeColorSpace = colorSpace;
+}
+
+void GfxState::setFillPattern(GfxPattern *pattern) {
+  if (fillPattern) {
+    delete fillPattern;
+  }
+  fillPattern = pattern;
+}
+
+void GfxState::setStrokePattern(GfxPattern *pattern) {
+  if (strokePattern) {
+    delete strokePattern;
+  }
+  strokePattern = pattern;
+}
+
+void GfxState::setLineDash(double *dash, int length, double start) {
+  if (lineDash)
+    gfree(lineDash);
+  lineDash = dash;
+  lineDashLength = length;
+  lineDashStart = start;
+}
+
+void GfxState::clearPath() {
+  delete path;
+  path = new GfxPath();
+}
+
+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) {
+  double dx, dy;
+
+  textTransformDelta(tx, ty, &dx, &dy);
+  curX += dx;
+  curY += dy;
+}
+
+void GfxState::shift(double dx, double dy) {
+  curX += dx;
+  curY += dy;
+}
+
+GfxState *GfxState::save() {
+  GfxState *newState;
+
+  newState = copy();
+  newState->saved = this;
+  return newState;
+}
+
+GfxState *GfxState::restore() {
+  GfxState *oldState;
+
+  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;
+}
+
+GBool GfxState::parseBlendMode(Object *obj, GfxBlendMode *mode) {
+  Object obj2;
+  int i, j;
+
+  if (obj->isName()) {
+    for (i = 0; i < nGfxBlendModeNames; ++i) {
+      if (!strcmp(obj->getName(), gfxBlendModeNames[i].name)) {
+       *mode = gfxBlendModeNames[i].mode;
+       return gTrue;
+      }
+    }
+    return gFalse;
+  } else if (obj->isArray()) {
+    for (i = 0; i < obj->arrayGetLength(); ++i) {
+      obj->arrayGet(i, &obj2);
+      if (!obj2.isName()) {
+       obj2.free();
+       return gFalse;
+      }
+      for (j = 0; j < nGfxBlendModeNames; ++j) {
+       if (!strcmp(obj2.getName(), gfxBlendModeNames[j].name)) {
+         obj2.free();
+         *mode = gfxBlendModeNames[j].mode;
+         return gTrue;
+       }
+      }
+      obj2.free();
+    }
+    *mode = gfxBlendNormal;
+    return gTrue;
+  } else {
+    return gFalse;
+  }
+}
diff --git a/lib/xpdf/GfxState.h b/lib/xpdf/GfxState.h
new file mode 100644 (file)
index 0000000..e1d6801
--- /dev/null
@@ -0,0 +1,1206 @@
+//========================================================================
+//
+// GfxState.h
+//
+// Copyright 1996-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef GFXSTATE_H
+#define GFXSTATE_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#include "gtypes.h"
+#include "Object.h"
+#include "Function.h"
+
+class Array;
+class GfxFont;
+class PDFRectangle;
+class GfxShading;
+
+//------------------------------------------------------------------------
+// GfxBlendMode
+//------------------------------------------------------------------------
+
+enum GfxBlendMode {
+  gfxBlendNormal,
+  gfxBlendMultiply,
+  gfxBlendScreen,
+  gfxBlendOverlay,
+  gfxBlendDarken,
+  gfxBlendLighten,
+  gfxBlendColorDodge,
+  gfxBlendColorBurn,
+  gfxBlendHardLight,
+  gfxBlendSoftLight,
+  gfxBlendDifference,
+  gfxBlendExclusion,
+  gfxBlendHue,
+  gfxBlendSaturation,
+  gfxBlendColor,
+  gfxBlendLuminosity
+};
+
+//------------------------------------------------------------------------
+// GfxColorComp
+//------------------------------------------------------------------------
+
+// 16.16 fixed point color component
+typedef int GfxColorComp;
+
+#define gfxColorComp1 0x10000
+
+static inline GfxColorComp dblToCol(double x) {
+  return (GfxColorComp)(x * gfxColorComp1);
+}
+
+static inline double colToDbl(GfxColorComp x) {
+  return (double)x / (double)gfxColorComp1;
+}
+
+static inline GfxColorComp byteToCol(Guchar x) {
+  // (x / 255) << 16  =  (0.0000000100000001... * x) << 16
+  //                  =  ((x << 8) + (x) + (x >> 8) + ...) << 16
+  //                  =  (x << 8) + (x) + (x >> 7)
+  //                                      [for rounding]
+  return (GfxColorComp)((x << 8) + x + (x >> 7));
+}
+
+static inline Guchar colToByte(GfxColorComp x) {
+  // 255 * x + 0.5  =  256 * x - x + 0x8000
+  return (Guchar)(((x << 8) - x + 0x8000) >> 16);
+}
+
+//------------------------------------------------------------------------
+// GfxColor
+//------------------------------------------------------------------------
+
+#define gfxColorMaxComps funcMaxOutputs
+
+struct GfxColor {
+  GfxColorComp c[gfxColorMaxComps];
+};
+
+//------------------------------------------------------------------------
+// GfxGray
+//------------------------------------------------------------------------
+
+typedef GfxColorComp GfxGray;
+
+//------------------------------------------------------------------------
+// GfxRGB
+//------------------------------------------------------------------------
+
+struct GfxRGB {
+  GfxColorComp r, g, b;
+};
+
+//------------------------------------------------------------------------
+// GfxCMYK
+//------------------------------------------------------------------------
+
+struct GfxCMYK {
+  GfxColorComp c, m, y, k;
+};
+
+//------------------------------------------------------------------------
+// GfxColorSpace
+//------------------------------------------------------------------------
+
+// NB: The nGfxColorSpaceModes constant and the gfxColorSpaceModeNames
+// array defined in GfxState.cc must match this enum.
+enum GfxColorSpaceMode {
+  csDeviceGray,
+  csCalGray,
+  csDeviceRGB,
+  csCalRGB,
+  csDeviceCMYK,
+  csLab,
+  csICCBased,
+  csIndexed,
+  csSeparation,
+  csDeviceN,
+  csPattern
+};
+
+class GfxColorSpace {
+public:
+
+  GfxColorSpace();
+  virtual ~GfxColorSpace();
+  virtual GfxColorSpace *copy() = 0;
+  virtual GfxColorSpaceMode getMode() = 0;
+
+  // Construct a color space.  Returns NULL if unsuccessful.
+  static GfxColorSpace *parse(Object *csObj);
+
+  // Convert to gray, RGB, or CMYK.
+  virtual void getGray(GfxColor *color, GfxGray *gray) = 0;
+  virtual void getRGB(GfxColor *color, GfxRGB *rgb) = 0;
+  virtual void getCMYK(GfxColor *color, GfxCMYK *cmyk) = 0;
+
+  // Return the number of color components.
+  virtual int getNComps() = 0;
+
+  // Return the default ranges for each component, assuming an image
+  // with a max pixel value of <maxImgPixel>.
+  virtual void getDefaultRanges(double *decodeLow, double *decodeRange,
+                               int maxImgPixel);
+
+  // Return the number of color space modes
+  static int getNumColorSpaceModes();
+
+  // Return the name of the <idx>th color space mode.
+  static char *getColorSpaceModeName(int idx);
+
+private:
+};
+
+//------------------------------------------------------------------------
+// GfxDeviceGrayColorSpace
+//------------------------------------------------------------------------
+
+class GfxDeviceGrayColorSpace: public GfxColorSpace {
+public:
+
+  GfxDeviceGrayColorSpace();
+  virtual ~GfxDeviceGrayColorSpace();
+  virtual GfxColorSpace *copy();
+  virtual GfxColorSpaceMode getMode() { return csDeviceGray; }
+
+  virtual void getGray(GfxColor *color, GfxGray *gray);
+  virtual void getRGB(GfxColor *color, GfxRGB *rgb);
+  virtual void getCMYK(GfxColor *color, GfxCMYK *cmyk);
+
+  virtual int getNComps() { return 1; }
+
+private:
+};
+
+//------------------------------------------------------------------------
+// GfxCalGrayColorSpace
+//------------------------------------------------------------------------
+
+class GfxCalGrayColorSpace: public GfxColorSpace {
+public:
+
+  GfxCalGrayColorSpace();
+  virtual ~GfxCalGrayColorSpace();
+  virtual GfxColorSpace *copy();
+  virtual GfxColorSpaceMode getMode() { return csCalGray; }
+
+  // Construct a CalGray color space.  Returns NULL if unsuccessful.
+  static GfxColorSpace *parse(Array *arr);
+
+  virtual void getGray(GfxColor *color, GfxGray *gray);
+  virtual void getRGB(GfxColor *color, GfxRGB *rgb);
+  virtual void getCMYK(GfxColor *color, GfxCMYK *cmyk);
+
+  virtual int getNComps() { return 1; }
+
+  // CalGray-specific access.
+  double getWhiteX() { return whiteX; }
+  double getWhiteY() { return whiteY; }
+  double getWhiteZ() { return whiteZ; }
+  double getBlackX() { return blackX; }
+  double getBlackY() { return blackY; }
+  double getBlackZ() { return blackZ; }
+  double getGamma() { return gamma; }
+
+private:
+
+  double whiteX, whiteY, whiteZ;    // white point
+  double blackX, blackY, blackZ;    // black point
+  double gamma;                            // gamma value
+};
+
+//------------------------------------------------------------------------
+// GfxDeviceRGBColorSpace
+//------------------------------------------------------------------------
+
+class GfxDeviceRGBColorSpace: public GfxColorSpace {
+public:
+
+  GfxDeviceRGBColorSpace();
+  virtual ~GfxDeviceRGBColorSpace();
+  virtual GfxColorSpace *copy();
+  virtual GfxColorSpaceMode getMode() { return csDeviceRGB; }
+
+  virtual void getGray(GfxColor *color, GfxGray *gray);
+  virtual void getRGB(GfxColor *color, GfxRGB *rgb);
+  virtual void getCMYK(GfxColor *color, GfxCMYK *cmyk);
+
+  virtual int getNComps() { return 3; }
+
+private:
+};
+
+//------------------------------------------------------------------------
+// GfxCalRGBColorSpace
+//------------------------------------------------------------------------
+
+class GfxCalRGBColorSpace: public GfxColorSpace {
+public:
+
+  GfxCalRGBColorSpace();
+  virtual ~GfxCalRGBColorSpace();
+  virtual GfxColorSpace *copy();
+  virtual GfxColorSpaceMode getMode() { return csCalRGB; }
+
+  // Construct a CalRGB color space.  Returns NULL if unsuccessful.
+  static GfxColorSpace *parse(Array *arr);
+
+  virtual void getGray(GfxColor *color, GfxGray *gray);
+  virtual void getRGB(GfxColor *color, GfxRGB *rgb);
+  virtual void getCMYK(GfxColor *color, GfxCMYK *cmyk);
+
+  virtual int getNComps() { return 3; }
+
+  // CalRGB-specific access.
+  double getWhiteX() { return whiteX; }
+  double getWhiteY() { return whiteY; }
+  double getWhiteZ() { return whiteZ; }
+  double getBlackX() { return blackX; }
+  double getBlackY() { return blackY; }
+  double getBlackZ() { return blackZ; }
+  double getGammaR() { return gammaR; }
+  double getGammaG() { return gammaG; }
+  double getGammaB() { return gammaB; }
+  double *getMatrix() { return mat; }
+
+private:
+
+  double whiteX, whiteY, whiteZ;    // white point
+  double blackX, blackY, blackZ;    // black point
+  double gammaR, gammaG, gammaB;    // gamma values
+  double mat[9];                   // ABC -> XYZ transform matrix
+};
+
+//------------------------------------------------------------------------
+// GfxDeviceCMYKColorSpace
+//------------------------------------------------------------------------
+
+class GfxDeviceCMYKColorSpace: public GfxColorSpace {
+public:
+
+  GfxDeviceCMYKColorSpace();
+  virtual ~GfxDeviceCMYKColorSpace();
+  virtual GfxColorSpace *copy();
+  virtual GfxColorSpaceMode getMode() { return csDeviceCMYK; }
+
+  virtual void getGray(GfxColor *color, GfxGray *gray);
+  virtual void getRGB(GfxColor *color, GfxRGB *rgb);
+  virtual void getCMYK(GfxColor *color, GfxCMYK *cmyk);
+
+  virtual int getNComps() { return 4; }
+
+private:
+};
+
+//------------------------------------------------------------------------
+// GfxLabColorSpace
+//------------------------------------------------------------------------
+
+class GfxLabColorSpace: public GfxColorSpace {
+public:
+
+  GfxLabColorSpace();
+  virtual ~GfxLabColorSpace();
+  virtual GfxColorSpace *copy();
+  virtual GfxColorSpaceMode getMode() { return csLab; }
+
+  // Construct a Lab color space.  Returns NULL if unsuccessful.
+  static GfxColorSpace *parse(Array *arr);
+
+  virtual void getGray(GfxColor *color, GfxGray *gray);
+  virtual void getRGB(GfxColor *color, GfxRGB *rgb);
+  virtual void getCMYK(GfxColor *color, GfxCMYK *cmyk);
+
+  virtual int getNComps() { return 3; }
+
+  virtual void getDefaultRanges(double *decodeLow, double *decodeRange,
+                               int maxImgPixel);
+
+  // Lab-specific access.
+  double getWhiteX() { return whiteX; }
+  double getWhiteY() { return whiteY; }
+  double getWhiteZ() { return whiteZ; }
+  double getBlackX() { return blackX; }
+  double getBlackY() { return blackY; }
+  double getBlackZ() { return blackZ; }
+  double getAMin() { return aMin; }
+  double getAMax() { return aMax; }
+  double getBMin() { return bMin; }
+  double getBMax() { return bMax; }
+
+private:
+
+  double whiteX, whiteY, whiteZ;    // white point
+  double blackX, blackY, blackZ;    // black point
+  double aMin, aMax, bMin, bMax;    // range for the a and b components
+  double kr, kg, kb;               // gamut mapping mulitpliers
+};
+
+//------------------------------------------------------------------------
+// GfxICCBasedColorSpace
+//------------------------------------------------------------------------
+
+class GfxICCBasedColorSpace: public GfxColorSpace {
+public:
+
+  GfxICCBasedColorSpace(int nCompsA, GfxColorSpace *altA,
+                       Ref *iccProfileStreamA);
+  virtual ~GfxICCBasedColorSpace();
+  virtual GfxColorSpace *copy();
+  virtual GfxColorSpaceMode getMode() { return csICCBased; }
+
+  // Construct an ICCBased color space.  Returns NULL if unsuccessful.
+  static GfxColorSpace *parse(Array *arr);
+
+  virtual void getGray(GfxColor *color, GfxGray *gray);
+  virtual void getRGB(GfxColor *color, GfxRGB *rgb);
+  virtual void getCMYK(GfxColor *color, GfxCMYK *cmyk);
+
+  virtual int getNComps() { return nComps; }
+
+  virtual void getDefaultRanges(double *decodeLow, double *decodeRange,
+                               int maxImgPixel);
+
+  // ICCBased-specific access.
+  GfxColorSpace *getAlt() { return alt; }
+
+private:
+
+  int nComps;                  // number of color components (1, 3, or 4)
+  GfxColorSpace *alt;          // alternate color space
+  double rangeMin[4];          // min values for each component
+  double rangeMax[4];          // max values for each component
+  Ref iccProfileStream;                // the ICC profile
+};
+
+//------------------------------------------------------------------------
+// GfxIndexedColorSpace
+//------------------------------------------------------------------------
+
+class GfxIndexedColorSpace: public GfxColorSpace {
+public:
+
+  GfxIndexedColorSpace(GfxColorSpace *baseA, int indexHighA);
+  virtual ~GfxIndexedColorSpace();
+  virtual GfxColorSpace *copy();
+  virtual GfxColorSpaceMode getMode() { return csIndexed; }
+
+  // Construct a Lab color space.  Returns NULL if unsuccessful.
+  static GfxColorSpace *parse(Array *arr);
+
+  virtual void getGray(GfxColor *color, GfxGray *gray);
+  virtual void getRGB(GfxColor *color, GfxRGB *rgb);
+  virtual void getCMYK(GfxColor *color, GfxCMYK *cmyk);
+
+  virtual int getNComps() { return 1; }
+
+  virtual void getDefaultRanges(double *decodeLow, double *decodeRange,
+                               int maxImgPixel);
+
+  // Indexed-specific access.
+  GfxColorSpace *getBase() { return base; }
+  int getIndexHigh() { return indexHigh; }
+  Guchar *getLookup() { return lookup; }
+  GfxColor *mapColorToBase(GfxColor *color, GfxColor *baseColor);
+
+private:
+
+  GfxColorSpace *base;         // base color space
+  int indexHigh;               // max pixel value
+  Guchar *lookup;              // lookup table
+};
+
+//------------------------------------------------------------------------
+// GfxSeparationColorSpace
+//------------------------------------------------------------------------
+
+class GfxSeparationColorSpace: public GfxColorSpace {
+public:
+
+  GfxSeparationColorSpace(GString *nameA, GfxColorSpace *altA,
+                         Function *funcA);
+  virtual ~GfxSeparationColorSpace();
+  virtual GfxColorSpace *copy();
+  virtual GfxColorSpaceMode getMode() { return csSeparation; }
+
+  // Construct a Separation color space.  Returns NULL if unsuccessful.
+  static GfxColorSpace *parse(Array *arr);
+
+  virtual void getGray(GfxColor *color, GfxGray *gray);
+  virtual void getRGB(GfxColor *color, GfxRGB *rgb);
+  virtual void getCMYK(GfxColor *color, GfxCMYK *cmyk);
+
+  virtual int getNComps() { return 1; }
+
+  // Separation-specific access.
+  GString *getName() { return name; }
+  GfxColorSpace *getAlt() { return alt; }
+  Function *getFunc() { return func; }
+
+private:
+
+  GString *name;               // colorant name
+  GfxColorSpace *alt;          // alternate color space
+  Function *func;              // tint transform (into alternate color space)
+};
+
+//------------------------------------------------------------------------
+// GfxDeviceNColorSpace
+//------------------------------------------------------------------------
+
+class GfxDeviceNColorSpace: public GfxColorSpace {
+public:
+
+  GfxDeviceNColorSpace(int nCompsA, GfxColorSpace *alt, Function *func);
+  virtual ~GfxDeviceNColorSpace();
+  virtual GfxColorSpace *copy();
+  virtual GfxColorSpaceMode getMode() { return csDeviceN; }
+
+  // Construct a DeviceN color space.  Returns NULL if unsuccessful.
+  static GfxColorSpace *parse(Array *arr);
+
+  virtual void getGray(GfxColor *color, GfxGray *gray);
+  virtual void getRGB(GfxColor *color, GfxRGB *rgb);
+  virtual void getCMYK(GfxColor *color, GfxCMYK *cmyk);
+
+  virtual int getNComps() { return nComps; }
+
+  // DeviceN-specific access.
+  GString *getColorantName(int i) { return names[i]; }
+  GfxColorSpace *getAlt() { return alt; }
+  Function *getTintTransformFunc() { return func; }
+
+private:
+
+  int nComps;                  // number of components
+  GString                      // colorant names
+    *names[gfxColorMaxComps];
+  GfxColorSpace *alt;          // alternate color space
+  Function *func;              // tint transform (into alternate color space)
+};
+
+//------------------------------------------------------------------------
+// GfxPatternColorSpace
+//------------------------------------------------------------------------
+
+class GfxPatternColorSpace: public GfxColorSpace {
+public:
+
+  GfxPatternColorSpace(GfxColorSpace *underA);
+  virtual ~GfxPatternColorSpace();
+  virtual GfxColorSpace *copy();
+  virtual GfxColorSpaceMode getMode() { return csPattern; }
+
+  // Construct a Pattern color space.  Returns NULL if unsuccessful.
+  static GfxColorSpace *parse(Array *arr);
+
+  virtual void getGray(GfxColor *color, GfxGray *gray);
+  virtual void getRGB(GfxColor *color, GfxRGB *rgb);
+  virtual void getCMYK(GfxColor *color, GfxCMYK *cmyk);
+
+  virtual int getNComps() { return 0; }
+
+  // Pattern-specific access.
+  GfxColorSpace *getUnder() { return under; }
+
+private:
+
+  GfxColorSpace *under;                // underlying color space (for uncolored
+                               //   patterns)
+};
+
+//------------------------------------------------------------------------
+// GfxPattern
+//------------------------------------------------------------------------
+
+class GfxPattern {
+public:
+
+  GfxPattern(int typeA);
+  virtual ~GfxPattern();
+
+  static GfxPattern *parse(Object *obj);
+
+  virtual GfxPattern *copy() = 0;
+
+  int getType() { return type; }
+
+private:
+
+  int type;
+};
+
+//------------------------------------------------------------------------
+// GfxTilingPattern
+//------------------------------------------------------------------------
+
+class GfxTilingPattern: public GfxPattern {
+public:
+
+  static GfxTilingPattern *parse(Object *patObj);
+  virtual ~GfxTilingPattern();
+
+  virtual GfxPattern *copy();
+
+  int getPaintType() { return paintType; }
+  int getTilingType() { return tilingType; }
+  double *getBBox() { return bbox; }
+  double getXStep() { return xStep; }
+  double getYStep() { return yStep; }
+  Dict *getResDict()
+    { return resDict.isDict() ? resDict.getDict() : (Dict *)NULL; }
+  double *getMatrix() { return matrix; }
+  Object *getContentStream() { return &contentStream; }
+
+private:
+
+  GfxTilingPattern(int paintTypeA, int tilingTypeA,
+                  double *bboxA, double xStepA, double yStepA,
+                  Object *resDictA, double *matrixA,
+                  Object *contentStreamA);
+
+  int paintType;
+  int tilingType;
+  double bbox[4];
+  double xStep, yStep;
+  Object resDict;
+  double matrix[6];
+  Object contentStream;
+};
+
+//------------------------------------------------------------------------
+// GfxShadingPattern
+//------------------------------------------------------------------------
+
+class GfxShadingPattern: public GfxPattern {
+public:
+
+  static GfxShadingPattern *parse(Object *patObj);
+  virtual ~GfxShadingPattern();
+
+  virtual GfxPattern *copy();
+
+  GfxShading *getShading() { return shading; }
+  double *getMatrix() { return matrix; }
+
+private:
+
+  GfxShadingPattern(GfxShading *shadingA, double *matrixA);
+
+  GfxShading *shading;
+  double matrix[6];
+};
+
+//------------------------------------------------------------------------
+// GfxShading
+//------------------------------------------------------------------------
+
+class GfxShading {
+public:
+
+  GfxShading(int typeA);
+  GfxShading(GfxShading *shading);
+  virtual ~GfxShading();
+
+  static GfxShading *parse(Object *obj);
+
+  virtual GfxShading *copy() = 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:
+
+  GBool init(Dict *dict);
+
+  int type;
+  GfxColorSpace *colorSpace;
+  GfxColor background;
+  GBool hasBackground;
+  double xMin, yMin, xMax, yMax;
+  GBool hasBBox;
+};
+
+//------------------------------------------------------------------------
+// GfxFunctionShading
+//------------------------------------------------------------------------
+
+class GfxFunctionShading: public GfxShading {
+public:
+
+  GfxFunctionShading(double x0A, double y0A,
+                    double x1A, double y1A,
+                    double *matrixA,
+                    Function **funcsA, int nFuncsA);
+  GfxFunctionShading(GfxFunctionShading *shading);
+  virtual ~GfxFunctionShading();
+
+  static GfxFunctionShading *parse(Dict *dict);
+
+  virtual GfxShading *copy();
+
+  void getDomain(double *x0A, double *y0A, double *x1A, double *y1A)
+    { *x0A = x0; *y0A = y0; *x1A = x1; *y1A = y1; }
+  double *getMatrix() { return matrix; }
+  int getNFuncs() { return nFuncs; }
+  Function *getFunc(int i) { return funcs[i]; }
+  void getColor(double x, double y, GfxColor *color);
+
+private:
+
+  double x0, y0, x1, y1;
+  double matrix[6];
+  Function *funcs[gfxColorMaxComps];
+  int nFuncs;
+};
+
+//------------------------------------------------------------------------
+// GfxAxialShading
+//------------------------------------------------------------------------
+
+class GfxAxialShading: public GfxShading {
+public:
+
+  GfxAxialShading(double x0A, double y0A,
+                 double x1A, double y1A,
+                 double t0A, double t1A,
+                 Function **funcsA, int nFuncsA,
+                 GBool extend0A, GBool extend1A);
+  GfxAxialShading(GfxAxialShading *shading);
+  virtual ~GfxAxialShading();
+
+  static GfxAxialShading *parse(Dict *dict);
+
+  virtual GfxShading *copy();
+
+  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; }
+  GBool getExtend0() { return extend0; }
+  GBool getExtend1() { return extend1; }
+  int getNFuncs() { return nFuncs; }
+  Function *getFunc(int i) { return funcs[i]; }
+  void getColor(double t, GfxColor *color);
+
+private:
+
+  double x0, y0, x1, y1;
+  double t0, t1;
+  Function *funcs[gfxColorMaxComps];
+  int nFuncs;
+  GBool extend0, extend1;
+};
+
+//------------------------------------------------------------------------
+// GfxRadialShading
+//------------------------------------------------------------------------
+
+class GfxRadialShading: public GfxShading {
+public:
+
+  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);
+  GfxRadialShading(GfxRadialShading *shading);
+  virtual ~GfxRadialShading();
+
+  static GfxRadialShading *parse(Dict *dict);
+
+  virtual GfxShading *copy();
+
+  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; }
+  GBool getExtend0() { return extend0; }
+  GBool getExtend1() { return extend1; }
+  int getNFuncs() { return nFuncs; }
+  Function *getFunc(int i) { return funcs[i]; }
+  void getColor(double t, GfxColor *color);
+
+private:
+
+  double x0, y0, r0, x1, y1, r1;
+  double t0, t1;
+  Function *funcs[gfxColorMaxComps];
+  int nFuncs;
+  GBool extend0, extend1;
+};
+
+//------------------------------------------------------------------------
+// GfxGouraudTriangleShading
+//------------------------------------------------------------------------
+
+struct GfxGouraudVertex {
+  double x, y;
+  GfxColor color;
+};
+
+class GfxGouraudTriangleShading: public GfxShading {
+public:
+
+  GfxGouraudTriangleShading(int typeA,
+                           GfxGouraudVertex *verticesA, int nVerticesA,
+                           int (*trianglesA)[3], int nTrianglesA,
+                           Function **funcsA, int nFuncsA);
+  GfxGouraudTriangleShading(GfxGouraudTriangleShading *shading);
+  virtual ~GfxGouraudTriangleShading();
+
+  static GfxGouraudTriangleShading *parse(int typeA, Dict *dict, Stream *str);
+
+  virtual GfxShading *copy();
+
+  int getNTriangles() { return nTriangles; }
+  void getTriangle(int i, double *x0, double *y0, GfxColor *color0,
+                  double *x1, double *y1, GfxColor *color1,
+                  double *x2, double *y2, GfxColor *color2);
+
+private:
+
+  GfxGouraudVertex *vertices;
+  int nVertices;
+  int (*triangles)[3];
+  int nTriangles;
+  Function *funcs[gfxColorMaxComps];
+  int nFuncs;
+};
+
+//------------------------------------------------------------------------
+// GfxPatchMeshShading
+//------------------------------------------------------------------------
+
+struct GfxPatch {
+  double x[4][4];
+  double y[4][4];
+  GfxColor color[2][2];
+};
+
+class GfxPatchMeshShading: public GfxShading {
+public:
+
+  GfxPatchMeshShading(int typeA, GfxPatch *patchesA, int nPatchesA,
+                     Function **funcsA, int nFuncsA);
+  GfxPatchMeshShading(GfxPatchMeshShading *shading);
+  virtual ~GfxPatchMeshShading();
+
+  static GfxPatchMeshShading *parse(int typeA, Dict *dict, Stream *str);
+
+  virtual GfxShading *copy();
+
+  int getNPatches() { return nPatches; }
+  GfxPatch *getPatch(int i) { return &patches[i]; }
+
+private:
+
+  GfxPatch *patches;
+  int nPatches;
+  Function *funcs[gfxColorMaxComps];
+  int nFuncs;
+};
+
+//------------------------------------------------------------------------
+// GfxImageColorMap
+//------------------------------------------------------------------------
+
+class GfxImageColorMap {
+public:
+
+  // Constructor.
+  GfxImageColorMap(int bitsA, Object *decode, GfxColorSpace *colorSpaceA);
+
+  // Destructor.
+  ~GfxImageColorMap();
+
+  // Return a copy of this color map.
+  GfxImageColorMap *copy() { return new GfxImageColorMap(this); }
+
+  // Is color map valid?
+  GBool isOk() { return ok; }
+
+  // Get the color space.
+  GfxColorSpace *getColorSpace() { return colorSpace; }
+
+  // Get stream decoding info.
+  int getNumPixelComps() { return nComps; }
+  int getBits() { return bits; }
+
+  // Get decode table.
+  double getDecodeLow(int i) { return decodeLow[i]; }
+  double getDecodeHigh(int i) { return decodeLow[i] + decodeRange[i]; }
+
+  // Convert an image pixel to a color.
+  void getGray(Guchar *x, GfxGray *gray);
+  void getRGB(Guchar *x, GfxRGB *rgb);
+  void getCMYK(Guchar *x, GfxCMYK *cmyk);
+  void getColor(Guchar *x, GfxColor *color);
+
+private:
+
+  GfxImageColorMap(GfxImageColorMap *colorMap);
+
+  GfxColorSpace *colorSpace;   // the image color space
+  int bits;                    // bits per component
+  int nComps;                  // number of components in a pixel
+  GfxColorSpace *colorSpace2;  // secondary color space
+  int nComps2;                 // number of components in colorSpace2
+  GfxColorComp *               // lookup table
+    lookup[gfxColorMaxComps];
+  double                       // minimum values for each component
+    decodeLow[gfxColorMaxComps];
+  double                       // max - min value for each component
+    decodeRange[gfxColorMaxComps];
+  GBool ok;
+};
+
+//------------------------------------------------------------------------
+// GfxSubpath and GfxPath
+//------------------------------------------------------------------------
+
+class GfxSubpath {
+public:
+
+  // Constructor.
+  GfxSubpath(double x1, double y1);
+
+  // Destructor.
+  ~GfxSubpath();
+
+  // Copy.
+  GfxSubpath *copy() { return new GfxSubpath(this); }
+
+  // Get points.
+  int getNumPoints() { return n; }
+  double getX(int i) { return x[i]; }
+  double getY(int i) { return y[i]; }
+  GBool getCurve(int i) { return curve[i]; }
+
+  // Get last point.
+  double getLastX() { return x[n-1]; }
+  double getLastY() { return y[n-1]; }
+
+  // Add a line segment.
+  void lineTo(double x1, double y1);
+
+  // Add a Bezier curve.
+  void curveTo(double x1, double y1, double x2, double y2,
+              double x3, double y3);
+
+  // Close the subpath.
+  void close();
+  GBool isClosed() { return closed; }
+
+  // Add (<dx>, <dy>) to each point in the subpath.
+  void offset(double dx, double dy);
+
+private:
+
+  double *x, *y;               // points
+  GBool *curve;                        // curve[i] => point i is a control point
+                               //   for a Bezier curve
+  int n;                       // number of points
+  int size;                    // size of x/y arrays
+  GBool closed;                        // set if path is closed
+
+  GfxSubpath(GfxSubpath *subpath);
+};
+
+class GfxPath {
+public:
+
+  // Constructor.
+  GfxPath();
+
+  // Destructor.
+  ~GfxPath();
+
+  // Copy.
+  GfxPath *copy()
+    { return new GfxPath(justMoved, firstX, firstY, subpaths, n, size); }
+
+  // Is there a current point?
+  GBool isCurPt() { return n > 0 || justMoved; }
+
+  // Is the path non-empty, i.e., is there at least one segment?
+  GBool isPath() { return n > 0; }
+
+  // Get subpaths.
+  int getNumSubpaths() { return n; }
+  GfxSubpath *getSubpath(int i) { return subpaths[i]; }
+
+  // Get last point on last subpath.
+  double getLastX() { return subpaths[n-1]->getLastX(); }
+  double getLastY() { return subpaths[n-1]->getLastY(); }
+
+  // Move the current point.
+  void moveTo(double x, double y);
+
+  // Add a segment to the last subpath.
+  void lineTo(double x, double y);
+
+  // Add a Bezier curve to the last subpath
+  void curveTo(double x1, double y1, double x2, double y2,
+              double x3, double y3);
+
+  // Close the last subpath.
+  void close();
+
+  // Append <path> to <this>.
+  void append(GfxPath *path);
+
+  // Add (<dx>, <dy>) to each point in the path.
+  void offset(double dx, double dy);
+
+private:
+
+  GBool justMoved;             // set if a new subpath was just started
+  double firstX, firstY;       // first point in new subpath
+  GfxSubpath **subpaths;       // subpaths
+  int n;                       // number of subpaths
+  int size;                    // size of subpaths array
+
+  GfxPath(GBool justMoved1, double firstX1, double firstY1,
+         GfxSubpath **subpaths1, int n1, int size1);
+};
+
+//------------------------------------------------------------------------
+// GfxState
+//------------------------------------------------------------------------
+
+class GfxState {
+public:
+
+  // Construct a default GfxState, for a device with resolution <hDPI>
+  // x <vDPI>, page box <pageBox>, page rotation <rotateA>, and
+  // coordinate system specified by <upsideDown>.
+  GfxState(double hDPI, double vDPI, PDFRectangle *pageBox,
+          int rotateA, GBool upsideDown);
+
+  // Destructor.
+  ~GfxState();
+
+  // Copy.
+  GfxState *copy() { return new GfxState(this); }
+
+  // Accessors.
+  double *getCTM() { return ctm; }
+  double getX1() { return px1; }
+  double getY1() { return py1; }
+  double getX2() { return px2; }
+  double getY2() { return py2; }
+  double getPageWidth() { return pageWidth; }
+  double getPageHeight() { return pageHeight; }
+  int getRotate() { return rotate; }
+  GfxColor *getFillColor() { return &fillColor; }
+  GfxColor *getStrokeColor() { return &strokeColor; }
+  void getFillGray(GfxGray *gray)
+    { fillColorSpace->getGray(&fillColor, gray); }
+  void getStrokeGray(GfxGray *gray)
+    { strokeColorSpace->getGray(&strokeColor, gray); }
+  void getFillRGB(GfxRGB *rgb)
+    { fillColorSpace->getRGB(&fillColor, rgb); }
+  void getStrokeRGB(GfxRGB *rgb)
+    { strokeColorSpace->getRGB(&strokeColor, rgb); }
+  void getFillCMYK(GfxCMYK *cmyk)
+    { fillColorSpace->getCMYK(&fillColor, cmyk); }
+  void getStrokeCMYK(GfxCMYK *cmyk)
+    { strokeColorSpace->getCMYK(&strokeColor, cmyk); }
+  GfxColorSpace *getFillColorSpace() { return fillColorSpace; }
+  GfxColorSpace *getStrokeColorSpace() { return strokeColorSpace; }
+  GfxPattern *getFillPattern() { return fillPattern; }
+  GfxPattern *getStrokePattern() { return strokePattern; }
+  GfxBlendMode getBlendMode() { return blendMode; }
+  double getFillOpacity() { return fillOpacity; }
+  double getStrokeOpacity() { return strokeOpacity; }
+  GBool getFillOverprint() { return fillOverprint; }
+  GBool getStrokeOverprint() { return strokeOverprint; }
+  double getLineWidth() { return lineWidth; }
+  void getLineDash(double **dash, int *length, double *start)
+    { *dash = lineDash; *length = lineDashLength; *start = lineDashStart; }
+  int getFlatness() { return flatness; }
+  int getLineJoin() { return lineJoin; }
+  int getLineCap() { return lineCap; }
+  double getMiterLimit() { return miterLimit; }
+  GfxFont *getFont() { return font; }
+  double getFontSize() { return fontSize; }
+  double *getTextMat() { return textMat; }
+  double getCharSpace() { return charSpace; }
+  double getWordSpace() { return wordSpace; }
+  double getHorizScaling() { return horizScaling; }
+  double getLeading() { return leading; }
+  double getRise() { return rise; }
+  int getRender() { return render; }
+  GfxPath *getPath() { return path; }
+  void setPath(GfxPath *pathA);
+  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; }
+
+  // Is there a current point/path?
+  GBool isCurPt() { return path->isCurPt(); }
+  GBool isPath() { return path->isPath(); }
+
+  // Transforms.
+  void transform(double x1, double y1, double *x2, double *y2)
+    { *x2 = ctm[0] * x1 + ctm[2] * y1 + ctm[4];
+      *y2 = ctm[1] * x1 + ctm[3] * y1 + ctm[5]; }
+  void transformDelta(double x1, double y1, double *x2, double *y2)
+    { *x2 = ctm[0] * x1 + ctm[2] * y1;
+      *y2 = ctm[1] * x1 + ctm[3] * y1; }
+  void textTransform(double x1, double y1, double *x2, double *y2)
+    { *x2 = textMat[0] * x1 + textMat[2] * y1 + textMat[4];
+      *y2 = textMat[1] * x1 + textMat[3] * y1 + textMat[5]; }
+  void textTransformDelta(double x1, double y1, double *x2, double *y2)
+    { *x2 = textMat[0] * x1 + textMat[2] * y1;
+      *y2 = textMat[1] * x1 + textMat[3] * y1; }
+  double transformWidth(double w);
+  double getTransformedLineWidth()
+    { return transformWidth(lineWidth); }
+  double getTransformedFontSize();
+  void getFontTransMat(double *m11, double *m12, double *m21, double *m22);
+
+  // Change state parameters.
+  void setCTM(double a, double b, double c,
+             double d, double e, double f);
+  void concatCTM(double a, double b, double c,
+                double d, double e, double f);
+  void setFillColorSpace(GfxColorSpace *colorSpace);
+  void setStrokeColorSpace(GfxColorSpace *colorSpace);
+  void setFillColor(GfxColor *color) { fillColor = *color; }
+  void setStrokeColor(GfxColor *color) { strokeColor = *color; }
+  void setFillPattern(GfxPattern *pattern);
+  void setStrokePattern(GfxPattern *pattern);
+  void setBlendMode(GfxBlendMode mode) { blendMode = mode; }
+  void setFillOpacity(double opac) { fillOpacity = opac; }
+  void setStrokeOpacity(double opac) { strokeOpacity = opac; }
+  void setFillOverprint(GBool op) { fillOverprint = op; }
+  void setStrokeOverprint(GBool op) { strokeOverprint = op; }
+  void setLineWidth(double width) { lineWidth = width; }
+  void setLineDash(double *dash, int length, double start);
+  void setFlatness(int flatness1) { flatness = flatness1; }
+  void setLineJoin(int lineJoin1) { lineJoin = lineJoin1; }
+  void setLineCap(int lineCap1) { lineCap = lineCap1; }
+  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;
+      textMat[3] = d; textMat[4] = e; textMat[5] = f; }
+  void setCharSpace(double space)
+    { charSpace = space; }
+  void setWordSpace(double space)
+    { wordSpace = space; }
+  void setHorizScaling(double scale)
+    { horizScaling = 0.01 * scale; }
+  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)
+    { path->moveTo(curX = x, curY = y); }
+  void lineTo(double x, double y)
+    { path->lineTo(curX = x, curY = y); }
+  void curveTo(double x1, double y1, double x2, double y2,
+              double x3, double y3)
+    { path->curveTo(x1, y1, x2, y2, curX = x3, curY = y3); }
+  void closePath()
+    { path->close(); curX = path->getLastX(); curY = path->getLastY(); }
+  void clearPath();
+
+  // Update clip region.
+  void clip();
+
+  // Text position.
+  void textSetPos(double tx, double ty) { lineX = tx; lineY = ty; }
+  void textMoveTo(double tx, double ty)
+    { lineX = tx; lineY = ty; textTransform(tx, ty, &curX, &curY); }
+  void textShift(double tx, double ty);
+  void shift(double dx, double dy);
+
+  // Push/pop GfxState on/off stack.
+  GfxState *save();
+  GfxState *restore();
+  GBool hasSaves() { return saved != NULL; }
+
+  // Misc
+  GBool parseBlendMode(Object *obj, GfxBlendMode *mode);
+
+private:
+
+  double ctm[6];               // coord transform matrix
+  double px1, py1, px2, py2;   // page corners (user coords)
+  double pageWidth, pageHeight;        // page size (pixels)
+  int rotate;                  // page rotation angle
+
+  GfxColorSpace *fillColorSpace;   // fill color space
+  GfxColorSpace *strokeColorSpace; // stroke color space
+  GfxColor fillColor;          // fill color
+  GfxColor strokeColor;                // stroke color
+  GfxPattern *fillPattern;     // fill pattern
+  GfxPattern *strokePattern;   // stroke pattern
+  GfxBlendMode blendMode;      // transparency blend mode
+  double fillOpacity;          // fill opacity
+  double strokeOpacity;                // stroke opacity
+  GBool fillOverprint;         // fill overprint
+  GBool strokeOverprint;       // stroke overprint
+
+  double lineWidth;            // line width
+  double *lineDash;            // line dash
+  int lineDashLength;
+  double lineDashStart;
+  int flatness;                        // curve flatness
+  int lineJoin;                        // line join style
+  int lineCap;                 // line cap style
+  double miterLimit;           // line miter limit
+
+  GfxFont *font;               // font
+  double fontSize;             // font size
+  double textMat[6];           // text matrix
+  double charSpace;            // character spacing
+  double wordSpace;            // word spacing
+  double horizScaling;         // horizontal scaling
+  double leading;              // text leading
+  double rise;                 // text rise
+  int render;                  // text rendering mode
+
+  GfxPath *path;               // array of path elements
+  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);
+};
+
+#endif
diff --git a/lib/xpdf/GlobalParams.cc b/lib/xpdf/GlobalParams.cc
new file mode 100644 (file)
index 0000000..8981ba9
--- /dev/null
@@ -0,0 +1,2057 @@
+//========================================================================
+//
+// GlobalParams.cc
+//
+// Copyright 2001-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <string.h>
+#include <stdio.h>
+#include <ctype.h>
+#ifdef ENABLE_PLUGINS
+#  ifndef WIN32
+#    include <dlfcn.h>
+#  endif
+#endif
+#ifdef WIN32
+#  include <shlobj.h>
+#endif
+#if HAVE_PAPER_H
+#include <paper.h>
+#endif
+#include "gmem.h"
+#include "GString.h"
+#include "GList.h"
+#include "GHash.h"
+#include "gfile.h"
+#include "Error.h"
+#include "NameToCharCode.h"
+#include "CharCodeToUnicode.h"
+#include "UnicodeMap.h"
+#include "CMap.h"
+#include "BuiltinFontTables.h"
+#include "FontEncodingTables.h"
+#ifdef ENABLE_PLUGINS
+#  include "XpdfPluginAPI.h"
+#endif
+#include "GlobalParams.h"
+
+#if MULTITHREADED
+#  define lockGlobalParams            gLockMutex(&mutex)
+#  define lockUnicodeMapCache         gLockMutex(&unicodeMapCacheMutex)
+#  define lockCMapCache               gLockMutex(&cMapCacheMutex)
+#  define unlockGlobalParams          gUnlockMutex(&mutex)
+#  define unlockUnicodeMapCache       gUnlockMutex(&unicodeMapCacheMutex)
+#  define unlockCMapCache             gUnlockMutex(&cMapCacheMutex)
+#else
+#  define lockGlobalParams
+#  define lockUnicodeMapCache
+#  define lockCMapCache
+#  define unlockGlobalParams
+#  define unlockUnicodeMapCache
+#  define unlockCMapCache
+#endif
+
+#include "NameToUnicodeTable.h"
+#include "UnicodeMapTables.h"
+#include "UTF8.h"
+
+#ifdef ENABLE_PLUGINS
+#  ifdef WIN32
+extern XpdfPluginVecTable xpdfPluginVecTable;
+#  endif
+#endif
+
+//------------------------------------------------------------------------
+
+#define cidToUnicodeCacheSize     4
+#define unicodeToUnicodeCacheSize 4
+
+//------------------------------------------------------------------------
+
+static struct {
+  char *name;
+  char *t1FileName;
+  char *ttFileName;
+} displayFontTab[] = {
+  {"Courier",               "n022003l.pfb", "cour.ttf"},
+  {"Courier-Bold",          "n022004l.pfb", "courbd.ttf"},
+  {"Courier-BoldOblique",   "n022024l.pfb", "courbi.ttf"},
+  {"Courier-Oblique",       "n022023l.pfb", "couri.ttf"},
+  {"Helvetica",             "n019003l.pfb", "arial.ttf"},
+  {"Helvetica-Bold",        "n019004l.pfb", "arialbd.ttf"},
+  {"Helvetica-BoldOblique", "n019024l.pfb", "arialbi.ttf"},
+  {"Helvetica-Oblique",     "n019023l.pfb", "ariali.ttf"},
+  {"Symbol",                "s050000l.pfb", NULL},
+  {"Times-Bold",            "n021004l.pfb", "timesbd.ttf"},
+  {"Times-BoldItalic",      "n021024l.pfb", "timesbi.ttf"},
+  {"Times-Italic",          "n021023l.pfb", "timesi.ttf"},
+  {"Times-Roman",           "n021003l.pfb", "times.ttf"},
+  {"ZapfDingbats",          "d050000l.pfb", NULL},
+  {NULL}
+};
+
+#ifdef WIN32
+static char *displayFontDirs[] = {
+  "c:/windows/fonts",
+  "c:/winnt/fonts",
+  NULL
+};
+#else
+static char *displayFontDirs[] = {
+  "/usr/share/ghostscript/fonts",
+  "/usr/local/share/ghostscript/fonts",
+  "/usr/share/fonts/default/Type1",
+  "/usr/share/fonts/default/ghostscript",
+  "/usr/share/fonts/type1/gsfonts",
+  NULL
+};
+#endif
+
+//------------------------------------------------------------------------
+
+GlobalParams *globalParams = NULL;
+
+//------------------------------------------------------------------------
+// DisplayFontParam
+//------------------------------------------------------------------------
+
+DisplayFontParam::DisplayFontParam(GString *nameA,
+                                  DisplayFontParamKind kindA) {
+  name = nameA;
+  kind = kindA;
+  switch (kind) {
+  case displayFontT1:
+    t1.fileName = NULL;
+    break;
+  case displayFontTT:
+    tt.fileName = NULL;
+    break;
+  }
+}
+
+DisplayFontParam::~DisplayFontParam() {
+  delete name;
+  switch (kind) {
+  case displayFontT1:
+    if (t1.fileName) {
+      delete t1.fileName;
+    }
+    break;
+  case displayFontTT:
+    if (tt.fileName) {
+      delete tt.fileName;
+    }
+    break;
+  }
+}
+
+//------------------------------------------------------------------------
+// PSFontParam
+//------------------------------------------------------------------------
+
+PSFontParam::PSFontParam(GString *pdfFontNameA, int wModeA,
+                        GString *psFontNameA, GString *encodingA) {
+  pdfFontName = pdfFontNameA;
+  wMode = wModeA;
+  psFontName = psFontNameA;
+  encoding = encodingA;
+}
+
+PSFontParam::~PSFontParam() {
+  delete pdfFontName;
+  delete psFontName;
+  if (encoding) {
+    delete encoding;
+  }
+}
+
+#ifdef ENABLE_PLUGINS
+//------------------------------------------------------------------------
+// Plugin
+//------------------------------------------------------------------------
+
+class Plugin {
+public:
+
+  static Plugin *load(char *type, char *name);
+  ~Plugin();
+
+private:
+
+#ifdef WIN32
+  Plugin(HMODULE libA);
+  HMODULE lib;
+#else
+  Plugin(void *dlA);
+  void *dl;
+#endif
+};
+
+Plugin *Plugin::load(char *type, char *name) {
+  GString *path;
+  Plugin *plugin;
+  XpdfPluginVecTable *vt;
+  XpdfBool (*xpdfInitPlugin)(void);
+#ifdef WIN32
+  HMODULE libA;
+#else
+  void *dlA;
+#endif
+
+  path = globalParams->getBaseDir();
+  appendToPath(path, "plugins");
+  appendToPath(path, type);
+  appendToPath(path, name);
+
+#ifdef WIN32
+  path->append(".dll");
+  if (!(libA = LoadLibrary(path->getCString()))) {
+    error(-1, "Failed to load plugin '%s'",
+         path->getCString());
+    goto err1;
+  }
+  if (!(vt = (XpdfPluginVecTable *)
+                GetProcAddress(libA, "xpdfPluginVecTable"))) {
+    error(-1, "Failed to find xpdfPluginVecTable in plugin '%s'",
+         path->getCString());
+    goto err2;
+  }
+#else
+  //~ need to deal with other extensions here
+  path->append(".so");
+  if (!(dlA = dlopen(path->getCString(), RTLD_NOW))) {
+    error(-1, "Failed to load plugin '%s': %s",
+         path->getCString(), dlerror());
+    goto err1;
+  }
+  if (!(vt = (XpdfPluginVecTable *)dlsym(dlA, "xpdfPluginVecTable"))) {
+    error(-1, "Failed to find xpdfPluginVecTable in plugin '%s'",
+         path->getCString());
+    goto err2;
+  }
+#endif
+
+  if (vt->version != xpdfPluginVecTable.version) {
+    error(-1, "Plugin '%s' is wrong version", path->getCString());
+    goto err2;
+  }
+  memcpy(vt, &xpdfPluginVecTable, sizeof(xpdfPluginVecTable));
+
+#ifdef WIN32
+  if (!(xpdfInitPlugin = (XpdfBool (*)(void))
+                            GetProcAddress(libA, "xpdfInitPlugin"))) {
+    error(-1, "Failed to find xpdfInitPlugin in plugin '%s'",
+         path->getCString());
+    goto err2;
+  }
+#else
+  if (!(xpdfInitPlugin = (XpdfBool (*)(void))dlsym(dlA, "xpdfInitPlugin"))) {
+    error(-1, "Failed to find xpdfInitPlugin in plugin '%s'",
+         path->getCString());
+    goto err2;
+  }
+#endif
+
+  if (!(*xpdfInitPlugin)()) {
+    error(-1, "Initialization of plugin '%s' failed",
+         path->getCString());
+    goto err2;
+  }
+
+#ifdef WIN32
+  plugin = new Plugin(libA);
+#else
+  plugin = new Plugin(dlA);
+#endif
+
+  delete path;
+  return plugin;
+
+ err2:
+#ifdef WIN32
+  FreeLibrary(libA);
+#else
+  dlclose(dlA);
+#endif
+ err1:
+  delete path;
+  return NULL;
+}
+
+#ifdef WIN32
+Plugin::Plugin(HMODULE libA) {
+  lib = libA;
+}
+#else
+Plugin::Plugin(void *dlA) {
+  dl = dlA;
+}
+#endif
+
+Plugin::~Plugin() {
+  void (*xpdfFreePlugin)(void);
+
+#ifdef WIN32
+  if ((xpdfFreePlugin = (void (*)(void))
+                            GetProcAddress(lib, "xpdfFreePlugin"))) {
+    (*xpdfFreePlugin)();
+  }
+  FreeLibrary(lib);
+#else
+  if ((xpdfFreePlugin = (void (*)(void))dlsym(dl, "xpdfFreePlugin"))) {
+    (*xpdfFreePlugin)();
+  }
+  dlclose(dl);
+#endif
+}
+
+#endif // ENABLE_PLUGINS
+
+//------------------------------------------------------------------------
+// parsing
+//------------------------------------------------------------------------
+
+GlobalParams::GlobalParams(char *cfgFileName) {
+  UnicodeMap *map;
+  GString *fileName;
+  FILE *f;
+  int i;
+
+#if MULTITHREADED
+  gInitMutex(&mutex);
+  gInitMutex(&unicodeMapCacheMutex);
+  gInitMutex(&cMapCacheMutex);
+#endif
+
+  initBuiltinFontTables();
+
+  // scan the encoding in reverse because we want the lowest-numbered
+  // index for each char name ('space' is encoded twice)
+  macRomanReverseMap = new NameToCharCode();
+  for (i = 255; i >= 0; --i) {
+    if (macRomanEncoding[i]) {
+      macRomanReverseMap->add(macRomanEncoding[i], (CharCode)i);
+    }
+  }
+
+#ifdef WIN32
+  // baseDir will be set by a call to setBaseDir
+  baseDir = new GString();
+#else
+  baseDir = appendToPath(getHomeDir(), ".xpdf");
+#endif
+  nameToUnicode = new NameToCharCode();
+  cidToUnicodes = new GHash(gTrue);
+  unicodeToUnicodes = new GHash(gTrue);
+  residentUnicodeMaps = new GHash();
+  unicodeMaps = new GHash(gTrue);
+  cMapDirs = new GHash(gTrue);
+  toUnicodeDirs = new GList();
+  displayFonts = new GHash();
+  displayCIDFonts = new GHash();
+  displayNamedCIDFonts = new GHash();
+#if HAVE_PAPER_H
+  char *paperName;
+  const struct paper *paperType;
+  paperinit();
+  if ((paperName = systempapername())) {
+    paperType = paperinfo(paperName);
+    psPaperWidth = (int)paperpswidth(paperType);
+    psPaperHeight = (int)paperpsheight(paperType);
+  } else {
+    error(-1, "No paper information available - using defaults");
+    psPaperWidth = defPaperWidth;
+    psPaperHeight = defPaperHeight;
+  }
+  paperdone();
+#else
+  psPaperWidth = defPaperWidth;
+  psPaperHeight = defPaperHeight;
+#endif
+  psImageableLLX = psImageableLLY = 0;
+  psImageableURX = psPaperWidth;
+  psImageableURY = psPaperHeight;
+  psCrop = gTrue;
+  psExpandSmaller = gFalse;
+  psShrinkLarger = gTrue;
+  psCenter = gTrue;
+  psDuplex = gFalse;
+  psLevel = psLevel2;
+  psFile = NULL;
+  psFonts = new GHash();
+  psNamedFonts16 = new GList();
+  psFonts16 = new GList();
+  psEmbedType1 = gTrue;
+  psEmbedTrueType = gTrue;
+  psEmbedCIDPostScript = gTrue;
+  psEmbedCIDTrueType = gTrue;
+  psOPI = gFalse;
+  psASCIIHex = gFalse;
+  textEncoding = new GString("Latin1");
+#if defined(WIN32)
+  textEOL = eolDOS;
+#elif defined(MACOS)
+  textEOL = eolMac;
+#else
+  textEOL = eolUnix;
+#endif
+  textPageBreaks = gTrue;
+  textKeepTinyChars = gFalse;
+  fontDirs = new GList();
+  initialZoom = new GString("125");
+  continuousView = gFalse;
+  enableT1lib = gTrue;
+  enableFreeType = gTrue;
+  antialias = gTrue;
+  urlCommand = NULL;
+  movieCommand = NULL;
+  mapNumericCharNames = gTrue;
+  printCommands = gFalse;
+  errQuiet = gFalse;
+
+  cidToUnicodeCache = new CharCodeToUnicodeCache(cidToUnicodeCacheSize);
+  unicodeToUnicodeCache =
+      new CharCodeToUnicodeCache(unicodeToUnicodeCacheSize);
+  unicodeMapCache = new UnicodeMapCache();
+  cMapCache = new CMapCache();
+
+#ifdef ENABLE_PLUGINS
+  plugins = new GList();
+  securityHandlers = new GList();
+#endif
+
+  // set up the initial nameToUnicode table
+  for (i = 0; nameToUnicodeTab[i].name; ++i) {
+    nameToUnicode->add(nameToUnicodeTab[i].name, nameToUnicodeTab[i].u);
+  }
+
+  // set up the residentUnicodeMaps table
+  map = new UnicodeMap("Latin1", gFalse,
+                      latin1UnicodeMapRanges, latin1UnicodeMapLen);
+  residentUnicodeMaps->add(map->getEncodingName(), map);
+  map = new UnicodeMap("ASCII7", gFalse,
+                      ascii7UnicodeMapRanges, ascii7UnicodeMapLen);
+  residentUnicodeMaps->add(map->getEncodingName(), map);
+  map = new UnicodeMap("Symbol", gFalse,
+                      symbolUnicodeMapRanges, symbolUnicodeMapLen);
+  residentUnicodeMaps->add(map->getEncodingName(), map);
+  map = new UnicodeMap("ZapfDingbats", gFalse, zapfDingbatsUnicodeMapRanges,
+                      zapfDingbatsUnicodeMapLen);
+  residentUnicodeMaps->add(map->getEncodingName(), map);
+  map = new UnicodeMap("UTF-8", gTrue, &mapUTF8);
+  residentUnicodeMaps->add(map->getEncodingName(), map);
+  map = new UnicodeMap("UCS-2", gTrue, &mapUCS2);
+  residentUnicodeMaps->add(map->getEncodingName(), map);
+
+  // look for a user config file, then a system-wide config file
+  f = NULL;
+  fileName = NULL;
+  if (cfgFileName && cfgFileName[0]) {
+    fileName = new GString(cfgFileName);
+    if (!(f = fopen(fileName->getCString(), "r"))) {
+      delete fileName;
+    }
+  }
+  if (!f) {
+    fileName = appendToPath(getHomeDir(), xpdfUserConfigFile);
+    if (!(f = fopen(fileName->getCString(), "r"))) {
+      delete fileName;
+    }
+  }
+  if (!f) {
+#if defined(WIN32) && !defined(__CYGWIN32__)
+    char buf[512];
+    i = GetModuleFileName(NULL, buf, sizeof(buf));
+    if (i <= 0 || i >= sizeof(buf)) {
+      // error or path too long for buffer - just use the current dir
+      buf[0] = '\0';
+    }
+    fileName = grabPath(buf);
+    appendToPath(fileName, xpdfSysConfigFile);
+#else
+    fileName = new GString(xpdfSysConfigFile);
+#endif
+    if (!(f = fopen(fileName->getCString(), "r"))) {
+      delete fileName;
+    }
+  }
+  if (f) {
+    parseFile(fileName, f);
+    delete fileName;
+    fclose(f);
+  }
+}
+
+void GlobalParams::parseFile(GString *fileName, FILE *f) {
+  int line;
+  GList *tokens;
+  GString *cmd, *incFile;
+  char *p1, *p2;
+  char buf[512];
+  FILE *f2;
+
+  /* extract path */
+  if(fileName) {
+    char* cfgFileName = fileName->getCString();
+    char* pos1 = strrchr(cfgFileName, '/');
+    char* pos2 = strrchr(cfgFileName, '\\');
+    char* p = pos1>pos2?pos1:pos2;
+    int pos = p ? p-cfgFileName : -1;
+    GString*path = new GString(new GString(cfgFileName), 0, (pos < 0 ? strlen(cfgFileName): pos));
+    if(pos1>=0)
+       path->append('/');
+    else if(pos2>=0)
+       path->append('\\');
+    else
+#ifdef WIN32
+       path->append('\\');
+#else
+       path->append('/');
+#endif
+    this->path = path;
+  } else {
+    this->path = new GString();
+  }
+  
+  line = 1;
+  while (getLine(buf, sizeof(buf) - 1, f)) {
+
+    // break the line into tokens
+    tokens = new GList();
+    p1 = buf;
+    while (*p1) {
+      for (; *p1 && isspace(*p1); ++p1) ;
+      if (!*p1) {
+       break;
+      }
+      if (*p1 == '"' || *p1 == '\'') {
+       for (p2 = p1 + 1; *p2 && *p2 != *p1; ++p2) ;
+       ++p1;
+      } else {
+       for (p2 = p1 + 1; *p2 && !isspace(*p2); ++p2) ;
+      }
+      tokens->append(new GString(p1, p2 - p1));
+      p1 = *p2 ? p2 + 1 : p2;
+    }
+
+    if (tokens->getLength() > 0 &&
+       ((GString *)tokens->get(0))->getChar(0) != '#') {
+      cmd = (GString *)tokens->get(0);
+      if (!cmd->cmp("include")) {
+       if (tokens->getLength() == 2) {
+         incFile = (GString *)tokens->get(1);
+         if ((f2 = fopen(incFile->getCString(), "r"))) {
+           parseFile(incFile, f2);
+           fclose(f2);
+         } else {
+           error(-1, "Couldn't find included config file: '%s' (%s:%d)",
+                 incFile->getCString(), fileName->getCString(), line);
+         }
+       } else {
+         error(-1, "Bad 'include' config file command (%s:%d)",
+               fileName->getCString(), line);
+       }
+      } else if (!cmd->cmp("nameToUnicode")) {
+       parseNameToUnicode(tokens, fileName, line);
+      } else if (!cmd->cmp("cidToUnicode")) {
+       parseCIDToUnicode(tokens, fileName, line);
+      } else if (!cmd->cmp("unicodeToUnicode")) {
+       parseUnicodeToUnicode(tokens, fileName, line);
+      } else if (!cmd->cmp("unicodeMap")) {
+       parseUnicodeMap(tokens, fileName, line);
+      } else if (!cmd->cmp("cMapDir")) {
+       parseCMapDir(tokens, fileName, line);
+      } else if (!cmd->cmp("toUnicodeDir")) {
+       parseToUnicodeDir(tokens, fileName, line);
+      } else if (!cmd->cmp("displayFontT1")) {
+       parseDisplayFont(tokens, displayFonts, displayFontT1, fileName, line);
+      } else if (!cmd->cmp("displayFontTT")) {
+       parseDisplayFont(tokens, displayFonts, displayFontTT, fileName, line);
+      } else if (!cmd->cmp("displayNamedCIDFontT1")) {
+       parseDisplayFont(tokens, displayNamedCIDFonts,
+                        displayFontT1, fileName, line);
+      } else if (!cmd->cmp("displayCIDFontT1")) {
+       parseDisplayFont(tokens, displayCIDFonts,
+                        displayFontT1, fileName, line);
+      } else if (!cmd->cmp("displayNamedCIDFontTT")) {
+       parseDisplayFont(tokens, displayNamedCIDFonts,
+                        displayFontTT, fileName, line);
+      } else if (!cmd->cmp("displayCIDFontTT")) {
+       parseDisplayFont(tokens, displayCIDFonts,
+                        displayFontTT, fileName, line);
+      } else if (!cmd->cmp("psFile")) {
+       parsePSFile(tokens, fileName, line);
+      } else if (!cmd->cmp("psFont")) {
+       parsePSFont(tokens, fileName, line);
+      } else if (!cmd->cmp("psNamedFont16")) {
+       parsePSFont16("psNamedFont16", psNamedFonts16,
+                     tokens, fileName, line);
+      } else if (!cmd->cmp("psFont16")) {
+       parsePSFont16("psFont16", psFonts16, tokens, fileName, line);
+      } else if (!cmd->cmp("psPaperSize")) {
+       parsePSPaperSize(tokens, fileName, line);
+      } else if (!cmd->cmp("psImageableArea")) {
+       parsePSImageableArea(tokens, fileName, line);
+      } else if (!cmd->cmp("psCrop")) {
+       parseYesNo("psCrop", &psCrop, tokens, fileName, line);
+      } else if (!cmd->cmp("psExpandSmaller")) {
+       parseYesNo("psExpandSmaller", &psExpandSmaller,
+                  tokens, fileName, line);
+      } else if (!cmd->cmp("psShrinkLarger")) {
+       parseYesNo("psShrinkLarger", &psShrinkLarger, tokens, fileName, line);
+      } else if (!cmd->cmp("psCenter")) {
+       parseYesNo("psCenter", &psCenter, tokens, fileName, line);
+      } else if (!cmd->cmp("psDuplex")) {
+       parseYesNo("psDuplex", &psDuplex, tokens, fileName, line);
+      } else if (!cmd->cmp("psLevel")) {
+       parsePSLevel(tokens, fileName, line);
+      } else if (!cmd->cmp("psEmbedType1Fonts")) {
+       parseYesNo("psEmbedType1", &psEmbedType1, tokens, fileName, line);
+      } else if (!cmd->cmp("psEmbedTrueTypeFonts")) {
+       parseYesNo("psEmbedTrueType", &psEmbedTrueType,
+                  tokens, fileName, line);
+      } else if (!cmd->cmp("psEmbedCIDPostScriptFonts")) {
+       parseYesNo("psEmbedCIDPostScript", &psEmbedCIDPostScript,
+                  tokens, fileName, line);
+      } else if (!cmd->cmp("psEmbedCIDTrueTypeFonts")) {
+       parseYesNo("psEmbedCIDTrueType", &psEmbedCIDTrueType,
+                  tokens, fileName, line);
+      } else if (!cmd->cmp("psOPI")) {
+       parseYesNo("psOPI", &psOPI, tokens, fileName, line);
+      } else if (!cmd->cmp("psASCIIHex")) {
+       parseYesNo("psASCIIHex", &psASCIIHex, tokens, fileName, line);
+      } else if (!cmd->cmp("textEncoding")) {
+       parseTextEncoding(tokens, fileName, line);
+      } else if (!cmd->cmp("textEOL")) {
+       parseTextEOL(tokens, fileName, line);
+      } else if (!cmd->cmp("textPageBreaks")) {
+       parseYesNo("textPageBreaks", &textPageBreaks,
+                  tokens, fileName, line);
+      } else if (!cmd->cmp("textKeepTinyChars")) {
+       parseYesNo("textKeepTinyChars", &textKeepTinyChars,
+                  tokens, fileName, line);
+      } else if (!cmd->cmp("fontDir")) {
+       parseFontDir(tokens, fileName, line);
+      } else if (!cmd->cmp("initialZoom")) {
+       parseInitialZoom(tokens, fileName, line);
+      } else if (!cmd->cmp("continuousView")) {
+       parseYesNo("continuousView", &continuousView, tokens, fileName, line);
+      } else if (!cmd->cmp("enableT1lib")) {
+       parseYesNo("enableT1lib", &enableT1lib, tokens, fileName, line);
+      } else if (!cmd->cmp("enableFreeType")) {
+       parseYesNo("enableFreeType", &enableFreeType, tokens, fileName, line);
+      } else if (!cmd->cmp("antialias")) {
+       parseYesNo("antialias", &antialias, tokens, fileName, line);
+      } else if (!cmd->cmp("urlCommand")) {
+       parseCommand("urlCommand", &urlCommand, tokens, fileName, line);
+      } else if (!cmd->cmp("movieCommand")) {
+       parseCommand("movieCommand", &movieCommand, tokens, fileName, line);
+      } else if (!cmd->cmp("mapNumericCharNames")) {
+       parseYesNo("mapNumericCharNames", &mapNumericCharNames,
+                  tokens, fileName, line);
+      } else if (!cmd->cmp("printCommands")) {
+       parseYesNo("printCommands", &printCommands, tokens, fileName, line);
+      } else if (!cmd->cmp("errQuiet")) {
+       parseYesNo("errQuiet", &errQuiet, tokens, fileName, line);
+      } else {
+       error(-1, "Unknown config file command '%s' (%s:%d)",
+             cmd->getCString(), fileName->getCString(), line);
+       if (!cmd->cmp("displayFontX") ||
+           !cmd->cmp("displayNamedCIDFontX") ||
+           !cmd->cmp("displayCIDFontX")) {
+         error(-1, "-- Xpdf no longer supports X fonts");
+       } else if (!cmd->cmp("t1libControl") || !cmd->cmp("freetypeControl")) {
+         error(-1, "-- The t1libControl and freetypeControl options have been replaced");
+         error(-1, "   by the enableT1lib, enableFreeType, and antialias options");
+       } else if (!cmd->cmp("fontpath") || !cmd->cmp("fontmap")) {
+         error(-1, "-- the config file format has changed since Xpdf 0.9x");
+       }
+      }
+    }
+
+    deleteGList(tokens, GString);
+    ++line;
+  }
+}
+
+static GString* qualify_filename(GString*path, GString*filename)
+{
+  GString*fullpath = 0;
+  char*prefix = "/usr/local/share/xpdf/";
+
+  if (filename->getChar(0) != '\\' && filename->getChar(0) != '/') {
+    /* relative path */
+    fullpath = path->copy();
+    fullpath->append(filename);
+  } else if (!strncmp(filename->getCString(), prefix, strlen(prefix))) {
+    /* xpdf default path */
+    char*s = strchr(filename->getCString()+strlen(prefix), '/');
+    if(s) {
+       fullpath = path->copy();
+       fullpath->append(s+1);
+    } else {
+       fullpath = filename->copy();
+    }
+  } else {
+    /* absolute path */
+    fullpath = filename->copy();
+  }
+  //printf("%s -%s-> %s\n", filename->getCString(), path->getCString(), fullpath->getCString());
+  return fullpath;
+}
+
+void GlobalParams::parseNameToUnicode(GList *tokens, GString *fileName,
+                                        int line) {
+  GString *name;
+  char *tok1, *tok2;
+  FILE *f;
+  char buf[256];
+  int line2;
+  Unicode u;
+
+  if (tokens->getLength() != 2) {
+    error(-1, "Bad 'nameToUnicode' config file command (%s:%d)",
+         fileName->getCString(), line);
+    return;
+  }
+  name = qualify_filename(this->path, (GString *)tokens->get(1));
+  if (!(f = fopen(name->getCString(), "r"))) {
+    error(-1, "Couldn't open 'nameToUnicode' file '%s'",
+         name->getCString());
+    return;
+  }
+  line2 = 1;
+  while (getLine(buf, sizeof(buf), f)) {
+    tok1 = strtok(buf, " \t\r\n");
+    tok2 = strtok(NULL, " \t\r\n");
+    if (tok1 && tok2) {
+      sscanf(tok1, "%x", &u);
+      nameToUnicode->add(tok2, u);
+    } else {
+      error(-1, "Bad line in 'nameToUnicode' file (%s:%d)", name, line2);
+    }
+    ++line2;
+  }
+  fclose(f);
+}
+
+void GlobalParams::parseCIDToUnicode(GList *tokens, GString *fileName,
+                                    int line) {
+  GString *collection, *name, *old;
+
+  if (tokens->getLength() != 3) {
+    error(-1, "Bad 'cidToUnicode' config file command (%s:%d)",
+         fileName->getCString(), line);
+    return;
+  }
+  collection = (GString *)tokens->get(1);
+  name = (GString *)tokens->get(2);
+
+  if ((old = (GString *)cidToUnicodes->remove(collection))) {
+    delete old;
+  }
+
+  cidToUnicodes->add(collection->copy(), qualify_filename(this->path, name));
+}
+
+void GlobalParams::parseUnicodeToUnicode(GList *tokens, GString *fileName,
+                                        int line) {
+  GString *font, *file, *old;
+
+  if (tokens->getLength() != 3) {
+    error(-1, "Bad 'unicodeToUnicode' config file command (%s:%d)",
+         fileName->getCString(), line);
+    return;
+  }
+  font = (GString *)tokens->get(1);
+  file = (GString *)tokens->get(2);
+  if ((old = (GString *)unicodeToUnicodes->remove(font))) {
+    delete old;
+  }
+
+  unicodeToUnicodes->add(font->copy(), qualify_filename(this->path, file));
+}
+
+void GlobalParams::parseUnicodeMap(GList *tokens, GString *fileName,
+                                  int line) {
+  GString *encodingName, *name, *old;
+
+  if (tokens->getLength() != 3) {
+    error(-1, "Bad 'unicodeMap' config file command (%s:%d)",
+         fileName->getCString(), line);
+    return;
+  }
+  encodingName = (GString *)tokens->get(1);
+  name = (GString *)tokens->get(2);
+  if ((old = (GString *)unicodeMaps->remove(encodingName))) {
+    delete old;
+  }
+
+  unicodeMaps->add(encodingName->copy(), qualify_filename(this->path, name));
+}
+
+void GlobalParams::parseCMapDir(GList *tokens, GString *fileName, int line) {
+  GString *collection, *dir;
+  GList *list;
+
+  if (tokens->getLength() != 3) {
+    error(-1, "Bad 'cMapDir' config file command (%s:%d)",
+         fileName->getCString(), line);
+    return;
+  }
+  collection = (GString *)tokens->get(1);
+  dir = (GString *)tokens->get(2);
+  if (!(list = (GList *)cMapDirs->lookup(collection))) {
+    list = new GList();
+    cMapDirs->add(collection->copy(), list);
+  }
+
+  list->append(qualify_filename(this->path, dir));
+}
+
+void GlobalParams::parseToUnicodeDir(GList *tokens, GString *fileName,
+                                    int line) {
+  GString *dir;
+
+  if (tokens->getLength() != 2) {
+    error(-1, "Bad 'toUnicodeDir' config file command (%s:%d)",
+         fileName->getCString(), line);
+    return;
+  }
+
+  dir = (GString *)tokens->get(1);
+
+  toUnicodeDirs->append(qualify_filename(this->path, dir));
+}
+
+void GlobalParams::parseDisplayFont(GList *tokens, GHash *fontHash,
+                                   DisplayFontParamKind kind,
+                                   GString *fileName, int line) {
+  DisplayFontParam *param, *old;
+  GString *file;
+
+  if (tokens->getLength() < 2) {
+    goto err1;
+  }
+  param = new DisplayFontParam(((GString *)tokens->get(1))->copy(), kind);
+  
+  switch (kind) {
+  case displayFontT1:
+    if (tokens->getLength() != 3) {
+      goto err2;
+    }
+    file = (GString *)tokens->get(2);
+    param->t1.fileName = qualify_filename(this->path, file);
+    break;
+  case displayFontTT:
+    if (tokens->getLength() != 3) {
+      goto err2;
+    }
+    file = (GString *)tokens->get(2);
+    param->tt.fileName = qualify_filename(this->path, file);
+    break;
+  }
+
+  if ((old = (DisplayFontParam *)fontHash->remove(param->name))) {
+    delete old;
+  }
+  fontHash->add(param->name, param);
+  return;
+
+ err2:
+  delete param;
+ err1:
+  error(-1, "Bad 'display*Font*' config file command (%s:%d)",
+       fileName->getCString(), line);
+}
+
+void GlobalParams::parsePSPaperSize(GList *tokens, GString *fileName,
+                                   int line) {
+  GString *tok;
+
+  if (tokens->getLength() == 2) {
+    tok = (GString *)tokens->get(1);
+    if (!setPSPaperSize(tok->getCString())) {
+      error(-1, "Bad 'psPaperSize' config file command (%s:%d)",
+           fileName->getCString(), line);
+    }
+  } else if (tokens->getLength() == 3) {
+    tok = (GString *)tokens->get(1);
+    psPaperWidth = atoi(tok->getCString());
+    tok = (GString *)tokens->get(2);
+    psPaperHeight = atoi(tok->getCString());
+    psImageableLLX = psImageableLLY = 0;
+    psImageableURX = psPaperWidth;
+    psImageableURY = psPaperHeight;
+  } else {
+    error(-1, "Bad 'psPaperSize' config file command (%s:%d)",
+         fileName->getCString(), line);
+  }
+}
+
+void GlobalParams::parsePSImageableArea(GList *tokens, GString *fileName,
+                                       int line) {
+  if (tokens->getLength() != 5) {
+    error(-1, "Bad 'psImageableArea' config file command (%s:%d)",
+         fileName->getCString(), line);
+    return;
+  }
+  psImageableLLX = atoi(((GString *)tokens->get(1))->getCString());
+  psImageableLLY = atoi(((GString *)tokens->get(2))->getCString());
+  psImageableURX = atoi(((GString *)tokens->get(3))->getCString());
+  psImageableURY = atoi(((GString *)tokens->get(4))->getCString());
+}
+
+void GlobalParams::parsePSLevel(GList *tokens, GString *fileName, int line) {
+  GString *tok;
+
+  if (tokens->getLength() != 2) {
+    error(-1, "Bad 'psLevel' config file command (%s:%d)",
+         fileName->getCString(), line);
+    return;
+  }
+  tok = (GString *)tokens->get(1);
+  if (!tok->cmp("level1")) {
+    psLevel = psLevel1;
+  } else if (!tok->cmp("level1sep")) {
+    psLevel = psLevel1Sep;
+  } else if (!tok->cmp("level2")) {
+    psLevel = psLevel2;
+  } else if (!tok->cmp("level2sep")) {
+    psLevel = psLevel2Sep;
+  } else if (!tok->cmp("level3")) {
+    psLevel = psLevel3;
+  } else if (!tok->cmp("level3Sep")) {
+    psLevel = psLevel3Sep;
+  } else {
+    error(-1, "Bad 'psLevel' config file command (%s:%d)",
+         fileName->getCString(), line);
+  }
+}
+
+void GlobalParams::parsePSFile(GList *tokens, GString *fileName, int line) {
+  if (tokens->getLength() != 2) {
+    error(-1, "Bad 'psFile' config file command (%s:%d)",
+         fileName->getCString(), line);
+    return;
+  }
+  if (psFile) {
+    delete psFile;
+  }
+  psFile = ((GString *)tokens->get(1))->copy();
+}
+
+void GlobalParams::parsePSFont(GList *tokens, GString *fileName, int line) {
+  PSFontParam *param;
+
+  if (tokens->getLength() != 3) {
+    error(-1, "Bad 'psFont' config file command (%s:%d)",
+         fileName->getCString(), line);
+    return;
+  }
+  param = new PSFontParam(((GString *)tokens->get(1))->copy(), 0,
+                         ((GString *)tokens->get(2))->copy(), NULL);
+  psFonts->add(param->pdfFontName, param);
+}
+
+void GlobalParams::parsePSFont16(char *cmdName, GList *fontList,
+                                GList *tokens, GString *fileName, int line) {
+  PSFontParam *param;
+  int wMode;
+  GString *tok;
+
+  if (tokens->getLength() != 5) {
+    error(-1, "Bad '%s' config file command (%s:%d)",
+         cmdName, fileName->getCString(), line);
+    return;
+  }
+  tok = (GString *)tokens->get(2);
+  if (!tok->cmp("H")) {
+    wMode = 0;
+  } else if (!tok->cmp("V")) {
+    wMode = 1;
+  } else {
+    error(-1, "Bad '%s' config file command (%s:%d)",
+         cmdName, fileName->getCString(), line);
+    return;
+  }
+  param = new PSFontParam(((GString *)tokens->get(1))->copy(),
+                         wMode,
+                         ((GString *)tokens->get(3))->copy(),
+                         ((GString *)tokens->get(4))->copy());
+  fontList->append(param);
+}
+
+void GlobalParams::parseTextEncoding(GList *tokens, GString *fileName,
+                                    int line) {
+  if (tokens->getLength() != 2) {
+    error(-1, "Bad 'textEncoding' config file command (%s:%d)",
+         fileName->getCString(), line);
+    return;
+  }
+  delete textEncoding;
+  textEncoding = ((GString *)tokens->get(1))->copy();
+}
+
+void GlobalParams::parseTextEOL(GList *tokens, GString *fileName, int line) {
+  GString *tok;
+
+  if (tokens->getLength() != 2) {
+    error(-1, "Bad 'textEOL' config file command (%s:%d)",
+         fileName->getCString(), line);
+    return;
+  }
+  tok = (GString *)tokens->get(1);
+  if (!tok->cmp("unix")) {
+    textEOL = eolUnix;
+  } else if (!tok->cmp("dos")) {
+    textEOL = eolDOS;
+  } else if (!tok->cmp("mac")) {
+    textEOL = eolMac;
+  } else {
+    error(-1, "Bad 'textEOL' config file command (%s:%d)",
+         fileName->getCString(), line);
+  }
+}
+
+void GlobalParams::parseFontDir(GList *tokens, GString *fileName, int line) {
+  if (tokens->getLength() != 2) {
+    error(-1, "Bad 'fontDir' config file command (%s:%d)",
+         fileName->getCString(), line);
+    return;
+  }
+  fontDirs->append(((GString *)tokens->get(1))->copy());
+}
+
+void GlobalParams::parseInitialZoom(GList *tokens,
+                                   GString *fileName, int line) {
+  if (tokens->getLength() != 2) {
+    error(-1, "Bad 'initialZoom' config file command (%s:%d)",
+         fileName->getCString(), line);
+    return;
+  }
+  delete initialZoom;
+  initialZoom = ((GString *)tokens->get(1))->copy();
+}
+
+void GlobalParams::parseCommand(char *cmdName, GString **val,
+                               GList *tokens, GString *fileName, int line) {
+  if (tokens->getLength() != 2) {
+    error(-1, "Bad '%s' config file command (%s:%d)",
+         cmdName, fileName->getCString(), line);
+    return;
+  }
+  if (*val) {
+    delete *val;
+  }
+  *val = ((GString *)tokens->get(1))->copy();
+}
+
+void GlobalParams::parseYesNo(char *cmdName, GBool *flag,
+                             GList *tokens, GString *fileName, int line) {
+  GString *tok;
+
+  if (tokens->getLength() != 2) {
+    error(-1, "Bad '%s' config file command (%s:%d)",
+         cmdName, fileName->getCString(), line);
+    return;
+  }
+  tok = (GString *)tokens->get(1);
+  if (!parseYesNo2(tok->getCString(), flag)) {
+    error(-1, "Bad '%s' config file command (%s:%d)",
+         cmdName, fileName->getCString(), line);
+  }
+}
+
+GBool GlobalParams::parseYesNo2(char *token, GBool *flag) {
+  if (!strcmp(token, "yes")) {
+    *flag = gTrue;
+  } else if (!strcmp(token, "no")) {
+    *flag = gFalse;
+  } else {
+    return gFalse;
+  }
+  return gTrue;
+}
+
+GlobalParams::~GlobalParams() {
+  GHashIter *iter;
+  GString *key;
+  GList *list;
+
+  freeBuiltinFontTables();
+
+  delete macRomanReverseMap;
+
+  delete baseDir;
+  delete nameToUnicode;
+  deleteGHash(cidToUnicodes, GString);
+  deleteGHash(unicodeToUnicodes, GString);
+  deleteGHash(residentUnicodeMaps, UnicodeMap);
+  deleteGHash(unicodeMaps, GString);
+  deleteGList(toUnicodeDirs, GString);
+  deleteGHash(displayFonts, DisplayFontParam);
+  deleteGHash(displayCIDFonts, DisplayFontParam);
+  deleteGHash(displayNamedCIDFonts, DisplayFontParam);
+  if (psFile) {
+    delete psFile;
+  }
+  deleteGHash(psFonts, PSFontParam);
+  deleteGList(psNamedFonts16, PSFontParam);
+  deleteGList(psFonts16, PSFontParam);
+  delete textEncoding;
+  deleteGList(fontDirs, GString);
+  delete initialZoom;
+  if (urlCommand) {
+    delete urlCommand;
+  }
+  if (movieCommand) {
+    delete movieCommand;
+  }
+
+  cMapDirs->startIter(&iter);
+  while (cMapDirs->getNext(&iter, &key, (void **)&list)) {
+    deleteGList(list, GString);
+  }
+  delete cMapDirs;
+
+  delete cidToUnicodeCache;
+  delete unicodeToUnicodeCache;
+  delete unicodeMapCache;
+  delete cMapCache;
+
+#ifdef ENABLE_PLUGINS
+  delete securityHandlers;
+  deleteGList(plugins, Plugin);
+#endif
+
+#if MULTITHREADED
+  gDestroyMutex(&mutex);
+  gDestroyMutex(&unicodeMapCacheMutex);
+  gDestroyMutex(&cMapCacheMutex);
+#endif
+}
+
+//------------------------------------------------------------------------
+
+void GlobalParams::setBaseDir(char *dir) {
+  delete baseDir;
+  baseDir = new GString(dir);
+}
+
+void GlobalParams::setupBaseFonts(char *dir) {
+  GString *fontName;
+  GString *fileName;
+#ifdef WIN32
+  HMODULE shell32Lib;
+  BOOL (__stdcall *SHGetSpecialFolderPathFunc)(HWND hwndOwner,
+                                              LPTSTR lpszPath,
+                                              int nFolder,
+                                              BOOL fCreate);
+  char winFontDir[MAX_PATH];
+#endif
+  FILE *f;
+  DisplayFontParamKind kind;
+  DisplayFontParam *dfp;
+  int i, j;
+
+#ifdef WIN32
+  // SHGetSpecialFolderPath isn't available in older versions of
+  // shell32.dll (Win95 and WinNT4), so do a dynamic load
+  winFontDir[0] = '\0';
+  if ((shell32Lib = LoadLibrary("shell32.dll"))) {
+    if ((SHGetSpecialFolderPathFunc = 
+        (BOOL (__stdcall *)(HWND hwndOwner, LPTSTR lpszPath,
+                            int nFolder, BOOL fCreate))
+        GetProcAddress(shell32Lib, "SHGetSpecialFolderPath"))) {
+      if (!(*SHGetSpecialFolderPathFunc)(NULL, winFontDir,
+                                        CSIDL_FONTS, FALSE)) {
+       winFontDir[0] = '\0';
+      }
+    }
+  }
+#endif
+  for (i = 0; displayFontTab[i].name; ++i) {
+    fontName = new GString(displayFontTab[i].name);
+    if (getDisplayFont(fontName)) {
+      delete fontName;
+      continue;
+    }
+    fileName = NULL;
+    kind = displayFontT1; // make gcc happy
+    if (dir) {
+      fileName = appendToPath(new GString(dir), displayFontTab[i].t1FileName);
+      kind = displayFontT1;
+      if ((f = fopen(fileName->getCString(), "rb"))) {
+       fclose(f);
+      } else {
+       delete fileName;
+       fileName = NULL;
+      }
+    }
+#ifdef WIN32
+    if (!fileName && winFontDir[0] && displayFontTab[i].ttFileName) {
+      fileName = appendToPath(new GString(winFontDir),
+                             displayFontTab[i].ttFileName);
+      kind = displayFontTT;
+      if ((f = fopen(fileName->getCString(), "rb"))) {
+       fclose(f);
+      } else {
+       delete fileName;
+       fileName = NULL;
+      }
+    }
+    // SHGetSpecialFolderPath(CSIDL_FONTS) doesn't work on Win 2k Server
+    // or Win2003 Server, or with older versions of shell32.dll, so check
+    // the "standard" directories
+    if (displayFontTab[i].ttFileName) {
+      for (j = 0; !fileName && displayFontDirs[j]; ++j) {
+       fileName = appendToPath(new GString(displayFontDirs[j]),
+                               displayFontTab[i].ttFileName);
+       kind = displayFontTT;
+       if ((f = fopen(fileName->getCString(), "rb"))) {
+         fclose(f);
+       } else {
+         delete fileName;
+         fileName = NULL;
+       }
+      }
+    }
+#else
+    for (j = 0; !fileName && displayFontDirs[j]; ++j) {
+      fileName = appendToPath(new GString(displayFontDirs[j]),
+                             displayFontTab[i].t1FileName);
+      kind = displayFontT1;
+      if ((f = fopen(fileName->getCString(), "rb"))) {
+       fclose(f);
+      } else {
+       delete fileName;
+       fileName = NULL;
+      }
+    }
+#endif
+    if (!fileName) {
+      error(-1, "No display font for '%s'", displayFontTab[i].name);
+      delete fontName;
+      continue;
+    }
+    dfp = new DisplayFontParam(fontName, kind);
+    dfp->t1.fileName = fileName;
+    globalParams->addDisplayFont(dfp);
+  }
+}
+
+//------------------------------------------------------------------------
+// accessors
+//------------------------------------------------------------------------
+
+CharCode GlobalParams::getMacRomanCharCode(char *charName) {
+  // no need to lock - macRomanReverseMap is constant
+  return macRomanReverseMap->lookup(charName);
+}
+
+GString *GlobalParams::getBaseDir() {
+  GString *s;
+
+  lockGlobalParams;
+  s = baseDir->copy();
+  unlockGlobalParams;
+  return s;
+}
+
+Unicode GlobalParams::mapNameToUnicode(char *charName) {
+  // no need to lock - nameToUnicode is constant
+  return nameToUnicode->lookup(charName);
+}
+
+UnicodeMap *GlobalParams::getResidentUnicodeMap(GString *encodingName) {
+  UnicodeMap *map;
+
+  lockGlobalParams;
+  map = (UnicodeMap *)residentUnicodeMaps->lookup(encodingName);
+  unlockGlobalParams;
+  if (map) {
+    map->incRefCnt();
+  }
+  return map;
+}
+
+FILE *GlobalParams::getUnicodeMapFile(GString *encodingName) {
+  GString *fileName;
+  FILE *f;
+
+  lockGlobalParams;
+  if ((fileName = (GString *)unicodeMaps->lookup(encodingName))) {
+    f = fopen(fileName->getCString(), "r");
+  } else {
+    f = NULL;
+  }
+  unlockGlobalParams;
+  return f;
+}
+
+FILE *GlobalParams::findCMapFile(GString *collection, GString *cMapName) {
+  GList *list;
+  GString *dir;
+  GString *fileName;
+  FILE *f;
+  int i;
+
+  lockGlobalParams;
+  if (!(list = (GList *)cMapDirs->lookup(collection))) {
+    unlockGlobalParams;
+    return NULL;
+  }
+  for (i = 0; i < list->getLength(); ++i) {
+    dir = (GString *)list->get(i);
+    fileName = appendToPath(dir->copy(), cMapName->getCString());
+    f = fopen(fileName->getCString(), "r");
+    delete fileName;
+    if (f) {
+      unlockGlobalParams;
+      return f;
+    }
+  }
+  unlockGlobalParams;
+  return NULL;
+}
+
+FILE *GlobalParams::findToUnicodeFile(GString *name) {
+  GString *dir, *fileName;
+  FILE *f;
+  int i;
+
+  lockGlobalParams;
+  for (i = 0; i < toUnicodeDirs->getLength(); ++i) {
+    dir = (GString *)toUnicodeDirs->get(i);
+    fileName = appendToPath(dir->copy(), name->getCString());
+    f = fopen(fileName->getCString(), "r");
+    delete fileName;
+    if (f) {
+      unlockGlobalParams;
+      return f;
+    }
+  }
+  unlockGlobalParams;
+  return NULL;
+}
+
+DisplayFontParam *GlobalParams::getDisplayFont(GString *fontName) {
+  DisplayFontParam *dfp;
+
+  lockGlobalParams;
+  dfp = (DisplayFontParam *)displayFonts->lookup(fontName);
+  unlockGlobalParams;
+  return dfp;
+}
+
+DisplayFontParam *GlobalParams::getDisplayCIDFont(GString *fontName,
+                                                 GString *collection) {
+  DisplayFontParam *dfp;
+
+  lockGlobalParams;
+  if (!fontName ||
+      !(dfp = (DisplayFontParam *)displayNamedCIDFonts->lookup(fontName))) {
+    dfp = (DisplayFontParam *)displayCIDFonts->lookup(collection);
+  }
+  unlockGlobalParams;
+  return dfp;
+}
+
+GString *GlobalParams::getPSFile() {
+  GString *s;
+
+  lockGlobalParams;
+  s = psFile ? psFile->copy() : (GString *)NULL;
+  unlockGlobalParams;
+  return s;
+}
+
+int GlobalParams::getPSPaperWidth() {
+  int w;
+
+  lockGlobalParams;
+  w = psPaperWidth;
+  unlockGlobalParams;
+  return w;
+}
+
+int GlobalParams::getPSPaperHeight() {
+  int h;
+
+  lockGlobalParams;
+  h = psPaperHeight;
+  unlockGlobalParams;
+  return h;
+}
+
+void GlobalParams::getPSImageableArea(int *llx, int *lly, int *urx, int *ury) {
+  lockGlobalParams;
+  *llx = psImageableLLX;
+  *lly = psImageableLLY;
+  *urx = psImageableURX;
+  *ury = psImageableURY;
+  unlockGlobalParams;
+}
+
+GBool GlobalParams::getPSCrop() {
+  GBool f;
+
+  lockGlobalParams;
+  f = psCrop;
+  unlockGlobalParams;
+  return f;
+}
+
+GBool GlobalParams::getPSExpandSmaller() {
+  GBool f;
+
+  lockGlobalParams;
+  f = psExpandSmaller;
+  unlockGlobalParams;
+  return f;
+}
+
+GBool GlobalParams::getPSShrinkLarger() {
+  GBool f;
+
+  lockGlobalParams;
+  f = psShrinkLarger;
+  unlockGlobalParams;
+  return f;
+}
+
+GBool GlobalParams::getPSCenter() {
+  GBool f;
+
+  lockGlobalParams;
+  f = psCenter;
+  unlockGlobalParams;
+  return f;
+}
+
+GBool GlobalParams::getPSDuplex() {
+  GBool d;
+
+  lockGlobalParams;
+  d = psDuplex;
+  unlockGlobalParams;
+  return d;
+}
+
+PSLevel GlobalParams::getPSLevel() {
+  PSLevel level;
+
+  lockGlobalParams;
+  level = psLevel;
+  unlockGlobalParams;
+  return level;
+}
+
+PSFontParam *GlobalParams::getPSFont(GString *fontName) {
+  PSFontParam *p;
+
+  lockGlobalParams;
+  p = (PSFontParam *)psFonts->lookup(fontName);
+  unlockGlobalParams;
+  return p;
+}
+
+PSFontParam *GlobalParams::getPSFont16(GString *fontName,
+                                      GString *collection, int wMode) {
+  PSFontParam *p;
+  int i;
+
+  lockGlobalParams;
+  p = NULL;
+  if (fontName) {
+    for (i = 0; i < psNamedFonts16->getLength(); ++i) {
+      p = (PSFontParam *)psNamedFonts16->get(i);
+      if (!p->pdfFontName->cmp(fontName) &&
+         p->wMode == wMode) {
+       break;
+      }
+      p = NULL;
+    }
+  }
+  if (!p && collection) {
+    for (i = 0; i < psFonts16->getLength(); ++i) {
+      p = (PSFontParam *)psFonts16->get(i);
+      if (!p->pdfFontName->cmp(collection) &&
+         p->wMode == wMode) {
+       break;
+      }
+      p = NULL;
+    }
+  }
+  unlockGlobalParams;
+  return p;
+}
+
+GBool GlobalParams::getPSEmbedType1() {
+  GBool e;
+
+  lockGlobalParams;
+  e = psEmbedType1;
+  unlockGlobalParams;
+  return e;
+}
+
+GBool GlobalParams::getPSEmbedTrueType() {
+  GBool e;
+
+  lockGlobalParams;
+  e = psEmbedTrueType;
+  unlockGlobalParams;
+  return e;
+}
+
+GBool GlobalParams::getPSEmbedCIDPostScript() {
+  GBool e;
+
+  lockGlobalParams;
+  e = psEmbedCIDPostScript;
+  unlockGlobalParams;
+  return e;
+}
+
+GBool GlobalParams::getPSEmbedCIDTrueType() {
+  GBool e;
+
+  lockGlobalParams;
+  e = psEmbedCIDTrueType;
+  unlockGlobalParams;
+  return e;
+}
+
+GBool GlobalParams::getPSOPI() {
+  GBool opi;
+
+  lockGlobalParams;
+  opi = psOPI;
+  unlockGlobalParams;
+  return opi;
+}
+
+GBool GlobalParams::getPSASCIIHex() {
+  GBool ah;
+
+  lockGlobalParams;
+  ah = psASCIIHex;
+  unlockGlobalParams;
+  return ah;
+}
+
+GString *GlobalParams::getTextEncodingName() {
+  GString *s;
+
+  lockGlobalParams;
+  s = textEncoding->copy();
+  unlockGlobalParams;
+  return s;
+}
+
+EndOfLineKind GlobalParams::getTextEOL() {
+  EndOfLineKind eol;
+
+  lockGlobalParams;
+  eol = textEOL;
+  unlockGlobalParams;
+  return eol;
+}
+
+GBool GlobalParams::getTextPageBreaks() {
+  GBool pageBreaks;
+
+  lockGlobalParams;
+  pageBreaks = textPageBreaks;
+  unlockGlobalParams;
+  return pageBreaks;
+}
+
+GBool GlobalParams::getTextKeepTinyChars() {
+  GBool tiny;
+
+  lockGlobalParams;
+  tiny = textKeepTinyChars;
+  unlockGlobalParams;
+  return tiny;
+}
+
+GString *GlobalParams::findFontFile(GString *fontName, char **exts) {
+  GString *dir, *fileName;
+  char **ext;
+  FILE *f;
+  int i;
+
+  lockGlobalParams;
+  for (i = 0; i < fontDirs->getLength(); ++i) {
+    dir = (GString *)fontDirs->get(i);
+    for (ext = exts; *ext; ++ext) {
+      fileName = appendToPath(dir->copy(), fontName->getCString());
+      fileName->append(*ext);
+      if ((f = fopen(fileName->getCString(), "rb"))) {
+       fclose(f);
+       unlockGlobalParams;
+       return fileName;
+      }
+      delete fileName;
+    }
+  }
+  unlockGlobalParams;
+  return NULL;
+}
+
+GString *GlobalParams::getInitialZoom() {
+  GString *s;
+
+  lockGlobalParams;
+  s = initialZoom->copy();
+  unlockGlobalParams;
+  return s;
+}
+
+GBool GlobalParams::getContinuousView() {
+  GBool f;
+
+  lockGlobalParams;
+  f = continuousView;
+  unlockGlobalParams;
+  return f;
+}
+
+GBool GlobalParams::getEnableT1lib() {
+  GBool f;
+
+  lockGlobalParams;
+  f = enableT1lib;
+  unlockGlobalParams;
+  return f;
+}
+
+GBool GlobalParams::getEnableFreeType() {
+  GBool f;
+
+  lockGlobalParams;
+  f = enableFreeType;
+  unlockGlobalParams;
+  return f;
+}
+
+
+GBool GlobalParams::getAntialias() {
+  GBool f;
+
+  lockGlobalParams;
+  f = antialias;
+  unlockGlobalParams;
+  return f;
+}
+
+GBool GlobalParams::getMapNumericCharNames() {
+  GBool map;
+
+  lockGlobalParams;
+  map = mapNumericCharNames;
+  unlockGlobalParams;
+  return map;
+}
+
+GBool GlobalParams::getPrintCommands() {
+  GBool p;
+
+  lockGlobalParams;
+  p = printCommands;
+  unlockGlobalParams;
+  return p;
+}
+
+GBool GlobalParams::getErrQuiet() {
+  GBool q;
+
+  lockGlobalParams;
+  q = errQuiet;
+  unlockGlobalParams;
+  return q;
+}
+
+CharCodeToUnicode *GlobalParams::getCIDToUnicode(GString *collection) {
+  GString *fileName;
+  CharCodeToUnicode *ctu;
+
+  lockGlobalParams;
+  if (!(ctu = cidToUnicodeCache->getCharCodeToUnicode(collection))) {
+    if ((fileName = (GString *)cidToUnicodes->lookup(collection)) &&
+       (ctu = CharCodeToUnicode::parseCIDToUnicode(fileName, collection))) {
+      cidToUnicodeCache->add(ctu);
+    }
+  }
+  unlockGlobalParams;
+  return ctu;
+}
+
+CharCodeToUnicode *GlobalParams::getUnicodeToUnicode(GString *fontName) {
+  CharCodeToUnicode *ctu;
+  GHashIter *iter;
+  GString *fontPattern, *fileName;
+
+  lockGlobalParams;
+  fileName = NULL;
+  unicodeToUnicodes->startIter(&iter);
+  while (unicodeToUnicodes->getNext(&iter, &fontPattern, (void **)&fileName)) {
+    if (strstr(fontName->getCString(), fontPattern->getCString())) {
+      unicodeToUnicodes->killIter(&iter);
+      break;
+    }
+    fileName = NULL;
+  }
+  if (fileName) {
+    if (!(ctu = unicodeToUnicodeCache->getCharCodeToUnicode(fileName))) {
+      if ((ctu = CharCodeToUnicode::parseUnicodeToUnicode(fileName))) {
+       unicodeToUnicodeCache->add(ctu);
+      }
+    }
+  } else {
+    ctu = NULL;
+  }
+  unlockGlobalParams;
+  return ctu;
+}
+
+UnicodeMap *GlobalParams::getUnicodeMap(GString *encodingName) {
+  return getUnicodeMap2(encodingName);
+}
+
+UnicodeMap *GlobalParams::getUnicodeMap2(GString *encodingName) {
+  UnicodeMap *map;
+
+  if (!(map = getResidentUnicodeMap(encodingName))) {
+    lockUnicodeMapCache;
+    map = unicodeMapCache->getUnicodeMap(encodingName);
+    unlockUnicodeMapCache;
+  }
+  return map;
+}
+
+CMap *GlobalParams::getCMap(GString *collection, GString *cMapName) {
+  CMap *cMap;
+
+  lockCMapCache;
+  cMap = cMapCache->getCMap(collection, cMapName);
+  unlockCMapCache;
+  return cMap;
+}
+
+UnicodeMap *GlobalParams::getTextEncoding() {
+  return getUnicodeMap2(textEncoding);
+}
+
+//------------------------------------------------------------------------
+// functions to set parameters
+//------------------------------------------------------------------------
+
+void GlobalParams::addDisplayFont(DisplayFontParam *param) {
+  DisplayFontParam *old;
+
+  lockGlobalParams;
+  if ((old = (DisplayFontParam *)displayFonts->remove(param->name))) {
+    delete old;
+  }
+  displayFonts->add(param->name, param);
+  unlockGlobalParams;
+}
+
+void GlobalParams::setPSFile(char *file) {
+  lockGlobalParams;
+  if (psFile) {
+    delete psFile;
+  }
+  psFile = new GString(file);
+  unlockGlobalParams;
+}
+
+GBool GlobalParams::setPSPaperSize(char *size) {
+  lockGlobalParams;
+  if (!strcmp(size, "match")) {
+    psPaperWidth = psPaperHeight = -1;
+  } else if (!strcmp(size, "letter")) {
+    psPaperWidth = 612;
+    psPaperHeight = 792;
+  } else if (!strcmp(size, "legal")) {
+    psPaperWidth = 612;
+    psPaperHeight = 1008;
+  } else if (!strcmp(size, "A4")) {
+    psPaperWidth = 595;
+    psPaperHeight = 842;
+  } else if (!strcmp(size, "A3")) {
+    psPaperWidth = 842;
+    psPaperHeight = 1190;
+  } else {
+    unlockGlobalParams;
+    return gFalse;
+  }
+  psImageableLLX = psImageableLLY = 0;
+  psImageableURX = psPaperWidth;
+  psImageableURY = psPaperHeight;
+  unlockGlobalParams;
+  return gTrue;
+}
+
+void GlobalParams::setPSPaperWidth(int width) {
+  lockGlobalParams;
+  psPaperWidth = width;
+  psImageableLLX = 0;
+  psImageableURX = psPaperWidth;
+  unlockGlobalParams;
+}
+
+void GlobalParams::setPSPaperHeight(int height) {
+  lockGlobalParams;
+  psPaperHeight = height;
+  psImageableLLY = 0;
+  psImageableURY = psPaperHeight;
+  unlockGlobalParams;
+}
+
+void GlobalParams::setPSImageableArea(int llx, int lly, int urx, int ury) {
+  lockGlobalParams;
+  psImageableLLX = llx;
+  psImageableLLY = lly;
+  psImageableURX = urx;
+  psImageableURY = ury;
+  unlockGlobalParams;
+}
+
+void GlobalParams::setPSCrop(GBool crop) {
+  lockGlobalParams;
+  psCrop = crop;
+  unlockGlobalParams;
+}
+
+void GlobalParams::setPSExpandSmaller(GBool expand) {
+  lockGlobalParams;
+  psExpandSmaller = expand;
+  unlockGlobalParams;
+}
+
+void GlobalParams::setPSShrinkLarger(GBool shrink) {
+  lockGlobalParams;
+  psShrinkLarger = shrink;
+  unlockGlobalParams;
+}
+
+void GlobalParams::setPSCenter(GBool center) {
+  lockGlobalParams;
+  psCenter = center;
+  unlockGlobalParams;
+}
+
+void GlobalParams::setPSDuplex(GBool duplex) {
+  lockGlobalParams;
+  psDuplex = duplex;
+  unlockGlobalParams;
+}
+
+void GlobalParams::setPSLevel(PSLevel level) {
+  lockGlobalParams;
+  psLevel = level;
+  unlockGlobalParams;
+}
+
+void GlobalParams::setPSEmbedType1(GBool embed) {
+  lockGlobalParams;
+  psEmbedType1 = embed;
+  unlockGlobalParams;
+}
+
+void GlobalParams::setPSEmbedTrueType(GBool embed) {
+  lockGlobalParams;
+  psEmbedTrueType = embed;
+  unlockGlobalParams;
+}
+
+void GlobalParams::setPSEmbedCIDPostScript(GBool embed) {
+  lockGlobalParams;
+  psEmbedCIDPostScript = embed;
+  unlockGlobalParams;
+}
+
+void GlobalParams::setPSEmbedCIDTrueType(GBool embed) {
+  lockGlobalParams;
+  psEmbedCIDTrueType = embed;
+  unlockGlobalParams;
+}
+
+void GlobalParams::setPSOPI(GBool opi) {
+  lockGlobalParams;
+  psOPI = opi;
+  unlockGlobalParams;
+}
+
+void GlobalParams::setPSASCIIHex(GBool hex) {
+  lockGlobalParams;
+  psASCIIHex = hex;
+  unlockGlobalParams;
+}
+
+void GlobalParams::setTextEncoding(char *encodingName) {
+  lockGlobalParams;
+  delete textEncoding;
+  textEncoding = new GString(encodingName);
+  unlockGlobalParams;
+}
+
+GBool GlobalParams::setTextEOL(char *s) {
+  lockGlobalParams;
+  if (!strcmp(s, "unix")) {
+    textEOL = eolUnix;
+  } else if (!strcmp(s, "dos")) {
+    textEOL = eolDOS;
+  } else if (!strcmp(s, "mac")) {
+    textEOL = eolMac;
+  } else {
+    unlockGlobalParams;
+    return gFalse;
+  }
+  unlockGlobalParams;
+  return gTrue;
+}
+
+void GlobalParams::setTextPageBreaks(GBool pageBreaks) {
+  lockGlobalParams;
+  textPageBreaks = pageBreaks;
+  unlockGlobalParams;
+}
+
+void GlobalParams::setTextKeepTinyChars(GBool keep) {
+  lockGlobalParams;
+  textKeepTinyChars = keep;
+  unlockGlobalParams;
+}
+
+void GlobalParams::setInitialZoom(char *s) {
+  lockGlobalParams;
+  delete initialZoom;
+  initialZoom = new GString(s);
+  unlockGlobalParams;
+}
+
+void GlobalParams::setContinuousView(GBool cont) {
+  lockGlobalParams;
+  continuousView = cont;
+  unlockGlobalParams;
+}
+
+GBool GlobalParams::setEnableT1lib(char *s) {
+  GBool ok;
+
+  lockGlobalParams;
+  ok = parseYesNo2(s, &enableT1lib);
+  unlockGlobalParams;
+  return ok;
+}
+
+GBool GlobalParams::setEnableFreeType(char *s) {
+  GBool ok;
+
+  lockGlobalParams;
+  ok = parseYesNo2(s, &enableFreeType);
+  unlockGlobalParams;
+  return ok;
+}
+
+
+GBool GlobalParams::setAntialias(char *s) {
+  GBool ok;
+
+  lockGlobalParams;
+  ok = parseYesNo2(s, &antialias);
+  unlockGlobalParams;
+  return ok;
+}
+
+void GlobalParams::setMapNumericCharNames(GBool map) {
+  lockGlobalParams;
+  mapNumericCharNames = map;
+  unlockGlobalParams;
+}
+
+void GlobalParams::setPrintCommands(GBool printCommandsA) {
+  lockGlobalParams;
+  printCommands = printCommandsA;
+  unlockGlobalParams;
+}
+
+void GlobalParams::setErrQuiet(GBool errQuietA) {
+  lockGlobalParams;
+  errQuiet = errQuietA;
+  unlockGlobalParams;
+}
+
+void GlobalParams::addSecurityHandler(XpdfSecurityHandler *handler) {
+#ifdef ENABLE_PLUGINS
+  lockGlobalParams;
+  securityHandlers->append(handler);
+  unlockGlobalParams;
+#endif
+}
+
+XpdfSecurityHandler *GlobalParams::getSecurityHandler(char *name) {
+#ifdef ENABLE_PLUGINS
+  XpdfSecurityHandler *hdlr;
+  int i;
+
+  lockGlobalParams;
+  for (i = 0; i < securityHandlers->getLength(); ++i) {
+    hdlr = (XpdfSecurityHandler *)securityHandlers->get(i);
+    if (!stricmp(hdlr->name, name)) {
+      unlockGlobalParams;
+      return hdlr;
+    }
+  }
+  unlockGlobalParams;
+
+  if (!loadPlugin("security", name)) {
+    return NULL;
+  }
+
+  lockGlobalParams;
+  for (i = 0; i < securityHandlers->getLength(); ++i) {
+    hdlr = (XpdfSecurityHandler *)securityHandlers->get(i);
+    if (!strcmp(hdlr->name, name)) {
+      unlockGlobalParams;
+      return hdlr;
+    }
+  }
+  unlockGlobalParams;
+#endif
+
+  return NULL;
+}
+
+#ifdef ENABLE_PLUGINS
+//------------------------------------------------------------------------
+// plugins
+//------------------------------------------------------------------------
+
+GBool GlobalParams::loadPlugin(char *type, char *name) {
+  Plugin *plugin;
+
+  if (!(plugin = Plugin::load(type, name))) {
+    return gFalse;
+  }
+  lockGlobalParams;
+  plugins->append(plugin);
+  unlockGlobalParams;
+  return gTrue;
+}
+
+#endif // ENABLE_PLUGINS
diff --git a/lib/xpdf/GlobalParams.h b/lib/xpdf/GlobalParams.h
new file mode 100644 (file)
index 0000000..e14ad4f
--- /dev/null
@@ -0,0 +1,340 @@
+//========================================================================
+//
+// GlobalParams.h
+//
+// Copyright 2001-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef GLOBALPARAMS_H
+#define GLOBALPARAMS_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#include <stdio.h>
+#include "gtypes.h"
+#include "CharTypes.h"
+
+#if MULTITHREADED
+#include "GMutex.h"
+#endif
+
+class GString;
+class GList;
+class GHash;
+class NameToCharCode;
+class CharCodeToUnicode;
+class CharCodeToUnicodeCache;
+class UnicodeMap;
+class UnicodeMapCache;
+class CMap;
+class CMapCache;
+struct XpdfSecurityHandler;
+class GlobalParams;
+
+//------------------------------------------------------------------------
+
+// The global parameters object.
+extern GlobalParams *globalParams;
+
+//------------------------------------------------------------------------
+
+enum DisplayFontParamKind {
+  displayFontT1,
+  displayFontTT
+};
+
+struct DisplayFontParamT1 {
+  GString *fileName;
+};
+
+struct DisplayFontParamTT {
+  GString *fileName;
+};
+
+class DisplayFontParam {
+public:
+
+  GString *name;               // font name for 8-bit fonts and named
+                               //   CID fonts; collection name for
+                               //   generic CID fonts
+  DisplayFontParamKind kind;
+  union {
+    DisplayFontParamT1 t1;
+    DisplayFontParamTT tt;
+  };
+
+  DisplayFontParam(GString *nameA, DisplayFontParamKind kindA);
+  ~DisplayFontParam();
+};
+
+//------------------------------------------------------------------------
+
+class PSFontParam {
+public:
+
+  GString *pdfFontName;                // PDF font name for 8-bit fonts and
+                               //   named 16-bit fonts; char collection
+                               //   name for generic 16-bit fonts
+  int wMode;                   // writing mode (0=horiz, 1=vert) for
+                               //   16-bit fonts
+  GString *psFontName;         // PostScript font name
+  GString *encoding;           // encoding, for 16-bit fonts only
+
+  PSFontParam(GString *pdfFontNameA, int wModeA,
+             GString *psFontNameA, GString *encodingA);
+  ~PSFontParam();
+};
+
+//------------------------------------------------------------------------
+
+enum PSLevel {
+  psLevel1,
+  psLevel1Sep,
+  psLevel2,
+  psLevel2Sep,
+  psLevel3,
+  psLevel3Sep
+};
+
+//------------------------------------------------------------------------
+
+enum EndOfLineKind {
+  eolUnix,                     // LF
+  eolDOS,                      // CR+LF
+  eolMac                       // CR
+};
+
+//------------------------------------------------------------------------
+
+class GlobalParams {
+public:
+
+  // Initialize the global parameters by attempting to read a config
+  // file.
+  GlobalParams(char *cfgFileName);
+
+  ~GlobalParams();
+
+  void setBaseDir(char *dir);
+  void setupBaseFonts(char *dir);
+
+  //----- accessors
+
+  CharCode getMacRomanCharCode(char *charName);
+
+  GString *getBaseDir();
+  Unicode mapNameToUnicode(char *charName);
+  UnicodeMap *getResidentUnicodeMap(GString *encodingName);
+  FILE *getUnicodeMapFile(GString *encodingName);
+  FILE *findCMapFile(GString *collection, GString *cMapName);
+  FILE *findToUnicodeFile(GString *name);
+  DisplayFontParam *getDisplayFont(GString *fontName);
+  DisplayFontParam *getDisplayCIDFont(GString *fontName, GString *collection);
+  GString *getPSFile();
+  int getPSPaperWidth();
+  int getPSPaperHeight();
+  void getPSImageableArea(int *llx, int *lly, int *urx, int *ury);
+  GBool getPSDuplex();
+  GBool getPSCrop();
+  GBool getPSExpandSmaller();
+  GBool getPSShrinkLarger();
+  GBool getPSCenter();
+  PSLevel getPSLevel();
+  PSFontParam *getPSFont(GString *fontName);
+  PSFontParam *getPSFont16(GString *fontName, GString *collection, int wMode);
+  GBool getPSEmbedType1();
+  GBool getPSEmbedTrueType();
+  GBool getPSEmbedCIDPostScript();
+  GBool getPSEmbedCIDTrueType();
+  GBool getPSOPI();
+  GBool getPSASCIIHex();
+  GString *getTextEncodingName();
+  EndOfLineKind getTextEOL();
+  GBool getTextPageBreaks();
+  GBool getTextKeepTinyChars();
+  GString *findFontFile(GString *fontName, char **exts);
+  GString *getInitialZoom();
+  GBool getContinuousView();
+  GBool getEnableT1lib();
+  GBool getEnableFreeType();
+  GBool getAntialias();
+  GString *getURLCommand() { return urlCommand; }
+  GString *getMovieCommand() { return movieCommand; }
+  GBool getMapNumericCharNames();
+  GBool getPrintCommands();
+  GBool getErrQuiet();
+
+  CharCodeToUnicode *getCIDToUnicode(GString *collection);
+  CharCodeToUnicode *getUnicodeToUnicode(GString *fontName);
+  UnicodeMap *getUnicodeMap(GString *encodingName);
+  CMap *getCMap(GString *collection, GString *cMapName);
+  UnicodeMap *getTextEncoding();
+
+  //----- functions to set parameters
+
+  void addDisplayFont(DisplayFontParam *param);
+  void setPSFile(char *file);
+  GBool setPSPaperSize(char *size);
+  void setPSPaperWidth(int width);
+  void setPSPaperHeight(int height);
+  void setPSImageableArea(int llx, int lly, int urx, int ury);
+  void setPSDuplex(GBool duplex);
+  void setPSCrop(GBool crop);
+  void setPSExpandSmaller(GBool expand);
+  void setPSShrinkLarger(GBool shrink);
+  void setPSCenter(GBool center);
+  void setPSLevel(PSLevel level);
+  void setPSEmbedType1(GBool embed);
+  void setPSEmbedTrueType(GBool embed);
+  void setPSEmbedCIDPostScript(GBool embed);
+  void setPSEmbedCIDTrueType(GBool embed);
+  void setPSOPI(GBool opi);
+  void setPSASCIIHex(GBool hex);
+  void setTextEncoding(char *encodingName);
+  GBool setTextEOL(char *s);
+  void setTextPageBreaks(GBool pageBreaks);
+  void setTextKeepTinyChars(GBool keep);
+  void setInitialZoom(char *s);
+  void setContinuousView(GBool cont);
+  GBool setEnableT1lib(char *s);
+  GBool setEnableFreeType(char *s);
+  GBool setAntialias(char *s);
+  void setMapNumericCharNames(GBool map);
+  void setPrintCommands(GBool printCommandsA);
+  void setErrQuiet(GBool errQuietA);
+
+  //----- security handlers
+
+  void addSecurityHandler(XpdfSecurityHandler *handler);
+  XpdfSecurityHandler *getSecurityHandler(char *name);
+  void parseFile(GString *fileName, FILE *f);
+
+private:
+
+  void parseNameToUnicode(GList *tokens, GString *fileName, int line);
+  void parseCIDToUnicode(GList *tokens, GString *fileName, int line);
+  void parseUnicodeToUnicode(GList *tokens, GString *fileName, int line);
+  void parseUnicodeMap(GList *tokens, GString *fileName, int line);
+  void parseCMapDir(GList *tokens, GString *fileName, int line);
+  void parseToUnicodeDir(GList *tokens, GString *fileName, int line);
+  void parseDisplayFont(GList *tokens, GHash *fontHash,
+                       DisplayFontParamKind kind,
+                       GString *fileName, int line);
+  void parsePSFile(GList *tokens, GString *fileName, int line);
+  void parsePSPaperSize(GList *tokens, GString *fileName, int line);
+  void parsePSImageableArea(GList *tokens, GString *fileName, int line);
+  void parsePSLevel(GList *tokens, GString *fileName, int line);
+  void parsePSFont(GList *tokens, GString *fileName, int line);
+  void parsePSFont16(char *cmdName, GList *fontList,
+                    GList *tokens, GString *fileName, int line);
+  void parseTextEncoding(GList *tokens, GString *fileName, int line);
+  void parseTextEOL(GList *tokens, GString *fileName, int line);
+  void parseFontDir(GList *tokens, GString *fileName, int line);
+  void parseInitialZoom(GList *tokens, GString *fileName, int line);
+  void parseCommand(char *cmdName, GString **val,
+                   GList *tokens, GString *fileName, int line);
+  void parseYesNo(char *cmdName, GBool *flag,
+                 GList *tokens, GString *fileName, int line);
+  GBool parseYesNo2(char *token, GBool *flag);
+  UnicodeMap *getUnicodeMap2(GString *encodingName);
+#ifdef ENABLE_PLUGINS
+  GBool loadPlugin(char *type, char *name);
+#endif
+
+  //----- config file base path
+
+  GString*path;
+
+  //----- static tables
+
+  NameToCharCode *             // mapping from char name to
+    macRomanReverseMap;                //   MacRomanEncoding index
+
+  //----- user-modifiable settings
+
+  GString *baseDir;            // base directory - for plugins, etc.
+  NameToCharCode *             // mapping from char name to Unicode
+    nameToUnicode;
+  GHash *cidToUnicodes;                // files for mappings from char collections
+                               //   to Unicode, indexed by collection name
+                               //   [GString]
+  GHash *unicodeToUnicodes;    // files for Unicode-to-Unicode mappings,
+                               //   indexed by font name pattern [GString]
+  GHash *residentUnicodeMaps;  // mappings from Unicode to char codes,
+                               //   indexed by encoding name [UnicodeMap]
+  GHash *unicodeMaps;          // files for mappings from Unicode to char
+                               //   codes, indexed by encoding name [GString]
+  GHash *cMapDirs;             // list of CMap dirs, indexed by collection
+                               //   name [GList[GString]]
+  GList *toUnicodeDirs;                // list of ToUnicode CMap dirs [GString]
+  GHash *displayFonts;         // display font info, indexed by font name
+                               //   [DisplayFontParam]
+  GHash *displayCIDFonts;      // display CID font info, indexed by
+                               //   collection [DisplayFontParam]
+  GHash *displayNamedCIDFonts; // display CID font info, indexed by
+                               //   font name [DisplayFontParam]
+  GString *psFile;             // PostScript file or command (for xpdf)
+  int psPaperWidth;            // paper size, in PostScript points, for
+  int psPaperHeight;           //   PostScript output
+  int psImageableLLX,          // imageable area, in PostScript points,
+      psImageableLLY,          //   for PostScript output
+      psImageableURX,
+      psImageableURY;
+  GBool psCrop;                        // crop PS output to CropBox
+  GBool psExpandSmaller;       // expand smaller pages to fill paper
+  GBool psShrinkLarger;                // shrink larger pages to fit paper
+  GBool psCenter;              // center pages on the paper
+  GBool psDuplex;              // enable duplexing in PostScript?
+  PSLevel psLevel;             // PostScript level to generate
+  GHash *psFonts;              // PostScript font info, indexed by PDF
+                               //   font name [PSFontParam]
+  GList *psNamedFonts16;       // named 16-bit fonts [PSFontParam]
+  GList *psFonts16;            // generic 16-bit fonts [PSFontParam]
+  GBool psEmbedType1;          // embed Type 1 fonts?
+  GBool psEmbedTrueType;       // embed TrueType fonts?
+  GBool psEmbedCIDPostScript;  // embed CID PostScript fonts?
+  GBool psEmbedCIDTrueType;    // embed CID TrueType fonts?
+  GBool psOPI;                 // generate PostScript OPI comments?
+  GBool psASCIIHex;            // use ASCIIHex instead of ASCII85?
+  GString *textEncoding;       // encoding (unicodeMap) to use for text
+                               //   output
+  EndOfLineKind textEOL;       // type of EOL marker to use for text
+                               //   output
+  GBool textPageBreaks;                // insert end-of-page markers?
+  GBool textKeepTinyChars;     // keep all characters in text output
+  GList *fontDirs;             // list of font dirs [GString]
+  GString *initialZoom;                // initial zoom level
+  GBool continuousView;                // continuous view mode
+  GBool enableT1lib;           // t1lib enable flag
+  GBool enableFreeType;                // FreeType enable flag
+  GBool antialias;             // anti-aliasing enable flag
+  GString *urlCommand;         // command executed for URL links
+  GString *movieCommand;       // command executed for movie annotations
+  GBool mapNumericCharNames;   // map numeric char names (from font subsets)?
+  GBool printCommands;         // print the drawing commands
+  GBool errQuiet;              // suppress error messages?
+
+  CharCodeToUnicodeCache *cidToUnicodeCache;
+  CharCodeToUnicodeCache *unicodeToUnicodeCache;
+  UnicodeMapCache *unicodeMapCache;
+  CMapCache *cMapCache;
+
+#ifdef ENABLE_PLUGINS
+  GList *plugins;              // list of plugins [Plugin]
+  GList *securityHandlers;     // list of loaded security handlers
+                               //   [XpdfSecurityHandler]
+#endif
+
+#if MULTITHREADED
+  GMutex mutex;
+  GMutex unicodeMapCacheMutex;
+  GMutex cMapCacheMutex;
+#endif
+};
+
+#endif
diff --git a/lib/xpdf/InfoOutputDev.cc b/lib/xpdf/InfoOutputDev.cc
new file mode 100644 (file)
index 0000000..6574d76
--- /dev/null
@@ -0,0 +1,112 @@
+#include "InfoOutputDev.h"
+#include "GfxState.h"
+#include "../log.h"
+#include <math.h>
+
+InfoOutputDev::InfoOutputDev() 
+{
+    num_links = 0;
+    num_images = 0;
+    num_fonts = 0;
+    id2font = new GHash();
+}
+InfoOutputDev::~InfoOutputDev() 
+{
+    delete id2font;
+}
+GBool InfoOutputDev::upsideDown() {return gTrue;}
+GBool InfoOutputDev::useDrawChar() {return gTrue;}
+GBool InfoOutputDev::interpretType3Chars() {return gTrue;}
+void InfoOutputDev::startPage(int pageNum, GfxState *state, double crop_x1, double crop_y1, double crop_x2, double crop_y2)
+{
+    double x1,y1,x2,y2;
+    state->transform(crop_x1,crop_y1,&x1,&y1);
+    state->transform(crop_x2,crop_y2,&x2,&y2);
+    if(x2<x1) {double x3=x1;x1=x2;x2=x3;}
+    if(y2<y1) {double y3=y1;y1=y2;y2=y3;}
+    this->x1 = (int)x1;
+    this->y1 = (int)y1;
+    this->x2 = (int)x2;
+    this->y2 = (int)y2;
+}
+void InfoOutputDev::drawLink(Link *link, Catalog *catalog) 
+{
+    num_links++;
+}
+double InfoOutputDev::getMaximumFontSize(char*id)
+{
+    FontInfo*info = (FontInfo*)id2font->lookup(id);
+    if(!info) {
+       msg("<error> Unknown font id: %s", id);
+       return 0.0;
+    }
+    return info->max_size;
+}
+
+static char*getFontID(GfxFont*font)
+{
+    Ref*ref = font->getID();
+    GString*gstr = font->getName();
+    char* fname = gstr==0?0:gstr->getCString();
+    char buf[128];
+    if(fname==0) {
+       sprintf(buf, "font-%d-%d", ref->num, ref->gen);
+    } else {
+       sprintf(buf, "%s-%d-%d", fname, ref->num, ref->gen);
+    }
+    return strdup(buf);
+}
+
+
+void InfoOutputDev::updateFont(GfxState *state) 
+{
+    GfxFont*font = state->getFont();
+    if(!font)
+       return;
+    char*id = getFontID(font);
+    
+    FontInfo*info = (FontInfo*)id2font->lookup(id);
+    if(!info) {
+      GString* idStr = new GString(id);
+      info = new FontInfo;
+      info->font = font;
+      info->max_size = 0;
+      id2font->add(idStr, (void*)info);
+      free(id);
+      num_fonts++;
+    }
+    currentfont = info;
+}
+
+void InfoOutputDev::drawChar(GfxState *state, double x, double y,
+                     double dx, double dy,
+                     double originX, double originY,
+                     CharCode code, int nBytes, Unicode *u, int uLen)
+{
+    int render = state->getRender();
+    if (render == 3)
+       return;
+    double m11,m21,m12,m22;
+    state->getFontTransMat(&m11, &m12, &m21, &m22);
+    m11 *= state->getHorizScaling();
+    m21 *= state->getHorizScaling();
+    double lenx = sqrt(m11*m11 + m12*m12);
+    double leny = sqrt(m21*m21 + m22*m22);
+    double len = lenx>leny?lenx:leny;
+    if(currentfont && currentfont->max_size < len) {
+       currentfont->max_size = len;
+    }
+}
+void InfoOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
+                          int width, int height, GBool invert,
+                          GBool inlineImg) 
+{
+    num_images++;
+}
+void InfoOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
+                      int width, int height, GfxImageColorMap *colorMap,
+                      int *maskColors, GBool inlineImg)
+{
+    num_images++;
+}
+
diff --git a/lib/xpdf/InfoOutputDev.h b/lib/xpdf/InfoOutputDev.h
new file mode 100644 (file)
index 0000000..27d3b3d
--- /dev/null
@@ -0,0 +1,45 @@
+#ifndef __infooutputdev_h__
+#define __infooutputdev_h__
+
+#include "GfxFont.h"
+#include "OutputDev.h"
+#include "GHash.h"
+
+struct FontInfo
+{
+    GfxFont*font;
+    double max_size;
+};
+
+class InfoOutputDev: public OutputDev 
+{
+  GHash* id2font;
+  FontInfo* currentfont;
+  public:
+  int x1,y1,x2,y2;
+  int num_links;
+  int num_images;
+  int num_fonts;
+
+  InfoOutputDev();
+  virtual ~InfoOutputDev();
+  virtual GBool upsideDown();
+  virtual GBool useDrawChar();
+  virtual GBool interpretType3Chars();
+  virtual void startPage(int pageNum, GfxState *state, double crop_x1, double crop_y1, double crop_x2, double crop_y2);
+  virtual void drawLink(Link *link, Catalog *catalog);
+  virtual double getMaximumFontSize(char*id);
+  virtual void updateFont(GfxState *state);
+  virtual void drawChar(GfxState *state, double x, double y,
+                       double dx, double dy,
+                       double originX, double originY,
+                       CharCode code, int nBytes, Unicode *u, int uLen);
+  virtual void drawImageMask(GfxState *state, Object *ref, Stream *str,
+                            int width, int height, GBool invert,
+                            GBool inlineImg);
+  virtual void drawImage(GfxState *state, Object *ref, Stream *str,
+                        int width, int height, GfxImageColorMap *colorMap,
+                        int *maskColors, GBool inlineImg);
+};
+
+#endif //__infooutputdev_h__
diff --git a/lib/xpdf/JArithmeticDecoder.cc b/lib/xpdf/JArithmeticDecoder.cc
new file mode 100644 (file)
index 0000000..195b73e
--- /dev/null
@@ -0,0 +1,322 @@
+//========================================================================
+//
+// JArithmeticDecoder.cc
+//
+// Copyright 2002-2004 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include "Object.h"
+#include "Stream.h"
+#include "JArithmeticDecoder.h"
+
+//------------------------------------------------------------------------
+// JArithmeticDecoderStates
+//------------------------------------------------------------------------
+
+JArithmeticDecoderStats::JArithmeticDecoderStats(int contextSizeA) {
+  contextSize = contextSizeA;
+  cxTab = (Guchar *)gmallocn(contextSize, sizeof(Guchar));
+  reset();
+}
+
+JArithmeticDecoderStats::~JArithmeticDecoderStats() {
+  gfree(cxTab);
+}
+
+JArithmeticDecoderStats *JArithmeticDecoderStats::copy() {
+  JArithmeticDecoderStats *stats;
+
+  stats = new JArithmeticDecoderStats(contextSize);
+  memcpy(stats->cxTab, cxTab, contextSize);
+  return stats;
+}
+
+void JArithmeticDecoderStats::reset() {
+  memset(cxTab, 0, contextSize);
+}
+
+void JArithmeticDecoderStats::copyFrom(JArithmeticDecoderStats *stats) {
+  memcpy(cxTab, stats->cxTab, contextSize);
+}
+
+void JArithmeticDecoderStats::setEntry(Guint cx, int i, int mps) {
+  cxTab[cx] = (i << 1) + mps;
+}
+
+//------------------------------------------------------------------------
+// JArithmeticDecoder
+//------------------------------------------------------------------------
+
+Guint JArithmeticDecoder::qeTab[47] = {
+  0x56010000, 0x34010000, 0x18010000, 0x0AC10000,
+  0x05210000, 0x02210000, 0x56010000, 0x54010000,
+  0x48010000, 0x38010000, 0x30010000, 0x24010000,
+  0x1C010000, 0x16010000, 0x56010000, 0x54010000,
+  0x51010000, 0x48010000, 0x38010000, 0x34010000,
+  0x30010000, 0x28010000, 0x24010000, 0x22010000,
+  0x1C010000, 0x18010000, 0x16010000, 0x14010000,
+  0x12010000, 0x11010000, 0x0AC10000, 0x09C10000,
+  0x08A10000, 0x05210000, 0x04410000, 0x02A10000,
+  0x02210000, 0x01410000, 0x01110000, 0x00850000,
+  0x00490000, 0x00250000, 0x00150000, 0x00090000,
+  0x00050000, 0x00010000, 0x56010000
+};
+
+int JArithmeticDecoder::nmpsTab[47] = {
+   1,  2,  3,  4,  5, 38,  7,  8,  9, 10, 11, 12, 13, 29, 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, 45, 46
+};
+
+int JArithmeticDecoder::nlpsTab[47] = {
+   1,  6,  9, 12, 29, 33,  6, 14, 14, 14, 17, 18, 20, 21, 14, 14,
+  15, 16, 17, 18, 19, 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, 46
+};
+
+int JArithmeticDecoder::switchTab[47] = {
+  1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 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
+};
+
+JArithmeticDecoder::JArithmeticDecoder() {
+  str = NULL;
+  dataLen = 0;
+  limitStream = gFalse;
+}
+
+inline Guint JArithmeticDecoder::readByte() {
+  if (limitStream) {
+    --dataLen;
+    if (dataLen < 0) {
+      return 0xff;
+    }
+  }
+  return (Guint)str->getChar() & 0xff;
+}
+
+JArithmeticDecoder::~JArithmeticDecoder() {
+  cleanup();
+}
+
+void JArithmeticDecoder::start() {
+  buf0 = readByte();
+  buf1 = readByte();
+
+  // INITDEC
+  c = (buf0 ^ 0xff) << 16;
+  byteIn();
+  c <<= 7;
+  ct -= 7;
+  a = 0x80000000;
+}
+
+void JArithmeticDecoder::restart(int dataLenA) {
+  int oldDataLen;
+
+  oldDataLen = dataLen;
+  dataLen = dataLenA;
+  if (oldDataLen == -1) {
+    buf1 = readByte();
+  } else if (oldDataLen <= -2) {
+    buf0 = readByte();
+    buf1 = readByte();
+  }
+}
+
+void JArithmeticDecoder::cleanup() {
+  if (limitStream) {
+    while (dataLen > 0) {
+      buf0 = buf1;
+      buf1 = readByte();
+    }
+  }
+}
+
+int JArithmeticDecoder::decodeBit(Guint context,
+                                 JArithmeticDecoderStats *stats) {
+  int bit;
+  Guint qe;
+  int iCX, mpsCX;
+
+  iCX = stats->cxTab[context] >> 1;
+  mpsCX = stats->cxTab[context] & 1;
+  qe = qeTab[iCX];
+  a -= qe;
+  if (c < a) {
+    if (a & 0x80000000) {
+      bit = mpsCX;
+    } else {
+      // MPS_EXCHANGE
+      if (a < qe) {
+       bit = 1 - mpsCX;
+       if (switchTab[iCX]) {
+         stats->cxTab[context] = (nlpsTab[iCX] << 1) | (1 - mpsCX);
+       } else {
+         stats->cxTab[context] = (nlpsTab[iCX] << 1) | mpsCX;
+       }
+      } else {
+       bit = mpsCX;
+       stats->cxTab[context] = (nmpsTab[iCX] << 1) | mpsCX;
+      }
+      // RENORMD
+      do {
+       if (ct == 0) {
+         byteIn();
+       }
+       a <<= 1;
+       c <<= 1;
+       --ct;
+      } while (!(a & 0x80000000));
+    }
+  } else {
+    c -= a;
+    // LPS_EXCHANGE
+    if (a < qe) {
+      bit = mpsCX;
+      stats->cxTab[context] = (nmpsTab[iCX] << 1) | mpsCX;
+    } else {
+      bit = 1 - mpsCX;
+      if (switchTab[iCX]) {
+       stats->cxTab[context] = (nlpsTab[iCX] << 1) | (1 - mpsCX);
+      } else {
+       stats->cxTab[context] = (nlpsTab[iCX] << 1) | mpsCX;
+      }
+    }
+    a = qe;
+    // RENORMD
+    do {
+      if (ct == 0) {
+       byteIn();
+      }
+      a <<= 1;
+      c <<= 1;
+      --ct;
+    } while (!(a & 0x80000000));
+  }
+  return bit;
+}
+
+int JArithmeticDecoder::decodeByte(Guint context,
+                                  JArithmeticDecoderStats *stats) {
+  int byte;
+  int i;
+
+  byte = 0;
+  for (i = 0; i < 8; ++i) {
+    byte = (byte << 1) | decodeBit(context, stats);
+  }
+  return byte;
+}
+
+GBool JArithmeticDecoder::decodeInt(int *x, JArithmeticDecoderStats *stats) {
+  int s;
+  Guint v;
+  int i;
+
+  prev = 1;
+  s = decodeIntBit(stats);
+  if (decodeIntBit(stats)) {
+    if (decodeIntBit(stats)) {
+      if (decodeIntBit(stats)) {
+       if (decodeIntBit(stats)) {
+         if (decodeIntBit(stats)) {
+           v = 0;
+           for (i = 0; i < 32; ++i) {
+             v = (v << 1) | decodeIntBit(stats);
+           }
+           v += 4436;
+         } else {
+           v = 0;
+           for (i = 0; i < 12; ++i) {
+             v = (v << 1) | decodeIntBit(stats);
+           }
+           v += 340;
+         }
+       } else {
+         v = 0;
+         for (i = 0; i < 8; ++i) {
+           v = (v << 1) | decodeIntBit(stats);
+         }
+         v += 84;
+       }
+      } else {
+       v = 0;
+       for (i = 0; i < 6; ++i) {
+         v = (v << 1) | decodeIntBit(stats);
+       }
+       v += 20;
+      }
+    } else {
+      v = decodeIntBit(stats);
+      v = (v << 1) | decodeIntBit(stats);
+      v = (v << 1) | decodeIntBit(stats);
+      v = (v << 1) | decodeIntBit(stats);
+      v += 4;
+    }
+  } else {
+    v = decodeIntBit(stats);
+    v = (v << 1) | decodeIntBit(stats);
+  }
+
+  if (s) {
+    if (v == 0) {
+      return gFalse;
+    }
+    *x = -(int)v;
+  } else {
+    *x = (int)v;
+  }
+  return gTrue;
+}
+
+int JArithmeticDecoder::decodeIntBit(JArithmeticDecoderStats *stats) {
+  int bit;
+
+  bit = decodeBit(prev, stats);
+  if (prev < 0x100) {
+    prev = (prev << 1) | bit;
+  } else {
+    prev = (((prev << 1) | bit) & 0x1ff) | 0x100;
+  }
+  return bit;
+}
+
+Guint JArithmeticDecoder::decodeIAID(Guint codeLen,
+                                    JArithmeticDecoderStats *stats) {
+  Guint i;
+  int bit;
+
+  prev = 1;
+  for (i = 0; i < codeLen; ++i) {
+    bit = decodeBit(prev, stats);
+    prev = (prev << 1) | bit;
+  }
+  return prev - (1 << codeLen);
+}
+
+void JArithmeticDecoder::byteIn() {
+  if (buf0 == 0xff) {
+    if (buf1 > 0x8f) {
+      ct = 8;
+    } else {
+      buf0 = buf1;
+      buf1 = readByte();
+      c = c + 0xfe00 - (buf0 << 9);
+      ct = 7;
+    }
+  } else {
+    buf0 = buf1;
+    buf1 = readByte();
+    c = c + 0xff00 - (buf0 << 8);
+    ct = 8;
+  }
+}
diff --git a/lib/xpdf/JArithmeticDecoder.h b/lib/xpdf/JArithmeticDecoder.h
new file mode 100644 (file)
index 0000000..a40823d
--- /dev/null
@@ -0,0 +1,109 @@
+//========================================================================
+//
+// JArithmeticDecoder.h
+//
+// Arithmetic decoder used by the JBIG2 and JPEG2000 decoders.
+//
+// Copyright 2002-2004 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef JARITHMETICDECODER_H
+#define JARITHMETICDECODER_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#include "gtypes.h"
+
+class Stream;
+
+//------------------------------------------------------------------------
+// JArithmeticDecoderStats
+//------------------------------------------------------------------------
+
+class JArithmeticDecoderStats {
+public:
+
+  JArithmeticDecoderStats(int contextSizeA);
+  ~JArithmeticDecoderStats();
+  JArithmeticDecoderStats *copy();
+  void reset();
+  int getContextSize() { return contextSize; }
+  void copyFrom(JArithmeticDecoderStats *stats);
+  void setEntry(Guint cx, int i, int mps);
+
+private:
+
+  Guchar *cxTab;               // cxTab[cx] = (i[cx] << 1) + mps[cx]
+  int contextSize;
+
+  friend class JArithmeticDecoder;
+};
+
+//------------------------------------------------------------------------
+// JArithmeticDecoder
+//------------------------------------------------------------------------
+
+class JArithmeticDecoder {
+public:
+
+  JArithmeticDecoder();
+  ~JArithmeticDecoder();
+
+  void setStream(Stream *strA)
+    { str = strA; dataLen = 0; limitStream = gFalse; }
+  void setStream(Stream *strA, int dataLenA)
+    { str = strA; dataLen = dataLenA; limitStream = gTrue; }
+
+  // Start decoding on a new stream.  This fills the byte buffers and
+  // runs INITDEC.
+  void start();
+
+  // Restart decoding on an interrupted stream.  This refills the
+  // buffers if needed, but does not run INITDEC.  (This is used in
+  // JPEG 2000 streams when codeblock data is split across multiple
+  // packets/layers.)
+  void restart(int dataLenA);
+
+  // Read any leftover data in the stream.
+  void cleanup();
+
+  // Decode one bit.
+  int decodeBit(Guint context, JArithmeticDecoderStats *stats);
+
+  // Decode eight bits.
+  int decodeByte(Guint context, JArithmeticDecoderStats *stats);
+
+  // Returns false for OOB, otherwise sets *<x> and returns true.
+  GBool decodeInt(int *x, JArithmeticDecoderStats *stats);
+
+  Guint decodeIAID(Guint codeLen,
+                  JArithmeticDecoderStats *stats);
+
+private:
+
+  Guint readByte();
+  int decodeIntBit(JArithmeticDecoderStats *stats);
+  void byteIn();
+
+  static Guint qeTab[47];
+  static int nmpsTab[47];
+  static int nlpsTab[47];
+  static int switchTab[47];
+
+  Guint buf0, buf1;
+  Guint c, a;
+  int ct;
+
+  Guint prev;                  // for the integer decoder
+
+  Stream *str;
+  int dataLen;
+  GBool limitStream;
+};
+
+#endif
diff --git a/lib/xpdf/JBIG2Stream.cc b/lib/xpdf/JBIG2Stream.cc
new file mode 100644 (file)
index 0000000..a90db80
--- /dev/null
@@ -0,0 +1,3386 @@
+//========================================================================
+//
+// JBIG2Stream.cc
+//
+// Copyright 2002-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <stdlib.h>
+#include "GList.h"
+#include "Error.h"
+#include "JArithmeticDecoder.h"
+#include "JBIG2Stream.h"
+
+//~ share these tables
+#include "Stream-CCITT.h"
+
+//------------------------------------------------------------------------
+
+static int contextSize[4] = { 16, 13, 10, 10 };
+static int refContextSize[2] = { 13, 10 };
+
+//------------------------------------------------------------------------
+// JBIG2HuffmanTable
+//------------------------------------------------------------------------
+
+#define jbig2HuffmanLOW 0xfffffffd
+#define jbig2HuffmanOOB 0xfffffffe
+#define jbig2HuffmanEOT 0xffffffff
+
+struct JBIG2HuffmanTable {
+  int val;
+  Guint prefixLen;
+  Guint rangeLen;              // can also be LOW, OOB, or EOT
+  Guint prefix;
+};
+
+JBIG2HuffmanTable huffTableA[] = {
+  {     0, 1,  4,              0x000 },
+  {    16, 2,  8,              0x002 },
+  {   272, 3, 16,              0x006 },
+  { 65808, 3, 32,              0x007 },
+  {     0, 0, jbig2HuffmanEOT, 0     }
+};
+
+JBIG2HuffmanTable huffTableB[] = {
+  {     0, 1,  0,              0x000 },
+  {     1, 2,  0,              0x002 },
+  {     2, 3,  0,              0x006 },
+  {     3, 4,  3,              0x00e },
+  {    11, 5,  6,              0x01e },
+  {    75, 6, 32,              0x03e },
+  {     0, 6, jbig2HuffmanOOB, 0x03f },
+  {     0, 0, jbig2HuffmanEOT, 0     }
+};
+
+JBIG2HuffmanTable huffTableC[] = {
+  {     0, 1,  0,              0x000 },
+  {     1, 2,  0,              0x002 },
+  {     2, 3,  0,              0x006 },
+  {     3, 4,  3,              0x00e },
+  {    11, 5,  6,              0x01e },
+  {     0, 6, jbig2HuffmanOOB, 0x03e },
+  {    75, 7, 32,              0x0fe },
+  {  -256, 8,  8,              0x0fe },
+  {  -257, 8, jbig2HuffmanLOW, 0x0ff },
+  {     0, 0, jbig2HuffmanEOT, 0     }
+};
+
+JBIG2HuffmanTable huffTableD[] = {
+  {     1, 1,  0,              0x000 },
+  {     2, 2,  0,              0x002 },
+  {     3, 3,  0,              0x006 },
+  {     4, 4,  3,              0x00e },
+  {    12, 5,  6,              0x01e },
+  {    76, 5, 32,              0x01f },
+  {     0, 0, jbig2HuffmanEOT, 0     }
+};
+
+JBIG2HuffmanTable huffTableE[] = {
+  {     1, 1,  0,              0x000 },
+  {     2, 2,  0,              0x002 },
+  {     3, 3,  0,              0x006 },
+  {     4, 4,  3,              0x00e },
+  {    12, 5,  6,              0x01e },
+  {    76, 6, 32,              0x03e },
+  {  -255, 7,  8,              0x07e },
+  {  -256, 7, jbig2HuffmanLOW, 0x07f },
+  {     0, 0, jbig2HuffmanEOT, 0     }
+};
+
+JBIG2HuffmanTable huffTableF[] = {
+  {     0, 2,  7,              0x000 },
+  {   128, 3,  7,              0x002 },
+  {   256, 3,  8,              0x003 },
+  { -1024, 4,  9,              0x008 },
+  {  -512, 4,  8,              0x009 },
+  {  -256, 4,  7,              0x00a },
+  {   -32, 4,  5,              0x00b },
+  {   512, 4,  9,              0x00c },
+  {  1024, 4, 10,              0x00d },
+  { -2048, 5, 10,              0x01c },
+  {  -128, 5,  6,              0x01d },
+  {   -64, 5,  5,              0x01e },
+  { -2049, 6, jbig2HuffmanLOW, 0x03e },
+  {  2048, 6, 32,              0x03f },
+  {     0, 0, jbig2HuffmanEOT, 0     }
+};
+
+JBIG2HuffmanTable huffTableG[] = {
+  {  -512, 3,  8,              0x000 },
+  {   256, 3,  8,              0x001 },
+  {   512, 3,  9,              0x002 },
+  {  1024, 3, 10,              0x003 },
+  { -1024, 4,  9,              0x008 },
+  {  -256, 4,  7,              0x009 },
+  {   -32, 4,  5,              0x00a },
+  {     0, 4,  5,              0x00b },
+  {   128, 4,  7,              0x00c },
+  {  -128, 5,  6,              0x01a },
+  {   -64, 5,  5,              0x01b },
+  {    32, 5,  5,              0x01c },
+  {    64, 5,  6,              0x01d },
+  { -1025, 5, jbig2HuffmanLOW, 0x01e },
+  {  2048, 5, 32,              0x01f },
+  {     0, 0, jbig2HuffmanEOT, 0     }
+};
+
+JBIG2HuffmanTable huffTableH[] = {
+  {     0, 2,  1,              0x000 },
+  {     0, 2, jbig2HuffmanOOB, 0x001 },
+  {     4, 3,  4,              0x004 },
+  {    -1, 4,  0,              0x00a },
+  {    22, 4,  4,              0x00b },
+  {    38, 4,  5,              0x00c },
+  {     2, 5,  0,              0x01a },
+  {    70, 5,  6,              0x01b },
+  {   134, 5,  7,              0x01c },
+  {     3, 6,  0,              0x03a },
+  {    20, 6,  1,              0x03b },
+  {   262, 6,  7,              0x03c },
+  {   646, 6, 10,              0x03d },
+  {    -2, 7,  0,              0x07c },
+  {   390, 7,  8,              0x07d },
+  {   -15, 8,  3,              0x0fc },
+  {    -5, 8,  1,              0x0fd },
+  {    -7, 9,  1,              0x1fc },
+  {    -3, 9,  0,              0x1fd },
+  {   -16, 9, jbig2HuffmanLOW, 0x1fe },
+  {  1670, 9, 32,              0x1ff },
+  {     0, 0, jbig2HuffmanEOT, 0     }
+};
+
+JBIG2HuffmanTable huffTableI[] = {
+  {     0, 2, jbig2HuffmanOOB, 0x000 },
+  {    -1, 3,  1,              0x002 },
+  {     1, 3,  1,              0x003 },
+  {     7, 3,  5,              0x004 },
+  {    -3, 4,  1,              0x00a },
+  {    43, 4,  5,              0x00b },
+  {    75, 4,  6,              0x00c },
+  {     3, 5,  1,              0x01a },
+  {   139, 5,  7,              0x01b },
+  {   267, 5,  8,              0x01c },
+  {     5, 6,  1,              0x03a },
+  {    39, 6,  2,              0x03b },
+  {   523, 6,  8,              0x03c },
+  {  1291, 6, 11,              0x03d },
+  {    -5, 7,  1,              0x07c },
+  {   779, 7,  9,              0x07d },
+  {   -31, 8,  4,              0x0fc },
+  {   -11, 8,  2,              0x0fd },
+  {   -15, 9,  2,              0x1fc },
+  {    -7, 9,  1,              0x1fd },
+  {   -32, 9, jbig2HuffmanLOW, 0x1fe },
+  {  3339, 9, 32,              0x1ff },
+  {     0, 0, jbig2HuffmanEOT, 0     }
+};
+
+JBIG2HuffmanTable huffTableJ[] = {
+  {    -2, 2,  2,              0x000 },
+  {     6, 2,  6,              0x001 },
+  {     0, 2, jbig2HuffmanOOB, 0x002 },
+  {    -3, 5,  0,              0x018 },
+  {     2, 5,  0,              0x019 },
+  {    70, 5,  5,              0x01a },
+  {     3, 6,  0,              0x036 },
+  {   102, 6,  5,              0x037 },
+  {   134, 6,  6,              0x038 },
+  {   198, 6,  7,              0x039 },
+  {   326, 6,  8,              0x03a },
+  {   582, 6,  9,              0x03b },
+  {  1094, 6, 10,              0x03c },
+  {   -21, 7,  4,              0x07a },
+  {    -4, 7,  0,              0x07b },
+  {     4, 7,  0,              0x07c },
+  {  2118, 7, 11,              0x07d },
+  {    -5, 8,  0,              0x0fc },
+  {     5, 8,  0,              0x0fd },
+  {   -22, 8, jbig2HuffmanLOW, 0x0fe },
+  {  4166, 8, 32,              0x0ff },
+  {     0, 0, jbig2HuffmanEOT, 0     }
+};
+
+JBIG2HuffmanTable huffTableK[] = {
+  {     1, 1,  0,              0x000 },
+  {     2, 2,  1,              0x002 },
+  {     4, 4,  0,              0x00c },
+  {     5, 4,  1,              0x00d },
+  {     7, 5,  1,              0x01c },
+  {     9, 5,  2,              0x01d },
+  {    13, 6,  2,              0x03c },
+  {    17, 7,  2,              0x07a },
+  {    21, 7,  3,              0x07b },
+  {    29, 7,  4,              0x07c },
+  {    45, 7,  5,              0x07d },
+  {    77, 7,  6,              0x07e },
+  {   141, 7, 32,              0x07f },
+  {     0, 0, jbig2HuffmanEOT, 0     }
+};
+
+JBIG2HuffmanTable huffTableL[] = {
+  {     1, 1,  0,              0x000 },
+  {     2, 2,  0,              0x002 },
+  {     3, 3,  1,              0x006 },
+  {     5, 5,  0,              0x01c },
+  {     6, 5,  1,              0x01d },
+  {     8, 6,  1,              0x03c },
+  {    10, 7,  0,              0x07a },
+  {    11, 7,  1,              0x07b },
+  {    13, 7,  2,              0x07c },
+  {    17, 7,  3,              0x07d },
+  {    25, 7,  4,              0x07e },
+  {    41, 8,  5,              0x0fe },
+  {    73, 8, 32,              0x0ff },
+  {     0, 0, jbig2HuffmanEOT, 0     }
+};
+
+JBIG2HuffmanTable huffTableM[] = {
+  {     1, 1,  0,              0x000 },
+  {     2, 3,  0,              0x004 },
+  {     7, 3,  3,              0x005 },
+  {     3, 4,  0,              0x00c },
+  {     5, 4,  1,              0x00d },
+  {     4, 5,  0,              0x01c },
+  {    15, 6,  1,              0x03a },
+  {    17, 6,  2,              0x03b },
+  {    21, 6,  3,              0x03c },
+  {    29, 6,  4,              0x03d },
+  {    45, 6,  5,              0x03e },
+  {    77, 7,  6,              0x07e },
+  {   141, 7, 32,              0x07f },
+  {     0, 0, jbig2HuffmanEOT, 0     }
+};
+
+JBIG2HuffmanTable huffTableN[] = {
+  {     0, 1,  0,              0x000 },
+  {    -2, 3,  0,              0x004 },
+  {    -1, 3,  0,              0x005 },
+  {     1, 3,  0,              0x006 },
+  {     2, 3,  0,              0x007 },
+  {     0, 0, jbig2HuffmanEOT, 0     }
+};
+
+JBIG2HuffmanTable huffTableO[] = {
+  {     0, 1,  0,              0x000 },
+  {    -1, 3,  0,              0x004 },
+  {     1, 3,  0,              0x005 },
+  {    -2, 4,  0,              0x00c },
+  {     2, 4,  0,              0x00d },
+  {    -4, 5,  1,              0x01c },
+  {     3, 5,  1,              0x01d },
+  {    -8, 6,  2,              0x03c },
+  {     5, 6,  2,              0x03d },
+  {   -24, 7,  4,              0x07c },
+  {     9, 7,  4,              0x07d },
+  {   -25, 7, jbig2HuffmanLOW, 0x07e },
+  {    25, 7, 32,              0x07f },
+  {     0, 0, jbig2HuffmanEOT, 0     }
+};
+
+//------------------------------------------------------------------------
+// JBIG2HuffmanDecoder
+//------------------------------------------------------------------------
+
+class JBIG2HuffmanDecoder {
+public:
+
+  JBIG2HuffmanDecoder();
+  ~JBIG2HuffmanDecoder();
+  void setStream(Stream *strA) { str = strA; }
+
+  void reset();
+
+  // Returns false for OOB, otherwise sets *<x> and returns true.
+  GBool decodeInt(int *x, JBIG2HuffmanTable *table);
+
+  Guint readBits(Guint n);
+  Guint readBit();
+
+  // Sort the table by prefix length and assign prefix values.
+  void buildTable(JBIG2HuffmanTable *table, Guint len);
+
+private:
+
+  Stream *str;
+  Guint buf;
+  Guint bufLen;
+};
+
+JBIG2HuffmanDecoder::JBIG2HuffmanDecoder() {
+  str = NULL;
+  reset();
+}
+
+JBIG2HuffmanDecoder::~JBIG2HuffmanDecoder() {
+}
+
+void JBIG2HuffmanDecoder::reset() {
+  buf = 0;
+  bufLen = 0;
+}
+
+//~ optimize this
+GBool JBIG2HuffmanDecoder::decodeInt(int *x, JBIG2HuffmanTable *table) {
+  Guint i, len, prefix;
+
+  i = 0;
+  len = 0;
+  prefix = 0;
+  while (table[i].rangeLen != jbig2HuffmanEOT) {
+    while (len < table[i].prefixLen) {
+      prefix = (prefix << 1) | readBit();
+      ++len;
+    }
+    if (prefix == table[i].prefix) {
+      if (table[i].rangeLen == jbig2HuffmanOOB) {
+       return gFalse;
+      }
+      if (table[i].rangeLen == jbig2HuffmanLOW) {
+       *x = table[i].val - readBits(32);
+      } else if (table[i].rangeLen > 0) {
+       *x = table[i].val + readBits(table[i].rangeLen);
+      } else {
+       *x = table[i].val;
+      }
+      return gTrue;
+    }
+    ++i;
+  }
+  return gFalse;
+}
+
+Guint JBIG2HuffmanDecoder::readBits(Guint n) {
+  Guint x, mask, nLeft;
+
+  mask = (n == 32) ? 0xffffffff : ((1 << n) - 1);
+  if (bufLen >= n) {
+    x = (buf >> (bufLen - n)) & mask;
+    bufLen -= n;
+  } else {
+    x = buf & ((1 << bufLen) - 1);
+    nLeft = n - bufLen;
+    bufLen = 0;
+    while (nLeft >= 8) {
+      x = (x << 8) | (str->getChar() & 0xff);
+      nLeft -= 8;
+    }
+    if (nLeft > 0) {
+      buf = str->getChar();
+      bufLen = 8 - nLeft;
+      x = (x << nLeft) | ((buf >> bufLen) & ((1 << nLeft) - 1));
+    }
+  }
+  return x;
+}
+
+Guint JBIG2HuffmanDecoder::readBit() {
+  if (bufLen == 0) {
+    buf = str->getChar();
+    bufLen = 8;
+  }
+  --bufLen;
+  return (buf >> bufLen) & 1;
+}
+
+void JBIG2HuffmanDecoder::buildTable(JBIG2HuffmanTable *table, Guint len) {
+  Guint i, j, k, prefix;
+  JBIG2HuffmanTable tab;
+
+  // stable selection sort:
+  // - entries with prefixLen > 0, in ascending prefixLen order
+  // - entry with prefixLen = 0, rangeLen = EOT
+  // - all other entries with prefixLen = 0
+  // (on entry, table[len] has prefixLen = 0, rangeLen = EOT)
+  for (i = 0; i < len; ++i) {
+    for (j = i; j < len && table[j].prefixLen == 0; ++j) ;
+    if (j == len) {
+      break;
+    }
+    for (k = j + 1; k < len; ++k) {
+      if (table[k].prefixLen > 0 &&
+         table[k].prefixLen < table[j].prefixLen) {
+       j = k;
+      }
+    }
+    if (j != i) {
+      tab = table[j];
+      for (k = j; k > i; --k) {
+       table[k] = table[k - 1];
+      }
+      table[i] = tab;
+    }
+  }
+  table[i] = table[len];
+
+  // assign prefixes
+  i = 0;
+  prefix = 0;
+  table[i++].prefix = prefix++;
+  for (; table[i].rangeLen != jbig2HuffmanEOT; ++i) {
+    prefix <<= table[i].prefixLen - table[i-1].prefixLen;
+    table[i].prefix = prefix++;
+  }
+}
+
+//------------------------------------------------------------------------
+// JBIG2MMRDecoder
+//------------------------------------------------------------------------
+
+class JBIG2MMRDecoder {
+public:
+
+  JBIG2MMRDecoder();
+  ~JBIG2MMRDecoder();
+  void setStream(Stream *strA) { str = strA; }
+  void reset();
+  int get2DCode();
+  int getBlackCode();
+  int getWhiteCode();
+  Guint get24Bits();
+  void skipTo(Guint length);
+
+private:
+
+  Stream *str;
+  Guint buf;
+  Guint bufLen;
+  Guint nBytesRead;
+};
+
+JBIG2MMRDecoder::JBIG2MMRDecoder() {
+  str = NULL;
+  reset();
+}
+
+JBIG2MMRDecoder::~JBIG2MMRDecoder() {
+}
+
+void JBIG2MMRDecoder::reset() {
+  buf = 0;
+  bufLen = 0;
+  nBytesRead = 0;
+}
+
+int JBIG2MMRDecoder::get2DCode() {
+  CCITTCode *p;
+
+  if (bufLen == 0) {
+    buf = str->getChar() & 0xff;
+    bufLen = 8;
+    ++nBytesRead;
+    p = &twoDimTab1[(buf >> 1) & 0x7f];
+  } else if (bufLen == 8) {
+    p = &twoDimTab1[(buf >> 1) & 0x7f];
+  } else {
+    p = &twoDimTab1[(buf << (7 - bufLen)) & 0x7f];
+    if (p->bits < 0 || p->bits > (int)bufLen) {
+      buf = (buf << 8) | (str->getChar() & 0xff);
+      bufLen += 8;
+      ++nBytesRead;
+      p = &twoDimTab1[(buf >> (bufLen - 7)) & 0x7f];
+    }
+  }
+  if (p->bits < 0) {
+    error(str->getPos(), "Bad two dim code in JBIG2 MMR stream");
+    return 0;
+  }
+  bufLen -= p->bits;
+  return p->n;
+}
+
+int JBIG2MMRDecoder::getWhiteCode() {
+  CCITTCode *p;
+  Guint code;
+
+  if (bufLen == 0) {
+    buf = str->getChar() & 0xff;
+    bufLen = 8;
+    ++nBytesRead;
+  }
+  while (1) {
+    if (bufLen >= 7 && ((buf >> (bufLen - 7)) & 0x7f) == 0) {
+      if (bufLen <= 12) {
+       code = buf << (12 - bufLen);
+      } else {
+       code = buf >> (bufLen - 12);
+      }
+      p = &whiteTab1[code & 0x1f];
+    } else {
+      if (bufLen <= 9) {
+       code = buf << (9 - bufLen);
+      } else {
+       code = buf >> (bufLen - 9);
+      }
+      p = &whiteTab2[code & 0x1ff];
+    }
+    if (p->bits > 0 && p->bits <= (int)bufLen) {
+      bufLen -= p->bits;
+      return p->n;
+    }
+    if (bufLen >= 12) {
+      break;
+    }
+    buf = (buf << 8) | (str->getChar() & 0xff);
+    bufLen += 8;
+    ++nBytesRead;
+  }
+  error(str->getPos(), "Bad white code in JBIG2 MMR stream");
+  // eat a bit and return a positive number so that the caller doesn't
+  // go into an infinite loop
+  --bufLen;
+  return 1;
+}
+
+int JBIG2MMRDecoder::getBlackCode() {
+  CCITTCode *p;
+  Guint code;
+
+  if (bufLen == 0) {
+    buf = str->getChar() & 0xff;
+    bufLen = 8;
+    ++nBytesRead;
+  }
+  while (1) {
+    if (bufLen >= 6 && ((buf >> (bufLen - 6)) & 0x3f) == 0) {
+      if (bufLen <= 13) {
+       code = buf << (13 - bufLen);
+      } else {
+       code = buf >> (bufLen - 13);
+      }
+      p = &blackTab1[code & 0x7f];
+    } else if (bufLen >= 4 && ((buf >> (bufLen - 4)) & 0x0f) == 0) {
+      if (bufLen <= 12) {
+       code = buf << (12 - bufLen);
+      } else {
+       code = buf >> (bufLen - 12);
+      }
+      p = &blackTab2[(code & 0xff) - 64];
+    } else {
+      if (bufLen <= 6) {
+       code = buf << (6 - bufLen);
+      } else {
+       code = buf >> (bufLen - 6);
+      }
+      p = &blackTab3[code & 0x3f];
+    }
+    if (p->bits > 0 && p->bits <= (int)bufLen) {
+      bufLen -= p->bits;
+      return p->n;
+    }
+    if (bufLen >= 13) {
+      break;
+    }
+    buf = (buf << 8) | (str->getChar() & 0xff);
+    bufLen += 8;
+    ++nBytesRead;
+  }
+  error(str->getPos(), "Bad black code in JBIG2 MMR stream");
+  // eat a bit and return a positive number so that the caller doesn't
+  // go into an infinite loop
+  --bufLen;
+  return 1;
+}
+
+Guint JBIG2MMRDecoder::get24Bits() {
+  while (bufLen < 24) {
+    buf = (buf << 8) | (str->getChar() & 0xff);
+    bufLen += 8;
+    ++nBytesRead;
+  }
+  return (buf >> (bufLen - 24)) & 0xffffff;
+}
+
+void JBIG2MMRDecoder::skipTo(Guint length) {
+  while (nBytesRead < length) {
+    str->getChar();
+    ++nBytesRead;
+  }
+}
+
+//------------------------------------------------------------------------
+// JBIG2Segment
+//------------------------------------------------------------------------
+
+enum JBIG2SegmentType {
+  jbig2SegBitmap,
+  jbig2SegSymbolDict,
+  jbig2SegPatternDict,
+  jbig2SegCodeTable
+};
+
+class JBIG2Segment {
+public:
+
+  JBIG2Segment(Guint segNumA) { segNum = segNumA; }
+  virtual ~JBIG2Segment() {}
+  void setSegNum(Guint segNumA) { segNum = segNumA; }
+  Guint getSegNum() { return segNum; }
+  virtual JBIG2SegmentType getType() = 0;
+
+private:
+
+  Guint segNum;
+};
+
+//------------------------------------------------------------------------
+// JBIG2Bitmap
+//------------------------------------------------------------------------
+
+struct JBIG2BitmapPtr {
+  Guchar *p;
+  int shift;
+  int x;
+};
+
+class JBIG2Bitmap: public JBIG2Segment {
+public:
+
+  JBIG2Bitmap(Guint segNumA, int wA, int hA);
+  virtual ~JBIG2Bitmap();
+  virtual JBIG2SegmentType getType() { return jbig2SegBitmap; }
+  JBIG2Bitmap *copy() { return new JBIG2Bitmap(0, this); }
+  JBIG2Bitmap *getSlice(Guint x, Guint y, Guint wA, Guint hA);
+  void expand(int newH, Guint pixel);
+  void clearToZero();
+  void clearToOne();
+  int getWidth() { return w; }
+  int getHeight() { return h; }
+  int getPixel(int x, int y)
+    { return (x < 0 || x >= w || y < 0 || y >= h) ? 0 :
+             (data[y * line + (x >> 3)] >> (7 - (x & 7))) & 1; }
+  void setPixel(int x, int y)
+    { data[y * line + (x >> 3)] |= 1 << (7 - (x & 7)); }
+  void clearPixel(int x, int y)
+    { data[y * line + (x >> 3)] &= 0x7f7f >> (x & 7); }
+  void getPixelPtr(int x, int y, JBIG2BitmapPtr *ptr);
+  int nextPixel(JBIG2BitmapPtr *ptr);
+  void duplicateRow(int yDest, int ySrc);
+  void combine(JBIG2Bitmap *bitmap, int x, int y, Guint combOp);
+  Guchar *getDataPtr() { return data; }
+  int getDataSize() { return h * line; }
+
+private:
+
+  JBIG2Bitmap(Guint segNumA, JBIG2Bitmap *bitmap);
+
+  int w, h, line;
+  Guchar *data;
+};
+
+JBIG2Bitmap::JBIG2Bitmap(Guint segNumA, int wA, int hA):
+  JBIG2Segment(segNumA)
+{
+  w = wA;
+  h = hA;
+  line = (wA + 7) >> 3;
+  // need to allocate one extra guard byte for use in combine()
+  data = (Guchar *)gmalloc(h * line + 1);
+  data[h * line] = 0;
+}
+
+JBIG2Bitmap::JBIG2Bitmap(Guint segNumA, JBIG2Bitmap *bitmap):
+  JBIG2Segment(segNumA)
+{
+  w = bitmap->w;
+  h = bitmap->h;
+  line = bitmap->line;
+  // need to allocate one extra guard byte for use in combine()
+  data = (Guchar *)gmalloc(h * line + 1);
+  memcpy(data, bitmap->data, h * line);
+  data[h * line] = 0;
+}
+
+JBIG2Bitmap::~JBIG2Bitmap() {
+  gfree(data);
+}
+
+//~ optimize this
+JBIG2Bitmap *JBIG2Bitmap::getSlice(Guint x, Guint y, Guint wA, Guint hA) {
+  JBIG2Bitmap *slice;
+  Guint xx, yy;
+
+  slice = new JBIG2Bitmap(0, wA, hA);
+  slice->clearToZero();
+  for (yy = 0; yy < hA; ++yy) {
+    for (xx = 0; xx < wA; ++xx) {
+      if (getPixel(x + xx, y + yy)) {
+       slice->setPixel(xx, yy);
+      }
+    }
+  }
+  return slice;
+}
+
+void JBIG2Bitmap::expand(int newH, Guint pixel) {
+  if (newH <= h) {
+    return;
+  }
+  // need to allocate one extra guard byte for use in combine()
+  data = (Guchar *)grealloc(data, newH * line + 1);
+  if (pixel) {
+    memset(data + h * line, 0xff, (newH - h) * line);
+  } else {
+    memset(data + h * line, 0x00, (newH - h) * line);
+  }
+  h = newH;
+  data[h * line] = 0;
+}
+
+void JBIG2Bitmap::clearToZero() {
+  memset(data, 0, h * line);
+}
+
+void JBIG2Bitmap::clearToOne() {
+  memset(data, 0xff, h * line);
+}
+
+inline void JBIG2Bitmap::getPixelPtr(int x, int y, JBIG2BitmapPtr *ptr) {
+  if (y < 0 || y >= h || x >= w) {
+    ptr->p = NULL;
+  } else if (x < 0) {
+    ptr->p = &data[y * line];
+    ptr->shift = 7;
+    ptr->x = x;
+  } else {
+    ptr->p = &data[y * line + (x >> 3)];
+    ptr->shift = 7 - (x & 7);
+    ptr->x = x;
+  }
+}
+
+inline int JBIG2Bitmap::nextPixel(JBIG2BitmapPtr *ptr) {
+  int pix;
+
+  if (!ptr->p) {
+    pix = 0;
+  } else if (ptr->x < 0) {
+    ++ptr->x;
+    pix = 0;
+  } else {
+    pix = (*ptr->p >> ptr->shift) & 1;
+    if (++ptr->x == w) {
+      ptr->p = NULL;
+    } else if (ptr->shift == 0) {
+      ++ptr->p;
+      ptr->shift = 7;
+    } else {
+      --ptr->shift;
+    }
+  }
+  return pix;
+}
+
+void JBIG2Bitmap::duplicateRow(int yDest, int ySrc) {
+  memcpy(data + yDest * line, data + ySrc * line, line);
+}
+
+void JBIG2Bitmap::combine(JBIG2Bitmap *bitmap, int x, int y,
+                         Guint combOp) {
+  int x0, x1, y0, y1, xx, yy;
+  Guchar *srcPtr, *destPtr;
+  Guint src0, src1, src, dest, s1, s2, m1, m2, m3;
+  GBool oneByte;
+
+  if (y < 0) {
+    y0 = -y;
+  } else {
+    y0 = 0;
+  }
+  if (y + bitmap->h > h) {
+    y1 = h - y;
+  } else {
+    y1 = bitmap->h;
+  }
+  if (y0 >= y1) {
+    return;
+  }
+
+  if (x >= 0) {
+    x0 = x & ~7;
+  } else {
+    x0 = 0;
+  }
+  x1 = x + bitmap->w;
+  if (x1 > w) {
+    x1 = w;
+  }
+  if (x0 >= x1) {
+    return;
+  }
+
+  s1 = x & 7;
+  s2 = 8 - s1;
+  m1 = 0xff >> (x1 & 7);
+  m2 = 0xff << (((x1 & 7) == 0) ? 0 : 8 - (x1 & 7));
+  m3 = (0xff >> s1) & m2;
+
+  oneByte = x0 == ((x1 - 1) & ~7);
+
+  for (yy = y0; yy < y1; ++yy) {
+
+    // one byte per line -- need to mask both left and right side
+    if (oneByte) {
+      if (x >= 0) {
+       destPtr = data + (y + yy) * line + (x >> 3);
+       srcPtr = bitmap->data + yy * bitmap->line;
+       dest = *destPtr;
+       src1 = *srcPtr;
+       switch (combOp) {
+       case 0: // or
+         dest |= (src1 >> s1) & m2;
+         break;
+       case 1: // and
+         dest &= ((0xff00 | src1) >> s1) | m1;
+         break;
+       case 2: // xor
+         dest ^= (src1 >> s1) & m2;
+         break;
+       case 3: // xnor
+         dest ^= ((src1 ^ 0xff) >> s1) & m2;
+         break;
+       case 4: // replace
+         dest = (dest & ~m3) | ((src1 >> s1) & m3);
+         break;
+       }
+       *destPtr = dest;
+      } else {
+       destPtr = data + (y + yy) * line;
+       srcPtr = bitmap->data + yy * bitmap->line + (-x >> 3);
+       dest = *destPtr;
+       src1 = *srcPtr;
+       switch (combOp) {
+       case 0: // or
+         dest |= src1 & m2;
+         break;
+       case 1: // and
+         dest &= src1 | m1;
+         break;
+       case 2: // xor
+         dest ^= src1 & m2;
+         break;
+       case 3: // xnor
+         dest ^= (src1 ^ 0xff) & m2;
+         break;
+       case 4: // replace
+         dest = (src1 & m2) | (dest & m1);
+         break;
+       }
+       *destPtr = dest;
+      }
+
+    // multiple bytes per line -- need to mask left side of left-most
+    // byte and right side of right-most byte
+    } else {
+
+      // left-most byte
+      if (x >= 0) {
+       destPtr = data + (y + yy) * line + (x >> 3);
+       srcPtr = bitmap->data + yy * bitmap->line;
+       src1 = *srcPtr++;
+       dest = *destPtr;
+       switch (combOp) {
+       case 0: // or
+         dest |= src1 >> s1;
+         break;
+       case 1: // and
+         dest &= (0xff00 | src1) >> s1;
+         break;
+       case 2: // xor
+         dest ^= src1 >> s1;
+         break;
+       case 3: // xnor
+         dest ^= (src1 ^ 0xff) >> s1;
+         break;
+       case 4: // replace
+         dest = (dest & (0xff << s2)) | (src1 >> s1);
+         break;
+       }
+       *destPtr++ = dest;
+       xx = x0 + 8;
+      } else {
+       destPtr = data + (y + yy) * line;
+       srcPtr = bitmap->data + yy * bitmap->line + (-x >> 3);
+       src1 = *srcPtr++;
+       xx = x0;
+      }
+
+      // middle bytes
+      for (; xx < x1 - 8; xx += 8) {
+       dest = *destPtr;
+       src0 = src1;
+       src1 = *srcPtr++;
+       src = (((src0 << 8) | src1) >> s1) & 0xff;
+       switch (combOp) {
+       case 0: // or
+         dest |= src;
+         break;
+       case 1: // and
+         dest &= src;
+         break;
+       case 2: // xor
+         dest ^= src;
+         break;
+       case 3: // xnor
+         dest ^= src ^ 0xff;
+         break;
+       case 4: // replace
+         dest = src;
+         break;
+       }
+       *destPtr++ = dest;
+      }
+
+      // right-most byte
+      // note: this last byte (src1) may not actually be used, depending
+      // on the values of s1, m1, and m2 - and in fact, it may be off
+      // the edge of the source bitmap, which means we need to allocate
+      // one extra guard byte at the end of each bitmap
+      dest = *destPtr;
+      src0 = src1;
+      src1 = *srcPtr++;
+      src = (((src0 << 8) | src1) >> s1) & 0xff;
+      switch (combOp) {
+      case 0: // or
+       dest |= src & m2;
+       break;
+      case 1: // and
+       dest &= src | m1;
+       break;
+      case 2: // xor
+       dest ^= src & m2;
+       break;
+      case 3: // xnor
+       dest ^= (src ^ 0xff) & m2;
+       break;
+      case 4: // replace
+       dest = (src & m2) | (dest & m1);
+       break;
+      }
+      *destPtr = dest;
+    }
+  }
+}
+
+//------------------------------------------------------------------------
+// JBIG2SymbolDict
+//------------------------------------------------------------------------
+
+class JBIG2SymbolDict: public JBIG2Segment {
+public:
+
+  JBIG2SymbolDict(Guint segNumA, Guint sizeA);
+  virtual ~JBIG2SymbolDict();
+  virtual JBIG2SegmentType getType() { return jbig2SegSymbolDict; }
+  Guint getSize() { return size; }
+  void setBitmap(Guint idx, JBIG2Bitmap *bitmap) { bitmaps[idx] = bitmap; }
+  JBIG2Bitmap *getBitmap(Guint idx) { return bitmaps[idx]; }
+  void setGenericRegionStats(JArithmeticDecoderStats *stats)
+    { genericRegionStats = stats; }
+  void setRefinementRegionStats(JArithmeticDecoderStats *stats)
+    { refinementRegionStats = stats; }
+  JArithmeticDecoderStats *getGenericRegionStats()
+    { return genericRegionStats; }
+  JArithmeticDecoderStats *getRefinementRegionStats()
+    { return refinementRegionStats; }
+
+private:
+
+  Guint size;
+  JBIG2Bitmap **bitmaps;
+  JArithmeticDecoderStats *genericRegionStats;
+  JArithmeticDecoderStats *refinementRegionStats;
+};
+
+JBIG2SymbolDict::JBIG2SymbolDict(Guint segNumA, Guint sizeA):
+  JBIG2Segment(segNumA)
+{
+  size = sizeA;
+  bitmaps = (JBIG2Bitmap **)gmallocn(size, sizeof(JBIG2Bitmap *));
+  genericRegionStats = NULL;
+  refinementRegionStats = NULL;
+}
+
+JBIG2SymbolDict::~JBIG2SymbolDict() {
+  Guint i;
+
+  for (i = 0; i < size; ++i) {
+    delete bitmaps[i];
+  }
+  gfree(bitmaps);
+  if (genericRegionStats) {
+    delete genericRegionStats;
+  }
+  if (refinementRegionStats) {
+    delete refinementRegionStats;
+  }
+}
+
+//------------------------------------------------------------------------
+// JBIG2PatternDict
+//------------------------------------------------------------------------
+
+class JBIG2PatternDict: public JBIG2Segment {
+public:
+
+  JBIG2PatternDict(Guint segNumA, Guint sizeA);
+  virtual ~JBIG2PatternDict();
+  virtual JBIG2SegmentType getType() { return jbig2SegPatternDict; }
+  Guint getSize() { return size; }
+  void setBitmap(Guint idx, JBIG2Bitmap *bitmap) { bitmaps[idx] = bitmap; }
+  JBIG2Bitmap *getBitmap(Guint idx) { return bitmaps[idx]; }
+
+private:
+
+  Guint size;
+  JBIG2Bitmap **bitmaps;
+};
+
+JBIG2PatternDict::JBIG2PatternDict(Guint segNumA, Guint sizeA):
+  JBIG2Segment(segNumA)
+{
+  size = sizeA;
+  bitmaps = (JBIG2Bitmap **)gmallocn(size, sizeof(JBIG2Bitmap *));
+}
+
+JBIG2PatternDict::~JBIG2PatternDict() {
+  Guint i;
+
+  for (i = 0; i < size; ++i) {
+    delete bitmaps[i];
+  }
+  gfree(bitmaps);
+}
+
+//------------------------------------------------------------------------
+// JBIG2CodeTable
+//------------------------------------------------------------------------
+
+class JBIG2CodeTable: public JBIG2Segment {
+public:
+
+  JBIG2CodeTable(Guint segNumA, JBIG2HuffmanTable *tableA);
+  virtual ~JBIG2CodeTable();
+  virtual JBIG2SegmentType getType() { return jbig2SegCodeTable; }
+  JBIG2HuffmanTable *getHuffTable() { return table; }
+
+private:
+
+  JBIG2HuffmanTable *table;
+};
+
+JBIG2CodeTable::JBIG2CodeTable(Guint segNumA, JBIG2HuffmanTable *tableA):
+  JBIG2Segment(segNumA)
+{
+  table = tableA;
+}
+
+JBIG2CodeTable::~JBIG2CodeTable() {
+  gfree(table);
+}
+
+//------------------------------------------------------------------------
+// JBIG2Stream
+//------------------------------------------------------------------------
+
+JBIG2Stream::JBIG2Stream(Stream *strA, Object *globalsStream):
+  FilterStream(strA)
+{
+  pageBitmap = NULL;
+
+  arithDecoder = new JArithmeticDecoder();
+  genericRegionStats = new JArithmeticDecoderStats(1 << 1);
+  refinementRegionStats = new JArithmeticDecoderStats(1 << 1);
+  iadhStats = new JArithmeticDecoderStats(1 << 9);
+  iadwStats = new JArithmeticDecoderStats(1 << 9);
+  iaexStats = new JArithmeticDecoderStats(1 << 9);
+  iaaiStats = new JArithmeticDecoderStats(1 << 9);
+  iadtStats = new JArithmeticDecoderStats(1 << 9);
+  iaitStats = new JArithmeticDecoderStats(1 << 9);
+  iafsStats = new JArithmeticDecoderStats(1 << 9);
+  iadsStats = new JArithmeticDecoderStats(1 << 9);
+  iardxStats = new JArithmeticDecoderStats(1 << 9);
+  iardyStats = new JArithmeticDecoderStats(1 << 9);
+  iardwStats = new JArithmeticDecoderStats(1 << 9);
+  iardhStats = new JArithmeticDecoderStats(1 << 9);
+  iariStats = new JArithmeticDecoderStats(1 << 9);
+  iaidStats = new JArithmeticDecoderStats(1 << 1);
+  huffDecoder = new JBIG2HuffmanDecoder();
+  mmrDecoder = new JBIG2MMRDecoder();
+
+  segments = globalSegments = new GList();
+  if (globalsStream->isStream()) {
+    curStr = globalsStream->getStream();
+    curStr->reset();
+    arithDecoder->setStream(curStr);
+    huffDecoder->setStream(curStr);
+    mmrDecoder->setStream(curStr);
+    readSegments();
+  }
+
+  segments = NULL;
+  curStr = NULL;
+  dataPtr = dataEnd = NULL;
+}
+
+JBIG2Stream::~JBIG2Stream() {
+  delete arithDecoder;
+  delete genericRegionStats;
+  delete refinementRegionStats;
+  delete iadhStats;
+  delete iadwStats;
+  delete iaexStats;
+  delete iaaiStats;
+  delete iadtStats;
+  delete iaitStats;
+  delete iafsStats;
+  delete iadsStats;
+  delete iardxStats;
+  delete iardyStats;
+  delete iardwStats;
+  delete iardhStats;
+  delete iariStats;
+  delete iaidStats;
+  delete huffDecoder;
+  delete mmrDecoder;
+  if (pageBitmap) {
+    delete pageBitmap;
+  }
+  if (segments) {
+    deleteGList(segments, JBIG2Segment);
+  }
+  if (globalSegments) {
+    deleteGList(globalSegments, JBIG2Segment);
+  }
+  delete str;
+}
+
+void JBIG2Stream::reset() {
+  if (pageBitmap) {
+    delete pageBitmap;
+    pageBitmap = NULL;
+  }
+  if (segments) {
+    deleteGList(segments, JBIG2Segment);
+  }
+  segments = new GList();
+
+  curStr = str;
+  curStr->reset();
+  arithDecoder->setStream(curStr);
+  huffDecoder->setStream(curStr);
+  mmrDecoder->setStream(curStr);
+  readSegments();
+
+  if (pageBitmap) {
+    dataPtr = pageBitmap->getDataPtr();
+    dataEnd = dataPtr + pageBitmap->getDataSize();
+  } else {
+    dataPtr = NULL;
+  }
+}
+
+int JBIG2Stream::getChar() {
+  if (dataPtr && dataPtr < dataEnd) {
+    return (*dataPtr++ ^ 0xff) & 0xff;
+  }
+  return EOF;
+}
+
+int JBIG2Stream::lookChar() {
+  if (dataPtr && dataPtr < dataEnd) {
+    return (*dataPtr ^ 0xff) & 0xff;
+  }
+  return EOF;
+}
+
+GString *JBIG2Stream::getPSFilter(int psLevel, char *indent) {
+  return NULL;
+}
+
+GBool JBIG2Stream::isBinary(GBool last) {
+  return str->isBinary(gTrue);
+}
+
+void JBIG2Stream::readSegments() {
+  Guint segNum, segFlags, segType, page, segLength;
+  Guint refFlags, nRefSegs;
+  Guint *refSegs;
+  int c1, c2, c3;
+  Guint i;
+
+  while (readULong(&segNum)) {
+
+    // segment header flags
+    if (!readUByte(&segFlags)) {
+      goto eofError1;
+    }
+    segType = segFlags & 0x3f;
+
+    // referred-to segment count and retention flags
+    if (!readUByte(&refFlags)) {
+      goto eofError1;
+    }
+    nRefSegs = refFlags >> 5;
+    if (nRefSegs == 7) {
+      if ((c1 = curStr->getChar()) == EOF ||
+         (c2 = curStr->getChar()) == EOF ||
+         (c3 = curStr->getChar()) == EOF) {
+       goto eofError1;
+      }
+      refFlags = (refFlags << 24) | (c1 << 16) | (c2 << 8) | c3;
+      nRefSegs = refFlags & 0x1fffffff;
+      for (i = 0; i < (nRefSegs + 9) >> 3; ++i) {
+       c1 = curStr->getChar();
+      }
+    }
+
+    // referred-to segment numbers
+    refSegs = (Guint *)gmallocn(nRefSegs, sizeof(Guint));
+    if (segNum <= 256) {
+      for (i = 0; i < nRefSegs; ++i) {
+       if (!readUByte(&refSegs[i])) {
+         goto eofError2;
+       }
+      }
+    } else if (segNum <= 65536) {
+      for (i = 0; i < nRefSegs; ++i) {
+       if (!readUWord(&refSegs[i])) {
+         goto eofError2;
+       }
+      }
+    } else {
+      for (i = 0; i < nRefSegs; ++i) {
+       if (!readULong(&refSegs[i])) {
+         goto eofError2;
+       }
+      }
+    }
+
+    // segment page association
+    if (segFlags & 0x40) {
+      if (!readULong(&page)) {
+       goto eofError2;
+      }
+    } else {
+      if (!readUByte(&page)) {
+       goto eofError2;
+      }
+    }
+
+    // segment data length
+    if (!readULong(&segLength)) {
+      goto eofError2;
+    }
+
+    // read the segment data
+    switch (segType) {
+    case 0:
+      if (!readSymbolDictSeg(segNum, segLength, refSegs, nRefSegs)) {
+       goto syntaxError;
+      }
+      break;
+    case 4:
+      readTextRegionSeg(segNum, gFalse, gFalse, segLength, refSegs, nRefSegs);
+      break;
+    case 6:
+      readTextRegionSeg(segNum, gTrue, gFalse, segLength, refSegs, nRefSegs);
+      break;
+    case 7:
+      readTextRegionSeg(segNum, gTrue, gTrue, segLength, refSegs, nRefSegs);
+      break;
+    case 16:
+      readPatternDictSeg(segNum, segLength);
+      break;
+    case 20:
+      readHalftoneRegionSeg(segNum, gFalse, gFalse, segLength,
+                           refSegs, nRefSegs);
+      break;
+    case 22:
+      readHalftoneRegionSeg(segNum, gTrue, gFalse, segLength,
+                           refSegs, nRefSegs);
+      break;
+    case 23:
+      readHalftoneRegionSeg(segNum, gTrue, gTrue, segLength,
+                           refSegs, nRefSegs);
+      break;
+    case 36:
+      readGenericRegionSeg(segNum, gFalse, gFalse, segLength);
+      break;
+    case 38:
+      readGenericRegionSeg(segNum, gTrue, gFalse, segLength);
+      break;
+    case 39:
+      readGenericRegionSeg(segNum, gTrue, gTrue, segLength);
+      break;
+    case 40:
+      readGenericRefinementRegionSeg(segNum, gFalse, gFalse, segLength,
+                                    refSegs, nRefSegs);
+      break;
+    case 42:
+      readGenericRefinementRegionSeg(segNum, gTrue, gFalse, segLength,
+                                    refSegs, nRefSegs);
+      break;
+    case 43:
+      readGenericRefinementRegionSeg(segNum, gTrue, gTrue, segLength,
+                                    refSegs, nRefSegs);
+      break;
+    case 48:
+      readPageInfoSeg(segLength);
+      break;
+    case 50:
+      readEndOfStripeSeg(segLength);
+      break;
+    case 52:
+      readProfilesSeg(segLength);
+      break;
+    case 53:
+      readCodeTableSeg(segNum, segLength);
+      break;
+    case 62:
+      readExtensionSeg(segLength);
+      break;
+    default:
+      error(getPos(), "Unknown segment type in JBIG2 stream");
+      for (i = 0; i < segLength; ++i) {
+       if ((c1 = curStr->getChar()) == EOF) {
+         goto eofError2;
+       }
+      }
+      break;
+    }
+
+    gfree(refSegs);
+  }
+
+  return;
+
+ syntaxError:
+  gfree(refSegs);
+  return;
+
+ eofError2:
+  gfree(refSegs);
+ eofError1:
+  error(getPos(), "Unexpected EOF in JBIG2 stream");
+}
+
+GBool JBIG2Stream::readSymbolDictSeg(Guint segNum, Guint length,
+                                    Guint *refSegs, Guint nRefSegs) {
+  JBIG2SymbolDict *symbolDict;
+  JBIG2HuffmanTable *huffDHTable, *huffDWTable;
+  JBIG2HuffmanTable *huffBMSizeTable, *huffAggInstTable;
+  JBIG2Segment *seg;
+  GList *codeTables;
+  JBIG2SymbolDict *inputSymbolDict;
+  Guint flags, sdTemplate, sdrTemplate, huff, refAgg;
+  Guint huffDH, huffDW, huffBMSize, huffAggInst;
+  Guint contextUsed, contextRetained;
+  int sdATX[4], sdATY[4], sdrATX[2], sdrATY[2];
+  Guint numExSyms, numNewSyms, numInputSyms, symCodeLen;
+  JBIG2Bitmap **bitmaps;
+  JBIG2Bitmap *collBitmap, *refBitmap;
+  Guint *symWidths;
+  Guint symHeight, symWidth, totalWidth, x, symID;
+  int dh, dw, refAggNum, refDX, refDY, bmSize;
+  GBool ex;
+  int run, cnt;
+  Guint i, j, k;
+  Guchar *p;
+
+  // symbol dictionary flags
+  if (!readUWord(&flags)) {
+    goto eofError;
+  }
+  sdTemplate = (flags >> 10) & 3;
+  sdrTemplate = (flags >> 12) & 1;
+  huff = flags & 1;
+  refAgg = (flags >> 1) & 1;
+  huffDH = (flags >> 2) & 3;
+  huffDW = (flags >> 4) & 3;
+  huffBMSize = (flags >> 6) & 1;
+  huffAggInst = (flags >> 7) & 1;
+  contextUsed = (flags >> 8) & 1;
+  contextRetained = (flags >> 9) & 1;
+
+  // symbol dictionary AT flags
+  if (!huff) {
+    if (sdTemplate == 0) {
+      if (!readByte(&sdATX[0]) ||
+         !readByte(&sdATY[0]) ||
+         !readByte(&sdATX[1]) ||
+         !readByte(&sdATY[1]) ||
+         !readByte(&sdATX[2]) ||
+         !readByte(&sdATY[2]) ||
+         !readByte(&sdATX[3]) ||
+         !readByte(&sdATY[3])) {
+       goto eofError;
+      }
+    } else {
+      if (!readByte(&sdATX[0]) ||
+         !readByte(&sdATY[0])) {
+       goto eofError;
+      }
+    }
+  }
+
+  // symbol dictionary refinement AT flags
+  if (refAgg && !sdrTemplate) {
+    if (!readByte(&sdrATX[0]) ||
+       !readByte(&sdrATY[0]) ||
+       !readByte(&sdrATX[1]) ||
+       !readByte(&sdrATY[1])) {
+      goto eofError;
+    }
+  }
+
+  // SDNUMEXSYMS and SDNUMNEWSYMS
+  if (!readULong(&numExSyms) || !readULong(&numNewSyms)) {
+    goto eofError;
+  }
+
+  // get referenced segments: input symbol dictionaries and code tables
+  codeTables = new GList();
+  numInputSyms = 0;
+  for (i = 0; i < nRefSegs; ++i) {
+    seg = findSegment(refSegs[i]);
+    if (seg->getType() == jbig2SegSymbolDict) {
+      numInputSyms += ((JBIG2SymbolDict *)seg)->getSize();
+    } else if (seg->getType() == jbig2SegCodeTable) {
+      codeTables->append(seg);
+    }
+  }
+
+  // compute symbol code length
+  symCodeLen = 0;
+  i = 1;
+  while (i < numInputSyms + numNewSyms) {
+    ++symCodeLen;
+    i <<= 1;
+  }
+
+  // get the input symbol bitmaps
+  bitmaps = (JBIG2Bitmap **)gmallocn(numInputSyms + numNewSyms,
+                                    sizeof(JBIG2Bitmap *));
+  for (i = 0; i < numInputSyms + numNewSyms; ++i) {
+    bitmaps[i] = NULL;
+  }
+  k = 0;
+  inputSymbolDict = NULL;
+  for (i = 0; i < nRefSegs; ++i) {
+    seg = findSegment(refSegs[i]);
+    if (seg->getType() == jbig2SegSymbolDict) {
+      inputSymbolDict = (JBIG2SymbolDict *)seg;
+      for (j = 0; j < inputSymbolDict->getSize(); ++j) {
+       bitmaps[k++] = inputSymbolDict->getBitmap(j);
+      }
+    }
+  }
+
+  // get the Huffman tables
+  huffDHTable = huffDWTable = NULL; // make gcc happy
+  huffBMSizeTable = huffAggInstTable = NULL; // make gcc happy
+  i = 0;
+  if (huff) {
+    if (huffDH == 0) {
+      huffDHTable = huffTableD;
+    } else if (huffDH == 1) {
+      huffDHTable = huffTableE;
+    } else {
+      huffDHTable = ((JBIG2CodeTable *)codeTables->get(i++))->getHuffTable();
+    }
+    if (huffDW == 0) {
+      huffDWTable = huffTableB;
+    } else if (huffDW == 1) {
+      huffDWTable = huffTableC;
+    } else {
+      huffDWTable = ((JBIG2CodeTable *)codeTables->get(i++))->getHuffTable();
+    }
+    if (huffBMSize == 0) {
+      huffBMSizeTable = huffTableA;
+    } else {
+      huffBMSizeTable =
+         ((JBIG2CodeTable *)codeTables->get(i++))->getHuffTable();
+    }
+    if (huffAggInst == 0) {
+      huffAggInstTable = huffTableA;
+    } else {
+      huffAggInstTable =
+         ((JBIG2CodeTable *)codeTables->get(i++))->getHuffTable();
+    }
+  }
+  delete codeTables;
+
+  // set up the Huffman decoder
+  if (huff) {
+    huffDecoder->reset();
+
+  // set up the arithmetic decoder
+  } else {
+    if (contextUsed && inputSymbolDict) {
+      resetGenericStats(sdTemplate, inputSymbolDict->getGenericRegionStats());
+    } else {
+      resetGenericStats(sdTemplate, NULL);
+    }
+    resetIntStats(symCodeLen);
+    arithDecoder->start();
+  }
+
+  // set up the arithmetic decoder for refinement/aggregation
+  if (refAgg) {
+    if (contextUsed && inputSymbolDict) {
+      resetRefinementStats(sdrTemplate,
+                          inputSymbolDict->getRefinementRegionStats());
+    } else {
+      resetRefinementStats(sdrTemplate, NULL);
+    }
+  }
+
+  // allocate symbol widths storage
+  symWidths = NULL;
+  if (huff && !refAgg) {
+    symWidths = (Guint *)gmallocn(numNewSyms, sizeof(Guint));
+  }
+
+  symHeight = 0;
+  i = 0;
+  while (i < numNewSyms) {
+
+    // read the height class delta height
+    if (huff) {
+      huffDecoder->decodeInt(&dh, huffDHTable);
+    } else {
+      arithDecoder->decodeInt(&dh, iadhStats);
+    }
+    if (dh < 0 && (Guint)-dh >= symHeight) {
+      error(getPos(), "Bad delta-height value in JBIG2 symbol dictionary");
+      goto syntaxError;
+    }
+    symHeight += dh;
+    symWidth = 0;
+    totalWidth = 0;
+    j = i;
+
+    // read the symbols in this height class
+    while (1) {
+
+      // read the delta width
+      if (huff) {
+       if (!huffDecoder->decodeInt(&dw, huffDWTable)) {
+         break;
+       }
+      } else {
+       if (!arithDecoder->decodeInt(&dw, iadwStats)) {
+         break;
+       }
+      }
+      if (dw < 0 && (Guint)-dw >= symWidth) {
+       error(getPos(), "Bad delta-height value in JBIG2 symbol dictionary");
+       goto syntaxError;
+      }
+      symWidth += dw;
+
+      // using a collective bitmap, so don't read a bitmap here
+      if (huff && !refAgg) {
+       symWidths[i] = symWidth;
+       totalWidth += symWidth;
+
+      // refinement/aggregate coding
+      } else if (refAgg) {
+       if (huff) {
+         if (!huffDecoder->decodeInt(&refAggNum, huffAggInstTable)) {
+           break;
+         }
+       } else {
+         if (!arithDecoder->decodeInt(&refAggNum, iaaiStats)) {
+           break;
+         }
+       }
+#if 0 //~ This special case was added about a year before the final draft
+      //~ of the JBIG2 spec was released.  I have encountered some old
+      //~ JBIG2 images that predate it.
+       if (0) {
+#else
+       if (refAggNum == 1) {
+#endif
+         if (huff) {
+           symID = huffDecoder->readBits(symCodeLen);
+           huffDecoder->decodeInt(&refDX, huffTableO);
+           huffDecoder->decodeInt(&refDY, huffTableO);
+           huffDecoder->decodeInt(&bmSize, huffTableA);
+           huffDecoder->reset();
+           arithDecoder->start();
+         } else {
+           symID = arithDecoder->decodeIAID(symCodeLen, iaidStats);
+           arithDecoder->decodeInt(&refDX, iardxStats);
+           arithDecoder->decodeInt(&refDY, iardyStats);
+         }
+         refBitmap = bitmaps[symID];
+         bitmaps[numInputSyms + i] =
+             readGenericRefinementRegion(symWidth, symHeight,
+                                         sdrTemplate, gFalse,
+                                         refBitmap, refDX, refDY,
+                                         sdrATX, sdrATY);
+         //~ do we need to use the bmSize value here (in Huffman mode)?
+       } else {
+         bitmaps[numInputSyms + i] =
+             readTextRegion(huff, gTrue, symWidth, symHeight,
+                            refAggNum, 0, numInputSyms + i, NULL,
+                            symCodeLen, bitmaps, 0, 0, 0, 1, 0,
+                            huffTableF, huffTableH, huffTableK, huffTableO,
+                            huffTableO, huffTableO, huffTableO, huffTableA,
+                            sdrTemplate, sdrATX, sdrATY);
+       }
+
+      // non-ref/agg coding
+      } else {
+       bitmaps[numInputSyms + i] =
+           readGenericBitmap(gFalse, symWidth, symHeight,
+                             sdTemplate, gFalse, gFalse, NULL,
+                             sdATX, sdATY, 0);
+      }
+
+      ++i;
+    }
+
+    // read the collective bitmap
+    if (huff && !refAgg) {
+      huffDecoder->decodeInt(&bmSize, huffBMSizeTable);
+      huffDecoder->reset();
+      if (bmSize == 0) {
+       collBitmap = new JBIG2Bitmap(0, totalWidth, symHeight);
+       bmSize = symHeight * ((totalWidth + 7) >> 3);
+       p = collBitmap->getDataPtr();
+       for (k = 0; k < (Guint)bmSize; ++k) {
+         *p++ = curStr->getChar();
+       }
+      } else {
+       collBitmap = readGenericBitmap(gTrue, totalWidth, symHeight,
+                                      0, gFalse, gFalse, NULL, NULL, NULL,
+                                      bmSize);
+      }
+      x = 0;
+      for (; j < i; ++j) {
+       bitmaps[numInputSyms + j] =
+           collBitmap->getSlice(x, 0, symWidths[j], symHeight);
+       x += symWidths[j];
+      }
+      delete collBitmap;
+    }
+  }
+
+  // create the symbol dict object
+  symbolDict = new JBIG2SymbolDict(segNum, numExSyms);
+
+  // exported symbol list
+  i = j = 0;
+  ex = gFalse;
+  while (i < numInputSyms + numNewSyms) {
+    if (huff) {
+      huffDecoder->decodeInt(&run, huffTableA);
+    } else {
+      arithDecoder->decodeInt(&run, iaexStats);
+    }
+    if (ex) {
+      for (cnt = 0; cnt < run; ++cnt) {
+       symbolDict->setBitmap(j++, bitmaps[i++]->copy());
+      }
+    } else {
+      i += run;
+    }
+    ex = !ex;
+  }
+
+  for (i = 0; i < numNewSyms; ++i) {
+    delete bitmaps[numInputSyms + i];
+  }
+  gfree(bitmaps);
+  if (symWidths) {
+    gfree(symWidths);
+  }
+
+  // save the arithmetic decoder stats
+  if (!huff && contextRetained) {
+    symbolDict->setGenericRegionStats(genericRegionStats->copy());
+    if (refAgg) {
+      symbolDict->setRefinementRegionStats(refinementRegionStats->copy());
+    }
+  }
+
+  // store the new symbol dict
+  segments->append(symbolDict);
+
+  return gTrue;
+
+ syntaxError:
+  for (i = 0; i < numNewSyms; ++i) {
+    if (bitmaps[numInputSyms + i]) {
+      delete bitmaps[numInputSyms + i];
+    }
+  }
+  gfree(bitmaps);
+  if (symWidths) {
+    gfree(symWidths);
+  }
+  return gFalse;
+
+ eofError:
+  error(getPos(), "Unexpected EOF in JBIG2 stream");
+  return gFalse;
+}
+
+void JBIG2Stream::readTextRegionSeg(Guint segNum, GBool imm,
+                                   GBool lossless, Guint length,
+                                   Guint *refSegs, Guint nRefSegs) {
+  JBIG2Bitmap *bitmap;
+  JBIG2HuffmanTable runLengthTab[36];
+  JBIG2HuffmanTable *symCodeTab;
+  JBIG2HuffmanTable *huffFSTable, *huffDSTable, *huffDTTable;
+  JBIG2HuffmanTable *huffRDWTable, *huffRDHTable;
+  JBIG2HuffmanTable *huffRDXTable, *huffRDYTable, *huffRSizeTable;
+  JBIG2Segment *seg;
+  GList *codeTables;
+  JBIG2SymbolDict *symbolDict;
+  JBIG2Bitmap **syms;
+  Guint w, h, x, y, segInfoFlags, extCombOp;
+  Guint flags, huff, refine, logStrips, refCorner, transposed;
+  Guint combOp, defPixel, templ;
+  int sOffset;
+  Guint huffFlags, huffFS, huffDS, huffDT;
+  Guint huffRDW, huffRDH, huffRDX, huffRDY, huffRSize;
+  Guint numInstances, numSyms, symCodeLen;
+  int atx[2], aty[2];
+  Guint i, k, kk;
+  int j;
+
+  // region segment info field
+  if (!readULong(&w) || !readULong(&h) ||
+      !readULong(&x) || !readULong(&y) ||
+      !readUByte(&segInfoFlags)) {
+    goto eofError;
+  }
+  extCombOp = segInfoFlags & 7;
+
+  // rest of the text region header
+  if (!readUWord(&flags)) {
+    goto eofError;
+  }
+  huff = flags & 1;
+  refine = (flags >> 1) & 1;
+  logStrips = (flags >> 2) & 3;
+  refCorner = (flags >> 4) & 3;
+  transposed = (flags >> 6) & 1;
+  combOp = (flags >> 7) & 3;
+  defPixel = (flags >> 9) & 1;
+  sOffset = (flags >> 10) & 0x1f;
+  if (sOffset & 0x10) {
+    sOffset |= -1 - 0x0f;
+  }
+  templ = (flags >> 15) & 1;
+  huffFS = huffDS = huffDT = 0; // make gcc happy
+  huffRDW = huffRDH = huffRDX = huffRDY = huffRSize = 0; // make gcc happy
+  if (huff) {
+    if (!readUWord(&huffFlags)) {
+      goto eofError;
+    }
+    huffFS = huffFlags & 3;
+    huffDS = (huffFlags >> 2) & 3;
+    huffDT = (huffFlags >> 4) & 3;
+    huffRDW = (huffFlags >> 6) & 3;
+    huffRDH = (huffFlags >> 8) & 3;
+    huffRDX = (huffFlags >> 10) & 3;
+    huffRDY = (huffFlags >> 12) & 3;
+    huffRSize = (huffFlags >> 14) & 1;
+  }
+  if (refine && templ == 0) {
+    if (!readByte(&atx[0]) || !readByte(&aty[0]) ||
+       !readByte(&atx[1]) || !readByte(&aty[1])) {
+      goto eofError;
+    }
+  }
+  if (!readULong(&numInstances)) {
+    goto eofError;
+  }
+
+  // get symbol dictionaries and tables
+  codeTables = new GList();
+  numSyms = 0;
+  for (i = 0; i < nRefSegs; ++i) {
+    if ((seg = findSegment(refSegs[i]))) {
+      if (seg->getType() == jbig2SegSymbolDict) {
+       numSyms += ((JBIG2SymbolDict *)seg)->getSize();
+      } else if (seg->getType() == jbig2SegCodeTable) {
+       codeTables->append(seg);
+      }
+    } else {
+      error(getPos(), "Invalid segment reference in JBIG2 text region");
+    }
+  }
+  symCodeLen = 0;
+  i = 1;
+  while (i < numSyms) {
+    ++symCodeLen;
+    i <<= 1;
+  }
+
+  // get the symbol bitmaps
+  syms = (JBIG2Bitmap **)gmallocn(numSyms, sizeof(JBIG2Bitmap *));
+  kk = 0;
+  for (i = 0; i < nRefSegs; ++i) {
+    if ((seg = findSegment(refSegs[i]))) {
+      if (seg->getType() == jbig2SegSymbolDict) {
+       symbolDict = (JBIG2SymbolDict *)seg;
+       for (k = 0; k < symbolDict->getSize(); ++k) {
+         syms[kk++] = symbolDict->getBitmap(k);
+       }
+      }
+    }
+  }
+
+  // get the Huffman tables
+  huffFSTable = huffDSTable = huffDTTable = NULL; // make gcc happy
+  huffRDWTable = huffRDHTable = NULL; // make gcc happy
+  huffRDXTable = huffRDYTable = huffRSizeTable = NULL; // make gcc happy
+  i = 0;
+  if (huff) {
+    if (huffFS == 0) {
+      huffFSTable = huffTableF;
+    } else if (huffFS == 1) {
+      huffFSTable = huffTableG;
+    } else {
+      huffFSTable = ((JBIG2CodeTable *)codeTables->get(i++))->getHuffTable();
+    }
+    if (huffDS == 0) {
+      huffDSTable = huffTableH;
+    } else if (huffDS == 1) {
+      huffDSTable = huffTableI;
+    } else if (huffDS == 2) {
+      huffDSTable = huffTableJ;
+    } else {
+      huffDSTable = ((JBIG2CodeTable *)codeTables->get(i++))->getHuffTable();
+    }
+    if (huffDT == 0) {
+      huffDTTable = huffTableK;
+    } else if (huffDT == 1) {
+      huffDTTable = huffTableL;
+    } else if (huffDT == 2) {
+      huffDTTable = huffTableM;
+    } else {
+      huffDTTable = ((JBIG2CodeTable *)codeTables->get(i++))->getHuffTable();
+    }
+    if (huffRDW == 0) {
+      huffRDWTable = huffTableN;
+    } else if (huffRDW == 1) {
+      huffRDWTable = huffTableO;
+    } else {
+      huffRDWTable = ((JBIG2CodeTable *)codeTables->get(i++))->getHuffTable();
+    }
+    if (huffRDH == 0) {
+      huffRDHTable = huffTableN;
+    } else if (huffRDH == 1) {
+      huffRDHTable = huffTableO;
+    } else {
+      huffRDHTable = ((JBIG2CodeTable *)codeTables->get(i++))->getHuffTable();
+    }
+    if (huffRDX == 0) {
+      huffRDXTable = huffTableN;
+    } else if (huffRDX == 1) {
+      huffRDXTable = huffTableO;
+    } else {
+      huffRDXTable = ((JBIG2CodeTable *)codeTables->get(i++))->getHuffTable();
+    }
+    if (huffRDY == 0) {
+      huffRDYTable = huffTableN;
+    } else if (huffRDY == 1) {
+      huffRDYTable = huffTableO;
+    } else {
+      huffRDYTable = ((JBIG2CodeTable *)codeTables->get(i++))->getHuffTable();
+    }
+    if (huffRSize == 0) {
+      huffRSizeTable = huffTableA;
+    } else {
+      huffRSizeTable =
+         ((JBIG2CodeTable *)codeTables->get(i++))->getHuffTable();
+    }
+  }
+  delete codeTables;
+
+  // symbol ID Huffman decoding table
+  if (huff) {
+    huffDecoder->reset();
+    for (i = 0; i < 32; ++i) {
+      runLengthTab[i].val = i;
+      runLengthTab[i].prefixLen = huffDecoder->readBits(4);
+      runLengthTab[i].rangeLen = 0;
+    }
+    runLengthTab[32].val = 0x103;
+    runLengthTab[32].prefixLen = huffDecoder->readBits(4);
+    runLengthTab[32].rangeLen = 2;
+    runLengthTab[33].val = 0x203;
+    runLengthTab[33].prefixLen = huffDecoder->readBits(4);
+    runLengthTab[33].rangeLen = 3;
+    runLengthTab[34].val = 0x20b;
+    runLengthTab[34].prefixLen = huffDecoder->readBits(4);
+    runLengthTab[34].rangeLen = 7;
+    runLengthTab[35].prefixLen = 0;
+    runLengthTab[35].rangeLen = jbig2HuffmanEOT;
+    huffDecoder->buildTable(runLengthTab, 35);
+    symCodeTab = (JBIG2HuffmanTable *)gmallocn(numSyms + 1,
+                                              sizeof(JBIG2HuffmanTable));
+    for (i = 0; i < numSyms; ++i) {
+      symCodeTab[i].val = i;
+      symCodeTab[i].rangeLen = 0;
+    }
+    i = 0;
+    while (i < numSyms) {
+      huffDecoder->decodeInt(&j, runLengthTab);
+      if (j > 0x200) {
+       for (j -= 0x200; j && i < numSyms; --j) {
+         symCodeTab[i++].prefixLen = 0;
+       }
+      } else if (j > 0x100) {
+       for (j -= 0x100; j && i < numSyms; --j) {
+         symCodeTab[i].prefixLen = symCodeTab[i-1].prefixLen;
+         ++i;
+       }
+      } else {
+       symCodeTab[i++].prefixLen = j;
+      }
+    }
+    symCodeTab[numSyms].prefixLen = 0;
+    symCodeTab[numSyms].rangeLen = jbig2HuffmanEOT;
+    huffDecoder->buildTable(symCodeTab, numSyms);
+    huffDecoder->reset();
+
+  // set up the arithmetic decoder
+  } else {
+    symCodeTab = NULL;
+    resetIntStats(symCodeLen);
+    arithDecoder->start();
+  }
+  if (refine) {
+    resetRefinementStats(templ, NULL);
+  }
+
+  bitmap = readTextRegion(huff, refine, w, h, numInstances,
+                         logStrips, numSyms, symCodeTab, symCodeLen, syms,
+                         defPixel, combOp, transposed, refCorner, sOffset,
+                         huffFSTable, huffDSTable, huffDTTable,
+                         huffRDWTable, huffRDHTable,
+                         huffRDXTable, huffRDYTable, huffRSizeTable,
+                         templ, atx, aty);
+
+  gfree(syms);
+
+  // combine the region bitmap into the page bitmap
+  if (imm) {
+    if (pageH == 0xffffffff && y + h > curPageH) {
+      pageBitmap->expand(y + h, pageDefPixel);
+    }
+    pageBitmap->combine(bitmap, x, y, extCombOp);
+    delete bitmap;
+
+  // store the region bitmap
+  } else {
+    bitmap->setSegNum(segNum);
+    segments->append(bitmap);
+  }
+
+  // clean up the Huffman decoder
+  if (huff) {
+    gfree(symCodeTab);
+  }
+
+  return;
+
+ eofError:
+  error(getPos(), "Unexpected EOF in JBIG2 stream");
+}
+
+JBIG2Bitmap *JBIG2Stream::readTextRegion(GBool huff, GBool refine,
+                                        int w, int h,
+                                        Guint numInstances,
+                                        Guint logStrips,
+                                        int numSyms,
+                                        JBIG2HuffmanTable *symCodeTab,
+                                        Guint symCodeLen,
+                                        JBIG2Bitmap **syms,
+                                        Guint defPixel, Guint combOp,
+                                        Guint transposed, Guint refCorner,
+                                        int sOffset,
+                                        JBIG2HuffmanTable *huffFSTable,
+                                        JBIG2HuffmanTable *huffDSTable,
+                                        JBIG2HuffmanTable *huffDTTable,
+                                        JBIG2HuffmanTable *huffRDWTable,
+                                        JBIG2HuffmanTable *huffRDHTable,
+                                        JBIG2HuffmanTable *huffRDXTable,
+                                        JBIG2HuffmanTable *huffRDYTable,
+                                        JBIG2HuffmanTable *huffRSizeTable,
+                                        Guint templ,
+                                        int *atx, int *aty) {
+  JBIG2Bitmap *bitmap;
+  JBIG2Bitmap *symbolBitmap;
+  Guint strips;
+  int t, dt, tt, s, ds, sFirst, j;
+  int rdw, rdh, rdx, rdy, ri, refDX, refDY, bmSize;
+  Guint symID, inst, bw, bh;
+
+  strips = 1 << logStrips;
+
+  // allocate the bitmap
+  bitmap = new JBIG2Bitmap(0, w, h);
+  if (defPixel) {
+    bitmap->clearToOne();
+  } else {
+    bitmap->clearToZero();
+  }
+
+  // decode initial T value
+  if (huff) {
+    huffDecoder->decodeInt(&t, huffDTTable);
+  } else {
+    arithDecoder->decodeInt(&t, iadtStats);
+  }
+  t *= -(int)strips;
+
+  inst = 0;
+  sFirst = 0;
+  while (inst < numInstances) {
+
+    // decode delta-T
+    if (huff) {
+      huffDecoder->decodeInt(&dt, huffDTTable);
+    } else {
+      arithDecoder->decodeInt(&dt, iadtStats);
+    }
+    t += dt * strips;
+
+    // first S value
+    if (huff) {
+      huffDecoder->decodeInt(&ds, huffFSTable);
+    } else {
+      arithDecoder->decodeInt(&ds, iafsStats);
+    }
+    sFirst += ds;
+    s = sFirst;
+
+    // read the instances
+    while (1) {
+
+      // T value
+      if (strips == 1) {
+       dt = 0;
+      } else if (huff) {
+       dt = huffDecoder->readBits(logStrips);
+      } else {
+       arithDecoder->decodeInt(&dt, iaitStats);
+      }
+      tt = t + dt;
+
+      // symbol ID
+      if (huff) {
+       if (symCodeTab) {
+         huffDecoder->decodeInt(&j, symCodeTab);
+         symID = (Guint)j;
+       } else {
+         symID = huffDecoder->readBits(symCodeLen);
+       }
+      } else {
+       symID = arithDecoder->decodeIAID(symCodeLen, iaidStats);
+      }
+
+      if (symID >= (Guint)numSyms) {
+       error(getPos(), "Invalid symbol number in JBIG2 text region");
+      } else {
+
+       // get the symbol bitmap
+       symbolBitmap = NULL;
+       if (refine) {
+         if (huff) {
+           ri = (int)huffDecoder->readBit();
+         } else {
+           arithDecoder->decodeInt(&ri, iariStats);
+         }
+       } else {
+         ri = 0;
+       }
+       if (ri) {
+         if (huff) {
+           huffDecoder->decodeInt(&rdw, huffRDWTable);
+           huffDecoder->decodeInt(&rdh, huffRDHTable);
+           huffDecoder->decodeInt(&rdx, huffRDXTable);
+           huffDecoder->decodeInt(&rdy, huffRDYTable);
+           huffDecoder->decodeInt(&bmSize, huffRSizeTable);
+           huffDecoder->reset();
+           arithDecoder->start();
+         } else {
+           arithDecoder->decodeInt(&rdw, iardwStats);
+           arithDecoder->decodeInt(&rdh, iardhStats);
+           arithDecoder->decodeInt(&rdx, iardxStats);
+           arithDecoder->decodeInt(&rdy, iardyStats);
+         }
+         refDX = ((rdw >= 0) ? rdw : rdw - 1) / 2 + rdx;
+         refDY = ((rdh >= 0) ? rdh : rdh - 1) / 2 + rdy;
+
+         symbolBitmap =
+           readGenericRefinementRegion(rdw + syms[symID]->getWidth(),
+                                       rdh + syms[symID]->getHeight(),
+                                       templ, gFalse, syms[symID],
+                                       refDX, refDY, atx, aty);
+         //~ do we need to use the bmSize value here (in Huffman mode)?
+       } else {
+         symbolBitmap = syms[symID];
+       }
+
+       // combine the symbol bitmap into the region bitmap
+       //~ something is wrong here - refCorner shouldn't degenerate into
+       //~   two cases
+       bw = symbolBitmap->getWidth() - 1;
+       bh = symbolBitmap->getHeight() - 1;
+       if (transposed) {
+         switch (refCorner) {
+         case 0: // bottom left
+           bitmap->combine(symbolBitmap, tt, s, combOp);
+           break;
+         case 1: // top left
+           bitmap->combine(symbolBitmap, tt, s, combOp);
+           break;
+         case 2: // bottom right
+           bitmap->combine(symbolBitmap, tt - bw, s, combOp);
+           break;
+         case 3: // top right
+           bitmap->combine(symbolBitmap, tt - bw, s, combOp);
+           break;
+         }
+         s += bh;
+       } else {
+         switch (refCorner) {
+         case 0: // bottom left
+           bitmap->combine(symbolBitmap, s, tt - bh, combOp);
+           break;
+         case 1: // top left
+           bitmap->combine(symbolBitmap, s, tt, combOp);
+           break;
+         case 2: // bottom right
+           bitmap->combine(symbolBitmap, s, tt - bh, combOp);
+           break;
+         case 3: // top right
+           bitmap->combine(symbolBitmap, s, tt, combOp);
+           break;
+         }
+         s += bw;
+       }
+       if (ri) {
+         delete symbolBitmap;
+       }
+      }
+
+      // next instance
+      ++inst;
+
+      // next S value
+      if (huff) {
+       if (!huffDecoder->decodeInt(&ds, huffDSTable)) {
+         break;
+       }
+      } else {
+       if (!arithDecoder->decodeInt(&ds, iadsStats)) {
+         break;
+       }
+      }
+      s += sOffset + ds;
+    }
+  }
+
+  return bitmap;
+}
+
+void JBIG2Stream::readPatternDictSeg(Guint segNum, Guint length) {
+  JBIG2PatternDict *patternDict;
+  JBIG2Bitmap *bitmap;
+  Guint flags, patternW, patternH, grayMax, templ, mmr;
+  int atx[4], aty[4];
+  Guint i, x;
+
+  // halftone dictionary flags, pattern width and height, max gray value
+  if (!readUByte(&flags) ||
+      !readUByte(&patternW) ||
+      !readUByte(&patternH) ||
+      !readULong(&grayMax)) {
+    goto eofError;
+  }
+  templ = (flags >> 1) & 3;
+  mmr = flags & 1;
+
+  // set up the arithmetic decoder
+  if (!mmr) {
+    resetGenericStats(templ, NULL);
+    arithDecoder->start();
+  }
+
+  // read the bitmap
+  atx[0] = -(int)patternW; aty[0] =  0;
+  atx[1] = -3;             aty[1] = -1;
+  atx[2] =  2;             aty[2] = -2;
+  atx[3] = -2;             aty[3] = -2;
+  bitmap = readGenericBitmap(mmr, (grayMax + 1) * patternW, patternH,
+                            templ, gFalse, gFalse, NULL,
+                            atx, aty, length - 7);
+
+  // create the pattern dict object
+  patternDict = new JBIG2PatternDict(segNum, grayMax + 1);
+
+  // split up the bitmap
+  x = 0;
+  for (i = 0; i <= grayMax; ++i) {
+    patternDict->setBitmap(i, bitmap->getSlice(x, 0, patternW, patternH));
+    x += patternW;
+  }
+
+  // free memory
+  delete bitmap;
+
+  // store the new pattern dict
+  segments->append(patternDict);
+
+  return;
+
+ eofError:
+  error(getPos(), "Unexpected EOF in JBIG2 stream");
+}
+
+void JBIG2Stream::readHalftoneRegionSeg(Guint segNum, GBool imm,
+                                       GBool lossless, Guint length,
+                                       Guint *refSegs, Guint nRefSegs) {
+  JBIG2Bitmap *bitmap;
+  JBIG2Segment *seg;
+  JBIG2PatternDict *patternDict;
+  JBIG2Bitmap *skipBitmap;
+  Guint *grayImg;
+  JBIG2Bitmap *grayBitmap;
+  JBIG2Bitmap *patternBitmap;
+  Guint w, h, x, y, segInfoFlags, extCombOp;
+  Guint flags, mmr, templ, enableSkip, combOp;
+  Guint gridW, gridH, stepX, stepY, patW, patH;
+  int atx[4], aty[4];
+  int gridX, gridY, xx, yy, bit, j;
+  Guint bpp, m, n, i;
+
+  // region segment info field
+  if (!readULong(&w) || !readULong(&h) ||
+      !readULong(&x) || !readULong(&y) ||
+      !readUByte(&segInfoFlags)) {
+    goto eofError;
+  }
+  extCombOp = segInfoFlags & 7;
+
+  // rest of the halftone region header
+  if (!readUByte(&flags)) {
+    goto eofError;
+  }
+  mmr = flags & 1;
+  templ = (flags >> 1) & 3;
+  enableSkip = (flags >> 3) & 1;
+  combOp = (flags >> 4) & 7;
+  if (!readULong(&gridW) || !readULong(&gridH) ||
+      !readLong(&gridX) || !readLong(&gridY) ||
+      !readUWord(&stepX) || !readUWord(&stepY)) {
+    goto eofError;
+  }
+
+  // get pattern dictionary
+  if (nRefSegs != 1) {
+    error(getPos(), "Bad symbol dictionary reference in JBIG2 halftone segment");
+    return;
+  }
+  seg = findSegment(refSegs[0]);
+  if (seg->getType() != jbig2SegPatternDict) {
+    error(getPos(), "Bad symbol dictionary reference in JBIG2 halftone segment");
+    return;
+  }
+  patternDict = (JBIG2PatternDict *)seg;
+  bpp = 0;
+  i = 1;
+  while (i < patternDict->getSize()) {
+    ++bpp;
+    i <<= 1;
+  }
+  patW = patternDict->getBitmap(0)->getWidth();
+  patH = patternDict->getBitmap(0)->getHeight();
+
+  // set up the arithmetic decoder
+  if (!mmr) {
+    resetGenericStats(templ, NULL);
+    arithDecoder->start();
+  }
+
+  // allocate the bitmap
+  bitmap = new JBIG2Bitmap(segNum, w, h);
+  if (flags & 0x80) { // HDEFPIXEL
+    bitmap->clearToOne();
+  } else {
+    bitmap->clearToZero();
+  }
+
+  // compute the skip bitmap
+  skipBitmap = NULL;
+  if (enableSkip) {
+    skipBitmap = new JBIG2Bitmap(0, gridW, gridH);
+    skipBitmap->clearToZero();
+    for (m = 0; m < gridH; ++m) {
+      xx = gridX + m * stepY;
+      yy = gridY + m * stepX;
+      for (n = 0; n < gridW; ++n) {
+       if (((xx + (int)patW) >> 8) <= 0 || (xx >> 8) >= (int)w ||
+           ((yy + (int)patH) >> 8) <= 0 || (yy >> 8) >= (int)h) {
+         skipBitmap->setPixel(n, m);
+       }
+      }
+    }
+  }
+
+  // read the gray-scale image
+  grayImg = (Guint *)gmallocn(gridW * gridH, sizeof(Guint));
+  memset(grayImg, 0, gridW * gridH * sizeof(Guint));
+  atx[0] = templ <= 1 ? 3 : 2;  aty[0] = -1;
+  atx[1] = -3;                  aty[1] = -1;
+  atx[2] =  2;                  aty[2] = -2;
+  atx[3] = -2;                  aty[3] = -2;
+  for (j = bpp - 1; j >= 0; --j) {
+    grayBitmap = readGenericBitmap(mmr, gridW, gridH, templ, gFalse,
+                                  enableSkip, skipBitmap, atx, aty, -1);
+    i = 0;
+    for (m = 0; m < gridH; ++m) {
+      for (n = 0; n < gridW; ++n) {
+       bit = grayBitmap->getPixel(n, m) ^ (grayImg[i] & 1);
+       grayImg[i] = (grayImg[i] << 1) | bit;
+       ++i;
+      }
+    }
+    delete grayBitmap;
+  }
+
+  // decode the image
+  i = 0;
+  for (m = 0; m < gridH; ++m) {
+    xx = gridX + m * stepY;
+    yy = gridY + m * stepX;
+    for (n = 0; n < gridW; ++n) {
+      if (!(enableSkip && skipBitmap->getPixel(n, m))) {
+       patternBitmap = patternDict->getBitmap(grayImg[i]);
+       bitmap->combine(patternBitmap, xx >> 8, yy >> 8, combOp);
+      }
+      xx += stepX;
+      yy -= stepY;
+      ++i;
+    }
+  }
+
+  gfree(grayImg);
+
+  // combine the region bitmap into the page bitmap
+  if (imm) {
+    if (pageH == 0xffffffff && y + h > curPageH) {
+      pageBitmap->expand(y + h, pageDefPixel);
+    }
+    pageBitmap->combine(bitmap, x, y, extCombOp);
+    delete bitmap;
+
+  // store the region bitmap
+  } else {
+    segments->append(bitmap);
+  }
+
+  return;
+
+ eofError:
+  error(getPos(), "Unexpected EOF in JBIG2 stream");
+}
+
+void JBIG2Stream::readGenericRegionSeg(Guint segNum, GBool imm,
+                                      GBool lossless, Guint length) {
+  JBIG2Bitmap *bitmap;
+  Guint w, h, x, y, segInfoFlags, extCombOp;
+  Guint flags, mmr, templ, tpgdOn;
+  int atx[4], aty[4];
+
+  // region segment info field
+  if (!readULong(&w) || !readULong(&h) ||
+      !readULong(&x) || !readULong(&y) ||
+      !readUByte(&segInfoFlags)) {
+    goto eofError;
+  }
+  extCombOp = segInfoFlags & 7;
+
+  // rest of the generic region segment header
+  if (!readUByte(&flags)) {
+    goto eofError;
+  }
+  mmr = flags & 1;
+  templ = (flags >> 1) & 3;
+  tpgdOn = (flags >> 3) & 1;
+
+  // AT flags
+  if (!mmr) {
+    if (templ == 0) {
+      if (!readByte(&atx[0]) ||
+         !readByte(&aty[0]) ||
+         !readByte(&atx[1]) ||
+         !readByte(&aty[1]) ||
+         !readByte(&atx[2]) ||
+         !readByte(&aty[2]) ||
+         !readByte(&atx[3]) ||
+         !readByte(&aty[3])) {
+       goto eofError;
+      }
+    } else {
+      if (!readByte(&atx[0]) ||
+         !readByte(&aty[0])) {
+       goto eofError;
+      }
+    }
+  }
+
+  // set up the arithmetic decoder
+  if (!mmr) {
+    resetGenericStats(templ, NULL);
+    arithDecoder->start();
+  }
+
+  // read the bitmap
+  bitmap = readGenericBitmap(mmr, w, h, templ, tpgdOn, gFalse,
+                            NULL, atx, aty, mmr ? 0 : length - 18);
+
+  // combine the region bitmap into the page bitmap
+  if (imm) {
+    if (pageH == 0xffffffff && y + h > curPageH) {
+      pageBitmap->expand(y + h, pageDefPixel);
+    }
+    pageBitmap->combine(bitmap, x, y, extCombOp);
+    delete bitmap;
+
+  // store the region bitmap
+  } else {
+    bitmap->setSegNum(segNum);
+    segments->append(bitmap);
+  }
+
+  return;
+
+ eofError:
+  error(getPos(), "Unexpected EOF in JBIG2 stream");
+}
+
+JBIG2Bitmap *JBIG2Stream::readGenericBitmap(GBool mmr, int w, int h,
+                                           int templ, GBool tpgdOn,
+                                           GBool useSkip, JBIG2Bitmap *skip,
+                                           int *atx, int *aty,
+                                           int mmrDataLength) {
+  JBIG2Bitmap *bitmap;
+  GBool ltp;
+  Guint ltpCX, cx, cx0, cx1, cx2;
+  JBIG2BitmapPtr cxPtr0, cxPtr1;
+  JBIG2BitmapPtr atPtr0, atPtr1, atPtr2, atPtr3;
+  int *refLine, *codingLine;
+  int code1, code2, code3;
+  int x, y, a0, pix, i, refI, codingI;
+
+  bitmap = new JBIG2Bitmap(0, w, h);
+  bitmap->clearToZero();
+
+  //----- MMR decode
+
+  if (mmr) {
+
+    mmrDecoder->reset();
+    refLine = (int *)gmallocn(w + 2, sizeof(int));
+    codingLine = (int *)gmallocn(w + 2, sizeof(int));
+    codingLine[0] = codingLine[1] = w;
+
+    for (y = 0; y < h; ++y) {
+
+      // copy coding line to ref line
+      for (i = 0; codingLine[i] < w; ++i) {
+       refLine[i] = codingLine[i];
+      }
+      refLine[i] = refLine[i + 1] = w;
+
+      // decode a line
+      refI = 0;     // b1 = refLine[refI]
+      codingI = 0;  // a1 = codingLine[codingI]
+      a0 = 0;
+      do {
+       code1 = mmrDecoder->get2DCode();
+       switch (code1) {
+       case twoDimPass:
+         if (refLine[refI] < w) {
+           a0 = refLine[refI + 1];
+           refI += 2;
+         }
+         break;
+       case twoDimHoriz:
+         if (codingI & 1) {
+           code1 = 0;
+           do {
+             code1 += code3 = mmrDecoder->getBlackCode();
+           } while (code3 >= 64);
+           code2 = 0;
+           do {
+             code2 += code3 = mmrDecoder->getWhiteCode();
+           } while (code3 >= 64);
+         } else {
+           code1 = 0;
+           do {
+             code1 += code3 = mmrDecoder->getWhiteCode();
+           } while (code3 >= 64);
+           code2 = 0;
+           do {
+             code2 += code3 = mmrDecoder->getBlackCode();
+           } while (code3 >= 64);
+         }
+         if (code1 > 0 || code2 > 0) {
+           a0 = codingLine[codingI++] = a0 + code1;
+           a0 = codingLine[codingI++] = a0 + code2;
+           while (refLine[refI] <= a0 && refLine[refI] < w) {
+             refI += 2;
+           }
+         }
+         break;
+       case twoDimVert0:
+         a0 = codingLine[codingI++] = refLine[refI];
+         if (refLine[refI] < w) {
+           ++refI;
+         }
+         break;
+       case twoDimVertR1:
+         a0 = codingLine[codingI++] = refLine[refI] + 1;
+         if (refLine[refI] < w) {
+           ++refI;
+           while (refLine[refI] <= a0 && refLine[refI] < w) {
+             refI += 2;
+           }
+         }
+         break;
+       case twoDimVertR2:
+         a0 = codingLine[codingI++] = refLine[refI] + 2;
+         if (refLine[refI] < w) {
+           ++refI;
+           while (refLine[refI] <= a0 && refLine[refI] < w) {
+             refI += 2;
+           }
+         }
+         break;
+       case twoDimVertR3:
+         a0 = codingLine[codingI++] = refLine[refI] + 3;
+         if (refLine[refI] < w) {
+           ++refI;
+           while (refLine[refI] <= a0 && refLine[refI] < w) {
+             refI += 2;
+           }
+         }
+         break;
+       case twoDimVertL1:
+         a0 = codingLine[codingI++] = refLine[refI] - 1;
+         if (refI > 0) {
+           --refI;
+         } else {
+           ++refI;
+         }
+         while (refLine[refI] <= a0 && refLine[refI] < w) {
+           refI += 2;
+         }
+         break;
+       case twoDimVertL2:
+         a0 = codingLine[codingI++] = refLine[refI] - 2;
+         if (refI > 0) {
+           --refI;
+         } else {
+           ++refI;
+         }
+         while (refLine[refI] <= a0 && refLine[refI] < w) {
+           refI += 2;
+         }
+         break;
+       case twoDimVertL3:
+         a0 = codingLine[codingI++] = refLine[refI] - 3;
+         if (refI > 0) {
+           --refI;
+         } else {
+           ++refI;
+         }
+         while (refLine[refI] <= a0 && refLine[refI] < w) {
+           refI += 2;
+         }
+         break;
+       default:
+         error(getPos(), "Illegal code in JBIG2 MMR bitmap data");
+         break;
+       }
+      } while (a0 < w);
+      codingLine[codingI++] = w;
+
+      // convert the run lengths to a bitmap line
+      i = 0;
+      while (codingLine[i] < w) {
+       for (x = codingLine[i]; x < codingLine[i+1]; ++x) {
+         bitmap->setPixel(x, y);
+       }
+       i += 2;
+      }
+    }
+
+    if (mmrDataLength >= 0) {
+      mmrDecoder->skipTo(mmrDataLength);
+    } else {
+      if (mmrDecoder->get24Bits() != 0x001001) {
+       error(getPos(), "Missing EOFB in JBIG2 MMR bitmap data");
+      }
+    }
+
+    gfree(refLine);
+    gfree(codingLine);
+
+  //----- arithmetic decode
+
+  } else {
+    // set up the typical row context
+    ltpCX = 0; // make gcc happy
+    if (tpgdOn) {
+      switch (templ) {
+      case 0:
+       ltpCX = 0x3953; // 001 11001 0101 0011
+       break;
+      case 1:
+       ltpCX = 0x079a; // 0011 11001 101 0
+       break;
+      case 2:
+       ltpCX = 0x0e3; // 001 1100 01 1
+       break;
+      case 3:
+       ltpCX = 0x18a; // 01100 0101 1
+       break;
+      }
+    }
+
+    ltp = 0;
+    cx = cx0 = cx1 = cx2 = 0; // make gcc happy
+    for (y = 0; y < h; ++y) {
+
+      // check for a "typical" (duplicate) row
+      if (tpgdOn) {
+       if (arithDecoder->decodeBit(ltpCX, genericRegionStats)) {
+         ltp = !ltp;
+       }
+       if (ltp) {
+         bitmap->duplicateRow(y, y-1);
+         continue;
+       }
+      }
+
+      switch (templ) {
+      case 0:
+
+       // set up the context
+       bitmap->getPixelPtr(0, y-2, &cxPtr0);
+       cx0 = bitmap->nextPixel(&cxPtr0);
+       cx0 = (cx0 << 1) | bitmap->nextPixel(&cxPtr0);
+       bitmap->getPixelPtr(0, y-1, &cxPtr1);
+       cx1 = bitmap->nextPixel(&cxPtr1);
+       cx1 = (cx1 << 1) | bitmap->nextPixel(&cxPtr1);
+       cx1 = (cx1 << 1) | bitmap->nextPixel(&cxPtr1);
+       cx2 = 0;
+       bitmap->getPixelPtr(atx[0], y + aty[0], &atPtr0);
+       bitmap->getPixelPtr(atx[1], y + aty[1], &atPtr1);
+       bitmap->getPixelPtr(atx[2], y + aty[2], &atPtr2);
+       bitmap->getPixelPtr(atx[3], y + aty[3], &atPtr3);
+
+       // decode the row
+       for (x = 0; x < w; ++x) {
+
+         // build the context
+         cx = (cx0 << 13) | (cx1 << 8) | (cx2 << 4) |
+              (bitmap->nextPixel(&atPtr0) << 3) |
+              (bitmap->nextPixel(&atPtr1) << 2) |
+              (bitmap->nextPixel(&atPtr2) << 1) |
+              bitmap->nextPixel(&atPtr3);
+
+         // check for a skipped pixel
+         if (useSkip && skip->getPixel(x, y)) {
+           pix = 0;
+
+         // decode the pixel
+         } else if ((pix = arithDecoder->decodeBit(cx, genericRegionStats))) {
+           bitmap->setPixel(x, y);
+         }
+
+         // update the context
+         cx0 = ((cx0 << 1) | bitmap->nextPixel(&cxPtr0)) & 0x07;
+         cx1 = ((cx1 << 1) | bitmap->nextPixel(&cxPtr1)) & 0x1f;
+         cx2 = ((cx2 << 1) | pix) & 0x0f;
+       }
+       break;
+
+      case 1:
+
+       // set up the context
+       bitmap->getPixelPtr(0, y-2, &cxPtr0);
+       cx0 = bitmap->nextPixel(&cxPtr0);
+       cx0 = (cx0 << 1) | bitmap->nextPixel(&cxPtr0);
+       cx0 = (cx0 << 1) | bitmap->nextPixel(&cxPtr0);
+       bitmap->getPixelPtr(0, y-1, &cxPtr1);
+       cx1 = bitmap->nextPixel(&cxPtr1);
+       cx1 = (cx1 << 1) | bitmap->nextPixel(&cxPtr1);
+       cx1 = (cx1 << 1) | bitmap->nextPixel(&cxPtr1);
+       cx2 = 0;
+       bitmap->getPixelPtr(atx[0], y + aty[0], &atPtr0);
+
+       // decode the row
+       for (x = 0; x < w; ++x) {
+
+         // build the context
+         cx = (cx0 << 9) | (cx1 << 4) | (cx2 << 1) |
+              bitmap->nextPixel(&atPtr0);
+
+         // check for a skipped pixel
+         if (useSkip && skip->getPixel(x, y)) {
+           pix = 0;
+
+         // decode the pixel
+         } else if ((pix = arithDecoder->decodeBit(cx, genericRegionStats))) {
+           bitmap->setPixel(x, y);
+         }
+
+         // update the context
+         cx0 = ((cx0 << 1) | bitmap->nextPixel(&cxPtr0)) & 0x0f;
+         cx1 = ((cx1 << 1) | bitmap->nextPixel(&cxPtr1)) & 0x1f;
+         cx2 = ((cx2 << 1) | pix) & 0x07;
+       }
+       break;
+
+      case 2:
+
+       // set up the context
+       bitmap->getPixelPtr(0, y-2, &cxPtr0);
+       cx0 = bitmap->nextPixel(&cxPtr0);
+       cx0 = (cx0 << 1) | bitmap->nextPixel(&cxPtr0);
+       bitmap->getPixelPtr(0, y-1, &cxPtr1);
+       cx1 = bitmap->nextPixel(&cxPtr1);
+       cx1 = (cx1 << 1) | bitmap->nextPixel(&cxPtr1);
+       cx2 = 0;
+       bitmap->getPixelPtr(atx[0], y + aty[0], &atPtr0);
+
+       // decode the row
+       for (x = 0; x < w; ++x) {
+
+         // build the context
+         cx = (cx0 << 7) | (cx1 << 3) | (cx2 << 1) |
+              bitmap->nextPixel(&atPtr0);
+
+         // check for a skipped pixel
+         if (useSkip && skip->getPixel(x, y)) {
+           pix = 0;
+
+         // decode the pixel
+         } else if ((pix = arithDecoder->decodeBit(cx, genericRegionStats))) {
+           bitmap->setPixel(x, y);
+         }
+
+         // update the context
+         cx0 = ((cx0 << 1) | bitmap->nextPixel(&cxPtr0)) & 0x07;
+         cx1 = ((cx1 << 1) | bitmap->nextPixel(&cxPtr1)) & 0x0f;
+         cx2 = ((cx2 << 1) | pix) & 0x03;
+       }
+       break;
+
+      case 3:
+
+       // set up the context
+       bitmap->getPixelPtr(0, y-1, &cxPtr1);
+       cx1 = bitmap->nextPixel(&cxPtr1);
+       cx1 = (cx1 << 1) | bitmap->nextPixel(&cxPtr1);
+       cx2 = 0;
+       bitmap->getPixelPtr(atx[0], y + aty[0], &atPtr0);
+
+       // decode the row
+       for (x = 0; x < w; ++x) {
+
+         // build the context
+         cx = (cx1 << 5) | (cx2 << 1) |
+              bitmap->nextPixel(&atPtr0);
+
+         // check for a skipped pixel
+         if (useSkip && skip->getPixel(x, y)) {
+           pix = 0;
+
+         // decode the pixel
+         } else if ((pix = arithDecoder->decodeBit(cx, genericRegionStats))) {
+           bitmap->setPixel(x, y);
+         }
+
+         // update the context
+         cx1 = ((cx1 << 1) | bitmap->nextPixel(&cxPtr1)) & 0x1f;
+         cx2 = ((cx2 << 1) | pix) & 0x0f;
+       }
+       break;
+      }
+    }
+  }
+
+  return bitmap;
+}
+
+void JBIG2Stream::readGenericRefinementRegionSeg(Guint segNum, GBool imm,
+                                                GBool lossless, Guint length,
+                                                Guint *refSegs,
+                                                Guint nRefSegs) {
+  JBIG2Bitmap *bitmap, *refBitmap;
+  Guint w, h, x, y, segInfoFlags, extCombOp;
+  Guint flags, templ, tpgrOn;
+  int atx[2], aty[2];
+  JBIG2Segment *seg;
+
+  // region segment info field
+  if (!readULong(&w) || !readULong(&h) ||
+      !readULong(&x) || !readULong(&y) ||
+      !readUByte(&segInfoFlags)) {
+    goto eofError;
+  }
+  extCombOp = segInfoFlags & 7;
+
+  // rest of the generic refinement region segment header
+  if (!readUByte(&flags)) {
+    goto eofError;
+  }
+  templ = flags & 1;
+  tpgrOn = (flags >> 1) & 1;
+
+  // AT flags
+  if (!templ) {
+    if (!readByte(&atx[0]) || !readByte(&aty[0]) ||
+       !readByte(&atx[1]) || !readByte(&aty[1])) {
+      goto eofError;
+    }
+  }
+
+  // resize the page bitmap if needed
+  if (nRefSegs == 0 || imm) {
+    if (pageH == 0xffffffff && y + h > curPageH) {
+      pageBitmap->expand(y + h, pageDefPixel);
+    }
+  }
+
+  // get referenced bitmap
+  if (nRefSegs > 1) {
+    error(getPos(), "Bad reference in JBIG2 generic refinement segment");
+    return;
+  }
+  if (nRefSegs == 1) {
+    seg = findSegment(refSegs[0]);
+    if (seg->getType() != jbig2SegBitmap) {
+      error(getPos(), "Bad bitmap reference in JBIG2 generic refinement segment");
+      return;
+    }
+    refBitmap = (JBIG2Bitmap *)seg;
+  } else {
+    refBitmap = pageBitmap->getSlice(x, y, w, h);
+  }
+
+  // set up the arithmetic decoder
+  resetRefinementStats(templ, NULL);
+  arithDecoder->start();
+
+  // read
+  bitmap = readGenericRefinementRegion(w, h, templ, tpgrOn,
+                                      refBitmap, 0, 0, atx, aty);
+
+  // combine the region bitmap into the page bitmap
+  if (imm) {
+    pageBitmap->combine(bitmap, x, y, extCombOp);
+    delete bitmap;
+
+  // store the region bitmap
+  } else {
+    bitmap->setSegNum(segNum);
+    segments->append(bitmap);
+  }
+
+  // delete the referenced bitmap
+  if (nRefSegs == 1) {
+    discardSegment(refSegs[0]);
+  } else {
+    delete refBitmap;
+  }
+
+  return;
+
+ eofError:
+  error(getPos(), "Unexpected EOF in JBIG2 stream");
+}
+
+JBIG2Bitmap *JBIG2Stream::readGenericRefinementRegion(int w, int h,
+                                                     int templ, GBool tpgrOn,
+                                                     JBIG2Bitmap *refBitmap,
+                                                     int refDX, int refDY,
+                                                     int *atx, int *aty) {
+  JBIG2Bitmap *bitmap;
+  GBool ltp;
+  Guint ltpCX, cx, cx0, cx2, cx3, cx4, tpgrCX0, tpgrCX1, tpgrCX2;
+  JBIG2BitmapPtr cxPtr0, cxPtr1, cxPtr2, cxPtr3, cxPtr4, cxPtr5, cxPtr6;
+  JBIG2BitmapPtr tpgrCXPtr0, tpgrCXPtr1, tpgrCXPtr2;
+  int x, y, pix;
+
+  bitmap = new JBIG2Bitmap(0, w, h);
+  bitmap->clearToZero();
+
+  // set up the typical row context
+  if (templ) {
+    ltpCX = 0x008;
+  } else {
+    ltpCX = 0x0010;
+  }
+
+  ltp = 0;
+  for (y = 0; y < h; ++y) {
+
+    if (templ) {
+
+      // set up the context
+      bitmap->getPixelPtr(0, y-1, &cxPtr0);
+      cx0 = bitmap->nextPixel(&cxPtr0);
+      bitmap->getPixelPtr(-1, y, &cxPtr1);
+      refBitmap->getPixelPtr(-refDX, y-1-refDY, &cxPtr2);
+      refBitmap->getPixelPtr(-1-refDX, y-refDY, &cxPtr3);
+      cx3 = refBitmap->nextPixel(&cxPtr3);
+      cx3 = (cx3 << 1) | refBitmap->nextPixel(&cxPtr3);
+      refBitmap->getPixelPtr(-refDX, y+1-refDY, &cxPtr4);
+      cx4 = refBitmap->nextPixel(&cxPtr4);
+
+      // set up the typical prediction context
+      tpgrCX0 = tpgrCX1 = tpgrCX2 = 0; // make gcc happy
+      if (tpgrOn) {
+       refBitmap->getPixelPtr(-1-refDX, y-1-refDY, &tpgrCXPtr0);
+       tpgrCX0 = refBitmap->nextPixel(&tpgrCXPtr0);
+       tpgrCX0 = (tpgrCX0 << 1) | refBitmap->nextPixel(&tpgrCXPtr0);
+       tpgrCX0 = (tpgrCX0 << 1) | refBitmap->nextPixel(&tpgrCXPtr0);
+       refBitmap->getPixelPtr(-1-refDX, y-refDY, &tpgrCXPtr1);
+       tpgrCX1 = refBitmap->nextPixel(&tpgrCXPtr1);
+       tpgrCX1 = (tpgrCX1 << 1) | refBitmap->nextPixel(&tpgrCXPtr1);
+       tpgrCX1 = (tpgrCX1 << 1) | refBitmap->nextPixel(&tpgrCXPtr1);
+       refBitmap->getPixelPtr(-1-refDX, y+1-refDY, &tpgrCXPtr2);
+       tpgrCX2 = refBitmap->nextPixel(&tpgrCXPtr2);
+       tpgrCX2 = (tpgrCX2 << 1) | refBitmap->nextPixel(&tpgrCXPtr2);
+       tpgrCX2 = (tpgrCX2 << 1) | refBitmap->nextPixel(&tpgrCXPtr2);
+      }
+
+      for (x = 0; x < w; ++x) {
+
+       // update the context
+       cx0 = ((cx0 << 1) | bitmap->nextPixel(&cxPtr0)) & 7;
+       cx3 = ((cx3 << 1) | refBitmap->nextPixel(&cxPtr3)) & 7;
+       cx4 = ((cx4 << 1) | refBitmap->nextPixel(&cxPtr4)) & 3;
+
+       if (tpgrOn) {
+         // update the typical predictor context
+         tpgrCX0 = ((tpgrCX0 << 1) | refBitmap->nextPixel(&tpgrCXPtr0)) & 7;
+         tpgrCX1 = ((tpgrCX1 << 1) | refBitmap->nextPixel(&tpgrCXPtr1)) & 7;
+         tpgrCX2 = ((tpgrCX2 << 1) | refBitmap->nextPixel(&tpgrCXPtr2)) & 7;
+
+         // check for a "typical" pixel
+         if (arithDecoder->decodeBit(ltpCX, refinementRegionStats)) {
+           ltp = !ltp;
+         }
+         if (tpgrCX0 == 0 && tpgrCX1 == 0 && tpgrCX2 == 0) {
+           bitmap->clearPixel(x, y);
+           continue;
+         } else if (tpgrCX0 == 7 && tpgrCX1 == 7 && tpgrCX2 == 7) {
+           bitmap->setPixel(x, y);
+           continue;
+         }
+       }
+
+       // build the context
+       cx = (cx0 << 7) | (bitmap->nextPixel(&cxPtr1) << 6) |
+            (refBitmap->nextPixel(&cxPtr2) << 5) |
+            (cx3 << 2) | cx4;
+
+       // decode the pixel
+       if ((pix = arithDecoder->decodeBit(cx, refinementRegionStats))) {
+         bitmap->setPixel(x, y);
+       }
+      }
+
+    } else {
+
+      // set up the context
+      bitmap->getPixelPtr(0, y-1, &cxPtr0);
+      cx0 = bitmap->nextPixel(&cxPtr0);
+      bitmap->getPixelPtr(-1, y, &cxPtr1);
+      refBitmap->getPixelPtr(-refDX, y-1-refDY, &cxPtr2);
+      cx2 = refBitmap->nextPixel(&cxPtr2);
+      refBitmap->getPixelPtr(-1-refDX, y-refDY, &cxPtr3);
+      cx3 = refBitmap->nextPixel(&cxPtr3);
+      cx3 = (cx3 << 1) | refBitmap->nextPixel(&cxPtr3);
+      refBitmap->getPixelPtr(-1-refDX, y+1-refDY, &cxPtr4);
+      cx4 = refBitmap->nextPixel(&cxPtr4);
+      cx4 = (cx4 << 1) | refBitmap->nextPixel(&cxPtr4);
+      bitmap->getPixelPtr(atx[0], y+aty[0], &cxPtr5);
+      refBitmap->getPixelPtr(atx[1]-refDX, y+aty[1]-refDY, &cxPtr6);
+
+      // set up the typical prediction context
+      tpgrCX0 = tpgrCX1 = tpgrCX2 = 0; // make gcc happy
+      if (tpgrOn) {
+       refBitmap->getPixelPtr(-1-refDX, y-1-refDY, &tpgrCXPtr0);
+       tpgrCX0 = refBitmap->nextPixel(&tpgrCXPtr0);
+       tpgrCX0 = (tpgrCX0 << 1) | refBitmap->nextPixel(&tpgrCXPtr0);
+       tpgrCX0 = (tpgrCX0 << 1) | refBitmap->nextPixel(&tpgrCXPtr0);
+       refBitmap->getPixelPtr(-1-refDX, y-refDY, &tpgrCXPtr1);
+       tpgrCX1 = refBitmap->nextPixel(&tpgrCXPtr1);
+       tpgrCX1 = (tpgrCX1 << 1) | refBitmap->nextPixel(&tpgrCXPtr1);
+       tpgrCX1 = (tpgrCX1 << 1) | refBitmap->nextPixel(&tpgrCXPtr1);
+       refBitmap->getPixelPtr(-1-refDX, y+1-refDY, &tpgrCXPtr2);
+       tpgrCX2 = refBitmap->nextPixel(&tpgrCXPtr2);
+       tpgrCX2 = (tpgrCX2 << 1) | refBitmap->nextPixel(&tpgrCXPtr2);
+       tpgrCX2 = (tpgrCX2 << 1) | refBitmap->nextPixel(&tpgrCXPtr2);
+      }
+
+      for (x = 0; x < w; ++x) {
+
+       // update the context
+       cx0 = ((cx0 << 1) | bitmap->nextPixel(&cxPtr0)) & 3;
+       cx2 = ((cx2 << 1) | refBitmap->nextPixel(&cxPtr2)) & 3;
+       cx3 = ((cx3 << 1) | refBitmap->nextPixel(&cxPtr3)) & 7;
+       cx4 = ((cx4 << 1) | refBitmap->nextPixel(&cxPtr4)) & 7;
+
+       if (tpgrOn) {
+         // update the typical predictor context
+         tpgrCX0 = ((tpgrCX0 << 1) | refBitmap->nextPixel(&tpgrCXPtr0)) & 7;
+         tpgrCX1 = ((tpgrCX1 << 1) | refBitmap->nextPixel(&tpgrCXPtr1)) & 7;
+         tpgrCX2 = ((tpgrCX2 << 1) | refBitmap->nextPixel(&tpgrCXPtr2)) & 7;
+
+         // check for a "typical" pixel
+         if (arithDecoder->decodeBit(ltpCX, refinementRegionStats)) {
+           ltp = !ltp;
+         }
+         if (tpgrCX0 == 0 && tpgrCX1 == 0 && tpgrCX2 == 0) {
+           bitmap->clearPixel(x, y);
+           continue;
+         } else if (tpgrCX0 == 7 && tpgrCX1 == 7 && tpgrCX2 == 7) {
+           bitmap->setPixel(x, y);
+           continue;
+         }
+       }
+
+       // build the context
+       cx = (cx0 << 11) | (bitmap->nextPixel(&cxPtr1) << 10) |
+            (cx2 << 8) | (cx3 << 5) | (cx4 << 2) |
+            (bitmap->nextPixel(&cxPtr5) << 1) |
+            refBitmap->nextPixel(&cxPtr6);
+
+       // decode the pixel
+       if ((pix = arithDecoder->decodeBit(cx, refinementRegionStats))) {
+         bitmap->setPixel(x, y);
+       }
+      }
+    }
+  }
+
+  return bitmap;
+}
+
+void JBIG2Stream::readPageInfoSeg(Guint length) {
+  Guint xRes, yRes, flags, striping;
+
+  if (!readULong(&pageW) || !readULong(&pageH) ||
+      !readULong(&xRes) || !readULong(&yRes) ||
+      !readUByte(&flags) || !readUWord(&striping)) {
+    goto eofError;
+  }
+  pageDefPixel = (flags >> 2) & 1;
+  defCombOp = (flags >> 3) & 3;
+
+  // allocate the page bitmap
+  if (pageH == 0xffffffff) {
+    curPageH = striping & 0x7fff;
+  } else {
+    curPageH = pageH;
+  }
+  pageBitmap = new JBIG2Bitmap(0, pageW, curPageH);
+
+  // default pixel value
+  if (pageDefPixel) {
+    pageBitmap->clearToOne();
+  } else {
+    pageBitmap->clearToZero();
+  }
+
+  return;
+
+ eofError:
+  error(getPos(), "Unexpected EOF in JBIG2 stream");
+}
+
+void JBIG2Stream::readEndOfStripeSeg(Guint length) {
+  Guint i;
+
+  // skip the segment
+  for (i = 0; i < length; ++i) {
+    curStr->getChar();
+  }
+}
+
+void JBIG2Stream::readProfilesSeg(Guint length) {
+  Guint i;
+
+  // skip the segment
+  for (i = 0; i < length; ++i) {
+    curStr->getChar();
+  }
+}
+
+void JBIG2Stream::readCodeTableSeg(Guint segNum, Guint length) {
+  JBIG2HuffmanTable *huffTab;
+  Guint flags, oob, prefixBits, rangeBits;
+  int lowVal, highVal, val;
+  Guint huffTabSize, i;
+
+  if (!readUByte(&flags) || !readLong(&lowVal) || !readLong(&highVal)) {
+    goto eofError;
+  }
+  oob = flags & 1;
+  prefixBits = ((flags >> 1) & 7) + 1;
+  rangeBits = ((flags >> 4) & 7) + 1;
+
+  huffDecoder->reset();
+  huffTabSize = 8;
+  huffTab = (JBIG2HuffmanTable *)
+                gmallocn(huffTabSize, sizeof(JBIG2HuffmanTable));
+  i = 0;
+  val = lowVal;
+  while (val < highVal) {
+    if (i == huffTabSize) {
+      huffTabSize *= 2;
+      huffTab = (JBIG2HuffmanTable *)
+                   greallocn(huffTab, huffTabSize, sizeof(JBIG2HuffmanTable));
+    }
+    huffTab[i].val = val;
+    huffTab[i].prefixLen = huffDecoder->readBits(prefixBits);
+    huffTab[i].rangeLen = huffDecoder->readBits(rangeBits);
+    val += 1 << huffTab[i].rangeLen;
+    ++i;
+  }
+  if (i + oob + 3 > huffTabSize) {
+    huffTabSize = i + oob + 3;
+    huffTab = (JBIG2HuffmanTable *)
+                  greallocn(huffTab, huffTabSize, sizeof(JBIG2HuffmanTable));
+  }
+  huffTab[i].val = lowVal - 1;
+  huffTab[i].prefixLen = huffDecoder->readBits(prefixBits);
+  huffTab[i].rangeLen = jbig2HuffmanLOW;
+  ++i;
+  huffTab[i].val = highVal;
+  huffTab[i].prefixLen = huffDecoder->readBits(prefixBits);
+  huffTab[i].rangeLen = 32;
+  ++i;
+  if (oob) {
+    huffTab[i].val = 0;
+    huffTab[i].prefixLen = huffDecoder->readBits(prefixBits);
+    huffTab[i].rangeLen = jbig2HuffmanOOB;
+    ++i;
+  }
+  huffTab[i].val = 0;
+  huffTab[i].prefixLen = 0;
+  huffTab[i].rangeLen = jbig2HuffmanEOT;
+  huffDecoder->buildTable(huffTab, i);
+
+  // create and store the new table segment
+  segments->append(new JBIG2CodeTable(segNum, huffTab));
+
+  return;
+
+ eofError:
+  error(getPos(), "Unexpected EOF in JBIG2 stream");
+}
+
+void JBIG2Stream::readExtensionSeg(Guint length) {
+  Guint i;
+
+  // skip the segment
+  for (i = 0; i < length; ++i) {
+    curStr->getChar();
+  }
+}
+
+JBIG2Segment *JBIG2Stream::findSegment(Guint segNum) {
+  JBIG2Segment *seg;
+  int i;
+
+  for (i = 0; i < globalSegments->getLength(); ++i) {
+    seg = (JBIG2Segment *)globalSegments->get(i);
+    if (seg->getSegNum() == segNum) {
+      return seg;
+    }
+  }
+  for (i = 0; i < segments->getLength(); ++i) {
+    seg = (JBIG2Segment *)segments->get(i);
+    if (seg->getSegNum() == segNum) {
+      return seg;
+    }
+  }
+  return NULL;
+}
+
+void JBIG2Stream::discardSegment(Guint segNum) {
+  JBIG2Segment *seg;
+  int i;
+
+  for (i = 0; i < globalSegments->getLength(); ++i) {
+    seg = (JBIG2Segment *)globalSegments->get(i);
+    if (seg->getSegNum() == segNum) {
+      globalSegments->del(i);
+      return;
+    }
+  }
+  for (i = 0; i < segments->getLength(); ++i) {
+    seg = (JBIG2Segment *)segments->get(i);
+    if (seg->getSegNum() == segNum) {
+      segments->del(i);
+      return;
+    }
+  }
+}
+
+void JBIG2Stream::resetGenericStats(Guint templ,
+                                   JArithmeticDecoderStats *prevStats) {
+  int size;
+
+  size = contextSize[templ];
+  if (prevStats && prevStats->getContextSize() == size) {
+    if (genericRegionStats->getContextSize() == size) {
+      genericRegionStats->copyFrom(prevStats);
+    } else {
+      delete genericRegionStats;
+      genericRegionStats = prevStats->copy();
+    }
+  } else {
+    if (genericRegionStats->getContextSize() == size) {
+      genericRegionStats->reset();
+    } else {
+      delete genericRegionStats;
+      genericRegionStats = new JArithmeticDecoderStats(1 << size);
+    }
+  }
+}
+
+void JBIG2Stream::resetRefinementStats(Guint templ,
+                                      JArithmeticDecoderStats *prevStats) {
+  int size;
+
+  size = refContextSize[templ];
+  if (prevStats && prevStats->getContextSize() == size) {
+    if (refinementRegionStats->getContextSize() == size) {
+      refinementRegionStats->copyFrom(prevStats);
+    } else {
+      delete refinementRegionStats;
+      refinementRegionStats = prevStats->copy();
+    }
+  } else {
+    if (refinementRegionStats->getContextSize() == size) {
+      refinementRegionStats->reset();
+    } else {
+      delete refinementRegionStats;
+      refinementRegionStats = new JArithmeticDecoderStats(1 << size);
+    }
+  }
+}
+
+void JBIG2Stream::resetIntStats(int symCodeLen) {
+  iadhStats->reset();
+  iadwStats->reset();
+  iaexStats->reset();
+  iaaiStats->reset();
+  iadtStats->reset();
+  iaitStats->reset();
+  iafsStats->reset();
+  iadsStats->reset();
+  iardxStats->reset();
+  iardyStats->reset();
+  iardwStats->reset();
+  iardhStats->reset();
+  iariStats->reset();
+  if (iaidStats->getContextSize() == symCodeLen + 1) {
+    iaidStats->reset();
+  } else {
+    delete iaidStats;
+    iaidStats = new JArithmeticDecoderStats(1 << (symCodeLen + 1));
+  }
+}
+
+GBool JBIG2Stream::readUByte(Guint *x) {
+  int c0;
+
+  if ((c0 = curStr->getChar()) == EOF) {
+    return gFalse;
+  }
+  *x = (Guint)c0;
+  return gTrue;
+}
+
+GBool JBIG2Stream::readByte(int *x) {
+ int c0;
+
+  if ((c0 = curStr->getChar()) == EOF) {
+    return gFalse;
+  }
+  *x = c0;
+  if (c0 & 0x80) {
+    *x |= -1 - 0xff;
+  }
+  return gTrue;
+}
+
+GBool JBIG2Stream::readUWord(Guint *x) {
+  int c0, c1;
+
+  if ((c0 = curStr->getChar()) == EOF ||
+      (c1 = curStr->getChar()) == EOF) {
+    return gFalse;
+  }
+  *x = (Guint)((c0 << 8) | c1);
+  return gTrue;
+}
+
+GBool JBIG2Stream::readULong(Guint *x) {
+  int c0, c1, c2, c3;
+
+  if ((c0 = curStr->getChar()) == EOF ||
+      (c1 = curStr->getChar()) == EOF ||
+      (c2 = curStr->getChar()) == EOF ||
+      (c3 = curStr->getChar()) == EOF) {
+    return gFalse;
+  }
+  *x = (Guint)((c0 << 24) | (c1 << 16) | (c2 << 8) | c3);
+  return gTrue;
+}
+
+GBool JBIG2Stream::readLong(int *x) {
+  int c0, c1, c2, c3;
+
+  if ((c0 = curStr->getChar()) == EOF ||
+      (c1 = curStr->getChar()) == EOF ||
+      (c2 = curStr->getChar()) == EOF ||
+      (c3 = curStr->getChar()) == EOF) {
+    return gFalse;
+  }
+  *x = ((c0 << 24) | (c1 << 16) | (c2 << 8) | c3);
+  if (c0 & 0x80) {
+    *x |= -1 - (int)0xffffffff;
+  }
+  return gTrue;
+}
diff --git a/lib/xpdf/JBIG2Stream.h b/lib/xpdf/JBIG2Stream.h
new file mode 100644 (file)
index 0000000..44e09db
--- /dev/null
@@ -0,0 +1,143 @@
+//========================================================================
+//
+// JBIG2Stream.h
+//
+// Copyright 2002-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef JBIG2STREAM_H
+#define JBIG2STREAM_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#include "gtypes.h"
+#include "Object.h"
+#include "Stream.h"
+
+class GList;
+class JBIG2Segment;
+class JBIG2Bitmap;
+class JArithmeticDecoder;
+class JArithmeticDecoderStats;
+class JBIG2HuffmanDecoder;
+struct JBIG2HuffmanTable;
+class JBIG2MMRDecoder;
+
+//------------------------------------------------------------------------
+
+class JBIG2Stream: public FilterStream {
+public:
+
+  JBIG2Stream(Stream *strA, Object *globalsStream);
+  virtual ~JBIG2Stream();
+  virtual StreamKind getKind() { return strJBIG2; }
+  virtual void reset();
+  virtual int getChar();
+  virtual int lookChar();
+  virtual GString *getPSFilter(int psLevel, char *indent);
+  virtual GBool isBinary(GBool last = gTrue);
+
+private:
+
+  void readSegments();
+  GBool readSymbolDictSeg(Guint segNum, Guint length,
+                         Guint *refSegs, Guint nRefSegs);
+  void readTextRegionSeg(Guint segNum, GBool imm,
+                        GBool lossless, Guint length,
+                        Guint *refSegs, Guint nRefSegs);
+  JBIG2Bitmap *readTextRegion(GBool huff, GBool refine,
+                             int w, int h,
+                             Guint numInstances,
+                             Guint logStrips,
+                             int numSyms,
+                             JBIG2HuffmanTable *symCodeTab,
+                             Guint symCodeLen,
+                             JBIG2Bitmap **syms,
+                             Guint defPixel, Guint combOp,
+                             Guint transposed, Guint refCorner,
+                             int sOffset,
+                             JBIG2HuffmanTable *huffFSTable,
+                             JBIG2HuffmanTable *huffDSTable,
+                             JBIG2HuffmanTable *huffDTTable,
+                             JBIG2HuffmanTable *huffRDWTable,
+                             JBIG2HuffmanTable *huffRDHTable,
+                             JBIG2HuffmanTable *huffRDXTable,
+                             JBIG2HuffmanTable *huffRDYTable,
+                             JBIG2HuffmanTable *huffRSizeTable,
+                             Guint templ,
+                             int *atx, int *aty);
+  void readPatternDictSeg(Guint segNum, Guint length);
+  void readHalftoneRegionSeg(Guint segNum, GBool imm,
+                            GBool lossless, Guint length,
+                            Guint *refSegs, Guint nRefSegs);
+  void readGenericRegionSeg(Guint segNum, GBool imm,
+                           GBool lossless, Guint length);
+  JBIG2Bitmap *readGenericBitmap(GBool mmr, int w, int h,
+                                int templ, GBool tpgdOn,
+                                GBool useSkip, JBIG2Bitmap *skip,
+                                int *atx, int *aty,
+                                int mmrDataLength);
+  void readGenericRefinementRegionSeg(Guint segNum, GBool imm,
+                                     GBool lossless, Guint length,
+                                     Guint *refSegs,
+                                     Guint nRefSegs);
+  JBIG2Bitmap *readGenericRefinementRegion(int w, int h,
+                                          int templ, GBool tpgrOn,
+                                          JBIG2Bitmap *refBitmap,
+                                          int refDX, int refDY,
+                                          int *atx, int *aty);
+  void readPageInfoSeg(Guint length);
+  void readEndOfStripeSeg(Guint length);
+  void readProfilesSeg(Guint length);
+  void readCodeTableSeg(Guint segNum, Guint length);
+  void readExtensionSeg(Guint length);
+  JBIG2Segment *findSegment(Guint segNum);
+  void discardSegment(Guint segNum);
+  void resetGenericStats(Guint templ,
+                        JArithmeticDecoderStats *prevStats);
+  void resetRefinementStats(Guint templ,
+                           JArithmeticDecoderStats *prevStats);
+  void resetIntStats(int symCodeLen);
+  GBool readUByte(Guint *x);
+  GBool readByte(int *x);
+  GBool readUWord(Guint *x);
+  GBool readULong(Guint *x);
+  GBool readLong(int *x);
+
+  Guint pageW, pageH, curPageH;
+  Guint pageDefPixel;
+  JBIG2Bitmap *pageBitmap;
+  Guint defCombOp;
+  GList *segments;             // [JBIG2Segment]
+  GList *globalSegments;       // [JBIG2Segment]
+  Stream *curStr;
+  Guchar *dataPtr;
+  Guchar *dataEnd;
+
+  JArithmeticDecoder *arithDecoder;
+  JArithmeticDecoderStats *genericRegionStats;
+  JArithmeticDecoderStats *refinementRegionStats;
+  JArithmeticDecoderStats *iadhStats;
+  JArithmeticDecoderStats *iadwStats;
+  JArithmeticDecoderStats *iaexStats;
+  JArithmeticDecoderStats *iaaiStats;
+  JArithmeticDecoderStats *iadtStats;
+  JArithmeticDecoderStats *iaitStats;
+  JArithmeticDecoderStats *iafsStats;
+  JArithmeticDecoderStats *iadsStats;
+  JArithmeticDecoderStats *iardxStats;
+  JArithmeticDecoderStats *iardyStats;
+  JArithmeticDecoderStats *iardwStats;
+  JArithmeticDecoderStats *iardhStats;
+  JArithmeticDecoderStats *iariStats;
+  JArithmeticDecoderStats *iaidStats;
+  JBIG2HuffmanDecoder *huffDecoder;
+  JBIG2MMRDecoder *mmrDecoder;
+};
+
+#endif
diff --git a/lib/xpdf/JPXStream.cc b/lib/xpdf/JPXStream.cc
new file mode 100644 (file)
index 0000000..336b7ba
--- /dev/null
@@ -0,0 +1,2953 @@
+//========================================================================
+//
+// JPXStream.cc
+//
+// Copyright 2002-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include "gmem.h"
+#include "Error.h"
+#include "JArithmeticDecoder.h"
+#include "JPXStream.h"
+
+//~ to do:
+//  - precincts
+//  - ROI
+//  - progression order changes
+//  - packed packet headers
+//  - support for palettes, channel maps, etc.
+//  - make sure all needed JP2/JPX subboxes are parsed (readBoxes)
+//  - can we assume that QCC segments must come after the QCD segment?
+//  - skip EPH markers (readTilePartData)
+//  - handle tilePartToEOC in readTilePartData
+//  - deal with multiple codeword segments (readTilePartData,
+//    readCodeBlockData)
+//  - progression orders 2, 3, and 4
+//  - in coefficient decoding (readCodeBlockData):
+//    - termination pattern: terminate after every coding pass
+//    - error resilience segmentation symbol
+//    - selective arithmetic coding bypass
+//    - vertically causal context formation
+//    - coeffs longer than 31 bits (should just ignore the extra bits?)
+//  - handle boxes larger than 2^32 bytes
+//  - the fixed-point arithmetic won't handle 16-bit pixels
+
+//------------------------------------------------------------------------
+
+// number of contexts for the arithmetic decoder
+#define jpxNContexts        19
+
+#define jpxContextSigProp    0 // 0 - 8: significance prop and cleanup
+#define jpxContextSign       9 // 9 - 13: sign
+#define jpxContextMagRef    14 // 14 -16: magnitude refinement
+#define jpxContextRunLength 17 // cleanup: run length
+#define jpxContextUniform   18 // cleanup: first signif coeff
+
+//------------------------------------------------------------------------
+
+#define jpxPassSigProp       0
+#define jpxPassMagRef        1
+#define jpxPassCleanup       2
+
+//------------------------------------------------------------------------
+
+// arithmetic decoder context for the significance propagation and
+// cleanup passes:
+//     [horiz][vert][diag][subband]
+// where subband = 0 for HL
+//               = 1 for LH and LL
+//               = 2 for HH
+static Guint sigPropContext[3][3][5][3] = {
+  {{{ 0, 0, 0 },   // horiz=0, vert=0, diag=0
+    { 1, 1, 3 },   // horiz=0, vert=0, diag=1
+    { 2, 2, 6 },   // horiz=0, vert=0, diag=2
+    { 2, 2, 8 },   // horiz=0, vert=0, diag=3
+    { 2, 2, 8 }},  // horiz=0, vert=0, diag=4
+   {{ 5, 3, 1 },   // horiz=0, vert=1, diag=0
+    { 6, 3, 4 },   // horiz=0, vert=1, diag=1
+    { 6, 3, 7 },   // horiz=0, vert=1, diag=2
+    { 6, 3, 8 },   // horiz=0, vert=1, diag=3
+    { 6, 3, 8 }},  // horiz=0, vert=1, diag=4
+   {{ 8, 4, 2 },   // horiz=0, vert=2, diag=0
+    { 8, 4, 5 },   // horiz=0, vert=2, diag=1
+    { 8, 4, 7 },   // horiz=0, vert=2, diag=2
+    { 8, 4, 8 },   // horiz=0, vert=2, diag=3
+    { 8, 4, 8 }}}, // horiz=0, vert=2, diag=4
+  {{{ 3, 5, 1 },   // horiz=1, vert=0, diag=0
+    { 3, 6, 4 },   // horiz=1, vert=0, diag=1
+    { 3, 6, 7 },   // horiz=1, vert=0, diag=2
+    { 3, 6, 8 },   // horiz=1, vert=0, diag=3
+    { 3, 6, 8 }},  // horiz=1, vert=0, diag=4
+   {{ 7, 7, 2 },   // horiz=1, vert=1, diag=0
+    { 7, 7, 5 },   // horiz=1, vert=1, diag=1
+    { 7, 7, 7 },   // horiz=1, vert=1, diag=2
+    { 7, 7, 8 },   // horiz=1, vert=1, diag=3
+    { 7, 7, 8 }},  // horiz=1, vert=1, diag=4
+   {{ 8, 7, 2 },   // horiz=1, vert=2, diag=0
+    { 8, 7, 5 },   // horiz=1, vert=2, diag=1
+    { 8, 7, 7 },   // horiz=1, vert=2, diag=2
+    { 8, 7, 8 },   // horiz=1, vert=2, diag=3
+    { 8, 7, 8 }}}, // horiz=1, vert=2, diag=4
+  {{{ 4, 8, 2 },   // horiz=2, vert=0, diag=0
+    { 4, 8, 5 },   // horiz=2, vert=0, diag=1
+    { 4, 8, 7 },   // horiz=2, vert=0, diag=2
+    { 4, 8, 8 },   // horiz=2, vert=0, diag=3
+    { 4, 8, 8 }},  // horiz=2, vert=0, diag=4
+   {{ 7, 8, 2 },   // horiz=2, vert=1, diag=0
+    { 7, 8, 5 },   // horiz=2, vert=1, diag=1
+    { 7, 8, 7 },   // horiz=2, vert=1, diag=2
+    { 7, 8, 8 },   // horiz=2, vert=1, diag=3
+    { 7, 8, 8 }},  // horiz=2, vert=1, diag=4
+   {{ 8, 8, 2 },   // horiz=2, vert=2, diag=0
+    { 8, 8, 5 },   // horiz=2, vert=2, diag=1
+    { 8, 8, 7 },   // horiz=2, vert=2, diag=2
+    { 8, 8, 8 },   // horiz=2, vert=2, diag=3
+    { 8, 8, 8 }}}  // horiz=2, vert=2, diag=4
+};
+
+// arithmetic decoder context and xor bit for the sign bit in the
+// significance propagation pass:
+//     [horiz][vert][k]
+// where horiz/vert are offset by 2 (i.e., range is -2 .. 2)
+// and k = 0 for the context
+//       = 1 for the xor bit
+static Guint signContext[5][5][2] = {
+  {{ 13, 1 },  // horiz=-2, vert=-2
+   { 13, 1 },  // horiz=-2, vert=-1
+   { 12, 1 },  // horiz=-2, vert= 0
+   { 11, 1 },  // horiz=-2, vert=+1
+   { 11, 1 }}, // horiz=-2, vert=+2
+  {{ 13, 1 },  // horiz=-1, vert=-2
+   { 13, 1 },  // horiz=-1, vert=-1
+   { 12, 1 },  // horiz=-1, vert= 0
+   { 11, 1 },  // horiz=-1, vert=+1
+   { 11, 1 }}, // horiz=-1, vert=+2
+  {{ 10, 1 },  // horiz= 0, vert=-2
+   { 10, 1 },  // horiz= 0, vert=-1
+   {  9, 0 },  // horiz= 0, vert= 0
+   { 10, 0 },  // horiz= 0, vert=+1
+   { 10, 0 }}, // horiz= 0, vert=+2
+  {{ 11, 0 },  // horiz=+1, vert=-2
+   { 11, 0 },  // horiz=+1, vert=-1
+   { 12, 0 },  // horiz=+1, vert= 0
+   { 13, 0 },  // horiz=+1, vert=+1
+   { 13, 0 }}, // horiz=+1, vert=+2
+  {{ 11, 0 },  // horiz=+2, vert=-2
+   { 11, 0 },  // horiz=+2, vert=-1
+   { 12, 0 },  // horiz=+2, vert= 0
+   { 13, 0 },  // horiz=+2, vert=+1
+   { 13, 0 }}, // horiz=+2, vert=+2
+};
+
+//------------------------------------------------------------------------
+
+// constants used in the IDWT
+#define idwtAlpha  -1.586134342059924
+#define idwtBeta   -0.052980118572961
+#define idwtGamma   0.882911075530934
+#define idwtDelta   0.443506852043971
+#define idwtKappa   1.230174104914001
+#define idwtIKappa  (1.0 / idwtKappa)
+
+// number of bits to the right of the decimal point for the fixed
+// point arithmetic used in the IDWT
+#define fracBits 16
+
+//------------------------------------------------------------------------
+
+// floor(x / y)
+#define jpxFloorDiv(x, y) ((x) / (y))
+
+// floor(x / 2^y)
+#define jpxFloorDivPow2(x, y) ((x) >> (y))
+
+// ceil(x / y)
+#define jpxCeilDiv(x, y) (((x) + (y) - 1) / (y))
+
+// ceil(x / 2^y)
+#define jpxCeilDivPow2(x, y) (((x) + (1 << (y)) - 1) >> (y))
+
+//------------------------------------------------------------------------
+
+JPXStream::JPXStream(Stream *strA):
+  FilterStream(strA)
+{
+  nComps = 0;
+  bpc = NULL;
+  width = height = 0;
+  haveCS = gFalse;
+  havePalette = gFalse;
+  haveCompMap = gFalse;
+  haveChannelDefn = gFalse;
+
+  img.tiles = NULL;
+  bitBuf = 0;
+  bitBufLen = 0;
+  bitBufSkip = gFalse;
+  byteCount = 0;
+}
+
+JPXStream::~JPXStream() {
+  JPXTile *tile;
+  JPXTileComp *tileComp;
+  JPXResLevel *resLevel;
+  JPXPrecinct *precinct;
+  JPXSubband *subband;
+  JPXCodeBlock *cb;
+  Guint comp, i, k, r, pre, sb;
+
+  gfree(bpc);
+  if (havePalette) {
+    gfree(palette.bpc);
+    gfree(palette.c);
+  }
+  if (haveCompMap) {
+    gfree(compMap.comp);
+    gfree(compMap.type);
+    gfree(compMap.pComp);
+  }
+  if (haveChannelDefn) {
+    gfree(channelDefn.idx);
+    gfree(channelDefn.type);
+    gfree(channelDefn.assoc);
+  }
+
+  if (img.tiles) {
+    for (i = 0; i < img.nXTiles * img.nYTiles; ++i) {
+      tile = &img.tiles[i];
+      if (tile->tileComps) {
+       for (comp = 0; comp < img.nComps; ++comp) {
+         tileComp = &tile->tileComps[comp];
+         gfree(tileComp->quantSteps);
+         gfree(tileComp->data);
+         gfree(tileComp->buf);
+         if (tileComp->resLevels) {
+           for (r = 0; r <= tileComp->nDecompLevels; ++r) {
+             resLevel = &tileComp->resLevels[r];
+             if (resLevel->precincts) {
+               for (pre = 0; pre < 1; ++pre) {
+                 precinct = &resLevel->precincts[pre];
+                 if (precinct->subbands) {
+                   for (sb = 0; sb < (r == 0 ? 1 : 3); ++sb) {
+                     subband = &precinct->subbands[sb];
+                     gfree(subband->inclusion);
+                     gfree(subband->zeroBitPlane);
+                     if (subband->cbs) {
+                       for (k = 0; k < subband->nXCBs * subband->nYCBs; ++k) {
+                         cb = &subband->cbs[k];
+                         gfree(cb->coeffs);
+                         if (cb->arithDecoder) {
+                           delete cb->arithDecoder;
+                         }
+                         if (cb->stats) {
+                           delete cb->stats;
+                         }
+                       }
+                       gfree(subband->cbs);
+                     }
+                   }
+                   gfree(precinct->subbands);
+                 }
+               }
+               gfree(img.tiles[i].tileComps[comp].resLevels[r].precincts);
+             }
+           }
+           gfree(img.tiles[i].tileComps[comp].resLevels);
+         }
+       }
+       gfree(img.tiles[i].tileComps);
+      }
+    }
+    gfree(img.tiles);
+  }
+  delete str;
+}
+
+void JPXStream::reset() {
+  str->reset();
+  if (readBoxes()) {
+    curY = img.yOffset;
+  } else {
+    // readBoxes reported an error, so we go immediately to EOF
+    curY = img.ySize;
+  }
+  curX = img.xOffset;
+  curComp = 0;
+  readBufLen = 0;
+}
+
+int JPXStream::getChar() {
+  int c;
+
+  if (readBufLen < 8) {
+    fillReadBuf();
+  }
+  if (readBufLen == 8) {
+    c = readBuf & 0xff;
+    readBufLen = 0;
+  } else if (readBufLen > 8) {
+    c = (readBuf >> (readBufLen - 8)) & 0xff;
+    readBufLen -= 8;
+  } else if (readBufLen == 0) {
+    c = EOF;
+  } else {
+    c = (readBuf << (8 - readBufLen)) & 0xff;
+    readBufLen = 0;
+  }
+  return c;
+}
+
+int JPXStream::lookChar() {
+  int c;
+
+  if (readBufLen < 8) {
+    fillReadBuf();
+  }
+  if (readBufLen == 8) {
+    c = readBuf & 0xff;
+  } else if (readBufLen > 8) {
+    c = (readBuf >> (readBufLen - 8)) & 0xff;
+  } else if (readBufLen == 0) {
+    c = EOF;
+  } else {
+    c = (readBuf << (8 - readBufLen)) & 0xff;
+  }
+  return c;
+}
+
+void JPXStream::fillReadBuf() {
+  JPXTileComp *tileComp;
+  Guint tileIdx, tx, ty;
+  int pix, pixBits;
+
+  do {
+    if (curY >= img.ySize) {
+      return;
+    }
+    tileIdx = ((curY - img.yTileOffset) / img.yTileSize) * img.nXTiles
+              + (curX - img.xTileOffset) / img.xTileSize;
+#if 1 //~ ignore the palette, assume the PDF ColorSpace object is valid
+    tileComp = &img.tiles[tileIdx].tileComps[curComp];
+#else
+    tileComp = &img.tiles[tileIdx].tileComps[havePalette ? 0 : curComp];
+#endif
+    tx = jpxCeilDiv((curX - img.xTileOffset) % img.xTileSize, tileComp->hSep);
+    ty = jpxCeilDiv((curY - img.yTileOffset) % img.yTileSize, tileComp->vSep);
+    pix = (int)tileComp->data[ty * (tileComp->x1 - tileComp->x0) + tx];
+    pixBits = tileComp->prec;
+#if 1 //~ ignore the palette, assume the PDF ColorSpace object is valid
+    if (++curComp == img.nComps) {
+#else
+    if (havePalette) {
+      if (pix >= 0 && pix < palette.nEntries) {
+       pix = palette.c[pix * palette.nComps + curComp];
+      } else {
+       pix = 
+      pixBits = palette.bpc[curComp];
+    }
+    if (++curComp == (Guint)(havePalette ? palette.nComps : img.nComps)) {
+#endif
+      curComp = 0;
+      if (++curX == img.xSize) {
+       curX = img.xOffset;
+       ++curY;
+      }
+    }
+    if (pixBits == 8) {
+      readBuf = (readBuf << 8) | (pix & 0xff);
+    } else {
+      readBuf = (readBuf << pixBits) | (pix & ((1 << pixBits) - 1));
+    }
+    readBufLen += pixBits;
+  } while (readBufLen < 8);
+}
+
+GString *JPXStream::getPSFilter(int psLevel, char *indent) {
+  return NULL;
+}
+
+GBool JPXStream::isBinary(GBool last) {
+  return str->isBinary(gTrue);
+}
+
+void JPXStream::getImageParams(int *bitsPerComponent,
+                              StreamColorSpaceMode *csMode) {
+  Guint boxType, boxLen, dataLen, csEnum;
+  Guint bpc1, dummy, i;
+  int csMeth, csPrec, csPrec1, dummy2;
+  StreamColorSpaceMode csMode1;
+  GBool haveBPC, haveCSMode;
+
+  csPrec = 0; // make gcc happy
+  haveBPC = haveCSMode = gFalse;
+  str->reset();
+  if (str->lookChar() == 0xff) {
+    getImageParams2(bitsPerComponent, csMode);
+  } else {
+    while (readBoxHdr(&boxType, &boxLen, &dataLen)) {
+      if (boxType == 0x6a703268) { // JP2 header
+       // skip the superbox
+      } else if (boxType == 0x69686472) { // image header
+       if (readULong(&dummy) &&
+           readULong(&dummy) &&
+           readUWord(&dummy) &&
+           readUByte(&bpc1) &&
+           readUByte(&dummy) &&
+           readUByte(&dummy) &&
+           readUByte(&dummy)) {
+         *bitsPerComponent = bpc1 + 1;
+         haveBPC = gTrue;
+       }
+      } else if (boxType == 0x636F6C72) { // color specification
+       if (readByte(&csMeth) &&
+           readByte(&csPrec1) &&
+           readByte(&dummy2)) {
+         if (csMeth == 1) {
+           if (readULong(&csEnum)) {
+             csMode1 = streamCSNone;
+             if (csEnum == jpxCSBiLevel ||
+                 csEnum == jpxCSGrayscale) {
+               csMode1 = streamCSDeviceGray;
+             } else if (csEnum == jpxCSCMYK) {
+               csMode1 = streamCSDeviceCMYK;
+             } else if (csEnum == jpxCSsRGB ||
+                        csEnum == jpxCSCISesRGB ||
+                        csEnum == jpxCSROMMRGB) {
+               csMode1 = streamCSDeviceRGB;
+             }
+             if (csMode1 != streamCSNone &&
+                 (!haveCSMode || csPrec1 > csPrec)) {
+               *csMode = csMode1;
+               csPrec = csPrec1;
+               haveCSMode = gTrue;
+             }
+             for (i = 0; i < dataLen - 7; ++i) {
+               str->getChar();
+             }
+           }
+         } else {
+           for (i = 0; i < dataLen - 3; ++i) {
+             str->getChar();
+           }
+         }
+       }
+      } else if (boxType == 0x6A703263) { // codestream
+       if (!(haveBPC && haveCSMode)) {
+         getImageParams2(bitsPerComponent, csMode);
+       }
+       break;
+      } else {
+       for (i = 0; i < dataLen; ++i) {
+         str->getChar();
+       }
+      }
+    }
+  }
+  str->close();
+}
+
+// Get image parameters from the codestream.
+void JPXStream::getImageParams2(int *bitsPerComponent,
+                               StreamColorSpaceMode *csMode) {
+  int segType;
+  Guint segLen, nComps1, bpc1, dummy, i;
+
+  while (readMarkerHdr(&segType, &segLen)) {
+    if (segType == 0x51) { // SIZ - image and tile size
+      if (readUWord(&dummy) &&
+         readULong(&dummy) &&
+         readULong(&dummy) &&
+         readULong(&dummy) &&
+         readULong(&dummy) &&
+         readULong(&dummy) &&
+         readULong(&dummy) &&
+         readULong(&dummy) &&
+         readULong(&dummy) &&
+         readUWord(&nComps1) &&
+         readUByte(&bpc1)) {
+       *bitsPerComponent = (bpc1 & 0x7f) + 1;
+       // if there's no color space info, take a guess
+       if (nComps1 == 1) {
+         *csMode = streamCSDeviceGray;
+       } else if (nComps1 == 3) {
+         *csMode = streamCSDeviceRGB;
+       } else if (nComps1 == 4) {
+         *csMode = streamCSDeviceCMYK;
+       }
+      }
+      break;
+    } else {
+      if (segLen > 2) {
+       for (i = 0; i < segLen - 2; ++i) {
+         str->getChar();
+       }
+      }
+    }
+  }
+}
+
+GBool JPXStream::readBoxes() {
+  Guint boxType, boxLen, dataLen;
+  Guint bpc1, compression, unknownColorspace, ipr;
+  Guint i, j;
+
+  haveImgHdr = gFalse;
+
+  // check for a naked JPEG 2000 codestream (without the JP2/JPX
+  // wrapper) -- this appears to be a violation of the PDF spec, but
+  // Acrobat allows it
+  if (str->lookChar() == 0xff) {
+    error(getPos(), "Naked JPEG 2000 codestream, missing JP2/JPX wrapper");
+    readCodestream(0);
+    nComps = img.nComps;
+    bpc = (Guint *)gmallocn(nComps, sizeof(Guint));
+    for (i = 0; i < nComps; ++i) {
+      bpc[i] = img.tiles[0].tileComps[i].prec;
+    }
+    width = img.xSize - img.xOffset;
+    height = img.ySize - img.yOffset;
+    return gTrue;
+  }
+
+  while (readBoxHdr(&boxType, &boxLen, &dataLen)) {
+    switch (boxType) {
+    case 0x6a703268:           // JP2 header
+      // this is a grouping box ('superbox') which has no real
+      // contents and doesn't appear to be used consistently, i.e.,
+      // some things which should be subboxes of the JP2 header box
+      // show up outside of it - so we simply ignore the JP2 header
+      // box
+      break;
+    case 0x69686472:           // image header
+      if (!readULong(&height) ||
+         !readULong(&width) ||
+         !readUWord(&nComps) ||
+         !readUByte(&bpc1) ||
+         !readUByte(&compression) ||
+         !readUByte(&unknownColorspace) ||
+         !readUByte(&ipr)) {
+       error(getPos(), "Unexpected EOF in JPX stream");
+       return gFalse;
+      }
+      if (compression != 7) {
+       error(getPos(), "Unknown compression type in JPX stream");
+       return gFalse;
+      }
+      bpc = (Guint *)gmallocn(nComps, sizeof(Guint));
+      for (i = 0; i < nComps; ++i) {
+       bpc[i] = bpc1;
+      }
+      haveImgHdr = gTrue;
+      break;
+    case 0x62706363:           // bits per component
+      if (!haveImgHdr) {
+       error(getPos(), "Found bits per component box before image header box in JPX stream");
+       return gFalse;
+      }
+      if (dataLen != nComps) {
+       error(getPos(), "Invalid bits per component box in JPX stream");
+       return gFalse;
+      }
+      for (i = 0; i < nComps; ++i) {
+       if (!readUByte(&bpc[i])) {
+         error(getPos(), "Unexpected EOF in JPX stream");
+         return gFalse;
+       }
+      }
+      break;
+    case 0x636F6C72:           // color specification
+      if (!readColorSpecBox(dataLen)) {
+       return gFalse;
+      }
+      break;
+    case 0x70636c72:           // palette
+      if (!readUWord(&palette.nEntries) ||
+         !readUByte(&palette.nComps)) {
+       error(getPos(), "Unexpected EOF in JPX stream");
+       return gFalse;
+      }
+      palette.bpc = (Guint *)gmallocn(palette.nComps, sizeof(Guint));
+      palette.c =
+          (int *)gmallocn(palette.nEntries * palette.nComps, sizeof(int));
+      for (i = 0; i < palette.nComps; ++i) {
+       if (!readUByte(&palette.bpc[i])) {
+         error(getPos(), "Unexpected EOF in JPX stream");
+         return gFalse;
+       }
+       ++palette.bpc[i];
+      }
+      for (i = 0; i < palette.nEntries; ++i) {
+       for (j = 0; j < palette.nComps; ++j) {
+         if (!readNBytes(((palette.bpc[j] & 0x7f) + 7) >> 3,
+                         (palette.bpc[j] & 0x80) ? gTrue : gFalse,
+                         &palette.c[i * palette.nComps + j])) {
+           error(getPos(), "Unexpected EOF in JPX stream");
+           return gFalse;
+         }
+       }
+      }
+      havePalette = gTrue;
+      break;
+    case 0x636d6170:           // component mapping
+      compMap.nChannels = dataLen / 4;
+      compMap.comp = (Guint *)gmallocn(compMap.nChannels, sizeof(Guint));
+      compMap.type = (Guint *)gmallocn(compMap.nChannels, sizeof(Guint));
+      compMap.pComp = (Guint *)gmallocn(compMap.nChannels, sizeof(Guint));
+      for (i = 0; i < compMap.nChannels; ++i) {
+       if (!readUWord(&compMap.comp[i]) ||
+           !readUByte(&compMap.type[i]) ||
+           !readUByte(&compMap.pComp[i])) {
+         error(getPos(), "Unexpected EOF in JPX stream");
+         return gFalse;
+       }
+      }
+      haveCompMap = gTrue;
+      break;
+    case 0x63646566:           // channel definition
+      if (!readUWord(&channelDefn.nChannels)) {
+       error(getPos(), "Unexpected EOF in JPX stream");
+       return gFalse;
+      }
+      channelDefn.idx =
+         (Guint *)gmallocn(channelDefn.nChannels, sizeof(Guint));
+      channelDefn.type =
+         (Guint *)gmallocn(channelDefn.nChannels, sizeof(Guint));
+      channelDefn.assoc =
+         (Guint *)gmallocn(channelDefn.nChannels, sizeof(Guint));
+      for (i = 0; i < channelDefn.nChannels; ++i) {
+       if (!readUWord(&channelDefn.idx[i]) ||
+           !readUWord(&channelDefn.type[i]) ||
+           !readUWord(&channelDefn.assoc[i])) {
+         error(getPos(), "Unexpected EOF in JPX stream");
+         return gFalse;
+       }
+      }
+      haveChannelDefn = gTrue;
+      break;
+    case 0x6A703263:           // contiguous codestream
+      if (!bpc) {
+       error(getPos(), "JPX stream is missing the image header box");
+      }
+      if (!haveCS) {
+       error(getPos(), "JPX stream has no supported color spec");
+      }
+      if (!readCodestream(dataLen)) {
+       return gFalse;
+      }
+      break;
+    default:
+      for (i = 0; i < dataLen; ++i) {
+       if (str->getChar() == EOF) {
+         error(getPos(), "Unexpected EOF in JPX stream");
+         return gFalse;
+       }
+      }
+      break;
+    }
+  }
+  return gTrue;
+}
+
+GBool JPXStream::readColorSpecBox(Guint dataLen) {
+  JPXColorSpec newCS;
+  Guint csApprox, csEnum;
+  Guint i;
+  GBool ok;
+
+  ok = gFalse;
+  if (!readUByte(&newCS.meth) ||
+      !readByte(&newCS.prec) ||
+      !readUByte(&csApprox)) {
+    goto err;
+  }
+  switch (newCS.meth) {
+  case 1:                      // enumerated colorspace
+    if (!readULong(&csEnum)) {
+      goto err;
+    }
+    newCS.enumerated.type = (JPXColorSpaceType)csEnum;
+    switch (newCS.enumerated.type) {
+    case jpxCSBiLevel:
+      ok = gTrue;
+      break;
+    case jpxCSYCbCr1:
+      ok = gTrue;
+      break;
+    case jpxCSYCbCr2:
+      ok = gTrue;
+      break;
+    case jpxCSYCBCr3:
+      ok = gTrue;
+      break;
+    case jpxCSPhotoYCC:
+      ok = gTrue;
+      break;
+    case jpxCSCMY:
+      ok = gTrue;
+      break;
+    case jpxCSCMYK:
+      ok = gTrue;
+      break;
+    case jpxCSYCCK:
+      ok = gTrue;
+      break;
+    case jpxCSCIELab:
+      if (dataLen == 7 + 7*4) {
+       if (!readULong(&newCS.enumerated.cieLab.rl) ||
+           !readULong(&newCS.enumerated.cieLab.ol) ||
+           !readULong(&newCS.enumerated.cieLab.ra) ||
+           !readULong(&newCS.enumerated.cieLab.oa) ||
+           !readULong(&newCS.enumerated.cieLab.rb) ||
+           !readULong(&newCS.enumerated.cieLab.ob) ||
+           !readULong(&newCS.enumerated.cieLab.il)) {
+         goto err;
+       }
+      } else if (dataLen == 7) {
+       //~ this assumes the 8-bit case
+       newCS.enumerated.cieLab.rl = 100;
+       newCS.enumerated.cieLab.ol = 0;
+       newCS.enumerated.cieLab.ra = 255;
+       newCS.enumerated.cieLab.oa = 128;
+       newCS.enumerated.cieLab.rb = 255;
+       newCS.enumerated.cieLab.ob = 96;
+       newCS.enumerated.cieLab.il = 0x00443530;
+      } else {
+       goto err;
+      }
+      ok = gTrue;
+      break;
+    case jpxCSsRGB:
+      ok = gTrue;
+      break;
+    case jpxCSGrayscale:
+      ok = gTrue;
+      break;
+    case jpxCSBiLevel2:
+      ok = gTrue;
+      break;
+    case jpxCSCIEJab:
+      // not allowed in PDF
+      goto err;
+    case jpxCSCISesRGB:
+      ok = gTrue;
+      break;
+    case jpxCSROMMRGB:
+      ok = gTrue;
+      break;
+    case jpxCSsRGBYCbCr:
+      ok = gTrue;
+      break;
+    case jpxCSYPbPr1125:
+      ok = gTrue;
+      break;
+    case jpxCSYPbPr1250:
+      ok = gTrue;
+      break;
+    default:
+      goto err;
+    }
+    break;
+  case 2:                      // restricted ICC profile
+  case 3:                      // any ICC profile (JPX)
+  case 4:                      // vendor color (JPX)
+    for (i = 0; i < dataLen - 3; ++i) {
+      if (str->getChar() == EOF) {
+       goto err;
+      }
+    }
+    break;
+  }
+
+  if (ok && (!haveCS || newCS.prec > cs.prec)) {
+    cs = newCS;
+    haveCS = gTrue;
+  }
+
+  return gTrue;
+
+ err:
+  error(getPos(), "Error in JPX color spec");
+  return gFalse;
+}
+
+GBool JPXStream::readCodestream(Guint len) {
+  JPXTile *tile;
+  JPXTileComp *tileComp;
+  int segType;
+  GBool haveSIZ, haveCOD, haveQCD, haveSOT;
+  Guint precinctSize, style;
+  Guint segLen, capabilities, nTiles, comp, i, j, r;
+
+  //----- main header
+  haveSIZ = haveCOD = haveQCD = haveSOT = gFalse;
+  do {
+    if (!readMarkerHdr(&segType, &segLen)) {
+      error(getPos(), "Error in JPX codestream");
+      return gFalse;
+    }
+    switch (segType) {
+    case 0x4f:                 // SOC - start of codestream
+      // marker only
+      break;
+    case 0x51:                 // SIZ - image and tile size
+      if (!readUWord(&capabilities) ||
+         !readULong(&img.xSize) ||
+         !readULong(&img.ySize) ||
+         !readULong(&img.xOffset) ||
+         !readULong(&img.yOffset) ||
+         !readULong(&img.xTileSize) ||
+         !readULong(&img.yTileSize) ||
+         !readULong(&img.xTileOffset) ||
+         !readULong(&img.yTileOffset) ||
+         !readUWord(&img.nComps)) {
+       error(getPos(), "Error in JPX SIZ marker segment");
+       return gFalse;
+      }
+      if (haveImgHdr && img.nComps != nComps) {
+       error(getPos(), "Different number of components in JPX SIZ marker segment");
+       return gFalse;
+      }
+      img.nXTiles = (img.xSize - img.xTileOffset + img.xTileSize - 1)
+                   / img.xTileSize;
+      img.nYTiles = (img.ySize - img.yTileOffset + img.yTileSize - 1)
+                   / img.yTileSize;
+      nTiles = img.nXTiles * img.nYTiles;
+      // check for overflow before allocating memory
+      if (nTiles == 0 || nTiles / img.nXTiles != img.nYTiles) {
+       error(getPos(), "Bad tile count in JPX SIZ marker segment");
+       return gFalse;
+      }
+      img.tiles = (JPXTile *)gmallocn(nTiles, sizeof(JPXTile));
+      for (i = 0; i < img.nXTiles * img.nYTiles; ++i) {
+       img.tiles[i].tileComps = (JPXTileComp *)gmallocn(img.nComps,
+                                                        sizeof(JPXTileComp));
+       for (comp = 0; comp < img.nComps; ++comp) {
+         img.tiles[i].tileComps[comp].quantSteps = NULL;
+         img.tiles[i].tileComps[comp].data = NULL;
+         img.tiles[i].tileComps[comp].buf = NULL;
+         img.tiles[i].tileComps[comp].resLevels = NULL;
+       }
+      }
+      for (comp = 0; comp < img.nComps; ++comp) {
+       if (!readUByte(&img.tiles[0].tileComps[comp].prec) ||
+           !readUByte(&img.tiles[0].tileComps[comp].hSep) ||
+           !readUByte(&img.tiles[0].tileComps[comp].vSep)) {
+         error(getPos(), "Error in JPX SIZ marker segment");
+         return gFalse;
+       }
+       img.tiles[0].tileComps[comp].sgned =
+           (img.tiles[0].tileComps[comp].prec & 0x80) ? gTrue : gFalse;
+       img.tiles[0].tileComps[comp].prec =
+           (img.tiles[0].tileComps[comp].prec & 0x7f) + 1;
+       for (i = 1; i < img.nXTiles * img.nYTiles; ++i) {
+         img.tiles[i].tileComps[comp] = img.tiles[0].tileComps[comp];
+       }
+      }
+      haveSIZ = gTrue;
+      break;
+    case 0x52:                 // COD - coding style default
+      if (!readUByte(&img.tiles[0].tileComps[0].style) ||
+         !readUByte(&img.tiles[0].progOrder) ||
+         !readUWord(&img.tiles[0].nLayers) ||
+         !readUByte(&img.tiles[0].multiComp) ||
+         !readUByte(&img.tiles[0].tileComps[0].nDecompLevels) ||
+         !readUByte(&img.tiles[0].tileComps[0].codeBlockW) ||
+         !readUByte(&img.tiles[0].tileComps[0].codeBlockH) ||
+         !readUByte(&img.tiles[0].tileComps[0].codeBlockStyle) ||
+         !readUByte(&img.tiles[0].tileComps[0].transform)) {
+       error(getPos(), "Error in JPX COD marker segment");
+       return gFalse;
+      }
+      img.tiles[0].tileComps[0].codeBlockW += 2;
+      img.tiles[0].tileComps[0].codeBlockH += 2;
+      for (i = 0; i < img.nXTiles * img.nYTiles; ++i) {
+       if (i != 0) {
+         img.tiles[i].progOrder = img.tiles[0].progOrder;
+         img.tiles[i].nLayers = img.tiles[0].nLayers;
+         img.tiles[i].multiComp = img.tiles[0].multiComp;
+       }
+       for (comp = 0; comp < img.nComps; ++comp) {
+         if (!(i == 0 && comp == 0)) {
+           img.tiles[i].tileComps[comp].style =
+               img.tiles[0].tileComps[0].style;
+           img.tiles[i].tileComps[comp].nDecompLevels =
+               img.tiles[0].tileComps[0].nDecompLevels;
+           img.tiles[i].tileComps[comp].codeBlockW =
+               img.tiles[0].tileComps[0].codeBlockW;
+           img.tiles[i].tileComps[comp].codeBlockH =
+               img.tiles[0].tileComps[0].codeBlockH;
+           img.tiles[i].tileComps[comp].codeBlockStyle =
+               img.tiles[0].tileComps[0].codeBlockStyle;
+           img.tiles[i].tileComps[comp].transform =
+               img.tiles[0].tileComps[0].transform;
+         }
+         img.tiles[i].tileComps[comp].resLevels =
+             (JPXResLevel *)gmallocn(
+                    (img.tiles[i].tileComps[comp].nDecompLevels + 1),
+                    sizeof(JPXResLevel));
+         for (r = 0; r <= img.tiles[i].tileComps[comp].nDecompLevels; ++r) {
+           img.tiles[i].tileComps[comp].resLevels[r].precincts = NULL;
+         }
+       }
+      }
+      for (r = 0; r <= img.tiles[0].tileComps[0].nDecompLevels; ++r) {
+       if (img.tiles[0].tileComps[0].style & 0x01) {
+         if (!readUByte(&precinctSize)) {
+           error(getPos(), "Error in JPX COD marker segment");
+           return gFalse;
+         }
+         img.tiles[0].tileComps[0].resLevels[r].precinctWidth =
+             precinctSize & 0x0f;
+         img.tiles[0].tileComps[0].resLevels[r].precinctHeight =
+             (precinctSize >> 4) & 0x0f;
+       } else {
+         img.tiles[0].tileComps[0].resLevels[r].precinctWidth = 15;
+         img.tiles[0].tileComps[0].resLevels[r].precinctHeight = 15;
+       }
+      }
+      for (i = 0; i < img.nXTiles * img.nYTiles; ++i) {
+       for (comp = 0; comp < img.nComps; ++comp) {
+         if (!(i == 0 && comp == 0)) {
+           for (r = 0; r <= img.tiles[i].tileComps[comp].nDecompLevels; ++r) {
+             img.tiles[i].tileComps[comp].resLevels[r].precinctWidth =
+                 img.tiles[0].tileComps[0].resLevels[r].precinctWidth;
+             img.tiles[i].tileComps[comp].resLevels[r].precinctHeight =
+                 img.tiles[0].tileComps[0].resLevels[r].precinctHeight;
+           }
+         }
+       }
+      }
+      haveCOD = gTrue;
+      break;
+    case 0x53:                 // COC - coding style component
+      if (!haveCOD) {
+       error(getPos(), "JPX COC marker segment before COD segment");
+       return gFalse;
+      }
+      if ((img.nComps > 256 && !readUWord(&comp)) ||
+         (img.nComps <= 256 && !readUByte(&comp)) ||
+         comp >= img.nComps ||
+         !readUByte(&style) ||
+         !readUByte(&img.tiles[0].tileComps[comp].nDecompLevels) ||
+         !readUByte(&img.tiles[0].tileComps[comp].codeBlockW) ||
+         !readUByte(&img.tiles[0].tileComps[comp].codeBlockH) ||
+         !readUByte(&img.tiles[0].tileComps[comp].codeBlockStyle) ||
+         !readUByte(&img.tiles[0].tileComps[comp].transform)) {
+       error(getPos(), "Error in JPX COC marker segment");
+       return gFalse;
+      }
+      img.tiles[0].tileComps[comp].style =
+         (img.tiles[0].tileComps[comp].style & ~1) | (style & 1);
+      img.tiles[0].tileComps[comp].codeBlockW += 2;
+      img.tiles[0].tileComps[comp].codeBlockH += 2;
+      for (i = 0; i < img.nXTiles * img.nYTiles; ++i) {
+       if (i != 0) {
+         img.tiles[i].tileComps[comp].style =
+             img.tiles[0].tileComps[comp].style;
+         img.tiles[i].tileComps[comp].nDecompLevels =
+             img.tiles[0].tileComps[comp].nDecompLevels;
+         img.tiles[i].tileComps[comp].codeBlockW =
+             img.tiles[0].tileComps[comp].codeBlockW;
+         img.tiles[i].tileComps[comp].codeBlockH =
+             img.tiles[0].tileComps[comp].codeBlockH;
+         img.tiles[i].tileComps[comp].codeBlockStyle =
+             img.tiles[0].tileComps[comp].codeBlockStyle;
+         img.tiles[i].tileComps[comp].transform =
+             img.tiles[0].tileComps[comp].transform;
+       }
+       img.tiles[i].tileComps[comp].resLevels =
+           (JPXResLevel *)greallocn(
+                    img.tiles[i].tileComps[comp].resLevels,
+                    (img.tiles[i].tileComps[comp].nDecompLevels + 1),
+                    sizeof(JPXResLevel));
+       for (r = 0; r <= img.tiles[i].tileComps[comp].nDecompLevels; ++r) {
+         img.tiles[i].tileComps[comp].resLevels[r].precincts = NULL;
+       }
+      }
+      for (r = 0; r <= img.tiles[0].tileComps[comp].nDecompLevels; ++r) {
+       if (img.tiles[0].tileComps[comp].style & 0x01) {
+         if (!readUByte(&precinctSize)) {
+           error(getPos(), "Error in JPX COD marker segment");
+           return gFalse;
+         }
+         img.tiles[0].tileComps[comp].resLevels[r].precinctWidth =
+             precinctSize & 0x0f;
+         img.tiles[0].tileComps[comp].resLevels[r].precinctHeight =
+             (precinctSize >> 4) & 0x0f;
+       } else {
+         img.tiles[0].tileComps[comp].resLevels[r].precinctWidth = 15;
+         img.tiles[0].tileComps[comp].resLevels[r].precinctHeight = 15;
+       }
+      }
+      for (i = 1; i < img.nXTiles * img.nYTiles; ++i) {
+       for (r = 0; r <= img.tiles[i].tileComps[comp].nDecompLevels; ++r) {
+         img.tiles[i].tileComps[comp].resLevels[r].precinctWidth =
+             img.tiles[0].tileComps[comp].resLevels[r].precinctWidth;
+         img.tiles[i].tileComps[comp].resLevels[r].precinctHeight =
+             img.tiles[0].tileComps[comp].resLevels[r].precinctHeight;
+       }
+      }
+      break;
+    case 0x5c:                 // QCD - quantization default
+      if (!readUByte(&img.tiles[0].tileComps[0].quantStyle)) {
+       error(getPos(), "Error in JPX QCD marker segment");
+       return gFalse;
+      }
+      if ((img.tiles[0].tileComps[0].quantStyle & 0x1f) == 0x00) {
+       img.tiles[0].tileComps[0].nQuantSteps = segLen - 3;
+       img.tiles[0].tileComps[0].quantSteps =
+           (Guint *)greallocn(img.tiles[0].tileComps[0].quantSteps,
+                              img.tiles[0].tileComps[0].nQuantSteps,
+                              sizeof(Guint));
+       for (i = 0; i < img.tiles[0].tileComps[0].nQuantSteps; ++i) {
+         if (!readUByte(&img.tiles[0].tileComps[0].quantSteps[i])) {
+           error(getPos(), "Error in JPX QCD marker segment");
+           return gFalse;
+         }
+       }
+      } else if ((img.tiles[0].tileComps[0].quantStyle & 0x1f) == 0x01) {
+       img.tiles[0].tileComps[0].nQuantSteps = 1;
+       img.tiles[0].tileComps[0].quantSteps =
+           (Guint *)greallocn(img.tiles[0].tileComps[0].quantSteps,
+                              img.tiles[0].tileComps[0].nQuantSteps,
+                              sizeof(Guint));
+       if (!readUWord(&img.tiles[0].tileComps[0].quantSteps[0])) {
+         error(getPos(), "Error in JPX QCD marker segment");
+         return gFalse;
+       }
+      } else if ((img.tiles[0].tileComps[0].quantStyle & 0x1f) == 0x02) {
+       img.tiles[0].tileComps[0].nQuantSteps = (segLen - 3) / 2;
+       img.tiles[0].tileComps[0].quantSteps =
+           (Guint *)greallocn(img.tiles[0].tileComps[0].quantSteps,
+                              img.tiles[0].tileComps[0].nQuantSteps,
+                              sizeof(Guint));
+       for (i = 0; i < img.tiles[0].tileComps[0].nQuantSteps; ++i) {
+         if (!readUWord(&img.tiles[0].tileComps[0].quantSteps[i])) {
+           error(getPos(), "Error in JPX QCD marker segment");
+           return gFalse;
+         }
+       }
+      } else {
+       error(getPos(), "Error in JPX QCD marker segment");
+       return gFalse;
+      }
+      for (i = 0; i < img.nXTiles * img.nYTiles; ++i) {
+       for (comp = 0; comp < img.nComps; ++comp) {
+         if (!(i == 0 && comp == 0)) {
+           img.tiles[i].tileComps[comp].quantStyle =
+               img.tiles[0].tileComps[0].quantStyle;
+           img.tiles[i].tileComps[comp].nQuantSteps =
+               img.tiles[0].tileComps[0].nQuantSteps;
+           img.tiles[i].tileComps[comp].quantSteps = 
+               (Guint *)greallocn(img.tiles[i].tileComps[comp].quantSteps,
+                                  img.tiles[0].tileComps[0].nQuantSteps,
+                                  sizeof(Guint));
+           for (j = 0; j < img.tiles[0].tileComps[0].nQuantSteps; ++j) {
+             img.tiles[i].tileComps[comp].quantSteps[j] =
+                 img.tiles[0].tileComps[0].quantSteps[j];
+           }
+         }
+       }
+      }
+      haveQCD = gTrue;
+      break;
+    case 0x5d:                 // QCC - quantization component
+      if (!haveQCD) {
+       error(getPos(), "JPX QCC marker segment before QCD segment");
+       return gFalse;
+      }
+      if ((img.nComps > 256 && !readUWord(&comp)) ||
+         (img.nComps <= 256 && !readUByte(&comp)) ||
+         comp >= img.nComps ||
+         !readUByte(&img.tiles[0].tileComps[comp].quantStyle)) {
+       error(getPos(), "Error in JPX QCC marker segment");
+       return gFalse;
+      }
+      if ((img.tiles[0].tileComps[comp].quantStyle & 0x1f) == 0x00) {
+       img.tiles[0].tileComps[comp].nQuantSteps =
+           segLen - (img.nComps > 256 ? 5 : 4);
+       img.tiles[0].tileComps[comp].quantSteps =
+           (Guint *)greallocn(img.tiles[0].tileComps[comp].quantSteps,
+                              img.tiles[0].tileComps[comp].nQuantSteps,
+                              sizeof(Guint));
+       for (i = 0; i < img.tiles[0].tileComps[comp].nQuantSteps; ++i) {
+         if (!readUByte(&img.tiles[0].tileComps[comp].quantSteps[i])) {
+           error(getPos(), "Error in JPX QCC marker segment");
+           return gFalse;
+         }
+       }
+      } else if ((img.tiles[0].tileComps[comp].quantStyle & 0x1f) == 0x01) {
+       img.tiles[0].tileComps[comp].nQuantSteps = 1;
+       img.tiles[0].tileComps[comp].quantSteps =
+           (Guint *)greallocn(img.tiles[0].tileComps[comp].quantSteps,
+                              img.tiles[0].tileComps[comp].nQuantSteps,
+                              sizeof(Guint));
+       if (!readUWord(&img.tiles[0].tileComps[comp].quantSteps[0])) {
+         error(getPos(), "Error in JPX QCC marker segment");
+         return gFalse;
+       }
+      } else if ((img.tiles[0].tileComps[comp].quantStyle & 0x1f) == 0x02) {
+       img.tiles[0].tileComps[comp].nQuantSteps =
+           (segLen - (img.nComps > 256 ? 5 : 4)) / 2;
+       img.tiles[0].tileComps[comp].quantSteps =
+           (Guint *)greallocn(img.tiles[0].tileComps[comp].quantSteps,
+                              img.tiles[0].tileComps[comp].nQuantSteps,
+                              sizeof(Guint));
+       for (i = 0; i < img.tiles[0].tileComps[comp].nQuantSteps; ++i) {
+         if (!readUWord(&img.tiles[0].tileComps[comp].quantSteps[i])) {
+           error(getPos(), "Error in JPX QCD marker segment");
+           return gFalse;
+         }
+       }
+      } else {
+       error(getPos(), "Error in JPX QCC marker segment");
+       return gFalse;
+      }
+      for (i = 1; i < img.nXTiles * img.nYTiles; ++i) {
+       img.tiles[i].tileComps[comp].quantStyle =
+           img.tiles[0].tileComps[comp].quantStyle;
+       img.tiles[i].tileComps[comp].nQuantSteps =
+           img.tiles[0].tileComps[comp].nQuantSteps;
+       img.tiles[i].tileComps[comp].quantSteps = 
+           (Guint *)greallocn(img.tiles[i].tileComps[comp].quantSteps,
+                              img.tiles[0].tileComps[comp].nQuantSteps,
+                              sizeof(Guint));
+       for (j = 0; j < img.tiles[0].tileComps[comp].nQuantSteps; ++j) {
+         img.tiles[i].tileComps[comp].quantSteps[j] =
+             img.tiles[0].tileComps[comp].quantSteps[j];
+       }
+      }
+      break;
+    case 0x5e:                 // RGN - region of interest
+#if 1 //~ ROI is unimplemented
+      fprintf(stderr, "RGN\n");
+      for (i = 0; i < segLen - 2; ++i) {
+       if (str->getChar() == EOF) {
+         error(getPos(), "Error in JPX PPM marker segment");
+         return gFalse;
+       }
+      }
+#else
+      if ((img.nComps > 256 && !readUWord(&comp)) ||
+         (img.nComps <= 256 && !readUByte(&comp)) ||
+         comp >= img.nComps ||
+         !readUByte(&compInfo[comp].defROI.style) ||
+         !readUByte(&compInfo[comp].defROI.shift)) {
+       error(getPos(), "Error in JPX RGN marker segment");
+       return gFalse;
+      }
+#endif
+      break;
+    case 0x5f:                 // POC - progression order change
+#if 1 //~ progression order changes are unimplemented
+      fprintf(stderr, "POC\n");
+      for (i = 0; i < segLen - 2; ++i) {
+       if (str->getChar() == EOF) {
+         error(getPos(), "Error in JPX PPM marker segment");
+         return gFalse;
+       }
+      }
+#else
+      nProgs = (segLen - 2) / (img.nComps > 256 ? 9 : 7);
+      progs = (JPXProgOrder *)gmallocn(nProgs, sizeof(JPXProgOrder));
+      for (i = 0; i < nProgs; ++i) {
+       if (!readUByte(&progs[i].startRes) ||
+           !(img.nComps > 256 && readUWord(&progs[i].startComp)) ||
+           !(img.nComps <= 256 && readUByte(&progs[i].startComp)) ||
+           !readUWord(&progs[i].endLayer) ||
+           !readUByte(&progs[i].endRes) ||
+           !(img.nComps > 256 && readUWord(&progs[i].endComp)) ||
+           !(img.nComps <= 256 && readUByte(&progs[i].endComp)) ||
+           !readUByte(&progs[i].progOrder)) {
+         error(getPos(), "Error in JPX POC marker segment");
+         return gFalse;
+       }
+      }
+#endif
+      break;
+    case 0x60:                 // PPM - packed packet headers, main header
+#if 1 //~ packed packet headers are unimplemented
+      fprintf(stderr, "PPM\n");
+      for (i = 0; i < segLen - 2; ++i) {
+       if (str->getChar() == EOF) {
+         error(getPos(), "Error in JPX PPM marker segment");
+         return gFalse;
+       }
+      }
+#endif
+      break;
+    case 0x55:                 // TLM - tile-part lengths
+      // skipped
+      for (i = 0; i < segLen - 2; ++i) {
+       if (str->getChar() == EOF) {
+         error(getPos(), "Error in JPX TLM marker segment");
+         return gFalse;
+       }
+      }
+      break;
+    case 0x57:                 // PLM - packet length, main header
+      // skipped
+      for (i = 0; i < segLen - 2; ++i) {
+       if (str->getChar() == EOF) {
+         error(getPos(), "Error in JPX PLM marker segment");
+         return gFalse;
+       }
+      }
+      break;
+    case 0x63:                 // CRG - component registration
+      // skipped
+      for (i = 0; i < segLen - 2; ++i) {
+       if (str->getChar() == EOF) {
+         error(getPos(), "Error in JPX CRG marker segment");
+         return gFalse;
+       }
+      }
+      break;
+    case 0x64:                 // COM - comment
+      // skipped
+      for (i = 0; i < segLen - 2; ++i) {
+       if (str->getChar() == EOF) {
+         error(getPos(), "Error in JPX COM marker segment");
+         return gFalse;
+       }
+      }
+      break;
+    case 0x90:                 // SOT - start of tile
+      haveSOT = gTrue;
+      break;
+    default:
+      error(getPos(), "Unknown marker segment %02x in JPX stream", segType);
+      for (i = 0; i < segLen - 2; ++i) {
+       if (str->getChar() == EOF) {
+         break;
+       }
+      }
+      break;
+    }
+  } while (!haveSOT);
+
+  if (!haveSIZ) {
+    error(getPos(), "Missing SIZ marker segment in JPX stream");
+    return gFalse;
+  }
+  if (!haveCOD) {
+    error(getPos(), "Missing COD marker segment in JPX stream");
+    return gFalse;
+  }
+  if (!haveQCD) {
+    error(getPos(), "Missing QCD marker segment in JPX stream");
+    return gFalse;
+  }
+
+  //----- read the tile-parts
+  while (1) {
+    if (!readTilePart()) {
+      return gFalse;
+    }
+    if (!readMarkerHdr(&segType, &segLen)) {
+      error(getPos(), "Error in JPX codestream");
+      return gFalse;
+    }
+    if (segType != 0x90) {     // SOT - start of tile
+      break;
+    }
+  }
+
+  if (segType != 0xd9) {       // EOC - end of codestream
+    error(getPos(), "Missing EOC marker in JPX codestream");
+    return gFalse;
+  }
+
+  //----- finish decoding the image
+  for (i = 0; i < img.nXTiles * img.nYTiles; ++i) {
+    tile = &img.tiles[i];
+    for (comp = 0; comp < img.nComps; ++comp) {
+      tileComp = &tile->tileComps[comp];
+      inverseTransform(tileComp);
+    }
+    if (!inverseMultiCompAndDC(tile)) {
+      return gFalse;
+    }
+  }
+
+  //~ can free memory below tileComps here, and also tileComp.buf
+
+  return gTrue;
+}
+
+GBool JPXStream::readTilePart() {
+  JPXTile *tile;
+  JPXTileComp *tileComp;
+  JPXResLevel *resLevel;
+  JPXPrecinct *precinct;
+  JPXSubband *subband;
+  JPXCodeBlock *cb;
+  GBool haveSOD;
+  Guint tileIdx, tilePartLen, tilePartIdx, nTileParts;
+  GBool tilePartToEOC;
+  Guint precinctSize, style;
+  Guint n, nSBs, nx, ny, sbx0, sby0, comp, segLen;
+  Guint i, j, k, cbX, cbY, r, pre, sb, cbi;
+  int segType, level;
+
+  // process the SOT marker segment
+  if (!readUWord(&tileIdx) ||
+      !readULong(&tilePartLen) ||
+      !readUByte(&tilePartIdx) ||
+      !readUByte(&nTileParts)) {
+    error(getPos(), "Error in JPX SOT marker segment");
+    return gFalse;
+  }
+
+  if (tileIdx >= img.nXTiles * img.nYTiles) {
+    error(getPos(), "Weird tile index in JPX stream");
+    return gFalse;
+  }
+
+  tilePartToEOC = tilePartLen == 0;
+  tilePartLen -= 12; // subtract size of SOT segment
+
+  haveSOD = gFalse;
+  do {
+    if (!readMarkerHdr(&segType, &segLen)) {
+      error(getPos(), "Error in JPX tile-part codestream");
+      return gFalse;
+    }
+    tilePartLen -= 2 + segLen;
+    switch (segType) {
+    case 0x52:                 // COD - coding style default
+      if (!readUByte(&img.tiles[tileIdx].tileComps[0].style) ||
+         !readUByte(&img.tiles[tileIdx].progOrder) ||
+         !readUWord(&img.tiles[tileIdx].nLayers) ||
+         !readUByte(&img.tiles[tileIdx].multiComp) ||
+         !readUByte(&img.tiles[tileIdx].tileComps[0].nDecompLevels) ||
+         !readUByte(&img.tiles[tileIdx].tileComps[0].codeBlockW) ||
+         !readUByte(&img.tiles[tileIdx].tileComps[0].codeBlockH) ||
+         !readUByte(&img.tiles[tileIdx].tileComps[0].codeBlockStyle) ||
+         !readUByte(&img.tiles[tileIdx].tileComps[0].transform)) {
+       error(getPos(), "Error in JPX COD marker segment");
+       return gFalse;
+      }
+      img.tiles[tileIdx].tileComps[0].codeBlockW += 2;
+      img.tiles[tileIdx].tileComps[0].codeBlockH += 2;
+      for (comp = 0; comp < img.nComps; ++comp) {
+       if (comp != 0) {
+         img.tiles[tileIdx].tileComps[comp].style =
+             img.tiles[tileIdx].tileComps[0].style;
+         img.tiles[tileIdx].tileComps[comp].nDecompLevels =
+             img.tiles[tileIdx].tileComps[0].nDecompLevels;
+         img.tiles[tileIdx].tileComps[comp].codeBlockW =
+             img.tiles[tileIdx].tileComps[0].codeBlockW;
+         img.tiles[tileIdx].tileComps[comp].codeBlockH =
+             img.tiles[tileIdx].tileComps[0].codeBlockH;
+         img.tiles[tileIdx].tileComps[comp].codeBlockStyle =
+             img.tiles[tileIdx].tileComps[0].codeBlockStyle;
+         img.tiles[tileIdx].tileComps[comp].transform =
+             img.tiles[tileIdx].tileComps[0].transform;
+       }
+       img.tiles[tileIdx].tileComps[comp].resLevels =
+           (JPXResLevel *)greallocn(
+                    img.tiles[tileIdx].tileComps[comp].resLevels,
+                    (img.tiles[tileIdx].tileComps[comp].nDecompLevels + 1),
+                    sizeof(JPXResLevel));
+       for (r = 0;
+            r <= img.tiles[tileIdx].tileComps[comp].nDecompLevels;
+            ++r) {
+         img.tiles[tileIdx].tileComps[comp].resLevels[r].precincts = NULL;
+       }
+      }
+      for (r = 0; r <= img.tiles[tileIdx].tileComps[0].nDecompLevels; ++r) {
+       if (img.tiles[tileIdx].tileComps[0].style & 0x01) {
+         if (!readUByte(&precinctSize)) {
+           error(getPos(), "Error in JPX COD marker segment");
+           return gFalse;
+         }
+         img.tiles[tileIdx].tileComps[0].resLevels[r].precinctWidth =
+             precinctSize & 0x0f;
+         img.tiles[tileIdx].tileComps[0].resLevels[r].precinctHeight =
+             (precinctSize >> 4) & 0x0f;
+       } else {
+         img.tiles[tileIdx].tileComps[0].resLevels[r].precinctWidth = 15;
+         img.tiles[tileIdx].tileComps[0].resLevels[r].precinctHeight = 15;
+       }
+      }
+      for (comp = 1; comp < img.nComps; ++comp) {
+       for (r = 0;
+            r <= img.tiles[tileIdx].tileComps[comp].nDecompLevels;
+            ++r) {
+         img.tiles[tileIdx].tileComps[comp].resLevels[r].precinctWidth =
+             img.tiles[tileIdx].tileComps[0].resLevels[r].precinctWidth;
+         img.tiles[tileIdx].tileComps[comp].resLevels[r].precinctHeight =
+             img.tiles[tileIdx].tileComps[0].resLevels[r].precinctHeight;
+       }
+      }
+      break;
+    case 0x53:                 // COC - coding style component
+      if ((img.nComps > 256 && !readUWord(&comp)) ||
+         (img.nComps <= 256 && !readUByte(&comp)) ||
+         comp >= img.nComps ||
+         !readUByte(&style) ||
+         !readUByte(&img.tiles[tileIdx].tileComps[comp].nDecompLevels) ||
+         !readUByte(&img.tiles[tileIdx].tileComps[comp].codeBlockW) ||
+         !readUByte(&img.tiles[tileIdx].tileComps[comp].codeBlockH) ||
+         !readUByte(&img.tiles[tileIdx].tileComps[comp].codeBlockStyle) ||
+         !readUByte(&img.tiles[tileIdx].tileComps[comp].transform)) {
+       error(getPos(), "Error in JPX COC marker segment");
+       return gFalse;
+      }
+      img.tiles[tileIdx].tileComps[comp].style =
+         (img.tiles[tileIdx].tileComps[comp].style & ~1) | (style & 1);
+      img.tiles[tileIdx].tileComps[comp].codeBlockW += 2;
+      img.tiles[tileIdx].tileComps[comp].codeBlockH += 2;
+      img.tiles[tileIdx].tileComps[comp].resLevels =
+         (JPXResLevel *)greallocn(
+                    img.tiles[tileIdx].tileComps[comp].resLevels,
+                    (img.tiles[tileIdx].tileComps[comp].nDecompLevels + 1),
+                    sizeof(JPXResLevel));
+      for (r = 0; r <= img.tiles[tileIdx].tileComps[comp].nDecompLevels; ++r) {
+       img.tiles[tileIdx].tileComps[comp].resLevels[r].precincts = NULL;
+      }
+      for (r = 0; r <= img.tiles[tileIdx].tileComps[comp].nDecompLevels; ++r) {
+       if (img.tiles[tileIdx].tileComps[comp].style & 0x01) {
+         if (!readUByte(&precinctSize)) {
+           error(getPos(), "Error in JPX COD marker segment");
+           return gFalse;
+         }
+         img.tiles[tileIdx].tileComps[comp].resLevels[r].precinctWidth =
+             precinctSize & 0x0f;
+         img.tiles[tileIdx].tileComps[comp].resLevels[r].precinctHeight =
+             (precinctSize >> 4) & 0x0f;
+       } else {
+         img.tiles[tileIdx].tileComps[comp].resLevels[r].precinctWidth = 15;
+         img.tiles[tileIdx].tileComps[comp].resLevels[r].precinctHeight = 15;
+       }
+      }
+      break;
+    case 0x5c:                 // QCD - quantization default
+      if (!readUByte(&img.tiles[tileIdx].tileComps[0].quantStyle)) {
+       error(getPos(), "Error in JPX QCD marker segment");
+       return gFalse;
+      }
+      if ((img.tiles[tileIdx].tileComps[0].quantStyle & 0x1f) == 0x00) {
+       img.tiles[tileIdx].tileComps[0].nQuantSteps =
+           segLen - 3;
+       img.tiles[tileIdx].tileComps[0].quantSteps =
+           (Guint *)greallocn(img.tiles[tileIdx].tileComps[0].quantSteps,
+                              img.tiles[tileIdx].tileComps[0].nQuantSteps,
+                              sizeof(Guint));
+       for (i = 0; i < img.tiles[tileIdx].tileComps[0].nQuantSteps; ++i) {
+         if (!readUByte(&img.tiles[tileIdx].tileComps[0].quantSteps[i])) {
+           error(getPos(), "Error in JPX QCD marker segment");
+           return gFalse;
+         }
+       }
+      } else if ((img.tiles[tileIdx].tileComps[0].quantStyle & 0x1f) == 0x01) {
+       img.tiles[tileIdx].tileComps[0].nQuantSteps = 1;
+       img.tiles[tileIdx].tileComps[0].quantSteps =
+           (Guint *)greallocn(img.tiles[tileIdx].tileComps[0].quantSteps,
+                              img.tiles[tileIdx].tileComps[0].nQuantSteps,
+                              sizeof(Guint));
+       if (!readUWord(&img.tiles[tileIdx].tileComps[0].quantSteps[0])) {
+         error(getPos(), "Error in JPX QCD marker segment");
+         return gFalse;
+       }
+      } else if ((img.tiles[tileIdx].tileComps[0].quantStyle & 0x1f) == 0x02) {
+       img.tiles[tileIdx].tileComps[0].nQuantSteps = (segLen - 3) / 2;
+       img.tiles[tileIdx].tileComps[0].quantSteps =
+           (Guint *)greallocn(img.tiles[tileIdx].tileComps[0].quantSteps,
+                              img.tiles[tileIdx].tileComps[0].nQuantSteps,
+                              sizeof(Guint));
+       for (i = 0; i < img.tiles[tileIdx].tileComps[0].nQuantSteps; ++i) {
+         if (!readUWord(&img.tiles[tileIdx].tileComps[0].quantSteps[i])) {
+           error(getPos(), "Error in JPX QCD marker segment");
+           return gFalse;
+         }
+       }
+      } else {
+       error(getPos(), "Error in JPX QCD marker segment");
+       return gFalse;
+      }
+      for (comp = 1; comp < img.nComps; ++comp) {
+       img.tiles[tileIdx].tileComps[comp].quantStyle =
+           img.tiles[tileIdx].tileComps[0].quantStyle;
+       img.tiles[tileIdx].tileComps[comp].nQuantSteps =
+           img.tiles[tileIdx].tileComps[0].nQuantSteps;
+       img.tiles[tileIdx].tileComps[comp].quantSteps = 
+           (Guint *)greallocn(img.tiles[tileIdx].tileComps[comp].quantSteps,
+                              img.tiles[tileIdx].tileComps[0].nQuantSteps,
+                              sizeof(Guint));
+       for (j = 0; j < img.tiles[tileIdx].tileComps[0].nQuantSteps; ++j) {
+         img.tiles[tileIdx].tileComps[comp].quantSteps[j] =
+             img.tiles[tileIdx].tileComps[0].quantSteps[j];
+       }
+      }
+      break;
+    case 0x5d:                 // QCC - quantization component
+      if ((img.nComps > 256 && !readUWord(&comp)) ||
+         (img.nComps <= 256 && !readUByte(&comp)) ||
+         comp >= img.nComps ||
+         !readUByte(&img.tiles[tileIdx].tileComps[comp].quantStyle)) {
+       error(getPos(), "Error in JPX QCC marker segment");
+       return gFalse;
+      }
+      if ((img.tiles[tileIdx].tileComps[comp].quantStyle & 0x1f) == 0x00) {
+       img.tiles[tileIdx].tileComps[comp].nQuantSteps =
+           segLen - (img.nComps > 256 ? 5 : 4);
+       img.tiles[tileIdx].tileComps[comp].quantSteps =
+           (Guint *)greallocn(img.tiles[tileIdx].tileComps[comp].quantSteps,
+                              img.tiles[tileIdx].tileComps[comp].nQuantSteps,
+                              sizeof(Guint));
+       for (i = 0; i < img.tiles[tileIdx].tileComps[comp].nQuantSteps; ++i) {
+         if (!readUByte(&img.tiles[tileIdx].tileComps[comp].quantSteps[i])) {
+           error(getPos(), "Error in JPX QCC marker segment");
+           return gFalse;
+         }
+       }
+      } else if ((img.tiles[tileIdx].tileComps[comp].quantStyle & 0x1f)
+                == 0x01) {
+       img.tiles[tileIdx].tileComps[comp].nQuantSteps = 1;
+       img.tiles[tileIdx].tileComps[comp].quantSteps =
+           (Guint *)greallocn(img.tiles[tileIdx].tileComps[comp].quantSteps,
+                              img.tiles[tileIdx].tileComps[comp].nQuantSteps,
+                              sizeof(Guint));
+       if (!readUWord(&img.tiles[tileIdx].tileComps[comp].quantSteps[0])) {
+         error(getPos(), "Error in JPX QCC marker segment");
+         return gFalse;
+       }
+      } else if ((img.tiles[tileIdx].tileComps[comp].quantStyle & 0x1f)
+                == 0x02) {
+       img.tiles[tileIdx].tileComps[comp].nQuantSteps =
+           (segLen - (img.nComps > 256 ? 5 : 4)) / 2;
+       img.tiles[tileIdx].tileComps[comp].quantSteps =
+           (Guint *)greallocn(img.tiles[tileIdx].tileComps[comp].quantSteps,
+                              img.tiles[tileIdx].tileComps[comp].nQuantSteps,
+                              sizeof(Guint));
+       for (i = 0; i < img.tiles[tileIdx].tileComps[comp].nQuantSteps; ++i) {
+         if (!readUWord(&img.tiles[tileIdx].tileComps[comp].quantSteps[i])) {
+           error(getPos(), "Error in JPX QCD marker segment");
+           return gFalse;
+         }
+       }
+      } else {
+       error(getPos(), "Error in JPX QCC marker segment");
+       return gFalse;
+      }
+      break;
+    case 0x5e:                 // RGN - region of interest
+#if 1 //~ ROI is unimplemented
+      fprintf(stderr, "RGN\n");
+      for (i = 0; i < segLen - 2; ++i) {
+       if (str->getChar() == EOF) {
+         error(getPos(), "Error in JPX PPM marker segment");
+         return gFalse;
+       }
+      }
+#else
+      if ((img.nComps > 256 && !readUWord(&comp)) ||
+         (img.nComps <= 256 && !readUByte(&comp)) ||
+         comp >= img.nComps ||
+         !readUByte(&compInfo[comp].roi.style) ||
+         !readUByte(&compInfo[comp].roi.shift)) {
+       error(getPos(), "Error in JPX RGN marker segment");
+       return gFalse;
+      }
+#endif
+      break;
+    case 0x5f:                 // POC - progression order change
+#if 1 //~ progression order changes are unimplemented
+      fprintf(stderr, "POC\n");
+      for (i = 0; i < segLen - 2; ++i) {
+       if (str->getChar() == EOF) {
+         error(getPos(), "Error in JPX PPM marker segment");
+         return gFalse;
+       }
+      }
+#else
+      nTileProgs = (segLen - 2) / (img.nComps > 256 ? 9 : 7);
+      tileProgs = (JPXProgOrder *)gmallocn(nTileProgs, sizeof(JPXProgOrder));
+      for (i = 0; i < nTileProgs; ++i) {
+       if (!readUByte(&tileProgs[i].startRes) ||
+           !(img.nComps > 256 && readUWord(&tileProgs[i].startComp)) ||
+           !(img.nComps <= 256 && readUByte(&tileProgs[i].startComp)) ||
+           !readUWord(&tileProgs[i].endLayer) ||
+           !readUByte(&tileProgs[i].endRes) ||
+           !(img.nComps > 256 && readUWord(&tileProgs[i].endComp)) ||
+           !(img.nComps <= 256 && readUByte(&tileProgs[i].endComp)) ||
+           !readUByte(&tileProgs[i].progOrder)) {
+         error(getPos(), "Error in JPX POC marker segment");
+         return gFalse;
+       }
+      }
+#endif
+      break;
+    case 0x61:                 // PPT - packed packet headers, tile-part hdr
+#if 1 //~ packed packet headers are unimplemented
+      fprintf(stderr, "PPT\n");
+      for (i = 0; i < segLen - 2; ++i) {
+       if (str->getChar() == EOF) {
+         error(getPos(), "Error in JPX PPT marker segment");
+         return gFalse;
+       }
+      }
+#endif
+    case 0x58:                 // PLT - packet length, tile-part header
+      // skipped
+      for (i = 0; i < segLen - 2; ++i) {
+       if (str->getChar() == EOF) {
+         error(getPos(), "Error in JPX PLT marker segment");
+         return gFalse;
+       }
+      }
+      break;
+    case 0x64:                 // COM - comment
+      // skipped
+      for (i = 0; i < segLen - 2; ++i) {
+       if (str->getChar() == EOF) {
+         error(getPos(), "Error in JPX COM marker segment");
+         return gFalse;
+       }
+      }
+      break;
+    case 0x93:                 // SOD - start of data
+      haveSOD = gTrue;
+      break;
+    default:
+      error(getPos(), "Unknown marker segment %02x in JPX tile-part stream",
+           segType);
+      for (i = 0; i < segLen - 2; ++i) {
+       if (str->getChar() == EOF) {
+         break;
+       }
+      }
+      break;
+    }
+  } while (!haveSOD);
+
+  //----- initialize the tile, precincts, and code-blocks
+  if (tilePartIdx == 0) {
+    tile = &img.tiles[tileIdx];
+    i = tileIdx / img.nXTiles;
+    j = tileIdx % img.nXTiles;
+    if ((tile->x0 = img.xTileOffset + j * img.xTileSize) < img.xOffset) {
+      tile->x0 = img.xOffset;
+    }
+    if ((tile->y0 = img.yTileOffset + i * img.yTileSize) < img.yOffset) {
+      tile->y0 = img.yOffset;
+    }
+    if ((tile->x1 = img.xTileOffset + (j + 1) * img.xTileSize) > img.xSize) {
+      tile->x1 = img.xSize;
+    }
+    if ((tile->y1 = img.yTileOffset + (i + 1) * img.yTileSize) > img.ySize) {
+      tile->y1 = img.ySize;
+    }
+    tile->comp = 0;
+    tile->res = 0;
+    tile->precinct = 0;
+    tile->layer = 0;
+    tile->maxNDecompLevels = 0;
+    for (comp = 0; comp < img.nComps; ++comp) {
+      tileComp = &tile->tileComps[comp];
+      if (tileComp->nDecompLevels > tile->maxNDecompLevels) {
+       tile->maxNDecompLevels = tileComp->nDecompLevels;
+      }
+      tileComp->x0 = jpxCeilDiv(tile->x0, tileComp->hSep);
+      tileComp->y0 = jpxCeilDiv(tile->y0, tileComp->hSep);
+      tileComp->x1 = jpxCeilDiv(tile->x1, tileComp->hSep);
+      tileComp->y1 = jpxCeilDiv(tile->y1, tileComp->hSep);
+      tileComp->cbW = 1 << tileComp->codeBlockW;
+      tileComp->cbH = 1 << tileComp->codeBlockH;
+      tileComp->data = (int *)gmallocn((tileComp->x1 - tileComp->x0) *
+                                      (tileComp->y1 - tileComp->y0),
+                                      sizeof(int));
+      if (tileComp->x1 - tileComp->x0 > tileComp->y1 - tileComp->y0) {
+       n = tileComp->x1 - tileComp->x0;
+      } else {
+       n = tileComp->y1 - tileComp->y0;
+      }
+      tileComp->buf = (int *)gmallocn(n + 8, sizeof(int));
+      for (r = 0; r <= tileComp->nDecompLevels; ++r) {
+       resLevel = &tileComp->resLevels[r];
+       k = r == 0 ? tileComp->nDecompLevels
+                  : tileComp->nDecompLevels - r + 1;
+       resLevel->x0 = jpxCeilDivPow2(tileComp->x0, k);
+       resLevel->y0 = jpxCeilDivPow2(tileComp->y0, k);
+       resLevel->x1 = jpxCeilDivPow2(tileComp->x1, k);
+       resLevel->y1 = jpxCeilDivPow2(tileComp->y1, k);
+       if (r == 0) {
+         resLevel->bx0[0] = resLevel->x0;
+         resLevel->by0[0] = resLevel->y0;
+         resLevel->bx1[0] = resLevel->x1;
+         resLevel->by1[0] = resLevel->y1;
+       } else {
+         resLevel->bx0[0] = jpxCeilDivPow2(tileComp->x0 - (1 << (k-1)), k);
+         resLevel->by0[0] = resLevel->y0;
+         resLevel->bx1[0] = jpxCeilDivPow2(tileComp->x1 - (1 << (k-1)), k);
+         resLevel->by1[0] = resLevel->y1;
+         resLevel->bx0[1] = resLevel->x0;
+         resLevel->by0[1] = jpxCeilDivPow2(tileComp->y0 - (1 << (k-1)), k);
+         resLevel->bx1[1] = resLevel->x1;
+         resLevel->by1[1] = jpxCeilDivPow2(tileComp->y1 - (1 << (k-1)), k);
+         resLevel->bx0[2] = jpxCeilDivPow2(tileComp->x0 - (1 << (k-1)), k);
+         resLevel->by0[2] = jpxCeilDivPow2(tileComp->y0 - (1 << (k-1)), k);
+         resLevel->bx1[2] = jpxCeilDivPow2(tileComp->x1 - (1 << (k-1)), k);
+         resLevel->by1[2] = jpxCeilDivPow2(tileComp->y1 - (1 << (k-1)), k);
+       }
+       resLevel->precincts = (JPXPrecinct *)gmallocn(1, sizeof(JPXPrecinct));
+       for (pre = 0; pre < 1; ++pre) {
+         precinct = &resLevel->precincts[pre];
+         precinct->x0 = resLevel->x0;
+         precinct->y0 = resLevel->y0;
+         precinct->x1 = resLevel->x1;
+         precinct->y1 = resLevel->y1;
+         nSBs = r == 0 ? 1 : 3;
+         precinct->subbands =
+             (JPXSubband *)gmallocn(nSBs, sizeof(JPXSubband));
+         for (sb = 0; sb < nSBs; ++sb) {
+           subband = &precinct->subbands[sb];
+           subband->x0 = resLevel->bx0[sb];
+           subband->y0 = resLevel->by0[sb];
+           subband->x1 = resLevel->bx1[sb];
+           subband->y1 = resLevel->by1[sb];
+           subband->nXCBs = jpxCeilDivPow2(subband->x1,
+                                           tileComp->codeBlockW)
+                            - jpxFloorDivPow2(subband->x0,
+                                              tileComp->codeBlockW);
+           subband->nYCBs = jpxCeilDivPow2(subband->y1,
+                                           tileComp->codeBlockH)
+                            - jpxFloorDivPow2(subband->y0,
+                                              tileComp->codeBlockH);
+           n = subband->nXCBs > subband->nYCBs ? subband->nXCBs
+                                               : subband->nYCBs;
+           for (subband->maxTTLevel = 0, --n;
+                n;
+                ++subband->maxTTLevel, n >>= 1) ;
+           n = 0;
+           for (level = subband->maxTTLevel; level >= 0; --level) {
+             nx = jpxCeilDivPow2(subband->nXCBs, level);
+             ny = jpxCeilDivPow2(subband->nYCBs, level);
+             n += nx * ny;
+           }
+           subband->inclusion =
+               (JPXTagTreeNode *)gmallocn(n, sizeof(JPXTagTreeNode));
+           subband->zeroBitPlane =
+               (JPXTagTreeNode *)gmallocn(n, sizeof(JPXTagTreeNode));
+           for (k = 0; k < n; ++k) {
+             subband->inclusion[k].finished = gFalse;
+             subband->inclusion[k].val = 0;
+             subband->zeroBitPlane[k].finished = gFalse;
+             subband->zeroBitPlane[k].val = 0;
+           }
+           subband->cbs = (JPXCodeBlock *)gmallocn(subband->nXCBs *
+                                                     subband->nYCBs,
+                                                   sizeof(JPXCodeBlock));
+           sbx0 = jpxFloorDivPow2(subband->x0, tileComp->codeBlockW);
+           sby0 = jpxFloorDivPow2(subband->y0, tileComp->codeBlockH);
+           cb = subband->cbs;
+           for (cbY = 0; cbY < subband->nYCBs; ++cbY) {
+             for (cbX = 0; cbX < subband->nXCBs; ++cbX) {
+               cb->x0 = (sbx0 + cbX) << tileComp->codeBlockW;
+               cb->x1 = cb->x0 + tileComp->cbW;
+               if (subband->x0 > cb->x0) {
+                 cb->x0 = subband->x0;
+               }
+               if (subband->x1 < cb->x1) {
+                 cb->x1 = subband->x1;
+               }
+               cb->y0 = (sby0 + cbY) << tileComp->codeBlockH;
+               cb->y1 = cb->y0 + tileComp->cbH;
+               if (subband->y0 > cb->y0) {
+                 cb->y0 = subband->y0;
+               }
+               if (subband->y1 < cb->y1) {
+                 cb->y1 = subband->y1;
+               }
+               cb->seen = gFalse;
+               cb->lBlock = 3;
+               cb->nextPass = jpxPassCleanup;
+               cb->nZeroBitPlanes = 0;
+               cb->coeffs =
+                   (JPXCoeff *)gmallocn((1 << (tileComp->codeBlockW
+                                               + tileComp->codeBlockH)),
+                                        sizeof(JPXCoeff));
+               for (cbi = 0;
+                    cbi < (Guint)(1 << (tileComp->codeBlockW
+                                        + tileComp->codeBlockH));
+                    ++cbi) {
+                 cb->coeffs[cbi].flags = 0;
+                 cb->coeffs[cbi].len = 0;
+                 cb->coeffs[cbi].mag = 0;
+               }
+               cb->arithDecoder = NULL;
+               cb->stats = NULL;
+               ++cb;
+             }
+           }
+         }
+       }
+      }
+    }
+  }
+
+  return readTilePartData(tileIdx, tilePartLen, tilePartToEOC);
+}
+
+GBool JPXStream::readTilePartData(Guint tileIdx,
+                                 Guint tilePartLen, GBool tilePartToEOC) {
+  JPXTile *tile;
+  JPXTileComp *tileComp;
+  JPXResLevel *resLevel;
+  JPXPrecinct *precinct;
+  JPXSubband *subband;
+  JPXCodeBlock *cb;
+  Guint ttVal;
+  Guint bits, cbX, cbY, nx, ny, i, j, n, sb;
+  int level;
+
+  tile = &img.tiles[tileIdx];
+
+  // read all packets from this tile-part
+  while (1) {
+    if (tilePartToEOC) {
+      //~ peek for an EOC marker
+    } else if (tilePartLen == 0) {
+      break;
+    }
+
+    tileComp = &tile->tileComps[tile->comp];
+    resLevel = &tileComp->resLevels[tile->res];
+    precinct = &resLevel->precincts[tile->precinct];
+
+    //----- packet header
+
+    // zero-length flag
+    if (!readBits(1, &bits)) {
+      goto err;
+    }
+    if (!bits) {
+      // packet is empty -- clear all code-block inclusion flags
+      for (sb = 0; sb < (tile->res == 0 ? 1 : 3); ++sb) {
+       subband = &precinct->subbands[sb];
+       for (cbY = 0; cbY < subband->nYCBs; ++cbY) {
+         for (cbX = 0; cbX < subband->nXCBs; ++cbX) {
+           cb = &subband->cbs[cbY * subband->nXCBs + cbX];
+           cb->included = gFalse;
+         }
+       }
+      }
+    } else {
+
+      for (sb = 0; sb < (tile->res == 0 ? 1 : 3); ++sb) {
+       subband = &precinct->subbands[sb];
+       for (cbY = 0; cbY < subband->nYCBs; ++cbY) {
+         for (cbX = 0; cbX < subband->nXCBs; ++cbX) {
+           cb = &subband->cbs[cbY * subband->nXCBs + cbX];
+
+           // skip code-blocks with no coefficients
+           if (cb->x0 >= cb->x1 || cb->y0 >= cb->y1) {
+             cb->included = gFalse;
+             continue;
+           }
+
+           // code-block inclusion
+           if (cb->seen) {
+             if (!readBits(1, &cb->included)) {
+               goto err;
+             }
+           } else {
+             ttVal = 0;
+             i = 0;
+             for (level = subband->maxTTLevel; level >= 0; --level) {
+               nx = jpxCeilDivPow2(subband->nXCBs, level);
+               ny = jpxCeilDivPow2(subband->nYCBs, level);
+               j = i + (cbY >> level) * nx + (cbX >> level);
+               if (!subband->inclusion[j].finished &&
+                   !subband->inclusion[j].val) {
+                 subband->inclusion[j].val = ttVal;
+               } else {
+                 ttVal = subband->inclusion[j].val;
+               }
+               while (!subband->inclusion[j].finished &&
+                      ttVal <= tile->layer) {
+                 if (!readBits(1, &bits)) {
+                   goto err;
+                 }
+                 if (bits == 1) {
+                   subband->inclusion[j].finished = gTrue;
+                 } else {
+                   ++ttVal;
+                 }
+               }
+               subband->inclusion[j].val = ttVal;
+               if (ttVal > tile->layer) {
+                 break;
+               }
+               i += nx * ny;
+             }
+             cb->included = level < 0;
+           }
+
+           if (cb->included) {
+
+             // zero bit-plane count
+             if (!cb->seen) {
+               ttVal = 0;
+               i = 0;
+               for (level = subband->maxTTLevel; level >= 0; --level) {
+                 nx = jpxCeilDivPow2(subband->nXCBs, level);
+                 ny = jpxCeilDivPow2(subband->nYCBs, level);
+                 j = i + (cbY >> level) * nx + (cbX >> level);
+                 if (!subband->zeroBitPlane[j].finished &&
+                     !subband->zeroBitPlane[j].val) {
+                   subband->zeroBitPlane[j].val = ttVal;
+                 } else {
+                   ttVal = subband->zeroBitPlane[j].val;
+                 }
+                 while (!subband->zeroBitPlane[j].finished) {
+                   if (!readBits(1, &bits)) {
+                     goto err;
+                   }
+                   if (bits == 1) {
+                     subband->zeroBitPlane[j].finished = gTrue;
+                   } else {
+                     ++ttVal;
+                   }
+                 }
+                 subband->zeroBitPlane[j].val = ttVal;
+                 i += nx * ny;
+               }
+               cb->nZeroBitPlanes = ttVal;
+             }
+
+             // number of coding passes
+             if (!readBits(1, &bits)) {
+               goto err;
+             }
+             if (bits == 0) {
+               cb->nCodingPasses = 1;
+             } else {
+               if (!readBits(1, &bits)) {
+                 goto err;
+               }
+               if (bits == 0) {
+                 cb->nCodingPasses = 2;
+               } else {
+                 if (!readBits(2, &bits)) {
+                   goto err;
+                 }
+                 if (bits < 3) {
+                   cb->nCodingPasses = 3 + bits;
+                 } else {
+                   if (!readBits(5, &bits)) {
+                     goto err;
+                   }
+                   if (bits < 31) {
+                     cb->nCodingPasses = 6 + bits;
+                   } else {
+                     if (!readBits(7, &bits)) {
+                       goto err;
+                     }
+                     cb->nCodingPasses = 37 + bits;
+                   }
+                 }
+               }
+             }
+
+             // update Lblock
+             while (1) {
+               if (!readBits(1, &bits)) {
+                 goto err;
+               }
+               if (!bits) {
+                 break;
+               }
+               ++cb->lBlock;
+             }
+
+             // length of compressed data
+             //~ deal with multiple codeword segments
+             for (n = cb->lBlock, i = cb->nCodingPasses >> 1;
+                  i;
+                  ++n, i >>= 1) ;
+             if (!readBits(n, &cb->dataLen)) {
+               goto err;
+             }
+           }
+         }
+       }
+      }
+    }
+    tilePartLen -= byteCount;
+    clearBitBuf();
+
+    //----- packet data
+
+    for (sb = 0; sb < (tile->res == 0 ? 1 : 3); ++sb) {
+      subband = &precinct->subbands[sb];
+      for (cbY = 0; cbY < subband->nYCBs; ++cbY) {
+       for (cbX = 0; cbX < subband->nXCBs; ++cbX) {
+         cb = &subband->cbs[cbY * subband->nXCBs + cbX];
+         if (cb->included) {
+           if (!readCodeBlockData(tileComp, resLevel, precinct, subband,
+                                  tile->res, sb, cb)) {
+             return gFalse;
+           }
+           tilePartLen -= cb->dataLen;
+           cb->seen = gTrue;
+         }
+       }
+      }
+    }
+
+    //----- next packet
+
+    switch (tile->progOrder) {
+    case 0: // layer, resolution level, component, precinct
+      if (++tile->comp == img.nComps) {
+       tile->comp = 0;
+       if (++tile->res == tile->maxNDecompLevels + 1) {
+         tile->res = 0;
+         if (++tile->layer == tile->nLayers) {
+           tile->layer = 0;
+         }
+       }
+      }
+      break;
+    case 1: // resolution level, layer, component, precinct
+      if (++tile->comp == img.nComps) {
+       tile->comp = 0;
+       if (++tile->layer == tile->nLayers) {
+         tile->layer = 0;
+         if (++tile->res == tile->maxNDecompLevels + 1) {
+           tile->res = 0;
+         }
+       }
+      }
+      break;
+    case 2: // resolution level, precinct, component, layer
+      //~ this isn't correct -- see B.12.1.3
+      if (++tile->layer == tile->nLayers) {
+       tile->layer = 0;
+       if (++tile->comp == img.nComps) {
+         tile->comp = 0;
+         if (++tile->res == tile->maxNDecompLevels + 1) {
+           tile->res = 0;
+         }
+       }
+      }
+      break;
+    case 3: // precinct, component, resolution level, layer
+      //~ this isn't correct -- see B.12.1.4
+      if (++tile->layer == tile->nLayers) {
+       tile->layer = 0;
+       if (++tile->res == tile->maxNDecompLevels + 1) {
+         tile->res = 0;
+         if (++tile->comp == img.nComps) {
+           tile->comp = 0;
+         }
+       }
+      }
+      break;
+    case 4: // component, precinct, resolution level, layer
+      //~ this isn't correct -- see B.12.1.5
+      if (++tile->layer == tile->nLayers) {
+       tile->layer = 0;
+       if (++tile->res == tile->maxNDecompLevels + 1) {
+         tile->res = 0;
+         if (++tile->comp == img.nComps) {
+           tile->comp = 0;
+         }
+       }
+      }
+      break;
+    }
+  }
+
+  return gTrue;
+
+ err:
+  error(getPos(), "Error in JPX stream");
+  return gFalse;
+}
+
+GBool JPXStream::readCodeBlockData(JPXTileComp *tileComp,
+                                  JPXResLevel *resLevel,
+                                  JPXPrecinct *precinct,
+                                  JPXSubband *subband,
+                                  Guint res, Guint sb,
+                                  JPXCodeBlock *cb) {
+  JPXCoeff *coeff0, *coeff1, *coeff;
+  Guint horiz, vert, diag, all, cx, xorBit;
+  int horizSign, vertSign;
+  Guint i, x, y0, y1, y2;
+
+  if (cb->arithDecoder) {
+    cb->arithDecoder->restart(cb->dataLen);
+  } else {
+    cb->arithDecoder = new JArithmeticDecoder();
+    cb->arithDecoder->setStream(str, cb->dataLen);
+    cb->arithDecoder->start();
+    cb->stats = new JArithmeticDecoderStats(jpxNContexts);
+    cb->stats->setEntry(jpxContextSigProp, 4, 0);
+    cb->stats->setEntry(jpxContextRunLength, 3, 0);
+    cb->stats->setEntry(jpxContextUniform, 46, 0);
+  }
+
+  for (i = 0; i < cb->nCodingPasses; ++i) {
+    switch (cb->nextPass) {
+
+    //----- significance propagation pass
+    case jpxPassSigProp:
+      for (y0 = cb->y0, coeff0 = cb->coeffs;
+          y0 < cb->y1;
+          y0 += 4, coeff0 += 4 << tileComp->codeBlockW) {
+       for (x = cb->x0, coeff1 = coeff0;
+            x < cb->x1;
+            ++x, ++coeff1) {
+         for (y1 = 0, coeff = coeff1;
+              y1 < 4 && y0+y1 < cb->y1;
+              ++y1, coeff += tileComp->cbW) {
+           if (!(coeff->flags & jpxCoeffSignificant)) {
+             horiz = vert = diag = 0;
+             horizSign = vertSign = 2;
+             if (x > cb->x0) {
+               if (coeff[-1].flags & jpxCoeffSignificant) {
+                 ++horiz;
+                 horizSign += (coeff[-1].flags & jpxCoeffSign) ? -1 : 1;
+               }
+               if (y0+y1 > cb->y0) {
+                 diag += (coeff[-(int)tileComp->cbW - 1].flags
+                          >> jpxCoeffSignificantB) & 1;
+               }
+               if (y0+y1 < cb->y1 - 1) {
+                 diag += (coeff[tileComp->cbW - 1].flags
+                          >> jpxCoeffSignificantB) & 1;
+               }
+             }
+             if (x < cb->x1 - 1) {
+               if (coeff[1].flags & jpxCoeffSignificant) {
+                 ++horiz;
+                 horizSign += (coeff[1].flags & jpxCoeffSign) ? -1 : 1;
+               }
+               if (y0+y1 > cb->y0) {
+                 diag += (coeff[-(int)tileComp->cbW + 1].flags
+                          >> jpxCoeffSignificantB) & 1;
+               }
+               if (y0+y1 < cb->y1 - 1) {
+                 diag += (coeff[tileComp->cbW + 1].flags
+                          >> jpxCoeffSignificantB) & 1;
+               }
+             }
+             if (y0+y1 > cb->y0) {
+               if (coeff[-(int)tileComp->cbW].flags & jpxCoeffSignificant) {
+                 ++vert;
+                 vertSign += (coeff[-(int)tileComp->cbW].flags & jpxCoeffSign)
+                             ? -1 : 1;
+               }
+             }
+             if (y0+y1 < cb->y1 - 1) {
+               if (coeff[tileComp->cbW].flags & jpxCoeffSignificant) {
+                 ++vert;
+                 vertSign += (coeff[tileComp->cbW].flags & jpxCoeffSign)
+                             ? -1 : 1;
+               }
+             }
+             cx = sigPropContext[horiz][vert][diag][res == 0 ? 1 : sb];
+             if (cx != 0) {
+               if (cb->arithDecoder->decodeBit(cx, cb->stats)) {
+                 coeff->flags |= jpxCoeffSignificant | jpxCoeffFirstMagRef;
+                 coeff->mag = (coeff->mag << 1) | 1;
+                 cx = signContext[horizSign][vertSign][0];
+                 xorBit = signContext[horizSign][vertSign][1];
+                 if (cb->arithDecoder->decodeBit(cx, cb->stats) ^ xorBit) {
+                   coeff->flags |= jpxCoeffSign;
+                 }
+               }
+               ++coeff->len;
+               coeff->flags |= jpxCoeffTouched;
+             }
+           }
+         }
+       }
+      }
+      ++cb->nextPass;
+      break;
+
+    //----- magnitude refinement pass
+    case jpxPassMagRef:
+      for (y0 = cb->y0, coeff0 = cb->coeffs;
+          y0 < cb->y1;
+          y0 += 4, coeff0 += 4 << tileComp->codeBlockW) {
+       for (x = cb->x0, coeff1 = coeff0;
+            x < cb->x1;
+            ++x, ++coeff1) {
+         for (y1 = 0, coeff = coeff1;
+              y1 < 4 && y0+y1 < cb->y1;
+              ++y1, coeff += tileComp->cbW) {
+           if ((coeff->flags & jpxCoeffSignificant) &&
+               !(coeff->flags & jpxCoeffTouched)) {
+             if (coeff->flags & jpxCoeffFirstMagRef) {
+               all = 0;
+               if (x > cb->x0) {
+                 all += (coeff[-1].flags >> jpxCoeffSignificantB) & 1;
+                 if (y0+y1 > cb->y0) {
+                   all += (coeff[-(int)tileComp->cbW - 1].flags
+                           >> jpxCoeffSignificantB) & 1;
+                 }
+                 if (y0+y1 < cb->y1 - 1) {
+                   all += (coeff[tileComp->cbW - 1].flags
+                           >> jpxCoeffSignificantB) & 1;
+                 }
+               }
+               if (x < cb->x1 - 1) {
+                 all += (coeff[1].flags >> jpxCoeffSignificantB) & 1;
+                 if (y0+y1 > cb->y0) {
+                   all += (coeff[-(int)tileComp->cbW + 1].flags
+                           >> jpxCoeffSignificantB) & 1;
+                 }
+                 if (y0+y1 < cb->y1 - 1) {
+                   all += (coeff[tileComp->cbW + 1].flags
+                           >> jpxCoeffSignificantB) & 1;
+                 }
+               }
+               if (y0+y1 > cb->y0) {
+                 all += (coeff[-(int)tileComp->cbW].flags
+                         >> jpxCoeffSignificantB) & 1;
+               }
+               if (y0+y1 < cb->y1 - 1) {
+                 all += (coeff[tileComp->cbW].flags
+                         >> jpxCoeffSignificantB) & 1;
+               }
+               cx = all ? 15 : 14;
+             } else {
+               cx = 16;
+             }
+             coeff->mag = (coeff->mag << 1) |
+                          cb->arithDecoder->decodeBit(cx, cb->stats);
+             ++coeff->len;
+             coeff->flags |= jpxCoeffTouched;
+             coeff->flags &= ~jpxCoeffFirstMagRef;
+           }
+         }
+       }
+      }
+      ++cb->nextPass;
+      break;
+
+    //----- cleanup pass
+    case jpxPassCleanup:
+      for (y0 = cb->y0, coeff0 = cb->coeffs;
+          y0 < cb->y1;
+          y0 += 4, coeff0 += 4 << tileComp->codeBlockW) {
+       for (x = cb->x0, coeff1 = coeff0;
+            x < cb->x1;
+            ++x, ++coeff1) {
+         y1 = 0;
+         if (y0 + 3 < cb->y1 &&
+             !(coeff1->flags & jpxCoeffTouched) &&
+             !(coeff1[tileComp->cbW].flags & jpxCoeffTouched) &&
+             !(coeff1[2 * tileComp->cbW].flags & jpxCoeffTouched) &&
+             !(coeff1[3 * tileComp->cbW].flags & jpxCoeffTouched) &&
+             (x == cb->x0 || y0 == cb->y0 ||
+              !(coeff1[-(int)tileComp->cbW - 1].flags
+                & jpxCoeffSignificant)) &&
+             (y0 == cb->y0 ||
+              !(coeff1[-(int)tileComp->cbW].flags
+                & jpxCoeffSignificant)) &&
+             (x == cb->x1 - 1 || y0 == cb->y0 ||
+              !(coeff1[-(int)tileComp->cbW + 1].flags
+                & jpxCoeffSignificant)) &&
+             (x == cb->x0 ||
+              (!(coeff1[-1].flags & jpxCoeffSignificant) &&
+               !(coeff1[tileComp->cbW - 1].flags
+                 & jpxCoeffSignificant) &&
+               !(coeff1[2 * tileComp->cbW - 1].flags
+                 & jpxCoeffSignificant) && 
+               !(coeff1[3 * tileComp->cbW - 1].flags
+                 & jpxCoeffSignificant))) &&
+             (x == cb->x1 - 1 ||
+              (!(coeff1[1].flags & jpxCoeffSignificant) &&
+               !(coeff1[tileComp->cbW + 1].flags
+                 & jpxCoeffSignificant) &&
+               !(coeff1[2 * tileComp->cbW + 1].flags
+                 & jpxCoeffSignificant) &&
+               !(coeff1[3 * tileComp->cbW + 1].flags
+                 & jpxCoeffSignificant))) &&
+             (x == cb->x0 || y0+4 == cb->y1 ||
+              !(coeff1[4 * tileComp->cbW - 1].flags & jpxCoeffSignificant)) &&
+             (y0+4 == cb->y1 ||
+              !(coeff1[4 * tileComp->cbW].flags & jpxCoeffSignificant)) &&
+             (x == cb->x1 - 1 || y0+4 == cb->y1 ||
+              !(coeff1[4 * tileComp->cbW + 1].flags
+                & jpxCoeffSignificant))) {
+           if (cb->arithDecoder->decodeBit(jpxContextRunLength, cb->stats)) {
+             y1 = cb->arithDecoder->decodeBit(jpxContextUniform, cb->stats);
+             y1 = (y1 << 1) |
+                  cb->arithDecoder->decodeBit(jpxContextUniform, cb->stats);
+             for (y2 = 0, coeff = coeff1;
+                  y2 < y1;
+                  ++y2, coeff += tileComp->cbW) {
+               ++coeff->len;
+             }
+             coeff->flags |= jpxCoeffSignificant | jpxCoeffFirstMagRef;
+             coeff->mag = (coeff->mag << 1) | 1;
+             ++coeff->len;
+             cx = signContext[2][2][0];
+             xorBit = signContext[2][2][1];
+             if (cb->arithDecoder->decodeBit(cx, cb->stats) ^ xorBit) {
+               coeff->flags |= jpxCoeffSign;
+             }
+             ++y1;
+           } else {
+             for (y1 = 0, coeff = coeff1;
+                  y1 < 4;
+                  ++y1, coeff += tileComp->cbW) {
+               ++coeff->len;
+             }
+             y1 = 4;
+           }
+         }
+         for (coeff = &coeff1[y1 << tileComp->codeBlockW];
+              y1 < 4 && y0 + y1 < cb->y1;
+              ++y1, coeff += tileComp->cbW) {
+           if (!(coeff->flags & jpxCoeffTouched)) {
+             horiz = vert = diag = 0;
+             horizSign = vertSign = 2;
+             if (x > cb->x0) {
+               if (coeff[-1].flags & jpxCoeffSignificant) {
+                 ++horiz;
+                 horizSign += (coeff[-1].flags & jpxCoeffSign) ? -1 : 1;
+               }
+               if (y0+y1 > cb->y0) {
+                 diag += (coeff[-(int)tileComp->cbW - 1].flags
+                          >> jpxCoeffSignificantB) & 1;
+               }
+               if (y0+y1 < cb->y1 - 1) {
+                 diag += (coeff[tileComp->cbW - 1].flags
+                          >> jpxCoeffSignificantB) & 1;
+               }
+             }
+             if (x < cb->x1 - 1) {
+               if (coeff[1].flags & jpxCoeffSignificant) {
+                 ++horiz;
+                 horizSign += (coeff[1].flags & jpxCoeffSign) ? -1 : 1;
+               }
+               if (y0+y1 > cb->y0) {
+                 diag += (coeff[-(int)tileComp->cbW + 1].flags
+                          >> jpxCoeffSignificantB) & 1;
+               }
+               if (y0+y1 < cb->y1 - 1) {
+                 diag += (coeff[tileComp->cbW + 1].flags
+                          >> jpxCoeffSignificantB) & 1;
+               }
+             }
+             if (y0+y1 > cb->y0) {
+               if (coeff[-(int)tileComp->cbW].flags & jpxCoeffSignificant) {
+                 ++vert;
+                 vertSign += (coeff[-(int)tileComp->cbW].flags & jpxCoeffSign)
+                             ? -1 : 1;
+               }
+             }
+             if (y0+y1 < cb->y1 - 1) {
+               if (coeff[tileComp->cbW].flags & jpxCoeffSignificant) {
+                 ++vert;
+                 vertSign += (coeff[tileComp->cbW].flags & jpxCoeffSign)
+                             ? -1 : 1;
+               }
+             }
+             cx = sigPropContext[horiz][vert][diag][res == 0 ? 1 : sb];
+             if (cb->arithDecoder->decodeBit(cx, cb->stats)) {
+               coeff->flags |= jpxCoeffSignificant | jpxCoeffFirstMagRef;
+               coeff->mag = (coeff->mag << 1) | 1;
+               cx = signContext[horizSign][vertSign][0];
+               xorBit = signContext[horizSign][vertSign][1];
+               if (cb->arithDecoder->decodeBit(cx, cb->stats) ^ xorBit) {
+                 coeff->flags |= jpxCoeffSign;
+               }
+             }
+             ++coeff->len;
+           } else {
+             coeff->flags &= ~jpxCoeffTouched;
+           }
+         }
+       }
+      }
+      cb->nextPass = jpxPassSigProp;
+      break;
+    }
+  }
+
+  cb->arithDecoder->cleanup();
+  return gTrue;
+}
+
+// Inverse quantization, and wavelet transform (IDWT).  This also does
+// the initial shift to convert to fixed point format.
+void JPXStream::inverseTransform(JPXTileComp *tileComp) {
+  JPXResLevel *resLevel;
+  JPXPrecinct *precinct;
+  JPXSubband *subband;
+  JPXCodeBlock *cb;
+  JPXCoeff *coeff0, *coeff;
+  Guint qStyle, guard, eps, shift;
+  int shift2;
+  double mu;
+  int val;
+  int *dataPtr;
+  Guint nx0, ny0, nx1, ny1;
+  Guint r, cbX, cbY, x, y;
+
+  //----- (NL)LL subband (resolution level 0)
+
+  resLevel = &tileComp->resLevels[0];
+  precinct = &resLevel->precincts[0];
+  subband = &precinct->subbands[0];
+
+  // i-quant parameters
+  qStyle = tileComp->quantStyle & 0x1f;
+  guard = (tileComp->quantStyle >> 5) & 7;
+  if (qStyle == 0) {
+    eps = (tileComp->quantSteps[0] >> 3) & 0x1f;
+    shift = guard + eps - 1;
+    mu = 0; // make gcc happy
+  } else {
+    shift = guard - 1 + tileComp->prec;
+    mu = (double)(0x800 + (tileComp->quantSteps[0] & 0x7ff)) / 2048.0;
+  }
+  if (tileComp->transform == 0) {
+    shift += fracBits;
+  }
+
+  // copy (NL)LL into the upper-left corner of the data array, doing
+  // the fixed point adjustment and dequantization along the way
+  cb = subband->cbs;
+  for (cbY = 0; cbY < subband->nYCBs; ++cbY) {
+    for (cbX = 0; cbX < subband->nXCBs; ++cbX) {
+      for (y = cb->y0, coeff0 = cb->coeffs;
+          y < cb->y1;
+          ++y, coeff0 += tileComp->cbW) {
+       dataPtr = &tileComp->data[(y - subband->y0)
+                                 * (tileComp->x1 - tileComp->x0)
+                                 + (cb->x0 - subband->x0)];
+       for (x = cb->x0, coeff = coeff0; x < cb->x1; ++x, ++coeff) {
+         val = (int)coeff->mag;
+         if (val != 0) {
+           shift2 = shift - (cb->nZeroBitPlanes + coeff->len);
+           if (shift2 > 0) {
+             val = (val << shift2) + (1 << (shift2 - 1));
+           } else {
+             val >>= -shift2;
+           }
+           if (qStyle == 0) {
+             if (tileComp->transform == 0) {
+               val &= -1 << fracBits;
+             }
+           } else {
+             val = (int)((double)val * mu);
+           }
+           if (coeff->flags & jpxCoeffSign) {
+             val = -val;
+           }
+         }
+         *dataPtr++ = val;
+       }
+      }
+      ++cb;
+    }
+  }
+
+  //----- IDWT for each level
+
+  for (r = 1; r <= tileComp->nDecompLevels; ++r) {
+    resLevel = &tileComp->resLevels[r];
+
+    // (n)LL is already in the upper-left corner of the
+    // tile-component data array -- interleave with (n)HL/LH/HH
+    // and inverse transform to get (n-1)LL, which will be stored
+    // in the upper-left corner of the tile-component data array
+    if (r == tileComp->nDecompLevels) {
+      nx0 = tileComp->x0;
+      ny0 = tileComp->y0;
+      nx1 = tileComp->x1;
+      ny1 = tileComp->y1;
+    } else {
+      nx0 = tileComp->resLevels[r+1].x0;
+      ny0 = tileComp->resLevels[r+1].y0;
+      nx1 = tileComp->resLevels[r+1].x1;
+      ny1 = tileComp->resLevels[r+1].y1;
+    }
+    inverseTransformLevel(tileComp, r, resLevel, nx0, ny0, nx1, ny1);
+  }
+}
+
+// Do one level of the inverse transform:
+// - take (n)LL from the tile-component data array
+// - take (n)HL/LH/HH from <resLevel>
+// - leave the resulting (n-1)LL in the tile-component data array
+void JPXStream::inverseTransformLevel(JPXTileComp *tileComp,
+                                     Guint r, JPXResLevel *resLevel,
+                                     Guint nx0, Guint ny0,
+                                     Guint nx1, Guint ny1) {
+  JPXPrecinct *precinct;
+  JPXSubband *subband;
+  JPXCodeBlock *cb;
+  JPXCoeff *coeff0, *coeff;
+  Guint qStyle, guard, eps, shift, t;
+  int shift2;
+  double mu;
+  int val;
+  int *dataPtr;
+  Guint xo, yo;
+  Guint x, y, sb, cbX, cbY;
+  int xx, yy;
+
+  //----- interleave
+
+  // spread out LL
+  for (yy = resLevel->y1 - 1; yy >= (int)resLevel->y0; --yy) {
+    for (xx = resLevel->x1 - 1; xx >= (int)resLevel->x0; --xx) {
+      tileComp->data[(2 * yy - ny0) * (tileComp->x1 - tileComp->x0)
+                    + (2 * xx - nx0)] =
+         tileComp->data[(yy - resLevel->y0) * (tileComp->x1 - tileComp->x0)
+                        + (xx - resLevel->x0)];
+    }
+  }
+
+  // i-quant parameters
+  qStyle = tileComp->quantStyle & 0x1f;
+  guard = (tileComp->quantStyle >> 5) & 7;
+
+  // interleave HL/LH/HH
+  precinct = &resLevel->precincts[0];
+  for (sb = 0; sb < 3; ++sb) {
+
+    // i-quant parameters
+    if (qStyle == 0) {
+      eps = (tileComp->quantSteps[3*r - 2 + sb] >> 3) & 0x1f;
+      shift = guard + eps - 1;
+      mu = 0; // make gcc happy
+    } else {
+      shift = guard + tileComp->prec;
+      if (sb == 2) {
+       ++shift;
+      }
+      t = tileComp->quantSteps[qStyle == 1 ? 0 : (3*r - 2 + sb)];
+      mu = (double)(0x800 + (t & 0x7ff)) / 2048.0;
+    }
+    if (tileComp->transform == 0) {
+      shift += fracBits;
+    }
+
+    // copy the subband coefficients into the data array, doing the
+    // fixed point adjustment and dequantization along the way
+    xo = (sb & 1) ? 0 : 1;
+    yo = (sb > 0) ? 1 : 0;
+    subband = &precinct->subbands[sb];
+    cb = subband->cbs;
+    for (cbY = 0; cbY < subband->nYCBs; ++cbY) {
+      for (cbX = 0; cbX < subband->nXCBs; ++cbX) {
+       for (y = cb->y0, coeff0 = cb->coeffs;
+            y < cb->y1;
+            ++y, coeff0 += tileComp->cbW) {
+         dataPtr = &tileComp->data[(2 * y + yo - ny0)
+                                   * (tileComp->x1 - tileComp->x0)
+                                   + (2 * cb->x0 + xo - nx0)];
+         for (x = cb->x0, coeff = coeff0; x < cb->x1; ++x, ++coeff) {
+           val = (int)coeff->mag;
+           if (val != 0) {
+             shift2 = shift - (cb->nZeroBitPlanes + coeff->len);
+             if (shift2 > 0) {
+               val = (val << shift2) + (1 << (shift2 - 1));
+             } else {
+               val >>= -shift2;
+             }
+             if (qStyle == 0) {
+               if (tileComp->transform == 0) {
+                 val &= -1 << fracBits;
+               }
+             } else {
+               val = (int)((double)val * mu);
+             }
+             if (coeff->flags & jpxCoeffSign) {
+               val = -val;
+             }
+           }
+           *dataPtr = val;
+           dataPtr += 2;
+         }
+       }
+       ++cb;
+      }
+    }
+  }
+
+  //----- horizontal (row) transforms
+  dataPtr = tileComp->data;
+  for (y = 0; y < ny1 - ny0; ++y) {
+    inverseTransform1D(tileComp, dataPtr, 1, nx0, nx1);
+    dataPtr += tileComp->x1 - tileComp->x0;
+  }
+
+  //----- vertical (column) transforms
+  dataPtr = tileComp->data;
+  for (x = 0; x < nx1 - nx0; ++x) {
+    inverseTransform1D(tileComp, dataPtr,
+                      tileComp->x1 - tileComp->x0, ny0, ny1);
+    ++dataPtr;
+  }
+}
+
+void JPXStream::inverseTransform1D(JPXTileComp *tileComp,
+                                  int *data, Guint stride,
+                                  Guint i0, Guint i1) {
+  int *buf;
+  Guint offset, end, i;
+
+  //----- special case for length = 1
+  if (i1 - i0 == 1) {
+    if (i0 & 1) {
+      *data >>= 1;
+    }
+
+  } else {
+
+    // choose an offset: this makes even buf[] indexes correspond to
+    // odd values of i, and vice versa
+    offset = 3 + (i0 & 1);
+    end = offset + i1 - i0;
+
+    //----- gather
+    buf = tileComp->buf;
+    for (i = 0; i < i1 - i0; ++i) {
+      buf[offset + i] = data[i * stride];
+    }
+
+    //----- extend right
+    buf[end] = buf[end - 2];
+    if (i1 - i0 == 2) {
+      buf[end+1] = buf[offset + 1];
+      buf[end+2] = buf[offset];
+      buf[end+3] = buf[offset + 1];
+    } else {
+      buf[end+1] = buf[end - 3];
+      if (i1 - i0 == 3) {
+       buf[end+2] = buf[offset + 1];
+       buf[end+3] = buf[offset + 2];
+      } else {
+       buf[end+2] = buf[end - 4];
+       if (i1 - i0 == 4) {
+         buf[end+3] = buf[offset + 1];
+       } else {
+         buf[end+3] = buf[end - 5];
+       }
+      }
+    }
+
+    //----- extend left
+    buf[offset - 1] = buf[offset + 1];
+    buf[offset - 2] = buf[offset + 2];
+    buf[offset - 3] = buf[offset + 3];
+    if (offset == 4) {
+      buf[0] = buf[offset + 4];
+    }
+
+    //----- 9-7 irreversible filter
+
+    if (tileComp->transform == 0) {
+      // step 1 (even)
+      for (i = 1; i <= end + 2; i += 2) {
+       buf[i] = (int)(idwtKappa * buf[i]);
+      }
+      // step 2 (odd)
+      for (i = 0; i <= end + 3; i += 2) {
+       buf[i] = (int)(idwtIKappa * buf[i]);
+      }
+      // step 3 (even)
+      for (i = 1; i <= end + 2; i += 2) {
+       buf[i] = (int)(buf[i] - idwtDelta * (buf[i-1] + buf[i+1]));
+      }
+      // step 4 (odd)
+      for (i = 2; i <= end + 1; i += 2) {
+       buf[i] = (int)(buf[i] - idwtGamma * (buf[i-1] + buf[i+1]));
+      }
+      // step 5 (even)
+      for (i = 3; i <= end; i += 2) {
+       buf[i] = (int)(buf[i] - idwtBeta * (buf[i-1] + buf[i+1]));
+      }
+      // step 6 (odd)
+      for (i = 4; i <= end - 1; i += 2) {
+       buf[i] = (int)(buf[i] - idwtAlpha * (buf[i-1] + buf[i+1]));
+      }
+
+    //----- 5-3 reversible filter
+
+    } else {
+      // step 1 (even)
+      for (i = 3; i <= end; i += 2) {
+       buf[i] -= (buf[i-1] + buf[i+1] + 2) >> 2;
+      }
+      // step 2 (odd)
+      for (i = 4; i < end; i += 2) {
+       buf[i] += (buf[i-1] + buf[i+1]) >> 1;
+      }
+    }
+
+    //----- scatter
+    for (i = 0; i < i1 - i0; ++i) {
+      data[i * stride] = buf[offset + i];
+    }
+  }
+}
+
+// Inverse multi-component transform and DC level shift.  This also
+// converts fixed point samples back to integers.
+GBool JPXStream::inverseMultiCompAndDC(JPXTile *tile) {
+  JPXTileComp *tileComp;
+  int coeff, d0, d1, d2, t, minVal, maxVal, zeroVal;
+  int *dataPtr;
+  Guint j, comp, x, y;
+
+  //----- inverse multi-component transform
+
+  if (tile->multiComp == 1) {
+    if (img.nComps < 3 ||
+       tile->tileComps[0].hSep != tile->tileComps[1].hSep ||
+       tile->tileComps[0].vSep != tile->tileComps[1].vSep ||
+       tile->tileComps[1].hSep != tile->tileComps[2].hSep ||
+       tile->tileComps[1].vSep != tile->tileComps[2].vSep) {
+      return gFalse;
+    }
+
+    // inverse irreversible multiple component transform
+    if (tile->tileComps[0].transform == 0) {
+      j = 0;
+      for (y = 0; y < tile->tileComps[0].y1 - tile->tileComps[0].y0; ++y) {
+       for (x = 0; x < tile->tileComps[0].x1 - tile->tileComps[0].x0; ++x) {
+         d0 = tile->tileComps[0].data[j];
+         d1 = tile->tileComps[1].data[j];
+         d2 = tile->tileComps[2].data[j];
+         tile->tileComps[0].data[j] = (int)(d0 + 1.402 * d2 + 0.5);
+         tile->tileComps[1].data[j] =
+             (int)(d0 - 0.34413 * d1 - 0.71414 * d2 + 0.5);
+         tile->tileComps[2].data[j] = (int)(d0 + 1.772 * d1 + 0.5);
+         ++j;
+       }
+      }
+
+    // inverse reversible multiple component transform
+    } else {
+      j = 0;
+      for (y = 0; y < tile->tileComps[0].y1 - tile->tileComps[0].y0; ++y) {
+       for (x = 0; x < tile->tileComps[0].x1 - tile->tileComps[0].x0; ++x) {
+         d0 = tile->tileComps[0].data[j];
+         d1 = tile->tileComps[1].data[j];
+         d2 = tile->tileComps[2].data[j];
+         tile->tileComps[1].data[j] = t = d0 - ((d2 + d1) >> 2);
+         tile->tileComps[0].data[j] = d2 + t;
+         tile->tileComps[2].data[j] = d1 + t;
+         ++j;
+       }
+      }
+    }
+  }
+
+  //----- DC level shift
+  for (comp = 0; comp < img.nComps; ++comp) {
+    tileComp = &tile->tileComps[comp];
+
+    // signed: clip
+    if (tileComp->sgned) {
+      minVal = -(1 << (tileComp->prec - 1));
+      maxVal = (1 << (tileComp->prec - 1)) - 1;
+      dataPtr = tileComp->data;
+      for (y = 0; y < tileComp->y1 - tileComp->y0; ++y) {
+       for (x = 0; x < tileComp->x1 - tileComp->x0; ++x) {
+         coeff = *dataPtr;
+         if (tileComp->transform == 0) {
+           coeff >>= fracBits;
+         }
+         if (coeff < minVal) {
+           coeff = minVal;
+         } else if (coeff > maxVal) {
+           coeff = maxVal;
+         }
+         *dataPtr++ = coeff;
+       }
+      }
+
+    // unsigned: inverse DC level shift and clip
+    } else {
+      maxVal = (1 << tileComp->prec) - 1;
+      zeroVal = 1 << (tileComp->prec - 1);
+      dataPtr = tileComp->data;
+      for (y = 0; y < tileComp->y1 - tileComp->y0; ++y) {
+       for (x = 0; x < tileComp->x1 - tileComp->x0; ++x) {
+         coeff = *dataPtr;
+         if (tileComp->transform == 0) {
+           coeff >>= fracBits;
+         }
+         coeff += zeroVal;
+         if (coeff < 0) {
+           coeff = 0;
+         } else if (coeff > maxVal) {
+           coeff = maxVal;
+         }
+         *dataPtr++ = coeff;
+       }
+      }
+    }
+  }
+
+  return gTrue;
+}
+
+GBool JPXStream::readBoxHdr(Guint *boxType, Guint *boxLen, Guint *dataLen) {
+  Guint len, lenH;
+
+  if (!readULong(&len) ||
+      !readULong(boxType)) {
+    return gFalse;
+  }
+  if (len == 1) {
+    if (!readULong(&lenH) || !readULong(&len)) {
+      return gFalse;
+    }
+    if (lenH) {
+      error(getPos(), "JPX stream contains a box larger than 2^32 bytes");
+      return gFalse;
+    }
+    *boxLen = len;
+    *dataLen = len - 16;
+  } else if (len == 0) {
+    *boxLen = 0;
+    *dataLen = 0;
+  } else {
+    *boxLen = len;
+    *dataLen = len - 8;
+  }
+  return gTrue;
+}
+
+int JPXStream::readMarkerHdr(int *segType, Guint *segLen) {
+  int c;
+
+  do {
+    do {
+      if ((c = str->getChar()) == EOF) {
+       return gFalse;
+      }
+    } while (c != 0xff);
+    do {
+      if ((c = str->getChar()) == EOF) {
+       return gFalse;
+      }
+    } while (c == 0xff);
+  } while (c == 0x00);
+  *segType = c;
+  if ((c >= 0x30 && c <= 0x3f) ||
+      c == 0x4f || c == 0x92 || c == 0x93 || c == 0xd9) {
+    *segLen = 0;
+    return gTrue;
+  }
+  return readUWord(segLen);
+}
+
+GBool JPXStream::readUByte(Guint *x) {
+  int c0;
+
+  if ((c0 = str->getChar()) == EOF) {
+    return gFalse;
+  }
+  *x = (Guint)c0;
+  return gTrue;
+}
+
+GBool JPXStream::readByte(int *x) {
+ int c0;
+
+  if ((c0 = str->getChar()) == EOF) {
+    return gFalse;
+  }
+  *x = c0;
+  if (c0 & 0x80) {
+    *x |= -1 - 0xff;
+  }
+  return gTrue;
+}
+
+GBool JPXStream::readUWord(Guint *x) {
+  int c0, c1;
+
+  if ((c0 = str->getChar()) == EOF ||
+      (c1 = str->getChar()) == EOF) {
+    return gFalse;
+  }
+  *x = (Guint)((c0 << 8) | c1);
+  return gTrue;
+}
+
+GBool JPXStream::readULong(Guint *x) {
+  int c0, c1, c2, c3;
+
+  if ((c0 = str->getChar()) == EOF ||
+      (c1 = str->getChar()) == EOF ||
+      (c2 = str->getChar()) == EOF ||
+      (c3 = str->getChar()) == EOF) {
+    return gFalse;
+  }
+  *x = (Guint)((c0 << 24) | (c1 << 16) | (c2 << 8) | c3);
+  return gTrue;
+}
+
+GBool JPXStream::readNBytes(int nBytes, GBool signd, int *x) {
+  int y, c, i;
+
+  y = 0;
+  for (i = 0; i < nBytes; ++i) {
+    if ((c = str->getChar()) == EOF) {
+      return gFalse;
+    }
+    y = (y << 8) + c;
+  }
+  if (signd) {
+    if (y & (1 << (8 * nBytes - 1))) {
+      y |= -1 << (8 * nBytes);
+    }
+  }
+  *x = y;
+  return gTrue;
+}
+
+GBool JPXStream::readBits(int nBits, Guint *x) {
+  int c;
+
+  while (bitBufLen < nBits) {
+    if ((c = str->getChar()) == EOF) {
+      return gFalse;
+    }
+    ++byteCount;
+    if (bitBufSkip) {
+      bitBuf = (bitBuf << 7) | (c & 0x7f);
+      bitBufLen += 7;
+    } else {
+      bitBuf = (bitBuf << 8) | (c & 0xff);
+      bitBufLen += 8;
+    }
+    bitBufSkip = c == 0xff;
+  }
+  *x = (bitBuf >> (bitBufLen - nBits)) & ((1 << nBits) - 1);
+  bitBufLen -= nBits;
+  return gTrue;
+}
+
+void JPXStream::clearBitBuf() {
+  bitBufLen = 0;
+  bitBufSkip = gFalse;
+  byteCount = 0;
+}
diff --git a/lib/xpdf/JPXStream.h b/lib/xpdf/JPXStream.h
new file mode 100644 (file)
index 0000000..e64731d
--- /dev/null
@@ -0,0 +1,349 @@
+//========================================================================
+//
+// JPXStream.h
+//
+// Copyright 2002-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef JPXSTREAM_H
+#define JPXSTREAM_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#include "gtypes.h"
+#include "Object.h"
+#include "Stream.h"
+
+class JArithmeticDecoderStats;
+
+//------------------------------------------------------------------------
+
+enum JPXColorSpaceType {
+  jpxCSBiLevel = 0,
+  jpxCSYCbCr1 = 1,
+  jpxCSYCbCr2 = 3,
+  jpxCSYCBCr3 = 4,
+  jpxCSPhotoYCC = 9,
+  jpxCSCMY = 11,
+  jpxCSCMYK = 12,
+  jpxCSYCCK = 13,
+  jpxCSCIELab = 14,
+  jpxCSsRGB = 16,
+  jpxCSGrayscale = 17,
+  jpxCSBiLevel2 = 18,
+  jpxCSCIEJab = 19,
+  jpxCSCISesRGB = 20,
+  jpxCSROMMRGB = 21,
+  jpxCSsRGBYCbCr = 22,
+  jpxCSYPbPr1125 = 23,
+  jpxCSYPbPr1250 = 24
+};
+
+struct JPXColorSpecCIELab {
+  Guint rl, ol, ra, oa, rb, ob, il;
+};
+
+struct JPXColorSpecEnumerated {
+  JPXColorSpaceType type;      // color space type
+  union {
+    JPXColorSpecCIELab cieLab;
+  };
+};
+
+struct JPXColorSpec {
+  Guint meth;                  // method
+  int prec;                    // precedence
+  union {
+    JPXColorSpecEnumerated enumerated;
+  };
+};
+
+//------------------------------------------------------------------------
+
+struct JPXPalette {
+  Guint nEntries;              // number of entries in the palette
+  Guint nComps;                        // number of components in each entry
+  Guint *bpc;                  // bits per component, for each component
+  int *c;                      // color data:
+                               //   c[i*nComps+j] = entry i, component j
+};
+
+//------------------------------------------------------------------------
+
+struct JPXCompMap {
+  Guint nChannels;             // number of channels
+  Guint *comp;                 // codestream components mapped to each channel
+  Guint *type;                 // 0 for direct use, 1 for palette mapping
+  Guint *pComp;                        // palette components to use
+};
+
+//------------------------------------------------------------------------
+
+struct JPXChannelDefn {
+  Guint nChannels;             // number of channels
+  Guint *idx;                  // channel indexes
+  Guint *type;                 // channel types
+  Guint *assoc;                        // channel associations
+};
+
+//------------------------------------------------------------------------
+
+struct JPXTagTreeNode {
+  GBool finished;              // true if this node is finished
+  Guint val;                   // current value
+};
+
+//------------------------------------------------------------------------
+
+struct JPXCoeff {
+  Gushort flags;               // flag bits
+  Gushort len;                 // number of significant bits in mag
+  Guint mag;                   // magnitude value
+};
+
+// coefficient flags
+#define jpxCoeffSignificantB  0
+#define jpxCoeffTouchedB      1
+#define jpxCoeffFirstMagRefB  2
+#define jpxCoeffSignB         7
+#define jpxCoeffSignificant   (1 << jpxCoeffSignificantB)
+#define jpxCoeffTouched       (1 << jpxCoeffTouchedB)
+#define jpxCoeffFirstMagRef   (1 << jpxCoeffFirstMagRefB)
+#define jpxCoeffSign          (1 << jpxCoeffSignB)
+
+//------------------------------------------------------------------------
+
+struct JPXCodeBlock {
+  //----- size
+  Guint x0, y0, x1, y1;                // bounds
+
+  //----- persistent state
+  GBool seen;                  // true if this code-block has already
+                               //   been seen
+  Guint lBlock;                        // base number of bits used for pkt data length
+  Guint nextPass;              // next coding pass
+
+  //---- info from first packet
+  Guint nZeroBitPlanes;                // number of zero bit planes
+
+  //----- info for the current packet
+  Guint included;              // code-block inclusion in this packet:
+                               //   0=not included, 1=included
+  Guint nCodingPasses;         // number of coding passes in this pkt
+  Guint dataLen;               // pkt data length
+
+  //----- coefficient data
+  JPXCoeff *coeffs;            // the coefficients
+  JArithmeticDecoder           // arithmetic decoder
+    *arithDecoder;
+  JArithmeticDecoderStats      // arithmetic decoder stats
+    *stats;
+};
+
+//------------------------------------------------------------------------
+
+struct JPXSubband {
+  //----- computed
+  Guint x0, y0, x1, y1;                // bounds
+  Guint nXCBs, nYCBs;          // number of code-blocks in the x and y
+                               //   directions
+
+  //----- tag trees
+  Guint maxTTLevel;            // max tag tree level
+  JPXTagTreeNode *inclusion;   // inclusion tag tree for each subband
+  JPXTagTreeNode *zeroBitPlane;        // zero-bit plane tag tree for each
+                               //   subband
+
+  //----- children
+  JPXCodeBlock *cbs;           // the code-blocks (len = nXCBs * nYCBs)
+};
+
+//------------------------------------------------------------------------
+
+struct JPXPrecinct {
+  //----- computed
+  Guint x0, y0, x1, y1;                // bounds of the precinct
+
+  //----- children
+  JPXSubband *subbands;                // the subbands
+};
+
+//------------------------------------------------------------------------
+
+struct JPXResLevel {
+  //----- from the COD and COC segments (main and tile)
+  Guint precinctWidth;         // log2(precinct width)
+  Guint precinctHeight;                // log2(precinct height)
+
+  //----- computed
+  Guint x0, y0, x1, y1;                // bounds of the tile-comp (for this res level)
+  Guint bx0[3], by0[3],                // subband bounds
+        bx1[3], by1[3];
+
+  //---- children
+  JPXPrecinct *precincts;      // the precincts
+};
+
+//------------------------------------------------------------------------
+
+struct JPXTileComp {
+  //----- from the SIZ segment
+  GBool sgned;                 // 1 for signed, 0 for unsigned
+  Guint prec;                  // precision, in bits
+  Guint hSep;                  // horizontal separation of samples
+  Guint vSep;                  // vertical separation of samples
+
+  //----- from the COD and COC segments (main and tile)
+  Guint style;                 // coding style parameter (Scod / Scoc)
+  Guint nDecompLevels;         // number of decomposition levels
+  Guint codeBlockW;            // log2(code-block width)
+  Guint codeBlockH;            // log2(code-block height)
+  Guint codeBlockStyle;                // code-block style
+  Guint transform;             // wavelet transformation
+
+  //----- from the QCD and QCC segments (main and tile)
+  Guint quantStyle;            // quantization style
+  Guint *quantSteps;           // quantization step size for each subband
+  Guint nQuantSteps;           // number of entries in quantSteps
+
+  //----- computed
+  Guint x0, y0, x1, y1;                // bounds of the tile-comp, in ref coords
+  Guint cbW;                   // code-block width
+  Guint cbH;                   // code-block height
+
+  //----- image data
+  int *data;                   // the decoded image data
+  int *buf;                    // intermediate buffer for the inverse
+                               //   transform
+
+  //----- children
+  JPXResLevel *resLevels;      // the resolution levels
+                               //   (len = nDecompLevels + 1)
+};
+
+//------------------------------------------------------------------------
+
+struct JPXTile {
+  //----- from the COD segments (main and tile)
+  Guint progOrder;             // progression order
+  Guint nLayers;               // number of layers
+  Guint multiComp;             // multiple component transformation
+
+  //----- computed
+  Guint x0, y0, x1, y1;                // bounds of the tile, in ref coords
+  Guint maxNDecompLevels;      // max number of decomposition levels used
+                               //   in any component in this tile
+
+  //----- progression order loop counters
+  Guint comp;                  //   component
+  Guint res;                   //   resolution level
+  Guint precinct;              //   precinct
+  Guint layer;                 //   layer
+
+  //----- children
+  JPXTileComp *tileComps;      // the tile-components (len = JPXImage.nComps)
+};
+
+//------------------------------------------------------------------------
+
+struct JPXImage {
+  //----- from the SIZ segment
+  Guint xSize, ySize;          // size of reference grid
+  Guint xOffset, yOffset;      // image offset
+  Guint xTileSize, yTileSize;  // size of tiles
+  Guint xTileOffset,           // offset of first tile
+        yTileOffset;
+  Guint nComps;                        // number of components
+
+  //----- computed
+  Guint nXTiles;               // number of tiles in x direction
+  Guint nYTiles;               // number of tiles in y direction
+
+  //----- children
+  JPXTile *tiles;              // the tiles (len = nXTiles * nYTiles)
+};
+
+//------------------------------------------------------------------------
+
+class JPXStream: public FilterStream {
+public:
+
+  JPXStream(Stream *strA);
+  virtual ~JPXStream();
+  virtual StreamKind getKind() { return strJPX; }
+  virtual void reset();
+  virtual int getChar();
+  virtual int lookChar();
+  virtual GString *getPSFilter(int psLevel, char *indent);
+  virtual GBool isBinary(GBool last = gTrue);
+  virtual void getImageParams(int *bitsPerComponent,
+                             StreamColorSpaceMode *csMode);
+
+private:
+
+  void fillReadBuf();
+  void getImageParams2(int *bitsPerComponent, StreamColorSpaceMode *csMode);
+  GBool readBoxes();
+  GBool readColorSpecBox(Guint dataLen);
+  GBool readCodestream(Guint len);
+  GBool readTilePart();
+  GBool readTilePartData(Guint tileIdx,
+                        Guint tilePartLen, GBool tilePartToEOC);
+  GBool readCodeBlockData(JPXTileComp *tileComp,
+                         JPXResLevel *resLevel,
+                         JPXPrecinct *precinct,
+                         JPXSubband *subband,
+                         Guint res, Guint sb,
+                         JPXCodeBlock *cb);
+  void inverseTransform(JPXTileComp *tileComp);
+  void inverseTransformLevel(JPXTileComp *tileComp,
+                            Guint r, JPXResLevel *resLevel,
+                            Guint nx0, Guint ny0,
+                            Guint nx1, Guint ny1);
+  void inverseTransform1D(JPXTileComp *tileComp,
+                         int *data, Guint stride,
+                         Guint i0, Guint i1);
+  GBool inverseMultiCompAndDC(JPXTile *tile);
+  GBool readBoxHdr(Guint *boxType, Guint *boxLen, Guint *dataLen);
+  int readMarkerHdr(int *segType, Guint *segLen);
+  GBool readUByte(Guint *x);
+  GBool readByte(int *x);
+  GBool readUWord(Guint *x);
+  GBool readULong(Guint *x);
+  GBool readNBytes(int nBytes, GBool signd, int *x);
+  GBool readBits(int nBits, Guint *x);
+  void clearBitBuf();
+
+  Guint nComps;                        // number of components
+  Guint *bpc;                  // bits per component, for each component
+  Guint width, height;         // image size
+  GBool haveImgHdr;            // set if a JP2/JPX image header has been
+                               //   found
+  JPXColorSpec cs;             // color specification
+  GBool haveCS;                        // set if a color spec has been found
+  JPXPalette palette;          // the palette
+  GBool havePalette;           // set if a palette has been found
+  JPXCompMap compMap;          // the component mapping
+  GBool haveCompMap;           // set if a component mapping has been found
+  JPXChannelDefn channelDefn;  // channel definition
+  GBool haveChannelDefn;       // set if a channel defn has been found
+
+  JPXImage img;                        // JPEG2000 decoder data
+  Guint bitBuf;                        // buffer for bit reads
+  int bitBufLen;               // number of bits in bitBuf
+  GBool bitBufSkip;            // true if next bit should be skipped
+                               //   (for bit stuffing)
+  Guint byteCount;             // number of bytes read since last call
+                               //   to clearBitBuf
+
+  Guint curX, curY, curComp;   // current position for lookChar/getChar
+  Guint readBuf;               // read buffer
+  Guint readBufLen;            // number of valid bits in readBuf
+};
+
+#endif
diff --git a/lib/xpdf/Lexer.cc b/lib/xpdf/Lexer.cc
new file mode 100644 (file)
index 0000000..ee9dc59
--- /dev/null
@@ -0,0 +1,493 @@
+//========================================================================
+//
+// Lexer.cc
+//
+// Copyright 1996-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <stdlib.h>
+#include <stddef.h>
+#include <string.h>
+#include <ctype.h>
+#include "Lexer.h"
+#include "Error.h"
+
+//------------------------------------------------------------------------
+
+// A '1' in this array means the character is white space.  A '1' or
+// '2' means the character ends a name or command.
+static char specialChars[256] = {
+  1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0,   // 0x
+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,   // 1x
+  1, 0, 0, 0, 0, 2, 0, 0, 2, 2, 0, 0, 0, 0, 0, 2,   // 2x
+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 2, 0,   // 3x
+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,   // 4x
+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 2, 0, 0,   // 5x
+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,   // 6x
+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 2, 0, 0,   // 7x
+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,   // 8x
+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,   // 9x
+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,   // ax
+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,   // bx
+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,   // cx
+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,   // dx
+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,   // ex
+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0    // fx
+};
+
+//------------------------------------------------------------------------
+// Lexer
+//------------------------------------------------------------------------
+
+Lexer::Lexer(XRef *xref, Stream *str) {
+  Object obj;
+
+  curStr.initStream(str);
+  streams = new Array(xref);
+  streams->add(curStr.copy(&obj));
+  strPtr = 0;
+  freeArray = gTrue;
+  curStr.streamReset();
+}
+
+Lexer::Lexer(XRef *xref, Object *obj) {
+  Object obj2;
+
+  if (obj->isStream()) {
+    streams = new Array(xref);
+    freeArray = gTrue;
+    streams->add(obj->copy(&obj2));
+  } else {
+    streams = obj->getArray();
+    freeArray = gFalse;
+  }
+  strPtr = 0;
+  if (streams->getLength() > 0) {
+    streams->get(strPtr, &curStr);
+    curStr.streamReset();
+  }
+}
+static int illegalChars = 0;
+
+Lexer::~Lexer() {
+  if (!curStr.isNone()) {
+    curStr.streamClose();
+    curStr.free();
+  }
+  if (freeArray) {
+    delete streams;
+  }
+  if(illegalChars)
+      error(0, "Illegal characters in hex string (%d)", illegalChars);
+  illegalChars = 0;
+}
+
+int Lexer::getChar() {
+  int c;
+
+  c = EOF;
+  while (!curStr.isNone() && (c = curStr.streamGetChar()) == EOF) {
+    curStr.streamClose();
+    curStr.free();
+    ++strPtr;
+    if (strPtr < streams->getLength()) {
+      streams->get(strPtr, &curStr);
+      curStr.streamReset();
+    }
+  }
+  return c;
+}
+
+int Lexer::lookChar() {
+  if (curStr.isNone()) {
+    return EOF;
+  }
+  return curStr.streamLookChar();
+}
+
+Object *Lexer::getObj(Object *obj) {
+  char *p;
+  int c, c2;
+  GBool comment, neg, done;
+  int numParen;
+  int xi;
+  double xf, scale;
+  GString *s;
+  int n, m;
+
+  // skip whitespace and comments
+  comment = gFalse;
+  while (1) {
+    if ((c = getChar()) == EOF) {
+      return obj->initEOF();
+    }
+    if (comment) {
+      if (c == '\r' || c == '\n')
+       comment = gFalse;
+    } else if (c == '%') {
+      comment = gTrue;
+    } else if (specialChars[c] != 1) {
+      break;
+    }
+  }
+
+  // start reading token
+  switch (c) {
+
+  // number
+  case '0': case '1': case '2': case '3': case '4':
+  case '5': case '6': case '7': case '8': case '9':
+  case '-': case '.':
+    neg = gFalse;
+    xi = 0;
+    if (c == '-') {
+      neg = gTrue;
+    } else if (c == '.') {
+      goto doReal;
+    } else {
+      xi = c - '0';
+    }
+    while (1) {
+      c = lookChar();
+      if (isdigit(c)) {
+       getChar();
+       xi = xi * 10 + (c - '0');
+      } else if (c == '.') {
+       getChar();
+       goto doReal;
+      } else {
+       break;
+      }
+    }
+    if (neg)
+      xi = -xi;
+    obj->initInt(xi);
+    break;
+  doReal:
+    xf = xi;
+    scale = 0.1;
+    while (1) {
+      c = lookChar();
+      if (c == '-') {
+       // ignore minus signs in the middle of numbers to match
+       // Adobe's behavior
+       error(getPos(), "Badly formatted number");
+       getChar();
+       continue;
+      }
+      if (!isdigit(c)) {
+       break;
+      }
+      getChar();
+      xf = xf + scale * (c - '0');
+      scale *= 0.1;
+    }
+    if (neg)
+      xf = -xf;
+    obj->initReal(xf);
+    break;
+
+  // string
+  case '(':
+    p = tokBuf;
+    n = 0;
+    numParen = 1;
+    done = gFalse;
+    s = NULL;
+    do {
+      c2 = EOF;
+      switch (c = getChar()) {
+
+      case EOF:
+#if 0
+      // This breaks some PDF files, e.g., ones from Photoshop.
+      case '\r':
+      case '\n':
+#endif
+       error(getPos(), "Unterminated string");
+       done = gTrue;
+       break;
+
+      case '(':
+       ++numParen;
+       c2 = c;
+       break;
+
+      case ')':
+       if (--numParen == 0) {
+         done = gTrue;
+       } else {
+         c2 = c;
+       }
+       break;
+
+      case '\\':
+       switch (c = getChar()) {
+       case 'n':
+         c2 = '\n';
+         break;
+       case 'r':
+         c2 = '\r';
+         break;
+       case 't':
+         c2 = '\t';
+         break;
+       case 'b':
+         c2 = '\b';
+         break;
+       case 'f':
+         c2 = '\f';
+         break;
+       case '\\':
+       case '(':
+       case ')':
+         c2 = c;
+         break;
+       case '0': case '1': case '2': case '3':
+       case '4': case '5': case '6': case '7':
+         c2 = c - '0';
+         c = lookChar();
+         if (c >= '0' && c <= '7') {
+           getChar();
+           c2 = (c2 << 3) + (c - '0');
+           c = lookChar();
+           if (c >= '0' && c <= '7') {
+             getChar();
+             c2 = (c2 << 3) + (c - '0');
+           }
+         }
+         break;
+       case '\r':
+         c = lookChar();
+         if (c == '\n') {
+           getChar();
+         }
+         break;
+       case '\n':
+         break;
+       case EOF:
+         error(getPos(), "Unterminated string");
+         done = gTrue;
+         break;
+       default:
+         c2 = c;
+         break;
+       }
+       break;
+
+      default:
+       c2 = c;
+       break;
+      }
+
+      if (c2 != EOF) {
+       if (n == tokBufSize) {
+         if (!s)
+           s = new GString(tokBuf, tokBufSize);
+         else
+           s->append(tokBuf, tokBufSize);
+         p = tokBuf;
+         n = 0;
+       }
+       *p++ = (char)c2;
+       ++n;
+      }
+    } while (!done);
+    if (!s)
+      s = new GString(tokBuf, n);
+    else
+      s->append(tokBuf, n);
+    obj->initString(s);
+    break;
+
+  // name
+  case '/':
+    p = tokBuf;
+    n = 0;
+    while ((c = lookChar()) != EOF && !specialChars[c]) {
+      getChar();
+      if (c == '#') {
+       c2 = lookChar();
+       if (c2 >= '0' && c2 <= '9') {
+         c = c2 - '0';
+       } else if (c2 >= 'A' && c2 <= 'F') {
+         c = c2 - 'A' + 10;
+       } else if (c2 >= 'a' && c2 <= 'f') {
+         c = c2 - 'a' + 10;
+       } else {
+         goto notEscChar;
+       }
+       getChar();
+       c <<= 4;
+       c2 = getChar();
+       if (c2 >= '0' && c2 <= '9') {
+         c += c2 - '0';
+       } else if (c2 >= 'A' && c2 <= 'F') {
+         c += c2 - 'A' + 10;
+       } else if (c2 >= 'a' && c2 <= 'f') {
+         c += c2 - 'a' + 10;
+       } else {
+         illegalChars++;
+         //error(getPos(), "Illegal digit in hex char in name");
+       }
+      }
+     notEscChar:
+      if (++n == tokBufSize) {
+       error(getPos(), "Name token too long");
+       break;
+      }
+      *p++ = c;
+    }
+    *p = '\0';
+    obj->initName(tokBuf);
+    break;
+
+  // array punctuation
+  case '[':
+  case ']':
+    tokBuf[0] = c;
+    tokBuf[1] = '\0';
+    obj->initCmd(tokBuf);
+    break;
+
+  // hex string or dict punctuation
+  case '<':
+    c = lookChar();
+
+    // dict punctuation
+    if (c == '<') {
+      getChar();
+      tokBuf[0] = tokBuf[1] = '<';
+      tokBuf[2] = '\0';
+      obj->initCmd(tokBuf);
+
+    // hex string
+    } else {
+      p = tokBuf;
+      m = n = 0;
+      c2 = 0;
+      s = NULL;
+      while (1) {
+       c = getChar();
+       if (c == '>') {
+         break;
+       } else if (c == EOF) {
+         error(getPos(), "Unterminated hex string");
+         break;
+       } else if (specialChars[c] != 1) {
+         c2 = c2 << 4;
+         if (c >= '0' && c <= '9')
+           c2 += c - '0';
+         else if (c >= 'A' && c <= 'F')
+           c2 += c - 'A' + 10;
+         else if (c >= 'a' && c <= 'f')
+           c2 += c - 'a' + 10;
+         else {
+           illegalChars++;
+           //error(getPos(), "Illegal character <%02x> in hex string", c);
+         }
+         if (++m == 2) {
+           if (n == tokBufSize) {
+             if (!s)
+               s = new GString(tokBuf, tokBufSize);
+             else
+               s->append(tokBuf, tokBufSize);
+             p = tokBuf;
+             n = 0;
+           }
+           *p++ = (char)c2;
+           ++n;
+           c2 = 0;
+           m = 0;
+         }
+       }
+      }
+      if (!s)
+       s = new GString(tokBuf, n);
+      else
+       s->append(tokBuf, n);
+      if (m == 1)
+       s->append((char)(c2 << 4));
+      obj->initString(s);
+    }
+    break;
+
+  // dict punctuation
+  case '>':
+    c = lookChar();
+    if (c == '>') {
+      getChar();
+      tokBuf[0] = tokBuf[1] = '>';
+      tokBuf[2] = '\0';
+      obj->initCmd(tokBuf);
+    } else {
+      illegalChars++;
+      //error(getPos(), "Illegal character '>'");
+      obj->initError();
+    }
+    break;
+
+  // error
+  case ')':
+  case '{':
+  case '}':
+    //error(getPos(), "Illegal character '%c'", c);
+    illegalChars++;
+    obj->initError();
+    break;
+
+  // command
+  default:
+    p = tokBuf;
+    *p++ = c;
+    n = 1;
+    while ((c = lookChar()) != EOF && !specialChars[c]) {
+      getChar();
+      if (++n == tokBufSize) {
+       error(getPos(), "Command token too long");
+       break;
+      }
+      *p++ = c;
+    }
+    *p = '\0';
+    if (tokBuf[0] == 't' && !strcmp(tokBuf, "true")) {
+      obj->initBool(gTrue);
+    } else if (tokBuf[0] == 'f' && !strcmp(tokBuf, "false")) {
+      obj->initBool(gFalse);
+    } else if (tokBuf[0] == 'n' && !strcmp(tokBuf, "null")) {
+      obj->initNull();
+    } else {
+      obj->initCmd(tokBuf);
+    }
+    break;
+  }
+  return obj;
+}
+
+void Lexer::skipToNextLine() {
+  int c;
+
+  while (1) {
+    c = getChar();
+    if (c == EOF || c == '\n') {
+      return;
+    }
+    if (c == '\r') {
+      if ((c = lookChar()) == '\n') {
+       getChar();
+      }
+      return;
+    }
+  }
+}
+
+GBool Lexer::isSpace(int c) {
+  return c >= 0 && c <= 0xff && specialChars[c] == 1;
+}
diff --git a/lib/xpdf/Lexer.h b/lib/xpdf/Lexer.h
new file mode 100644 (file)
index 0000000..f6ad9ce
--- /dev/null
@@ -0,0 +1,80 @@
+//========================================================================
+//
+// Lexer.h
+//
+// Copyright 1996-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef LEXER_H
+#define LEXER_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#include "Object.h"
+#include "Stream.h"
+
+class XRef;
+
+#define tokBufSize 128         // size of token buffer
+
+//------------------------------------------------------------------------
+// Lexer
+//------------------------------------------------------------------------
+
+class Lexer {
+public:
+
+  // Construct a lexer for a single stream.  Deletes the stream when
+  // lexer is deleted.
+  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(XRef *xref, Object *obj);
+
+  // Destructor.
+  ~Lexer();
+
+  // Get the next object from the input stream.
+  Object *getObj(Object *obj);
+
+  // Skip to the beginning of the next line in the input stream.
+  void skipToNextLine();
+
+  // Skip over one character.
+  void skipChar() { getChar(); }
+
+  // Get stream.
+  Stream *getStream()
+    { return curStr.isNone() ? (Stream *)NULL : curStr.getStream(); }
+
+  // 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 : (int)curStr.streamGetPos(); }
+
+  // Set position in file.
+  void setPos(Guint pos, int dir = 0)
+    { if (!curStr.isNone()) curStr.streamSetPos(pos, dir); }
+
+  // Returns true if <c> is a whitespace character.
+  static GBool isSpace(int c);
+
+private:
+
+  int getChar();
+  int lookChar();
+
+  Array *streams;              // array of input streams
+  int strPtr;                  // index of current stream
+  Object curStr;               // current stream
+  GBool freeArray;             // should lexer free the streams array?
+  char tokBuf[tokBufSize];     // temporary token buffer
+};
+
+#endif
diff --git a/lib/xpdf/Link.cc b/lib/xpdf/Link.cc
new file mode 100644 (file)
index 0000000..422edc8
--- /dev/null
@@ -0,0 +1,905 @@
+//========================================================================
+//
+// Link.cc
+//
+// Copyright 1996-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <stddef.h>
+#include <string.h>
+#include "gmem.h"
+#include "GString.h"
+#include "Error.h"
+#include "Object.h"
+#include "Array.h"
+#include "Dict.h"
+#include "Link.h"
+
+//------------------------------------------------------------------------
+// LinkAction
+//------------------------------------------------------------------------
+
+LinkAction *LinkAction::parseDest(Object *obj) {
+  LinkAction *action;
+
+  action = new LinkGoTo(obj);
+  if (!action->isOk()) {
+    delete action;
+    return NULL;
+  }
+  return action;
+}
+
+LinkAction *LinkAction::parseAction(Object *obj, GString *baseURI) {
+  LinkAction *action;
+  Object obj2, obj3, obj4;
+
+  if (!obj->isDict()) {
+    error(-1, "Bad annotation action");
+    return NULL;
+  }
+
+  obj->dictLookup("S", &obj2);
+
+  // GoTo action
+  if (obj2.isName("GoTo")) {
+    obj->dictLookup("D", &obj3);
+    action = new LinkGoTo(&obj3);
+    obj3.free();
+
+  // GoToR action
+  } else if (obj2.isName("GoToR")) {
+    obj->dictLookup("F", &obj3);
+    obj->dictLookup("D", &obj4);
+    action = new LinkGoToR(&obj3, &obj4);
+    obj3.free();
+    obj4.free();
+
+  // Launch action
+  } else if (obj2.isName("Launch")) {
+    action = new LinkLaunch(obj);
+
+  // URI action
+  } else if (obj2.isName("URI")) {
+    obj->dictLookup("URI", &obj3);
+    action = new LinkURI(&obj3, baseURI);
+    obj3.free();
+
+  // Named action
+  } else if (obj2.isName("Named")) {
+    obj->dictLookup("N", &obj3);
+    action = new LinkNamed(&obj3);
+    obj3.free();
+
+  // Movie action
+  } else if (obj2.isName("Movie")) {
+    obj->dictLookupNF("Annot", &obj3);
+    obj->dictLookup("T", &obj4);
+    action = new LinkMovie(&obj3, &obj4);
+    obj3.free();
+    obj4.free();
+
+  // unknown action
+  } else if (obj2.isName()) {
+    action = new LinkUnknown(obj2.getName());
+
+  // action is missing or wrong type
+  } else {
+    error(-1, "Bad annotation action");
+    action = NULL;
+  }
+
+  obj2.free();
+
+  if (action && !action->isOk()) {
+    delete action;
+    return NULL;
+  }
+  return action;
+}
+
+GString *LinkAction::getFileSpecName(Object *fileSpecObj) {
+  GString *name;
+  Object obj1;
+
+  name = NULL;
+
+  // string
+  if (fileSpecObj->isString()) {
+    name = fileSpecObj->getString()->copy();
+
+  // dictionary
+  } else if (fileSpecObj->isDict()) {
+#ifdef WIN32
+    if (!fileSpecObj->dictLookup("DOS", &obj1)->isString()) {
+#else
+    if (!fileSpecObj->dictLookup("Unix", &obj1)->isString()) {
+#endif
+      obj1.free();
+      fileSpecObj->dictLookup("F", &obj1);
+    }
+    if (obj1.isString()) {
+      name = obj1.getString()->copy();
+    } else {
+      error(-1, "Illegal file spec in link");
+    }
+    obj1.free();
+
+  // error
+  } else {
+    error(-1, "Illegal file spec in link");
+  }
+
+  // system-dependent path manipulation
+  if (name) {
+#ifdef WIN32
+    int i, j;
+
+    // "//...."             --> "\...."
+    // "/x/...."            --> "x:\...."
+    // "/server/share/...." --> "\\server\share\...."
+    // convert escaped slashes to slashes and unescaped slashes to backslashes
+    i = 0;
+    if (name->getChar(0) == '/') {
+      if (name->getLength() >= 2 && name->getChar(1) == '/') {
+       name->del(0);
+       i = 0;
+      } else if (name->getLength() >= 2 &&
+                ((name->getChar(1) >= 'a' && name->getChar(1) <= 'z') ||
+                 (name->getChar(1) >= 'A' && name->getChar(1) <= 'Z')) &&
+                (name->getLength() == 2 || name->getChar(2) == '/')) {
+       name->setChar(0, name->getChar(1));
+       name->setChar(1, ':');
+       i = 2;
+      } else {
+       for (j = 2; j < name->getLength(); ++j) {
+         if (name->getChar(j-1) != '\\' &&
+             name->getChar(j) == '/') {
+           break;
+         }
+       }
+       if (j < name->getLength()) {
+         name->setChar(0, '\\');
+         name->insert(0, '\\');
+         i = 2;
+       }
+      }
+    }
+    for (; i < name->getLength(); ++i) {
+      if (name->getChar(i) == '/') {
+       name->setChar(i, '\\');
+      } else if (name->getChar(i) == '\\' &&
+                i+1 < name->getLength() &&
+                name->getChar(i+1) == '/') {
+       name->del(i);
+      }
+    }
+#else
+    // no manipulation needed for Unix
+#endif
+  }
+
+  return name;
+}
+
+//------------------------------------------------------------------------
+// LinkDest
+//------------------------------------------------------------------------
+
+LinkDest::LinkDest(Array *a) {
+  Object obj1, obj2;
+
+  // initialize fields
+  left = bottom = right = top = zoom = 0;
+  ok = gFalse;
+
+  // get page
+  if (a->getLength() < 2) {
+    error(-1, "Annotation destination array is too short");
+    return;
+  }
+  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();
+    pageIsRef = gTrue;
+  } else {
+    error(-1, "Bad annotation destination");
+    goto err2;
+  }
+  obj1.free();
+
+  // get destination type
+  a->get(1, &obj1);
+
+  // XYZ link
+  if (obj1.isName("XYZ")) {
+    kind = destXYZ;
+    if (a->getLength() < 3) {
+      changeLeft = gFalse;
+    } else {
+      a->get(2, &obj2);
+      if (obj2.isNull()) {
+       changeLeft = gFalse;
+      } else if (obj2.isNum()) {
+       changeLeft = gTrue;
+       left = obj2.getNum();
+      } else {
+       error(-1, "Bad annotation destination position");
+       goto err1;
+      }
+      obj2.free();
+    }
+    if (a->getLength() < 4) {
+      changeTop = gFalse;
+    } else {
+      a->get(3, &obj2);
+      if (obj2.isNull()) {
+       changeTop = gFalse;
+      } else if (obj2.isNum()) {
+       changeTop = gTrue;
+       top = obj2.getNum();
+      } else {
+       error(-1, "Bad annotation destination position");
+       goto err1;
+      }
+      obj2.free();
+    }
+    if (a->getLength() < 5) {
+      changeZoom = gFalse;
+    } else {
+      a->get(4, &obj2);
+      if (obj2.isNull()) {
+       changeZoom = gFalse;
+      } else if (obj2.isNum()) {
+       changeZoom = gTrue;
+       zoom = obj2.getNum();
+      } else {
+       error(-1, "Bad annotation destination position");
+       goto err1;
+      }
+      obj2.free();
+    }
+
+  // Fit link
+  } else if (obj1.isName("Fit")) {
+    if (a->getLength() < 2) {
+      error(-1, "Annotation destination array is too short");
+      goto err2;
+    }
+    kind = destFit;
+
+  // FitH link
+  } else if (obj1.isName("FitH")) {
+    if (a->getLength() < 3) {
+      error(-1, "Annotation destination array is too short");
+      goto err2;
+    }
+    kind = destFitH;
+    if (!a->get(2, &obj2)->isNum()) {
+      error(-1, "Bad annotation destination position");
+      goto err1;
+    }
+    top = obj2.getNum();
+    obj2.free();
+
+  // FitV link
+  } else if (obj1.isName("FitV")) {
+    if (a->getLength() < 3) {
+      error(-1, "Annotation destination array is too short");
+      goto err2;
+    }
+    kind = destFitV;
+    if (!a->get(2, &obj2)->isNum()) {
+      error(-1, "Bad annotation destination position");
+      goto err1;
+    }
+    left = obj2.getNum();
+    obj2.free();
+
+  // FitR link
+  } else if (obj1.isName("FitR")) {
+    if (a->getLength() < 6) {
+      error(-1, "Annotation destination array is too short");
+      goto err2;
+    }
+    kind = destFitR;
+    if (!a->get(2, &obj2)->isNum()) {
+      error(-1, "Bad annotation destination position");
+      goto err1;
+    }
+    left = obj2.getNum();
+    obj2.free();
+    if (!a->get(3, &obj2)->isNum()) {
+      error(-1, "Bad annotation destination position");
+      goto err1;
+    }
+    bottom = obj2.getNum();
+    obj2.free();
+    if (!a->get(4, &obj2)->isNum()) {
+      error(-1, "Bad annotation destination position");
+      goto err1;
+    }
+    right = obj2.getNum();
+    obj2.free();
+    if (!a->get(5, &obj2)->isNum()) {
+      error(-1, "Bad annotation destination position");
+      goto err1;
+    }
+    top = obj2.getNum();
+    obj2.free();
+
+  // FitB link
+  } else if (obj1.isName("FitB")) {
+    if (a->getLength() < 2) {
+      error(-1, "Annotation destination array is too short");
+      goto err2;
+    }
+    kind = destFitB;
+
+  // FitBH link
+  } else if (obj1.isName("FitBH")) {
+    if (a->getLength() < 3) {
+      error(-1, "Annotation destination array is too short");
+      goto err2;
+    }
+    kind = destFitBH;
+    if (!a->get(2, &obj2)->isNum()) {
+      error(-1, "Bad annotation destination position");
+      goto err1;
+    }
+    top = obj2.getNum();
+    obj2.free();
+
+  // FitBV link
+  } else if (obj1.isName("FitBV")) {
+    if (a->getLength() < 3) {
+      error(-1, "Annotation destination array is too short");
+      goto err2;
+    }
+    kind = destFitBV;
+    if (!a->get(2, &obj2)->isNum()) {
+      error(-1, "Bad annotation destination position");
+      goto err1;
+    }
+    left = obj2.getNum();
+    obj2.free();
+
+  // unknown link kind
+  } else {
+    error(-1, "Unknown annotation destination type");
+    goto err2;
+  }
+
+  obj1.free();
+  ok = gTrue;
+  return;
+
+ err1:
+  obj2.free();
+ err2:
+  obj1.free();
+}
+
+LinkDest::LinkDest(LinkDest *dest) {
+  kind = dest->kind;
+  pageIsRef = dest->pageIsRef;
+  if (pageIsRef)
+    pageRef = dest->pageRef;
+  else
+    pageNum = dest->pageNum;
+  left = dest->left;
+  bottom = dest->bottom;
+  right = dest->right;
+  top = dest->top;
+  zoom = dest->zoom;
+  changeLeft = dest->changeLeft;
+  changeTop = dest->changeTop;
+  changeZoom = dest->changeZoom;
+  ok = gTrue;
+}
+
+//------------------------------------------------------------------------
+// LinkGoTo
+//------------------------------------------------------------------------
+
+LinkGoTo::LinkGoTo(Object *destObj) {
+  dest = NULL;
+  namedDest = NULL;
+
+  // named destination
+  if (destObj->isName()) {
+    namedDest = new GString(destObj->getName());
+  } else if (destObj->isString()) {
+    namedDest = destObj->getString()->copy();
+
+  // destination dictionary
+  } else if (destObj->isArray()) {
+    dest = new LinkDest(destObj->getArray());
+    if (!dest->isOk()) {
+      delete dest;
+      dest = NULL;
+    }
+  // error
+  } else {
+    error(-1, "Illegal annotation destination %d", destObj->getType());
+  }
+}
+
+LinkGoTo::~LinkGoTo() {
+  if (dest)
+    delete dest;
+  if (namedDest)
+    delete namedDest;
+}
+
+//------------------------------------------------------------------------
+// LinkGoToR
+//------------------------------------------------------------------------
+
+LinkGoToR::LinkGoToR(Object *fileSpecObj, Object *destObj) {
+  dest = NULL;
+  namedDest = NULL;
+
+  // get file name
+  fileName = getFileSpecName(fileSpecObj);
+
+  // named destination
+  if (destObj->isName()) {
+    namedDest = new GString(destObj->getName());
+  } else if (destObj->isString()) {
+    namedDest = destObj->getString()->copy();
+
+  // destination dictionary
+  } else if (destObj->isArray()) {
+    dest = new LinkDest(destObj->getArray());
+    if (!dest->isOk()) {
+      delete dest;
+      dest = NULL;
+    }
+  // error
+  } else {
+    error(-1, "Illegal annotation destination %d", destObj->getType());
+  }
+}
+
+LinkGoToR::~LinkGoToR() {
+  if (fileName)
+    delete fileName;
+  if (dest)
+    delete dest;
+  if (namedDest)
+    delete namedDest;
+}
+
+
+//------------------------------------------------------------------------
+// LinkLaunch
+//------------------------------------------------------------------------
+
+LinkLaunch::LinkLaunch(Object *actionObj) {
+  Object obj1, obj2;
+
+  fileName = NULL;
+  params = NULL;
+
+  if (actionObj->isDict()) {
+    if (!actionObj->dictLookup("F", &obj1)->isNull()) {
+      fileName = getFileSpecName(&obj1);
+    } else {
+      obj1.free();
+#ifdef WIN32
+      if (actionObj->dictLookup("Win", &obj1)->isDict()) {
+       obj1.dictLookup("F", &obj2);
+       fileName = getFileSpecName(&obj2);
+       obj2.free();
+       if (obj1.dictLookup("P", &obj2)->isString()) {
+         params = obj2.getString()->copy();
+       }
+       obj2.free();
+      } else {
+       error(-1, "Bad launch-type link action");
+      }
+#else
+      //~ This hasn't been defined by Adobe yet, so assume it looks
+      //~ just like the Win dictionary until they say otherwise.
+      if (actionObj->dictLookup("Unix", &obj1)->isDict()) {
+       obj1.dictLookup("F", &obj2);
+       fileName = getFileSpecName(&obj2);
+       obj2.free();
+       if (obj1.dictLookup("P", &obj2)->isString()) {
+         params = obj2.getString()->copy();
+       }
+       obj2.free();
+      } else {
+       error(-1, "Bad launch-type link action");
+      }
+#endif
+    }
+    obj1.free();
+  }
+}
+
+LinkLaunch::~LinkLaunch() {
+  if (fileName)
+    delete fileName;
+  if (params)
+    delete params;
+}
+
+//------------------------------------------------------------------------
+// LinkURI
+//------------------------------------------------------------------------
+
+LinkURI::LinkURI(Object *uriObj, GString *baseURI) {
+  GString *uri2;
+  int n;
+  char c;
+
+  uri = NULL;
+  if (uriObj->isString()) {
+    uri2 = uriObj->getString()->copy();
+    if (baseURI && baseURI->getLength() > 0) {
+      n = strcspn(uri2->getCString(), "/:");
+      if (n == uri2->getLength() || uri2->getChar(n) == '/') {
+       uri = baseURI->copy();
+       c = uri->getChar(uri->getLength() - 1);
+       if (c == '/' || c == '?') {
+         if (uri2->getChar(0) == '/') {
+           uri2->del(0);
+         }
+       } else {
+         if (uri2->getChar(0) != '/') {
+           uri->append('/');
+         }
+       }
+       uri->append(uri2);
+       delete uri2;
+      } else {
+       uri = uri2;
+      }
+    } else {
+      uri = uri2;
+    }
+  } else {
+    error(-1, "Illegal URI-type link");
+  }
+}
+
+LinkURI::~LinkURI() {
+  if (uri)
+    delete uri;
+}
+
+//------------------------------------------------------------------------
+// LinkNamed
+//------------------------------------------------------------------------
+
+LinkNamed::LinkNamed(Object *nameObj) {
+  name = NULL;
+  if (nameObj->isName()) {
+    name = new GString(nameObj->getName());
+  }
+}
+
+LinkNamed::~LinkNamed() {
+  if (name) {
+    delete name;
+  }
+}
+
+//------------------------------------------------------------------------
+// LinkMovie
+//------------------------------------------------------------------------
+
+LinkMovie::LinkMovie(Object *annotObj, Object *titleObj) {
+  annotRef.num = -1;
+  title = NULL;
+  if (annotObj->isRef()) {
+    annotRef = annotObj->getRef();
+  } else if (titleObj->isString()) {
+    title = titleObj->getString()->copy();
+  } else {
+    error(-1, "Movie action is missing both the Annot and T keys");
+  }
+}
+
+LinkMovie::~LinkMovie() {
+  if (title) {
+    delete title;
+  }
+}
+
+//------------------------------------------------------------------------
+// LinkUnknown
+//------------------------------------------------------------------------
+
+LinkUnknown::LinkUnknown(char *actionA) {
+  action = new GString(actionA);
+}
+
+LinkUnknown::~LinkUnknown() {
+  delete action;
+}
+
+//------------------------------------------------------------------------
+// LinkBorderStyle
+//------------------------------------------------------------------------
+
+LinkBorderStyle::LinkBorderStyle(LinkBorderType typeA, double widthA,
+                                double *dashA, int dashLengthA,
+                                double rA, double gA, double bA) {
+  type = typeA;
+  width = widthA;
+  dash = dashA;
+  dashLength = dashLengthA;
+  r = rA;
+  g = gA;
+  b = bA;
+}
+
+LinkBorderStyle::~LinkBorderStyle() {
+  if (dash) {
+    gfree(dash);
+  }
+}
+
+//------------------------------------------------------------------------
+// Link
+//------------------------------------------------------------------------
+
+Link::Link(Dict *dict, GString *baseURI) {
+  Object obj1, obj2, obj3;
+  LinkBorderType borderType;
+  double borderWidth;
+  double *borderDash;
+  int borderDashLength;
+  double borderR, borderG, borderB;
+  double t;
+  int i;
+
+  borderStyle = NULL;
+  action = NULL;
+  ok = gFalse;
+
+  // get rectangle
+  if (!dict->lookup("Rect", &obj1)->isArray()) {
+    error(-1, "Annotation rectangle is wrong type");
+    goto err2;
+  }
+  if (!obj1.arrayGet(0, &obj2)->isNum()) {
+    error(-1, "Bad annotation rectangle");
+    goto err1;
+  }
+  x1 = obj2.getNum();
+  obj2.free();
+  if (!obj1.arrayGet(1, &obj2)->isNum()) {
+    error(-1, "Bad annotation rectangle");
+    goto err1;
+  }
+  y1 = obj2.getNum();
+  obj2.free();
+  if (!obj1.arrayGet(2, &obj2)->isNum()) {
+    error(-1, "Bad annotation rectangle");
+    goto err1;
+  }
+  x2 = obj2.getNum();
+  obj2.free();
+  if (!obj1.arrayGet(3, &obj2)->isNum()) {
+    error(-1, "Bad annotation rectangle");
+    goto err1;
+  }
+  y2 = obj2.getNum();
+  obj2.free();
+  obj1.free();
+  if (x1 > x2) {
+    t = x1;
+    x1 = x2;
+    x2 = t;
+  }
+  if (y1 > y2) {
+    t = y1;
+    y1 = y2;
+    y2 = t;
+  }
+
+  // get the border style info
+  borderType = linkBorderSolid;
+  borderWidth = 1;
+  borderDash = NULL;
+  borderDashLength = 0;
+  borderR = 0;
+  borderG = 0;
+  borderB = 1;
+  if (dict->lookup("BS", &obj1)->isDict()) {
+    if (obj1.dictLookup("S", &obj2)->isName()) {
+      if (obj2.isName("S")) {
+       borderType = linkBorderSolid;
+      } else if (obj2.isName("D")) {
+       borderType = linkBorderDashed;
+      } else if (obj2.isName("B")) {
+       borderType = linkBorderEmbossed;
+      } else if (obj2.isName("I")) {
+       borderType = linkBorderEngraved;
+      } else if (obj2.isName("U")) {
+       borderType = linkBorderUnderlined;
+      }
+    }
+    obj2.free();
+    if (obj1.dictLookup("W", &obj2)->isNum()) {
+      borderWidth = obj2.getNum();
+    }
+    obj2.free();
+    if (obj1.dictLookup("D", &obj2)->isArray()) {
+      borderDashLength = obj2.arrayGetLength();
+      borderDash = (double *)gmallocn(borderDashLength, sizeof(double));
+      for (i = 0; i < borderDashLength; ++i) {
+       if (obj2.arrayGet(i, &obj3)->isNum()) {
+         borderDash[i] = obj3.getNum();
+       } else {
+         borderDash[i] = 1;
+       }
+       obj3.free();
+      }
+    }
+    obj2.free();
+  } else {
+    obj1.free();
+    if (dict->lookup("Border", &obj1)->isArray()) {
+      if (obj1.arrayGetLength() >= 3) {
+       if (obj1.arrayGet(2, &obj2)->isNum()) {
+         borderWidth = obj2.getNum();
+       }
+       obj2.free();
+       if (obj1.arrayGetLength() >= 4) {
+         if (obj1.arrayGet(3, &obj2)->isArray()) {
+           borderType = linkBorderDashed;
+           borderDashLength = obj2.arrayGetLength();
+           borderDash = (double *)gmallocn(borderDashLength, sizeof(double));
+           for (i = 0; i < borderDashLength; ++i) {
+             if (obj2.arrayGet(i, &obj3)->isNum()) {
+               borderDash[i] = obj3.getNum();
+             } else {
+               borderDash[i] = 1;
+             }
+             obj3.free();
+           }
+         } else {
+           // Adobe draws no border at all if the last element is of
+           // the wrong type.
+           borderWidth = 0;
+         }
+         obj2.free();
+       }
+      }
+    }
+  }
+  obj1.free();
+  if (dict->lookup("C", &obj1)->isArray() && obj1.arrayGetLength() == 3) {
+    if (obj1.arrayGet(0, &obj2)->isNum()) {
+      borderR = obj2.getNum();
+    }
+    obj1.free();
+    if (obj1.arrayGet(1, &obj2)->isNum()) {
+      borderG = obj2.getNum();
+    }
+    obj1.free();
+    if (obj1.arrayGet(2, &obj2)->isNum()) {
+      borderB = obj2.getNum();
+    }
+    obj1.free();
+  }
+  obj1.free();
+  borderStyle = new LinkBorderStyle(borderType, borderWidth,
+                                   borderDash, borderDashLength,
+                                   borderR, borderG, borderB);
+
+  // look for destination
+  if (!dict->lookup("Dest", &obj1)->isNull()) {
+    action = LinkAction::parseDest(&obj1);
+
+  // look for action
+  } else {
+    obj1.free();
+    if (dict->lookup("A", &obj1)->isDict()) {
+      action = LinkAction::parseAction(&obj1, baseURI);
+    }
+  }
+  obj1.free();
+
+  // check for bad action
+  if (action) {
+    ok = gTrue;
+  }
+
+  return;
+
+ err1:
+  obj2.free();
+ err2:
+  obj1.free();
+}
+
+Link::~Link() {
+  if (borderStyle) {
+    delete borderStyle;
+  }
+  if (action) {
+    delete action;
+  }
+}
+
+//------------------------------------------------------------------------
+// Links
+//------------------------------------------------------------------------
+
+Links::Links(Object *annots, GString *baseURI) {
+  Link *link;
+  Object obj1, obj2;
+  int size;
+  int i;
+
+  links = NULL;
+  size = 0;
+  numLinks = 0;
+
+  if (annots->isArray()) {
+    for (i = 0; i < annots->arrayGetLength(); ++i) {
+      if (annots->arrayGet(i, &obj1)->isDict()) {
+       if (obj1.dictLookup("Subtype", &obj2)->isName("Link")) {
+         link = new Link(obj1.getDict(), baseURI);
+         if (link->isOk()) {
+           if (numLinks >= size) {
+             size += 16;
+             links = (Link **)greallocn(links, size, sizeof(Link *));
+           }
+           links[numLinks++] = link;
+         } else {
+           delete link;
+         }
+       }
+       obj2.free();
+      }
+      obj1.free();
+    }
+  }
+}
+
+Links::~Links() {
+  int i;
+
+  for (i = 0; i < numLinks; ++i)
+    delete links[i];
+  gfree(links);
+}
+
+LinkAction *Links::find(double x, double y) {
+  int i;
+
+  for (i = numLinks - 1; i >= 0; --i) {
+    if (links[i]->inRect(x, y)) {
+      return links[i]->getAction();
+    }
+  }
+  return NULL;
+}
+
+GBool Links::onLink(double x, double y) {
+  int i;
+
+  for (i = 0; i < numLinks; ++i) {
+    if (links[i]->inRect(x, y))
+      return gTrue;
+  }
+  return gFalse;
+}
diff --git a/lib/xpdf/Link.h b/lib/xpdf/Link.h
new file mode 100644 (file)
index 0000000..9f04420
--- /dev/null
@@ -0,0 +1,409 @@
+//========================================================================
+//
+// Link.h
+//
+// Copyright 1996-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef LINK_H
+#define LINK_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#include "Object.h"
+
+class GString;
+class Array;
+class Dict;
+
+//------------------------------------------------------------------------
+// LinkAction
+//------------------------------------------------------------------------
+
+enum LinkActionKind {
+  actionGoTo,                  // go to destination
+  actionGoToR,                 // go to destination in new file
+  actionLaunch,                        // launch app (or open document)
+  actionURI,                   // URI
+  actionNamed,                 // named action
+  actionMovie,                 // movie action
+  actionUnknown                        // anything else
+};
+
+class LinkAction {
+public:
+
+  // Destructor.
+  virtual ~LinkAction() {}
+
+  // Was the LinkAction created successfully?
+  virtual GBool isOk() = 0;
+
+  // Check link action type.
+  virtual LinkActionKind getKind() = 0;
+
+  // Parse a destination (old-style action) name, string, or array.
+  static LinkAction *parseDest(Object *obj);
+
+  // Parse an action dictionary.
+  static LinkAction *parseAction(Object *obj, GString *baseURI = NULL);
+
+  // Extract a file name from a file specification (string or
+  // dictionary).
+  static GString *getFileSpecName(Object *fileSpecObj);
+};
+
+//------------------------------------------------------------------------
+// LinkDest
+//------------------------------------------------------------------------
+
+enum LinkDestKind {
+  destXYZ,
+  destFit,
+  destFitH,
+  destFitV,
+  destFitR,
+  destFitB,
+  destFitBH,
+  destFitBV
+};
+
+class LinkDest {
+public:
+
+  // Build a LinkDest from the array.
+  LinkDest(Array *a);
+
+  // Copy a LinkDest.
+  LinkDest *copy() { return new LinkDest(this); }
+
+  // Was the LinkDest created successfully?
+  GBool isOk() { return ok; }
+
+  // Accessors.
+  LinkDestKind getKind() { return kind; }
+  GBool isPageRef() { return pageIsRef; }
+  int getPageNum() { return pageNum; }
+  Ref getPageRef() { return pageRef; }
+  double getLeft() { return left; }
+  double getBottom() { return bottom; }
+  double getRight() { return right; }
+  double getTop() { return top; }
+  double getZoom() { return zoom; }
+  GBool getChangeLeft() { return changeLeft; }
+  GBool getChangeTop() { return changeTop; }
+  GBool getChangeZoom() { return changeZoom; }
+
+private:
+
+  LinkDestKind kind;           // destination type
+  GBool pageIsRef;             // is the page a reference or number?
+  union {
+    Ref pageRef;               // reference to page
+    int pageNum;               // one-relative page number
+  };
+  double left, bottom;         // position
+  double right, top;
+  double zoom;                 // zoom factor
+  GBool changeLeft, changeTop; // for destXYZ links, which position
+  GBool changeZoom;            //   components to change
+  GBool ok;                    // set if created successfully
+
+  LinkDest(LinkDest *dest);
+};
+
+//------------------------------------------------------------------------
+// LinkGoTo
+//------------------------------------------------------------------------
+
+class LinkGoTo: public LinkAction {
+public:
+
+  // Build a LinkGoTo from a destination (dictionary, name, or string).
+  LinkGoTo(Object *destObj);
+
+  // Destructor.
+  virtual ~LinkGoTo();
+
+  // Was the LinkGoTo created successfully?
+  virtual GBool isOk() { return dest || namedDest; }
+
+  // Accessors.
+  virtual LinkActionKind getKind() { return actionGoTo; }
+  LinkDest *getDest() { return dest; }
+  GString *getNamedDest() { return namedDest; }
+
+private:
+
+  LinkDest *dest;              // regular destination (NULL for remote
+                               //   link with bad destination)
+  GString *namedDest;          // named destination (only one of dest and
+                               //   and namedDest may be non-NULL)
+};
+
+//------------------------------------------------------------------------
+// LinkGoToR
+//------------------------------------------------------------------------
+
+class LinkGoToR: public LinkAction {
+public:
+
+  // Build a LinkGoToR from a file spec (dictionary) and destination
+  // (dictionary, name, or string).
+  LinkGoToR(Object *fileSpecObj, Object *destObj);
+
+  // Destructor.
+  virtual ~LinkGoToR();
+
+  // Was the LinkGoToR created successfully?
+  virtual GBool isOk() { return fileName && (dest || namedDest); }
+
+  // Accessors.
+  virtual LinkActionKind getKind() { return actionGoToR; }
+  GString *getFileName() { return fileName; }
+  LinkDest *getDest() { return dest; }
+  GString *getNamedDest() { return namedDest; }
+
+private:
+
+  GString *fileName;           // file name
+  LinkDest *dest;              // regular destination (NULL for remote
+                               //   link with bad destination)
+  GString *namedDest;          // named destination (only one of dest and
+                               //   and namedDest may be non-NULL)
+};
+
+//------------------------------------------------------------------------
+// LinkLaunch
+//------------------------------------------------------------------------
+
+class LinkLaunch: public LinkAction {
+public:
+
+  // Build a LinkLaunch from an action dictionary.
+  LinkLaunch(Object *actionObj);
+
+  // Destructor.
+  virtual ~LinkLaunch();
+
+  // Was the LinkLaunch created successfully?
+  virtual GBool isOk() { return fileName != NULL; }
+
+  // Accessors.
+  virtual LinkActionKind getKind() { return actionLaunch; }
+  GString *getFileName() { return fileName; }
+  GString *getParams() { return params; }
+
+private:
+
+  GString *fileName;           // file name
+  GString *params;             // parameters
+};
+
+//------------------------------------------------------------------------
+// LinkURI
+//------------------------------------------------------------------------
+
+class LinkURI: public LinkAction {
+public:
+
+  // Build a LinkURI given the URI (string) and base URI.
+  LinkURI(Object *uriObj, GString *baseURI);
+
+  // Destructor.
+  virtual ~LinkURI();
+
+  // Was the LinkURI created successfully?
+  virtual GBool isOk() { return uri != NULL; }
+
+  // Accessors.
+  virtual LinkActionKind getKind() { return actionURI; }
+  GString *getURI() { return uri; }
+
+private:
+
+  GString *uri;                        // the URI
+};
+
+//------------------------------------------------------------------------
+// LinkNamed
+//------------------------------------------------------------------------
+
+class LinkNamed: public LinkAction {
+public:
+
+  // Build a LinkNamed given the action name.
+  LinkNamed(Object *nameObj);
+
+  virtual ~LinkNamed();
+
+  virtual GBool isOk() { return name != NULL; }
+
+  virtual LinkActionKind getKind() { return actionNamed; }
+  GString *getName() { return name; }
+
+private:
+
+  GString *name;
+};
+
+//------------------------------------------------------------------------
+// LinkMovie
+//------------------------------------------------------------------------
+
+class LinkMovie: public LinkAction {
+public:
+
+  LinkMovie(Object *annotObj, Object *titleObj);
+
+  virtual ~LinkMovie();
+
+  virtual GBool isOk() { return annotRef.num >= 0 || title != NULL; }
+
+  virtual LinkActionKind getKind() { return actionMovie; }
+  GBool hasAnnotRef() { return annotRef.num >= 0; }
+  Ref *getAnnotRef() { return &annotRef; }
+  GString *getTitle() { return title; }
+
+private:
+
+  Ref annotRef;
+  GString *title;
+};
+
+//------------------------------------------------------------------------
+// LinkUnknown
+//------------------------------------------------------------------------
+
+class LinkUnknown: public LinkAction {
+public:
+
+  // Build a LinkUnknown with the specified action type.
+  LinkUnknown(char *actionA);
+
+  // Destructor.
+  virtual ~LinkUnknown();
+
+  // Was the LinkUnknown create successfully?
+  virtual GBool isOk() { return action != NULL; }
+
+  // Accessors.
+  virtual LinkActionKind getKind() { return actionUnknown; }
+  GString *getAction() { return action; }
+
+private:
+
+  GString *action;             // action subtype
+};
+
+//------------------------------------------------------------------------
+// LinkBorderStyle
+//------------------------------------------------------------------------
+
+enum LinkBorderType {
+  linkBorderSolid,
+  linkBorderDashed,
+  linkBorderEmbossed,
+  linkBorderEngraved,
+  linkBorderUnderlined
+};
+
+class LinkBorderStyle {
+public:
+
+  LinkBorderStyle(LinkBorderType typeA, double widthA,
+                 double *dashA, int dashLengthA,
+                 double rA, double gA, double bA);
+  ~LinkBorderStyle();
+
+  LinkBorderType getType() { return type; }
+  double getWidth() { return width; }
+  void getDash(double **dashA, int *dashLengthA)
+    { *dashA = dash; *dashLengthA = dashLength; }
+  void getColor(double *rA, double *gA, double *bA)
+    { *rA = r; *gA = g; *bA = b; }
+
+private:
+
+  LinkBorderType type;
+  double width;
+  double *dash;
+  int dashLength;
+  double r, g, b;
+};
+
+//------------------------------------------------------------------------
+// Link
+//------------------------------------------------------------------------
+
+class Link {
+public:
+
+  // Construct a link, given its dictionary.
+  Link(Dict *dict, GString *baseURI);
+
+  // Destructor.
+  ~Link();
+
+  // Was the link created successfully?
+  GBool isOk() { return ok; }
+
+  // Check if point is inside the link rectangle.
+  GBool inRect(double x, double y)
+    { return x1 <= x && x <= x2 && y1 <= y && y <= y2; }
+
+  // Get action.
+  LinkAction *getAction() { return action; }
+
+  // Get the link rectangle.
+  void getRect(double *xa1, double *ya1, double *xa2, double *ya2)
+    { *xa1 = x1; *ya1 = y1; *xa2 = x2; *ya2 = y2; }
+
+  // Get the border style info.
+  LinkBorderStyle *getBorderStyle() { return borderStyle; }
+
+private:
+
+  double x1, y1;               // lower left corner
+  double x2, y2;               // upper right corner
+  LinkBorderStyle *borderStyle;        // border style
+  LinkAction *action;          // action
+  GBool ok;                    // is link valid?
+};
+
+//------------------------------------------------------------------------
+// Links
+//------------------------------------------------------------------------
+
+class Links {
+public:
+
+  // Extract links from array of annotations.
+  Links(Object *annots, GString *baseURI);
+
+  // Destructor.
+  ~Links();
+
+  // Iterate through list of links.
+  int getNumLinks() { return numLinks; }
+  Link *getLink(int i) { return links[i]; }
+
+  // If point <x>,<y> is in a link, return the associated action;
+  // else return NULL.
+  LinkAction *find(double x, double y);
+
+  // Return true if <x>,<y> is in a link.
+  GBool onLink(double x, double y);
+
+private:
+
+  Link **links;
+  int numLinks;
+};
+
+#endif
diff --git a/lib/xpdf/Makefile b/lib/xpdf/Makefile
new file mode 100644 (file)
index 0000000..79f236c
--- /dev/null
@@ -0,0 +1,126 @@
+# Generated automatically from Makefile.in by configure.
+top_builddir = ../..
+srcdir = .
+top_srcdir = ../..
+include ../../Makefile.common
+
+all: ../libpdf$(A)
+
+libpdf_objects =  GHash.$(O) GList.$(O) GString.$(O) gmem.$(O) gfile.$(O) \
+                 FoFiTrueType.$(O) FoFiType1.$(O) FoFiType1C.$(O) FoFiBase.$(O) FoFiEncodings.$(O) \
+                 OutputDev.$(O) PDFDoc.$(O) Error.$(O) Stream.$(O) Object.$(O) \
+                 Decrypt.$(O) Array.$(O) XRef.$(O) Dict.$(O) Parser.$(O) \
+                 Lexer.$(O) Outline.$(O) PDFDocEncoding.$(O) Catalog.$(O) \
+                 Link.$(O) GlobalParams.$(O) JBIG2Stream.$(O) Page.$(O) JPXStream.$(O) \
+                 JArithmeticDecoder.$(O) Gfx.$(O) GfxFont.$(O) CMap.$(O) CharCodeToUnicode.$(O) \
+                 PSTokenizer.$(O) FontEncodingTables.$(O) BuiltinFont.$(O) BuiltinFontTables.$(O) \
+                 GfxState.$(O) Function.$(O) Annot.$(O) NameToCharCode.$(O) UnicodeMap.$(O) cmyk.$(O) \
+                 SecurityHandler.$(O) GFXOutputDev.$(O) InfoOutputDev.$(O) pdf.$(O)
+
+pdf.$(O): pdf.cc
+       $(CC) -I./ pdf.cc -o $@
+cmyk.$(O): cmyk.cc
+       $(CC) -I./ cmyk.cc -o $@
+UnicodeMap.$(O): UnicodeMap.cc
+       $(CC) -I./ UnicodeMap.cc -o $@
+InfoOutputDev.$(O): InfoOutputDev.cc
+       $(CC) -I./ InfoOutputDev.cc -o $@
+GFXOutputDev.$(O): GFXOutputDev.cc
+       $(CC) -I./ GFXOutputDev.cc -o $@
+NameToCharCode.$(O): NameToCharCode.cc
+       $(CC) -I./ NameToCharCode.cc -o $@
+Annot.$(O): Annot.cc
+       $(CC) -I./ Annot.cc -o $@
+Function.$(O): Function.cc
+       $(CC) -I./ Function.cc -o $@
+BuiltinFontTables.$(O): BuiltinFontTables.cc
+       $(CC) -I./ BuiltinFontTables.cc -o $@
+BuiltinFont.$(O): BuiltinFont.cc
+       $(CC) -I./ BuiltinFont.cc -o $@
+FontEncodingTables.$(O): FontEncodingTables.cc
+       $(CC) -I./ FontEncodingTables.cc -o $@
+PSTokenizer.$(O): PSTokenizer.cc
+       $(CC) -I./ PSTokenizer.cc -o $@
+CharCodeToUnicode.$(O): CharCodeToUnicode.cc
+       $(CC) -I./ CharCodeToUnicode.cc -o $@
+CMap.$(O): CMap.cc
+       $(CC) -I./ CMap.cc -o $@
+GfxFont.$(O): GfxFont.cc
+       $(CC) -I./ GfxFont.cc -o $@
+Gfx.$(O): Gfx.cc
+       $(CC) -I./ Gfx.cc -o $@
+GfxState.$(O): GfxState.cc
+       $(CC) -I./ GfxState.cc -o $@
+JArithmeticDecoder.$(O): JArithmeticDecoder.cc
+       $(CC) -I./ JArithmeticDecoder.cc -o $@
+JPXStream.$(O): JPXStream.cc
+       $(CC) -I./ JPXStream.cc -o $@
+GlobalParams.$(O): GlobalParams.cc GlobalParams.h
+       $(CC) -I./ GlobalParams.cc -o $@
+JBIG2Stream.$(O): JBIG2Stream.cc
+       $(CC) -I./ JBIG2Stream.cc -o $@
+Page.$(O): Page.cc
+       $(CC) -I./ Page.cc -o $@
+Link.$(O): Link.cc
+       $(CC) -I./ Link.cc -o $@
+Catalog.$(O): Catalog.cc
+       $(CC) -I./ Catalog.cc -o $@
+PDFDocEncoding.$(O): PDFDocEncoding.cc
+       $(CC) -I./ PDFDocEncoding.cc -o $@
+Outline.$(O): Outline.cc
+       $(CC) -I./ Outline.cc -o $@
+Lexer.$(O): Lexer.cc
+       $(CC) -I./ Lexer.cc -o $@
+Parser.$(O): Parser.cc
+       $(CC) -I./ Parser.cc -o $@
+XRef.$(O): XRef.cc
+       $(CC) -I./ XRef.cc -o $@
+Array.$(O): Array.cc
+       $(CC) -I./ Array.cc -o $@
+Dict.$(O): Dict.cc
+       $(CC) -I./ Dict.cc -o $@
+Decrypt.$(O): Decrypt.cc
+       $(CC) -I./ Decrypt.cc -o $@
+Object.$(O): Object.cc
+       $(CC) -I./ Object.cc -o $@
+Error.$(O): Error.cc aconf.h
+       $(CC) -I./ Error.cc -o $@
+Stream.$(O): Stream.cc
+       $(CC) -I./ Stream.cc -o $@
+PDFDoc.$(O): PDFDoc.cc
+       $(CC) -I./ PDFDoc.cc -o $@
+SecurityHandler.$(O): SecurityHandler.cc SecurityHandler.h
+       $(CC) -I./ SecurityHandler.cc -o $@
+OutputDev.$(O): OutputDev.cc GfxState.h Stream.h Object.h OutputDev.h gtypes.h CharTypes.h
+       $(CC) -I./ OutputDev.cc -o $@
+FoFiBase.$(O): FoFiBase.cc
+       $(CC) -I./ FoFiBase.cc -o $@
+FoFiTrueType.$(O): FoFiTrueType.cc FoFiBase.h
+       $(CC) -I./ FoFiTrueType.cc -o $@
+FoFiEncodings.$(O): FoFiEncodings.cc FoFiEncodings.h
+       $(CC) -I./ FoFiEncodings.cc -o $@
+FoFiType1C.$(O): FoFiType1C.cc FoFiBase.h
+       $(CC) -I./ FoFiType1C.cc -o $@
+FoFiType1.$(O): FoFiType1.cc FoFiBase.h
+       $(CC) -I./ FoFiType1.cc -o $@
+GList.$(O): GList.cc
+       $(CC) -I./ GList.cc -o $@
+GString.$(O): GString.cc
+       $(CC) -I./ GString.cc -o $@
+GHash.$(O): GHash.cc
+       $(CC) -I./ GHash.cc -o $@
+gfile.$(O): gfile.cc
+       $(CC) -I./ gfile.cc -o $@
+gmem.$(O): gmem.c
+       $(C) -I./ gmem.c -o $@
+
+../libpdf$(A): $(libpdf_objects)
+       $(AR) r ../libpdf$(A) $(libpdf_objects)
+       $(RANLIB) ../libpdf$(A)
+
+install:
+uninstall:
+
+clean: 
+       rm -f *.o *.obj *.lo *.a *.lib *.la gmon.out
+
diff --git a/lib/xpdf/NameToCharCode.cc b/lib/xpdf/NameToCharCode.cc
new file mode 100644 (file)
index 0000000..7ebf4e1
--- /dev/null
@@ -0,0 +1,116 @@
+//========================================================================
+//
+// NameToCharCode.cc
+//
+// Copyright 2001-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <string.h>
+#include "gmem.h"
+#include "NameToCharCode.h"
+
+//------------------------------------------------------------------------
+
+struct NameToCharCodeEntry {
+  char *name;
+  CharCode c;
+};
+
+//------------------------------------------------------------------------
+
+NameToCharCode::NameToCharCode() {
+  int i;
+
+  size = 31;
+  len = 0;
+  tab = (NameToCharCodeEntry *)gmallocn(size, sizeof(NameToCharCodeEntry));
+  for (i = 0; i < size; ++i) {
+    tab[i].name = NULL;
+  }
+}
+
+NameToCharCode::~NameToCharCode() {
+  int i;
+
+  for (i = 0; i < size; ++i) {
+    if (tab[i].name) {
+      gfree(tab[i].name);
+    }
+  }
+  gfree(tab);
+}
+
+void NameToCharCode::add(char *name, CharCode c) {
+  NameToCharCodeEntry *oldTab;
+  int h, i, oldSize;
+
+  // expand the table if necessary
+  if (len >= size / 2) {
+    oldSize = size;
+    oldTab = tab;
+    size = 2*size + 1;
+    tab = (NameToCharCodeEntry *)gmallocn(size, sizeof(NameToCharCodeEntry));
+    for (h = 0; h < size; ++h) {
+      tab[h].name = NULL;
+    }
+    for (i = 0; i < oldSize; ++i) {
+      if (oldTab[i].name) {
+       h = hash(oldTab[i].name);
+       while (tab[h].name) {
+         if (++h == size) {
+           h = 0;
+         }
+       }
+       tab[h] = oldTab[i];
+      }
+    }
+    gfree(oldTab);
+  }
+
+  // add the new name
+  h = hash(name);
+  while (tab[h].name && strcmp(tab[h].name, name)) {
+    if (++h == size) {
+      h = 0;
+    }
+  }
+  if (!tab[h].name) {
+    tab[h].name = copyString(name);
+  }
+  tab[h].c = c;
+
+  ++len;
+}
+
+CharCode NameToCharCode::lookup(char *name) {
+  int h;
+
+  h = hash(name);
+  while (tab[h].name) {
+    if (!strcmp(tab[h].name, name)) {
+      return tab[h].c;
+    }
+    if (++h == size) {
+      h = 0;
+    }
+  }
+  return 0;
+}
+
+int NameToCharCode::hash(char *name) {
+  char *p;
+  unsigned int h;
+
+  h = 0;
+  for (p = name; *p; ++p) {
+    h = 17 * h + (int)(*p & 0xff);
+  }
+  return (int)(h % size);
+}
diff --git a/lib/xpdf/NameToCharCode.h b/lib/xpdf/NameToCharCode.h
new file mode 100644 (file)
index 0000000..65453c3
--- /dev/null
@@ -0,0 +1,42 @@
+//========================================================================
+//
+// NameToCharCode.h
+//
+// Copyright 2001-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef NAMETOCHARCODE_H
+#define NAMETOCHARCODE_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#include "CharTypes.h"
+
+struct NameToCharCodeEntry;
+
+//------------------------------------------------------------------------
+
+class NameToCharCode {
+public:
+
+  NameToCharCode();
+  ~NameToCharCode();
+
+  void add(char *name, CharCode c);
+  CharCode lookup(char *name);
+
+private:
+
+  int hash(char *name);
+
+  NameToCharCodeEntry *tab;
+  int size;
+  int len;
+};
+
+#endif
diff --git a/lib/xpdf/NameToUnicodeTable.h b/lib/xpdf/NameToUnicodeTable.h
new file mode 100644 (file)
index 0000000..c5ecba4
--- /dev/null
@@ -0,0 +1,1097 @@
+//========================================================================
+//
+// NameToUnicodeTable.h
+//
+// Copyright 2001-2004 Glyph & Cog, LLC
+//
+//========================================================================
+
+static struct {
+  Unicode u;
+  char *name;
+} nameToUnicodeTab[] = {
+  {0x0021, "!"},
+  {0x0023, "#"},
+  {0x0024, "$"},
+  {0x0025, "%"},
+  {0x0026, "&"},
+  {0x0027, "'"},
+  {0x0028, "("},
+  {0x0029, ")"},
+  {0x002a, "*"},
+  {0x002b, "+"},
+  {0x002c, ","},
+  {0x002d, "-"},
+  {0x002e, "."},
+  {0x002f, "/"},
+  {0x0030, "0"},
+  {0x0031, "1"},
+  {0x0032, "2"},
+  {0x0033, "3"},
+  {0x0034, "4"},
+  {0x0035, "5"},
+  {0x0036, "6"},
+  {0x0037, "7"},
+  {0x0038, "8"},
+  {0x0039, "9"},
+  {0x003a, ":"},
+  {0x003b, ";"},
+  {0x003c, "<"},
+  {0x003d, "="},
+  {0x003e, ">"},
+  {0x003f, "?"},
+  {0x0040, "@"},
+  {0x0041, "A"},
+  {0x00c6, "AE"},
+  {0x01fc, "AEacute"},
+  {0xf7e6, "AEsmall"},
+  {0x00c1, "Aacute"},
+  {0xf7e1, "Aacutesmall"},
+  {0x0102, "Abreve"},
+  {0x00c2, "Acircumflex"},
+  {0xf7e2, "Acircumflexsmall"},
+  {0xf6c9, "Acute"},
+  {0xf7b4, "Acutesmall"},
+  {0x00c4, "Adieresis"},
+  {0xf7e4, "Adieresissmall"},
+  {0x00c0, "Agrave"},
+  {0xf7e0, "Agravesmall"},
+  {0x0391, "Alpha"},
+  {0x0386, "Alphatonos"},
+  {0x0100, "Amacron"},
+  {0x0104, "Aogonek"},
+  {0x00c5, "Aring"},
+  {0x01fa, "Aringacute"},
+  {0xf7e5, "Aringsmall"},
+  {0xf761, "Asmall"},
+  {0x00c3, "Atilde"},
+  {0xf7e3, "Atildesmall"},
+  {0x0042, "B"},
+  {0x0392, "Beta"},
+  {0xf6f4, "Brevesmall"},
+  {0xf762, "Bsmall"},
+  {0x0043, "C"},
+  {0x0106, "Cacute"},
+  {0xf6ca, "Caron"},
+  {0xf6f5, "Caronsmall"},
+  {0x010c, "Ccaron"},
+  {0x00c7, "Ccedilla"},
+  {0xf7e7, "Ccedillasmall"},
+  {0x0108, "Ccircumflex"},
+  {0x010a, "Cdotaccent"},
+  {0xf7b8, "Cedillasmall"},
+  {0x03a7, "Chi"},
+  {0xf6f6, "Circumflexsmall"},
+  {0xf763, "Csmall"},
+  {0x0044, "D"},
+  {0x010e, "Dcaron"},
+  {0x0110, "Dcroat"},
+  {0x2206, "Delta"},
+  {0xf6cb, "Dieresis"},
+  {0xf6cc, "DieresisAcute"},
+  {0xf6cd, "DieresisGrave"},
+  {0xf7a8, "Dieresissmall"},
+  {0xf6f7, "Dotaccentsmall"},
+  {0xf764, "Dsmall"},
+  {0x0045, "E"},
+  {0x00c9, "Eacute"},
+  {0xf7e9, "Eacutesmall"},
+  {0x0114, "Ebreve"},
+  {0x011a, "Ecaron"},
+  {0x00ca, "Ecircumflex"},
+  {0xf7ea, "Ecircumflexsmall"},
+  {0x00cb, "Edieresis"},
+  {0xf7eb, "Edieresissmall"},
+  {0x0116, "Edotaccent"},
+  {0x00c8, "Egrave"},
+  {0xf7e8, "Egravesmall"},
+  {0x0112, "Emacron"},
+  {0x014a, "Eng"},
+  {0x0118, "Eogonek"},
+  {0x0395, "Epsilon"},
+  {0x0388, "Epsilontonos"},
+  {0xf765, "Esmall"},
+  {0x0397, "Eta"},
+  {0x0389, "Etatonos"},
+  {0x00d0, "Eth"},
+  {0xf7f0, "Ethsmall"},
+  {0x20ac, "Euro"},
+  {0x0046, "F"},
+  {0xf766, "Fsmall"},
+  {0x0047, "G"},
+  {0x0393, "Gamma"},
+  {0x011e, "Gbreve"},
+  {0x01e6, "Gcaron"},
+  {0x011c, "Gcircumflex"},
+  {0x0122, "Gcommaaccent"},
+  {0x0120, "Gdotaccent"},
+  {0xf6ce, "Grave"},
+  {0xf760, "Gravesmall"},
+  {0xf767, "Gsmall"},
+  {0x0048, "H"},
+  {0x25cf, "H18533"},
+  {0x25aa, "H18543"},
+  {0x25ab, "H18551"},
+  {0x25a1, "H22073"},
+  {0x0126, "Hbar"},
+  {0x0124, "Hcircumflex"},
+  {0xf768, "Hsmall"},
+  {0xf6cf, "Hungarumlaut"},
+  {0xf6f8, "Hungarumlautsmall"},
+  {0x0049, "I"},
+  {0x0132, "IJ"},
+  {0x00cd, "Iacute"},
+  {0xf7ed, "Iacutesmall"},
+  {0x012c, "Ibreve"},
+  {0x00ce, "Icircumflex"},
+  {0xf7ee, "Icircumflexsmall"},
+  {0x00cf, "Idieresis"},
+  {0xf7ef, "Idieresissmall"},
+  {0x0130, "Idotaccent"},
+  {0x2111, "Ifraktur"},
+  {0x00cc, "Igrave"},
+  {0xf7ec, "Igravesmall"},
+  {0x012a, "Imacron"},
+  {0x012e, "Iogonek"},
+  {0x0399, "Iota"},
+  {0x03aa, "Iotadieresis"},
+  {0x038a, "Iotatonos"},
+  {0xf769, "Ismall"},
+  {0x0128, "Itilde"},
+  {0x004a, "J"},
+  {0x0134, "Jcircumflex"},
+  {0xf76a, "Jsmall"},
+  {0x004b, "K"},
+  {0x039a, "Kappa"},
+  {0x0136, "Kcommaaccent"},
+  {0xf76b, "Ksmall"},
+  {0x004c, "L"},
+  {0xf6bf, "LL"},
+  {0x0139, "Lacute"},
+  {0x039b, "Lambda"},
+  {0x013d, "Lcaron"},
+  {0x013b, "Lcommaaccent"},
+  {0x013f, "Ldot"},
+  {0x0141, "Lslash"},
+  {0xf6f9, "Lslashsmall"},
+  {0xf76c, "Lsmall"},
+  {0x004d, "M"},
+  {0xf6d0, "Macron"},
+  {0xf7af, "Macronsmall"},
+  {0xf76d, "Msmall"},
+  {0x039c, "Mu"},
+  {0x004e, "N"},
+  {0x0143, "Nacute"},
+  {0x0147, "Ncaron"},
+  {0x0145, "Ncommaaccent"},
+  {0xf76e, "Nsmall"},
+  {0x00d1, "Ntilde"},
+  {0xf7f1, "Ntildesmall"},
+  {0x039d, "Nu"},
+  {0x004f, "O"},
+  {0x0152, "OE"},
+  {0xf6fa, "OEsmall"},
+  {0x00d3, "Oacute"},
+  {0xf7f3, "Oacutesmall"},
+  {0x014e, "Obreve"},
+  {0x00d4, "Ocircumflex"},
+  {0xf7f4, "Ocircumflexsmall"},
+  {0x00d6, "Odieresis"},
+  {0xf7f6, "Odieresissmall"},
+  {0xf6fb, "Ogoneksmall"},
+  {0x00d2, "Ograve"},
+  {0xf7f2, "Ogravesmall"},
+  {0x01a0, "Ohorn"},
+  {0x0150, "Ohungarumlaut"},
+  {0x014c, "Omacron"},
+  {0x2126, "Omega"},
+  {0x038f, "Omegatonos"},
+  {0x039f, "Omicron"},
+  {0x038c, "Omicrontonos"},
+  {0x00d8, "Oslash"},
+  {0x01fe, "Oslashacute"},
+  {0xf7f8, "Oslashsmall"},
+  {0xf76f, "Osmall"},
+  {0x00d5, "Otilde"},
+  {0xf7f5, "Otildesmall"},
+  {0x0050, "P"},
+  {0x03a6, "Phi"},
+  {0x03a0, "Pi"},
+  {0x03a8, "Psi"},
+  {0xf770, "Psmall"},
+  {0x0051, "Q"},
+  {0xf771, "Qsmall"},
+  {0x0052, "R"},
+  {0x0154, "Racute"},
+  {0x0158, "Rcaron"},
+  {0x0156, "Rcommaaccent"},
+  {0x211c, "Rfraktur"},
+  {0x03a1, "Rho"},
+  {0xf6fc, "Ringsmall"},
+  {0xf772, "Rsmall"},
+  {0x0053, "S"},
+  {0x250c, "SF010000"},
+  {0x2514, "SF020000"},
+  {0x2510, "SF030000"},
+  {0x2518, "SF040000"},
+  {0x253c, "SF050000"},
+  {0x252c, "SF060000"},
+  {0x2534, "SF070000"},
+  {0x251c, "SF080000"},
+  {0x2524, "SF090000"},
+  {0x2500, "SF100000"},
+  {0x2502, "SF110000"},
+  {0x2561, "SF190000"},
+  {0x2562, "SF200000"},
+  {0x2556, "SF210000"},
+  {0x2555, "SF220000"},
+  {0x2563, "SF230000"},
+  {0x2551, "SF240000"},
+  {0x2557, "SF250000"},
+  {0x255d, "SF260000"},
+  {0x255c, "SF270000"},
+  {0x255b, "SF280000"},
+  {0x255e, "SF360000"},
+  {0x255f, "SF370000"},
+  {0x255a, "SF380000"},
+  {0x2554, "SF390000"},
+  {0x2569, "SF400000"},
+  {0x2566, "SF410000"},
+  {0x2560, "SF420000"},
+  {0x2550, "SF430000"},
+  {0x256c, "SF440000"},
+  {0x2567, "SF450000"},
+  {0x2568, "SF460000"},
+  {0x2564, "SF470000"},
+  {0x2565, "SF480000"},
+  {0x2559, "SF490000"},
+  {0x2558, "SF500000"},
+  {0x2552, "SF510000"},
+  {0x2553, "SF520000"},
+  {0x256b, "SF530000"},
+  {0x256a, "SF540000"},
+  {0x015a, "Sacute"},
+  {0x0160, "Scaron"},
+  {0xf6fd, "Scaronsmall"},
+  {0x015e, "Scedilla"},
+  {0x015c, "Scircumflex"},
+  {0x0218, "Scommaaccent"},
+  {0x03a3, "Sigma"},
+  {0xf773, "Ssmall"},
+  {0x0054, "T"},
+  {0x03a4, "Tau"},
+  {0x0166, "Tbar"},
+  {0x0164, "Tcaron"},
+  {0x0162, "Tcommaaccent"},
+  {0x0398, "Theta"},
+  {0x00de, "Thorn"},
+  {0xf7fe, "Thornsmall"},
+  {0xf6fe, "Tildesmall"},
+  {0xf774, "Tsmall"},
+  {0x0055, "U"},
+  {0x00da, "Uacute"},
+  {0xf7fa, "Uacutesmall"},
+  {0x016c, "Ubreve"},
+  {0x00db, "Ucircumflex"},
+  {0xf7fb, "Ucircumflexsmall"},
+  {0x00dc, "Udieresis"},
+  {0xf7fc, "Udieresissmall"},
+  {0x00d9, "Ugrave"},
+  {0xf7f9, "Ugravesmall"},
+  {0x01af, "Uhorn"},
+  {0x0170, "Uhungarumlaut"},
+  {0x016a, "Umacron"},
+  {0x0172, "Uogonek"},
+  {0x03a5, "Upsilon"},
+  {0x03d2, "Upsilon1"},
+  {0x03ab, "Upsilondieresis"},
+  {0x038e, "Upsilontonos"},
+  {0x016e, "Uring"},
+  {0xf775, "Usmall"},
+  {0x0168, "Utilde"},
+  {0x0056, "V"},
+  {0xf776, "Vsmall"},
+  {0x0057, "W"},
+  {0x1e82, "Wacute"},
+  {0x0174, "Wcircumflex"},
+  {0x1e84, "Wdieresis"},
+  {0x1e80, "Wgrave"},
+  {0xf777, "Wsmall"},
+  {0x0058, "X"},
+  {0x039e, "Xi"},
+  {0xf778, "Xsmall"},
+  {0x0059, "Y"},
+  {0x00dd, "Yacute"},
+  {0xf7fd, "Yacutesmall"},
+  {0x0176, "Ycircumflex"},
+  {0x0178, "Ydieresis"},
+  {0xf7ff, "Ydieresissmall"},
+  {0x1ef2, "Ygrave"},
+  {0xf779, "Ysmall"},
+  {0x005a, "Z"},
+  {0x0179, "Zacute"},
+  {0x017d, "Zcaron"},
+  {0xf6ff, "Zcaronsmall"},
+  {0x017b, "Zdotaccent"},
+  {0x0396, "Zeta"},
+  {0xf77a, "Zsmall"},
+  {0x0022, "\""},
+  {0x005c, "\\"},
+  {0x005d, "]"},
+  {0x005e, "^"},
+  {0x005f, "_"},
+  {0x0060, "`"},
+  {0x0061, "a"},
+  {0x00e1, "aacute"},
+  {0x0103, "abreve"},
+  {0x00e2, "acircumflex"},
+  {0x00b4, "acute"},
+  {0x0301, "acutecomb"},
+  {0x00e4, "adieresis"},
+  {0x00e6, "ae"},
+  {0x01fd, "aeacute"},
+  {0x2015, "afii00208"},
+  {0x0410, "afii10017"},
+  {0x0411, "afii10018"},
+  {0x0412, "afii10019"},
+  {0x0413, "afii10020"},
+  {0x0414, "afii10021"},
+  {0x0415, "afii10022"},
+  {0x0401, "afii10023"},
+  {0x0416, "afii10024"},
+  {0x0417, "afii10025"},
+  {0x0418, "afii10026"},
+  {0x0419, "afii10027"},
+  {0x041a, "afii10028"},
+  {0x041b, "afii10029"},
+  {0x041c, "afii10030"},
+  {0x041d, "afii10031"},
+  {0x041e, "afii10032"},
+  {0x041f, "afii10033"},
+  {0x0420, "afii10034"},
+  {0x0421, "afii10035"},
+  {0x0422, "afii10036"},
+  {0x0423, "afii10037"},
+  {0x0424, "afii10038"},
+  {0x0425, "afii10039"},
+  {0x0426, "afii10040"},
+  {0x0427, "afii10041"},
+  {0x0428, "afii10042"},
+  {0x0429, "afii10043"},
+  {0x042a, "afii10044"},
+  {0x042b, "afii10045"},
+  {0x042c, "afii10046"},
+  {0x042d, "afii10047"},
+  {0x042e, "afii10048"},
+  {0x042f, "afii10049"},
+  {0x0490, "afii10050"},
+  {0x0402, "afii10051"},
+  {0x0403, "afii10052"},
+  {0x0404, "afii10053"},
+  {0x0405, "afii10054"},
+  {0x0406, "afii10055"},
+  {0x0407, "afii10056"},
+  {0x0408, "afii10057"},
+  {0x0409, "afii10058"},
+  {0x040a, "afii10059"},
+  {0x040b, "afii10060"},
+  {0x040c, "afii10061"},
+  {0x040e, "afii10062"},
+  {0xf6c4, "afii10063"},
+  {0xf6c5, "afii10064"},
+  {0x0430, "afii10065"},
+  {0x0431, "afii10066"},
+  {0x0432, "afii10067"},
+  {0x0433, "afii10068"},
+  {0x0434, "afii10069"},
+  {0x0435, "afii10070"},
+  {0x0451, "afii10071"},
+  {0x0436, "afii10072"},
+  {0x0437, "afii10073"},
+  {0x0438, "afii10074"},
+  {0x0439, "afii10075"},
+  {0x043a, "afii10076"},
+  {0x043b, "afii10077"},
+  {0x043c, "afii10078"},
+  {0x043d, "afii10079"},
+  {0x043e, "afii10080"},
+  {0x043f, "afii10081"},
+  {0x0440, "afii10082"},
+  {0x0441, "afii10083"},
+  {0x0442, "afii10084"},
+  {0x0443, "afii10085"},
+  {0x0444, "afii10086"},
+  {0x0445, "afii10087"},
+  {0x0446, "afii10088"},
+  {0x0447, "afii10089"},
+  {0x0448, "afii10090"},
+  {0x0449, "afii10091"},
+  {0x044a, "afii10092"},
+  {0x044b, "afii10093"},
+  {0x044c, "afii10094"},
+  {0x044d, "afii10095"},
+  {0x044e, "afii10096"},
+  {0x044f, "afii10097"},
+  {0x0491, "afii10098"},
+  {0x0452, "afii10099"},
+  {0x0453, "afii10100"},
+  {0x0454, "afii10101"},
+  {0x0455, "afii10102"},
+  {0x0456, "afii10103"},
+  {0x0457, "afii10104"},
+  {0x0458, "afii10105"},
+  {0x0459, "afii10106"},
+  {0x045a, "afii10107"},
+  {0x045b, "afii10108"},
+  {0x045c, "afii10109"},
+  {0x045e, "afii10110"},
+  {0x040f, "afii10145"},
+  {0x0462, "afii10146"},
+  {0x0472, "afii10147"},
+  {0x0474, "afii10148"},
+  {0xf6c6, "afii10192"},
+  {0x045f, "afii10193"},
+  {0x0463, "afii10194"},
+  {0x0473, "afii10195"},
+  {0x0475, "afii10196"},
+  {0xf6c7, "afii10831"},
+  {0xf6c8, "afii10832"},
+  {0x04d9, "afii10846"},
+  {0x200e, "afii299"},
+  {0x200f, "afii300"},
+  {0x200d, "afii301"},
+  {0x066a, "afii57381"},
+  {0x060c, "afii57388"},
+  {0x0660, "afii57392"},
+  {0x0661, "afii57393"},
+  {0x0662, "afii57394"},
+  {0x0663, "afii57395"},
+  {0x0664, "afii57396"},
+  {0x0665, "afii57397"},
+  {0x0666, "afii57398"},
+  {0x0667, "afii57399"},
+  {0x0668, "afii57400"},
+  {0x0669, "afii57401"},
+  {0x061b, "afii57403"},
+  {0x061f, "afii57407"},
+  {0x0621, "afii57409"},
+  {0x0622, "afii57410"},
+  {0x0623, "afii57411"},
+  {0x0624, "afii57412"},
+  {0x0625, "afii57413"},
+  {0x0626, "afii57414"},
+  {0x0627, "afii57415"},
+  {0x0628, "afii57416"},
+  {0x0629, "afii57417"},
+  {0x062a, "afii57418"},
+  {0x062b, "afii57419"},
+  {0x062c, "afii57420"},
+  {0x062d, "afii57421"},
+  {0x062e, "afii57422"},
+  {0x062f, "afii57423"},
+  {0x0630, "afii57424"},
+  {0x0631, "afii57425"},
+  {0x0632, "afii57426"},
+  {0x0633, "afii57427"},
+  {0x0634, "afii57428"},
+  {0x0635, "afii57429"},
+  {0x0636, "afii57430"},
+  {0x0637, "afii57431"},
+  {0x0638, "afii57432"},
+  {0x0639, "afii57433"},
+  {0x063a, "afii57434"},
+  {0x0640, "afii57440"},
+  {0x0641, "afii57441"},
+  {0x0642, "afii57442"},
+  {0x0643, "afii57443"},
+  {0x0644, "afii57444"},
+  {0x0645, "afii57445"},
+  {0x0646, "afii57446"},
+  {0x0648, "afii57448"},
+  {0x0649, "afii57449"},
+  {0x064a, "afii57450"},
+  {0x064b, "afii57451"},
+  {0x064c, "afii57452"},
+  {0x064d, "afii57453"},
+  {0x064e, "afii57454"},
+  {0x064f, "afii57455"},
+  {0x0650, "afii57456"},
+  {0x0651, "afii57457"},
+  {0x0652, "afii57458"},
+  {0x0647, "afii57470"},
+  {0x06a4, "afii57505"},
+  {0x067e, "afii57506"},
+  {0x0686, "afii57507"},
+  {0x0698, "afii57508"},
+  {0x06af, "afii57509"},
+  {0x0679, "afii57511"},
+  {0x0688, "afii57512"},
+  {0x0691, "afii57513"},
+  {0x06ba, "afii57514"},
+  {0x06d2, "afii57519"},
+  {0x06d5, "afii57534"},
+  {0x20aa, "afii57636"},
+  {0x05be, "afii57645"},
+  {0x05c3, "afii57658"},
+  {0x05d0, "afii57664"},
+  {0x05d1, "afii57665"},
+  {0x05d2, "afii57666"},
+  {0x05d3, "afii57667"},
+  {0x05d4, "afii57668"},
+  {0x05d5, "afii57669"},
+  {0x05d6, "afii57670"},
+  {0x05d7, "afii57671"},
+  {0x05d8, "afii57672"},
+  {0x05d9, "afii57673"},
+  {0x05da, "afii57674"},
+  {0x05db, "afii57675"},
+  {0x05dc, "afii57676"},
+  {0x05dd, "afii57677"},
+  {0x05de, "afii57678"},
+  {0x05df, "afii57679"},
+  {0x05e0, "afii57680"},
+  {0x05e1, "afii57681"},
+  {0x05e2, "afii57682"},
+  {0x05e3, "afii57683"},
+  {0x05e4, "afii57684"},
+  {0x05e5, "afii57685"},
+  {0x05e6, "afii57686"},
+  {0x05e7, "afii57687"},
+  {0x05e8, "afii57688"},
+  {0x05e9, "afii57689"},
+  {0x05ea, "afii57690"},
+  {0xfb2a, "afii57694"},
+  {0xfb2b, "afii57695"},
+  {0xfb4b, "afii57700"},
+  {0xfb1f, "afii57705"},
+  {0x05f0, "afii57716"},
+  {0x05f1, "afii57717"},
+  {0x05f2, "afii57718"},
+  {0xfb35, "afii57723"},
+  {0x05b4, "afii57793"},
+  {0x05b5, "afii57794"},
+  {0x05b6, "afii57795"},
+  {0x05bb, "afii57796"},
+  {0x05b8, "afii57797"},
+  {0x05b7, "afii57798"},
+  {0x05b0, "afii57799"},
+  {0x05b2, "afii57800"},
+  {0x05b1, "afii57801"},
+  {0x05b3, "afii57802"},
+  {0x05c2, "afii57803"},
+  {0x05c1, "afii57804"},
+  {0x05b9, "afii57806"},
+  {0x05bc, "afii57807"},
+  {0x05bd, "afii57839"},
+  {0x05bf, "afii57841"},
+  {0x05c0, "afii57842"},
+  {0x02bc, "afii57929"},
+  {0x2105, "afii61248"},
+  {0x2113, "afii61289"},
+  {0x2116, "afii61352"},
+  {0x202c, "afii61573"},
+  {0x202d, "afii61574"},
+  {0x202e, "afii61575"},
+  {0x200c, "afii61664"},
+  {0x066d, "afii63167"},
+  {0x02bd, "afii64937"},
+  {0x00e0, "agrave"},
+  {0x2135, "aleph"},
+  {0x03b1, "alpha"},
+  {0x03ac, "alphatonos"},
+  {0x0101, "amacron"},
+  {0x0026, "ampersand"},
+  {0xf726, "ampersandsmall"},
+  {0x2220, "angle"},
+  {0x2329, "angleleft"},
+  {0x232a, "angleright"},
+  {0x0387, "anoteleia"},
+  {0x0105, "aogonek"},
+  {0x2248, "approxequal"},
+  {0x00e5, "aring"},
+  {0x01fb, "aringacute"},
+  {0x2194, "arrowboth"},
+  {0x21d4, "arrowdblboth"},
+  {0x21d3, "arrowdbldown"},
+  {0x21d0, "arrowdblleft"},
+  {0x21d2, "arrowdblright"},
+  {0x21d1, "arrowdblup"},
+  {0x2193, "arrowdown"},
+  {0xf8e7, "arrowhorizex"},
+  {0x2190, "arrowleft"},
+  {0x2192, "arrowright"},
+  {0x2191, "arrowup"},
+  {0x2195, "arrowupdn"},
+  {0x21a8, "arrowupdnbse"},
+  {0xf8e6, "arrowvertex"},
+  {0x005e, "asciicircum"},
+  {0x007e, "asciitilde"},
+  {0x002a, "asterisk"},
+  {0x2217, "asteriskmath"},
+  {0xf6e9, "asuperior"},
+  {0x0040, "at"},
+  {0x00e3, "atilde"},
+  {0x0062, "b"},
+  {0x005c, "backslash"},
+  {0x007c, "bar"},
+  {0x03b2, "beta"},
+  {0x2588, "block"},
+  {0xf8f4, "braceex"},
+  {0x007b, "braceleft"},
+  {0xf8f3, "braceleftbt"},
+  {0xf8f2, "braceleftmid"},
+  {0xf8f1, "bracelefttp"},
+  {0x007d, "braceright"},
+  {0xf8fe, "bracerightbt"},
+  {0xf8fd, "bracerightmid"},
+  {0xf8fc, "bracerighttp"},
+  {0x005b, "bracketleft"},
+  {0xf8f0, "bracketleftbt"},
+  {0xf8ef, "bracketleftex"},
+  {0xf8ee, "bracketlefttp"},
+  {0x005d, "bracketright"},
+  {0xf8fb, "bracketrightbt"},
+  {0xf8fa, "bracketrightex"},
+  {0xf8f9, "bracketrighttp"},
+  {0x02d8, "breve"},
+  {0x00a6, "brokenbar"},
+  {0xf6ea, "bsuperior"},
+  {0x2022, "bullet"},
+  {0x0063, "c"},
+  {0x0107, "cacute"},
+  {0x02c7, "caron"},
+  {0x21b5, "carriagereturn"},
+  {0x010d, "ccaron"},
+  {0x00e7, "ccedilla"},
+  {0x0109, "ccircumflex"},
+  {0x010b, "cdotaccent"},
+  {0x00b8, "cedilla"},
+  {0x00a2, "cent"},
+  {0xf6df, "centinferior"},
+  {0xf7a2, "centoldstyle"},
+  {0xf6e0, "centsuperior"},
+  {0x03c7, "chi"},
+  {0x25cb, "circle"},
+  {0x2297, "circlemultiply"},
+  {0x2295, "circleplus"},
+  {0x02c6, "circumflex"},
+  {0x2663, "club"},
+  {0x003a, "colon"},
+  {0x20a1, "colonmonetary"},
+  {0x002c, "comma"},
+  {0xf6c3, "commaaccent"},
+  {0xf6e1, "commainferior"},
+  {0xf6e2, "commasuperior"},
+  {0x2245, "congruent"},
+  {0x00a9, "copyright"},
+  {0x00a9, "copyrightsans"},
+  {0x00a9, "copyrightserif"},
+  {0x00a4, "currency"},
+  {0xf6d1, "cyrBreve"},
+  {0xf6d2, "cyrFlex"},
+  {0xf6d4, "cyrbreve"},
+  {0xf6d5, "cyrflex"},
+  {0x0064, "d"},
+  {0x2020, "dagger"},
+  {0x2021, "daggerdbl"},
+  {0xf6d3, "dblGrave"},
+  {0xf6d6, "dblgrave"},
+  {0x010f, "dcaron"},
+  {0x0111, "dcroat"},
+  {0x00b0, "degree"},
+  {0x03b4, "delta"},
+  {0x2666, "diamond"},
+  {0x00a8, "dieresis"},
+  {0xf6d7, "dieresisacute"},
+  {0xf6d8, "dieresisgrave"},
+  {0x0385, "dieresistonos"},
+  {0x00f7, "divide"},
+  {0x2593, "dkshade"},
+  {0x2584, "dnblock"},
+  {0x0024, "dollar"},
+  {0xf6e3, "dollarinferior"},
+  {0xf724, "dollaroldstyle"},
+  {0xf6e4, "dollarsuperior"},
+  {0x20ab, "dong"},
+  {0x02d9, "dotaccent"},
+  {0x0323, "dotbelowcomb"},
+  {0x0131, "dotlessi"},
+  {0xf6be, "dotlessj"},
+  {0x22c5, "dotmath"},
+  {0xf6eb, "dsuperior"},
+  {0x0065, "e"},
+  {0x00e9, "eacute"},
+  {0x0115, "ebreve"},
+  {0x011b, "ecaron"},
+  {0x00ea, "ecircumflex"},
+  {0x00eb, "edieresis"},
+  {0x0117, "edotaccent"},
+  {0x00e8, "egrave"},
+  {0x0038, "eight"},
+  {0x2088, "eightinferior"},
+  {0xf738, "eightoldstyle"},
+  {0x2078, "eightsuperior"},
+  {0x2208, "element"},
+  {0x2026, "ellipsis"},
+  {0x0113, "emacron"},
+  {0x2014, "emdash"},
+  {0x2205, "emptyset"},
+  {0x2013, "endash"},
+  {0x014b, "eng"},
+  {0x0119, "eogonek"},
+  {0x03b5, "epsilon"},
+  {0x03ad, "epsilontonos"},
+  {0x003d, "equal"},
+  {0x2261, "equivalence"},
+  {0x212e, "estimated"},
+  {0xf6ec, "esuperior"},
+  {0x03b7, "eta"},
+  {0x03ae, "etatonos"},
+  {0x00f0, "eth"},
+  {0x0021, "exclam"},
+  {0x203c, "exclamdbl"},
+  {0x00a1, "exclamdown"},
+  {0xf7a1, "exclamdownsmall"},
+  {0x0021, "exclamleft"},
+  {0xf721, "exclamsmall"},
+  {0x2203, "existential"},
+  {0x0066, "f"},
+  {0x2640, "female"},
+  {0xfb00, "ff"},
+  {0xfb03, "ffi"},
+  {0xfb04, "ffl"},
+  {0xfb01, "fi"},
+  {0x2012, "figuredash"},
+  {0x25a0, "filledbox"},
+  {0x25ac, "filledrect"},
+  {0x0035, "five"},
+  {0x215d, "fiveeighths"},
+  {0x2085, "fiveinferior"},
+  {0xf735, "fiveoldstyle"},
+  {0x2075, "fivesuperior"},
+  {0xfb02, "fl"},
+  {0x0192, "florin"},
+  {0x0034, "four"},
+  {0x2084, "fourinferior"},
+  {0xf734, "fouroldstyle"},
+  {0x2074, "foursuperior"},
+  {0x2044, "fraction"},
+  {0x20a3, "franc"},
+  {0x0067, "g"},
+  {0x03b3, "gamma"},
+  {0x011f, "gbreve"},
+  {0x01e7, "gcaron"},
+  {0x011d, "gcircumflex"},
+  {0x0123, "gcommaaccent"},
+  {0x0121, "gdotaccent"},
+  {0x00df, "germandbls"},
+  {0x2207, "gradient"},
+  {0x0060, "grave"},
+  {0x0300, "gravecomb"},
+  {0x003e, "greater"},
+  {0x2265, "greaterequal"},
+  {0x00ab, "guillemotleft"},
+  {0x00bb, "guillemotright"},
+  {0x2039, "guilsinglleft"},
+  {0x203a, "guilsinglright"},
+  {0x0068, "h"},
+  {0x0127, "hbar"},
+  {0x0125, "hcircumflex"},
+  {0x2665, "heart"},
+  {0x0309, "hookabovecomb"},
+  {0x2302, "house"},
+  {0x02dd, "hungarumlaut"},
+  {0x002d, "hyphen"},
+  {0xf6e5, "hypheninferior"},
+  {0xf6e6, "hyphensuperior"},
+  {0x0069, "i"},
+  {0x00ed, "iacute"},
+  {0x012d, "ibreve"},
+  {0x00ee, "icircumflex"},
+  {0x00ef, "idieresis"},
+  {0x00ec, "igrave"},
+  {0x0133, "ij"},
+  {0x012b, "imacron"},
+  {0x221e, "infinity"},
+  {0x222b, "integral"},
+  {0x2321, "integralbt"},
+  {0xf8f5, "integralex"},
+  {0x2320, "integraltp"},
+  {0x2229, "intersection"},
+  {0x25d8, "invbullet"},
+  {0x25d9, "invcircle"},
+  {0x263b, "invsmileface"},
+  {0x012f, "iogonek"},
+  {0x03b9, "iota"},
+  {0x03ca, "iotadieresis"},
+  {0x0390, "iotadieresistonos"},
+  {0x03af, "iotatonos"},
+  {0xf6ed, "isuperior"},
+  {0x0129, "itilde"},
+  {0x006a, "j"},
+  {0x0135, "jcircumflex"},
+  {0x006b, "k"},
+  {0x03ba, "kappa"},
+  {0x0137, "kcommaaccent"},
+  {0x0138, "kgreenlandic"},
+  {0x006c, "l"},
+  {0x013a, "lacute"},
+  {0x03bb, "lambda"},
+  {0x013e, "lcaron"},
+  {0x013c, "lcommaaccent"},
+  {0x0140, "ldot"},
+  {0x003c, "less"},
+  {0x2264, "lessequal"},
+  {0x258c, "lfblock"},
+  {0x20a4, "lira"},
+  {0xf6c0, "ll"},
+  {0x2227, "logicaland"},
+  {0x00ac, "logicalnot"},
+  {0x2228, "logicalor"},
+  {0x017f, "longs"},
+  {0x25ca, "lozenge"},
+  {0x0142, "lslash"},
+  {0xf6ee, "lsuperior"},
+  {0x2591, "ltshade"},
+  {0x006d, "m"},
+  {0x00af, "macron"},
+  {0x2642, "male"},
+  {0x2212, "minus"},
+  {0x2032, "minute"},
+  {0xf6ef, "msuperior"},
+  {0x00b5, "mu"},
+  {0x00d7, "multiply"},
+  {0x266a, "musicalnote"},
+  {0x266b, "musicalnotedbl"},
+  {0x006e, "n"},
+  {0x0144, "nacute"},
+  {0x0149, "napostrophe"},
+  {0x00a0, "nbspace"},
+  {0x0148, "ncaron"},
+  {0x0146, "ncommaaccent"},
+  {0x0039, "nine"},
+  {0x2089, "nineinferior"},
+  {0xf739, "nineoldstyle"},
+  {0x2079, "ninesuperior"},
+  {0x00a0, "nonbreakingspace"},
+  {0x2209, "notelement"},
+  {0x2260, "notequal"},
+  {0x2284, "notsubset"},
+  {0x207f, "nsuperior"},
+  {0x00f1, "ntilde"},
+  {0x03bd, "nu"},
+  {0x0023, "numbersign"},
+  {0x006f, "o"},
+  {0x00f3, "oacute"},
+  {0x014f, "obreve"},
+  {0x00f4, "ocircumflex"},
+  {0x00f6, "odieresis"},
+  {0x0153, "oe"},
+  {0x02db, "ogonek"},
+  {0x00f2, "ograve"},
+  {0x01a1, "ohorn"},
+  {0x0151, "ohungarumlaut"},
+  {0x014d, "omacron"},
+  {0x03c9, "omega"},
+  {0x03d6, "omega1"},
+  {0x03ce, "omegatonos"},
+  {0x03bf, "omicron"},
+  {0x03cc, "omicrontonos"},
+  {0x0031, "one"},
+  {0x2024, "onedotenleader"},
+  {0x215b, "oneeighth"},
+  {0xf6dc, "onefitted"},
+  {0x00bd, "onehalf"},
+  {0x2081, "oneinferior"},
+  {0xf731, "oneoldstyle"},
+  {0x00bc, "onequarter"},
+  {0x00b9, "onesuperior"},
+  {0x2153, "onethird"},
+  {0x25e6, "openbullet"},
+  {0x00aa, "ordfeminine"},
+  {0x00ba, "ordmasculine"},
+  {0x221f, "orthogonal"},
+  {0x00f8, "oslash"},
+  {0x01ff, "oslashacute"},
+  {0xf6f0, "osuperior"},
+  {0x00f5, "otilde"},
+  {0x0070, "p"},
+  {0x00b6, "paragraph"},
+  {0x0028, "parenleft"},
+  {0xf8ed, "parenleftbt"},
+  {0xf8ec, "parenleftex"},
+  {0x208d, "parenleftinferior"},
+  {0x207d, "parenleftsuperior"},
+  {0xf8eb, "parenlefttp"},
+  {0x0029, "parenright"},
+  {0xf8f8, "parenrightbt"},
+  {0xf8f7, "parenrightex"},
+  {0x208e, "parenrightinferior"},
+  {0x207e, "parenrightsuperior"},
+  {0xf8f6, "parenrighttp"},
+  {0x2202, "partialdiff"},
+  {0x0025, "percent"},
+  {0x002e, "period"},
+  {0x00b7, "periodcentered"},
+  {0xf6e7, "periodinferior"},
+  {0xf6e8, "periodsuperior"},
+  {0x22a5, "perpendicular"},
+  {0x2030, "perthousand"},
+  {0x20a7, "peseta"},
+  {0x03c6, "phi"},
+  {0x03d5, "phi1"},
+  {0x03c0, "pi"},
+  {0x002b, "plus"},
+  {0x00b1, "plusminus"},
+  {0x211e, "prescription"},
+  {0x220f, "product"},
+  {0x2282, "propersubset"},
+  {0x2283, "propersuperset"},
+  {0x221d, "proportional"},
+  {0x03c8, "psi"},
+  {0x0071, "q"},
+  {0x003f, "question"},
+  {0x00bf, "questiondown"},
+  {0xf7bf, "questiondownsmall"},
+  {0xf73f, "questionsmall"},
+  {0x0022, "quotedbl"},
+  {0x201e, "quotedblbase"},
+  {0x201c, "quotedblleft"},
+  {0x201d, "quotedblright"},
+  {0x2018, "quoteleft"},
+  {0x201b, "quotereversed"},
+  {0x2019, "quoteright"},
+  {0x201a, "quotesinglbase"},
+  {0x0027, "quotesingle"},
+  {0x0072, "r"},
+  {0x0155, "racute"},
+  {0x221a, "radical"},
+  {0xf8e5, "radicalex"},
+  {0x0159, "rcaron"},
+  {0x0157, "rcommaaccent"},
+  {0x2286, "reflexsubset"},
+  {0x2287, "reflexsuperset"},
+  {0x00ae, "registered"},
+  {0x00ae, "registersans"},
+  {0x00ae, "registerserif"},
+  {0x2310, "revlogicalnot"},
+  {0x03c1, "rho"},
+  {0x02da, "ring"},
+  {0xf6f1, "rsuperior"},
+  {0x2590, "rtblock"},
+  {0xf6dd, "rupiah"},
+  {0x0073, "s"},
+  {0x015b, "sacute"},
+  {0x0161, "scaron"},
+  {0x015f, "scedilla"},
+  {0x015d, "scircumflex"},
+  {0x0219, "scommaaccent"},
+  {0x2033, "second"},
+  {0x00a7, "section"},
+  {0x003b, "semicolon"},
+  {0x0037, "seven"},
+  {0x215e, "seveneighths"},
+  {0x2087, "seveninferior"},
+  {0xf737, "sevenoldstyle"},
+  {0x2077, "sevensuperior"},
+  {0x2592, "shade"},
+  {0x03c3, "sigma"},
+  {0x03c2, "sigma1"},
+  {0x223c, "similar"},
+  {0x0036, "six"},
+  {0x2086, "sixinferior"},
+  {0xf736, "sixoldstyle"},
+  {0x2076, "sixsuperior"},
+  {0x002f, "slash"},
+  {0x263a, "smileface"},
+  {0x0020, "space"},
+  {0x2660, "spade"},
+  {0xf6f2, "ssuperior"},
+  {0x00a3, "sterling"},
+  {0x220b, "suchthat"},
+  {0x2211, "summation"},
+  {0x263c, "sun"},
+  {0x0074, "t"},
+  {0x03c4, "tau"},
+  {0x0167, "tbar"},
+  {0x0165, "tcaron"},
+  {0x0163, "tcommaaccent"},
+  {0x2234, "therefore"},
+  {0x03b8, "theta"},
+  {0x03d1, "theta1"},
+  {0x00fe, "thorn"},
+  {0x0033, "three"},
+  {0x215c, "threeeighths"},
+  {0x2083, "threeinferior"},
+  {0xf733, "threeoldstyle"},
+  {0x00be, "threequarters"},
+  {0xf6de, "threequartersemdash"},
+  {0x00b3, "threesuperior"},
+  {0x02dc, "tilde"},
+  {0x0303, "tildecomb"},
+  {0x0384, "tonos"},
+  {0x2122, "trademark"},
+  {0x2122, "trademarksans"},
+  {0x2122, "trademarkserif"},
+  {0x25bc, "triagdn"},
+  {0x25c4, "triaglf"},
+  {0x25ba, "triagrt"},
+  {0x25b2, "triagup"},
+  {0xf6f3, "tsuperior"},
+  {0x0032, "two"},
+  {0x2025, "twodotenleader"},
+  {0x2082, "twoinferior"},
+  {0xf732, "twooldstyle"},
+  {0x00b2, "twosuperior"},
+  {0x2154, "twothirds"},
+  {0x0075, "u"},
+  {0x00fa, "uacute"},
+  {0x016d, "ubreve"},
+  {0x00fb, "ucircumflex"},
+  {0x00fc, "udieresis"},
+  {0x00f9, "ugrave"},
+  {0x01b0, "uhorn"},
+  {0x0171, "uhungarumlaut"},
+  {0x016b, "umacron"},
+  {0x005f, "underscore"},
+  {0x2017, "underscoredbl"},
+  {0x222a, "union"},
+  {0x2200, "universal"},
+  {0x0173, "uogonek"},
+  {0x2580, "upblock"},
+  {0x03c5, "upsilon"},
+  {0x03cb, "upsilondieresis"},
+  {0x03b0, "upsilondieresistonos"},
+  {0x03cd, "upsilontonos"},
+  {0x016f, "uring"},
+  {0x0169, "utilde"},
+  {0x0076, "v"},
+  {0x0077, "w"},
+  {0x1e83, "wacute"},
+  {0x0175, "wcircumflex"},
+  {0x1e85, "wdieresis"},
+  {0x2118, "weierstrass"},
+  {0x1e81, "wgrave"},
+  {0x0078, "x"},
+  {0x03be, "xi"},
+  {0x0079, "y"},
+  {0x00fd, "yacute"},
+  {0x0177, "ycircumflex"},
+  {0x00ff, "ydieresis"},
+  {0x00a5, "yen"},
+  {0x1ef3, "ygrave"},
+  {0x007a, "z"},
+  {0x017a, "zacute"},
+  {0x017e, "zcaron"},
+  {0x017c, "zdotaccent"},
+  {0x0030, "zero"},
+  {0x2080, "zeroinferior"},
+  {0xf730, "zerooldstyle"},
+  {0x2070, "zerosuperior"},
+  {0x03b6, "zeta"},
+  {0x007b, "{"},
+  {0x007c, "|"},
+  {0x007d, "}"},
+  {0x007e, "~"},
+  { 0, NULL }
+};
diff --git a/lib/xpdf/Object.cc b/lib/xpdf/Object.cc
new file mode 100644 (file)
index 0000000..71c632a
--- /dev/null
@@ -0,0 +1,231 @@
+//========================================================================
+//
+// Object.cc
+//
+// Copyright 1996-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <stddef.h>
+#include "Object.h"
+#include "Array.h"
+#include "Dict.h"
+#include "Error.h"
+#include "Stream.h"
+#include "XRef.h"
+
+//------------------------------------------------------------------------
+// Object
+//------------------------------------------------------------------------
+
+char *objTypeNames[numObjTypes] = {
+  "boolean",
+  "integer",
+  "real",
+  "string",
+  "name",
+  "null",
+  "array",
+  "dictionary",
+  "stream",
+  "ref",
+  "cmd",
+  "error",
+  "eof",
+  "none"
+};
+
+#ifdef DEBUG_MEM
+int Object::numAlloc[numObjTypes] =
+  {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+#endif
+
+Object *Object::initArray(XRef *xref) {
+  initObj(objArray);
+  array = new Array(xref);
+  return this;
+}
+
+Object *Object::initDict(XRef *xref) {
+  initObj(objDict);
+  dict = new Dict(xref);
+  return this;
+}
+
+Object *Object::initDict(Dict *dictA) {
+  initObj(objDict);
+  dict = dictA;
+  dict->incRef();
+  return this;
+}
+
+Object *Object::initStream(Stream *streamA) {
+  initObj(objStream);
+  stream = streamA;
+  return this;
+}
+
+Object *Object::copy(Object *obj) {
+  *obj = *this;
+  switch (type) {
+  case objString:
+    obj->string = string->copy();
+    break;
+  case objName:
+    obj->name = copyString(name);
+    break;
+  case objArray:
+    array->incRef();
+    break;
+  case objDict:
+    dict->incRef();
+    break;
+  case objStream:
+    stream->incRef();
+    break;
+  case objCmd:
+    obj->cmd = copyString(cmd);
+    break;
+  default:
+    break;
+  }
+#ifdef DEBUG_MEM
+  ++numAlloc[type];
+#endif
+  return obj;
+}
+
+Object *Object::fetch(XRef *xref, Object *obj) {
+  return (type == objRef && xref) ?
+         xref->fetch(ref.num, ref.gen, obj) : copy(obj);
+}
+
+void Object::free() {
+  switch (type) {
+  case objString:
+    delete string;
+    break;
+  case objName:
+    gfree(name);
+    break;
+  case objArray:
+    if (!array->decRef()) {
+      delete array;
+    }
+    break;
+  case objDict:
+    if (!dict->decRef()) {
+      delete dict;
+    }
+    break;
+  case objStream:
+    if (!stream->decRef()) {
+      delete stream;
+    }
+    break;
+  case objCmd:
+    gfree(cmd);
+    break;
+  default:
+    break;
+  }
+#ifdef DEBUG_MEM
+  --numAlloc[type];
+#endif
+  type = objNone;
+}
+
+char *Object::getTypeName() {
+  return objTypeNames[type];
+}
+
+void Object::print(FILE *f) {
+  Object obj;
+  int i;
+
+  switch (type) {
+  case objBool:
+    fprintf(f, "%s", booln ? "true" : "false");
+    break;
+  case objInt:
+    fprintf(f, "%d", intg);
+    break;
+  case objReal:
+    fprintf(f, "%g", real);
+    break;
+  case objString:
+    fprintf(f, "(");
+    fwrite(string->getCString(), 1, string->getLength(), f);
+    fprintf(f, ")");
+    break;
+  case objName:
+    fprintf(f, "/%s", name);
+    break;
+  case objNull:
+    fprintf(f, "null");
+    break;
+  case objArray:
+    fprintf(f, "[");
+    for (i = 0; i < arrayGetLength(); ++i) {
+      if (i > 0)
+       fprintf(f, " ");
+      arrayGetNF(i, &obj);
+      obj.print(f);
+      obj.free();
+    }
+    fprintf(f, "]");
+    break;
+  case objDict:
+    fprintf(f, "<<");
+    for (i = 0; i < dictGetLength(); ++i) {
+      fprintf(f, " /%s ", dictGetKey(i));
+      dictGetValNF(i, &obj);
+      obj.print(f);
+      obj.free();
+    }
+    fprintf(f, " >>");
+    break;
+  case objStream:
+    fprintf(f, "<stream>");
+    break;
+  case objRef:
+    fprintf(f, "%d %d R", ref.num, ref.gen);
+    break;
+  case objCmd:
+    fprintf(f, "%s", cmd);
+    break;
+  case objError:
+    fprintf(f, "<error>");
+    break;
+  case objEOF:
+    fprintf(f, "<EOF>");
+    break;
+  case objNone:
+    fprintf(f, "<none>");
+    break;
+  }
+}
+
+void Object::memCheck(FILE *f) {
+#ifdef DEBUG_MEM
+  int i;
+  int t;
+
+  t = 0;
+  for (i = 0; i < numObjTypes; ++i)
+    t += numAlloc[i];
+  if (t > 0) {
+    fprintf(f, "Allocated objects:\n");
+    for (i = 0; i < numObjTypes; ++i) {
+      if (numAlloc[i] > 0)
+       fprintf(f, "  %-20s: %6d\n", objTypeNames[i], numAlloc[i]);
+    }
+  }
+#endif
+}
diff --git a/lib/xpdf/Object.h b/lib/xpdf/Object.h
new file mode 100644 (file)
index 0000000..8b1807c
--- /dev/null
@@ -0,0 +1,303 @@
+//========================================================================
+//
+// Object.h
+//
+// Copyright 1996-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef OBJECT_H
+#define OBJECT_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include "gtypes.h"
+#include "gmem.h"
+#include "GString.h"
+
+class XRef;
+class Array;
+class Dict;
+class Stream;
+
+//------------------------------------------------------------------------
+// Ref
+//------------------------------------------------------------------------
+
+struct Ref {
+  int num;                     // object number
+  int gen;                     // generation number
+};
+
+//------------------------------------------------------------------------
+// object types
+//------------------------------------------------------------------------
+
+enum ObjType {
+  // simple objects
+  objBool,                     // boolean
+  objInt,                      // integer
+  objReal,                     // real
+  objString,                   // string
+  objName,                     // name
+  objNull,                     // null
+
+  // complex objects
+  objArray,                    // array
+  objDict,                     // dictionary
+  objStream,                   // stream
+  objRef,                      // indirect reference
+
+  // special objects
+  objCmd,                      // command name
+  objError,                    // error return from Lexer
+  objEOF,                      // end of file return from Lexer
+  objNone                      // uninitialized object
+};
+
+#define numObjTypes 14         // total number of object types
+
+//------------------------------------------------------------------------
+// Object
+//------------------------------------------------------------------------
+
+#ifdef DEBUG_MEM
+#define initObj(t) ++numAlloc[type = t]
+#else
+#define initObj(t) type = t
+#endif
+
+class Object {
+public:
+
+  // Default constructor.
+  Object():
+    type(objNone) {}
+
+  // Initialize an object.
+  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(XRef *xref);
+  Object *initDict(XRef *xref);
+  Object *initDict(Dict *dictA);
+  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()
+    { initObj(objEOF); return this; }
+
+  // Copy an object.
+  Object *copy(Object *obj);
+
+  // If object is a Ref, fetch and return the referenced object.
+  // Otherwise, return a copy of the object.
+  Object *fetch(XRef *xref, Object *obj);
+
+  // Free object contents.
+  void free();
+
+  // Type checking.
+  ObjType getType() { return type; }
+  GBool isBool() { return type == objBool; }
+  GBool isInt() { return type == objInt; }
+  GBool isReal() { return type == objReal; }
+  GBool isNum() { return type == objInt || type == objReal; }
+  GBool isString() { return type == objString; }
+  GBool isName() { return type == objName; }
+  GBool isNull() { return type == objNull; }
+  GBool isArray() { return type == objArray; }
+  GBool isDict() { return type == objDict; }
+  GBool isStream() { return type == objStream; }
+  GBool isRef() { return type == objRef; }
+  GBool isCmd() { return type == objCmd; }
+  GBool isError() { return type == objError; }
+  GBool isEOF() { return type == objEOF; }
+  GBool isNone() { return type == objNone; }
+
+  // Special type checking.
+  GBool isName(char *nameA)
+    { return type == objName && !strcmp(name, nameA); }
+  GBool isDict(char *dictType);
+  GBool isStream(char *dictType);
+  GBool isCmd(char *cmdA)
+    { return type == objCmd && !strcmp(cmd, cmdA); }
+
+  // Accessors.  NB: these assume object is of correct type.
+  GBool getBool() { return booln; }
+  int getInt() { return intg; }
+  double getReal() { return real; }
+  double getNum() { return type == objInt ? (double)intg : real; }
+  GString *getString() { return string; }
+  char *getName() { return name; }
+  Array *getArray() { return array; }
+  Dict *getDict() { return dict; }
+  Stream *getStream() { return stream; }
+  Ref getRef() { return ref; }
+  int getRefNum() { return ref.num; }
+  int getRefGen() { return ref.gen; }
+  char *getCmd() { return cmd; }
+
+  // Array accessors.
+  int arrayGetLength();
+  void arrayAdd(Object *elem);
+  Object *arrayGet(int i, Object *obj);
+  Object *arrayGetNF(int i, Object *obj);
+
+  // Dict accessors.
+  int dictGetLength();
+  void dictAdd(char *key, Object *val);
+  GBool dictIs(char *dictType);
+  Object *dictLookup(char *key, Object *obj);
+  Object *dictLookupNF(char *key, Object *obj);
+  char *dictGetKey(int i);
+  Object *dictGetVal(int i, Object *obj);
+  Object *dictGetValNF(int i, Object *obj);
+
+  // Stream accessors.
+  GBool streamIs(char *dictType);
+  void streamReset();
+  void streamClose();
+  int streamGetChar();
+  int streamLookChar();
+  char *streamGetLine(char *buf, int size);
+  Guint streamGetPos();
+  void streamSetPos(Guint pos, int dir = 0);
+  Dict *streamGetDict();
+
+  // Output.
+  char *getTypeName();
+  void print(FILE *f = stdout);
+
+  // Memory testing.
+  static void memCheck(FILE *f);
+
+private:
+
+  ObjType type;                        // object type
+  union {                      // value for each type:
+    GBool booln;               //   boolean
+    int intg;                  //   integer
+    double real;               //   real
+    GString *string;           //   string
+    char *name;                        //   name
+    Array *array;              //   array
+    Dict *dict;                        //   dictionary
+    Stream *stream;            //   stream
+    Ref ref;                   //   indirect reference
+    char *cmd;                 //   command
+  };
+
+#ifdef DEBUG_MEM
+  static int                   // number of each type of object
+    numAlloc[numObjTypes];     //   currently allocated
+#endif
+};
+
+//------------------------------------------------------------------------
+// Array accessors.
+//------------------------------------------------------------------------
+
+#include "Array.h"
+
+inline int Object::arrayGetLength()
+  { return array->getLength(); }
+
+inline void Object::arrayAdd(Object *elem)
+  { array->add(elem); }
+
+inline Object *Object::arrayGet(int i, Object *obj)
+  { return array->get(i, obj); }
+
+inline Object *Object::arrayGetNF(int i, Object *obj)
+  { return array->getNF(i, obj); }
+
+//------------------------------------------------------------------------
+// Dict accessors.
+//------------------------------------------------------------------------
+
+#include "Dict.h"
+
+inline int Object::dictGetLength()
+  { return dict->getLength(); }
+
+inline void Object::dictAdd(char *key, Object *val)
+  { dict->add(key, val); }
+
+inline GBool Object::dictIs(char *dictType)
+  { return dict->is(dictType); }
+
+inline GBool Object::isDict(char *dictType)
+  { return type == objDict && dictIs(dictType); }
+
+inline Object *Object::dictLookup(char *key, Object *obj)
+  { return dict->lookup(key, obj); }
+
+inline Object *Object::dictLookupNF(char *key, Object *obj)
+  { return dict->lookupNF(key, obj); }
+
+inline char *Object::dictGetKey(int i)
+  { return dict->getKey(i); }
+
+inline Object *Object::dictGetVal(int i, Object *obj)
+  { return dict->getVal(i, obj); }
+
+inline Object *Object::dictGetValNF(int i, Object *obj)
+  { return dict->getValNF(i, obj); }
+
+//------------------------------------------------------------------------
+// Stream accessors.
+//------------------------------------------------------------------------
+
+#include "Stream.h"
+
+inline GBool Object::streamIs(char *dictType)
+  { return stream->getDict()->is(dictType); }
+
+inline GBool Object::isStream(char *dictType)
+  { return type == objStream && streamIs(dictType); }
+
+inline void Object::streamReset()
+  { stream->reset(); }
+
+inline void Object::streamClose()
+  { stream->close(); }
+
+inline int Object::streamGetChar()
+  { return stream->getChar(); }
+
+inline int Object::streamLookChar()
+  { return stream->lookChar(); }
+
+inline char *Object::streamGetLine(char *buf, int size)
+  { return stream->getLine(buf, size); }
+
+inline Guint Object::streamGetPos()
+  { return stream->getPos(); }
+
+inline void Object::streamSetPos(Guint pos, int dir)
+  { stream->setPos(pos, dir); }
+
+inline Dict *Object::streamGetDict()
+  { return stream->getDict(); }
+
+#endif
diff --git a/lib/xpdf/Outline.cc b/lib/xpdf/Outline.cc
new file mode 100644 (file)
index 0000000..39e89a3
--- /dev/null
@@ -0,0 +1,151 @@
+//========================================================================
+//
+// Outline.cc
+//
+// Copyright 2002-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include "gmem.h"
+#include "GString.h"
+#include "GList.h"
+#include "Link.h"
+#include "PDFDocEncoding.h"
+#include "Outline.h"
+
+//------------------------------------------------------------------------
+
+Outline::Outline(Object *outlineObj, XRef *xref) {
+  Object first, last;
+
+  items = NULL;
+  if (!outlineObj->isDict()) {
+    return;
+  }
+  items = OutlineItem::readItemList(outlineObj->dictLookupNF("First", &first),
+                                   outlineObj->dictLookupNF("Last", &last),
+                                   xref);
+  first.free();
+  last.free();
+}
+
+Outline::~Outline() {
+  if (items) {
+    deleteGList(items, OutlineItem);
+  }
+}
+
+//------------------------------------------------------------------------
+
+OutlineItem::OutlineItem(Dict *dict, XRef *xrefA) {
+  Object obj1;
+  GString *s;
+  int i;
+
+  xref = xrefA;
+  title = NULL;
+  action = NULL;
+  kids = NULL;
+
+  if (dict->lookup("Title", &obj1)->isString()) {
+    s = obj1.getString();
+    if ((s->getChar(0) & 0xff) == 0xfe &&
+       (s->getChar(1) & 0xff) == 0xff) {
+      titleLen = (s->getLength() - 2) / 2;
+      title = (Unicode *)gmallocn(titleLen, sizeof(Unicode));
+      for (i = 0; i < titleLen; ++i) {
+       title[i] = ((s->getChar(2 + 2*i) & 0xff) << 8) |
+                  (s->getChar(3 + 2*i) & 0xff);
+      }
+    } else {
+      titleLen = s->getLength();
+      title = (Unicode *)gmallocn(titleLen, sizeof(Unicode));
+      for (i = 0; i < titleLen; ++i) {
+       title[i] = pdfDocEncoding[s->getChar(i) & 0xff];
+      }
+    }
+  } else {
+    titleLen = 0;
+  }
+  obj1.free();
+
+  if (!dict->lookup("Dest", &obj1)->isNull()) {
+    action = LinkAction::parseDest(&obj1);
+  } else {
+    obj1.free();
+    if (!dict->lookup("A", &obj1)->isNull()) {
+      action = LinkAction::parseAction(&obj1);
+    }
+  }
+  obj1.free();
+
+  dict->lookupNF("First", &firstRef);
+  dict->lookupNF("Last", &lastRef);
+  dict->lookupNF("Next", &nextRef);
+
+  startsOpen = gFalse;
+  if (dict->lookup("Count", &obj1)->isInt()) {
+    if (obj1.getInt() > 0) {
+      startsOpen = gTrue;
+    }
+  }
+  obj1.free();
+}
+
+OutlineItem::~OutlineItem() {
+  close();
+  if (title) {
+    gfree(title);
+  }
+  if (action) {
+    delete action;
+  }
+  firstRef.free();
+  lastRef.free();
+  nextRef.free();
+}
+
+GList *OutlineItem::readItemList(Object *firstItemRef, Object *lastItemRef,
+                                XRef *xrefA) {
+  GList *items;
+  OutlineItem *item;
+  Object obj;
+  Object *p;
+
+  items = new GList();
+  p = firstItemRef;
+  while (p->isRef()) {
+    if (!p->fetch(xrefA, &obj)->isDict()) {
+      obj.free();
+      break;
+    }
+    item = new OutlineItem(obj.getDict(), xrefA);
+    obj.free();
+    items->append(item);
+    if (p->getRef().num == lastItemRef->getRef().num &&
+       p->getRef().gen == lastItemRef->getRef().gen) {
+      break;
+    }
+    p = &item->nextRef;
+  }
+  return items;
+}
+
+void OutlineItem::open() {
+  if (!kids) {
+    kids = readItemList(&firstRef, &lastRef, xref);
+  }
+}
+
+void OutlineItem::close() {
+  if (kids) {
+    deleteGList(kids, OutlineItem);
+    kids = NULL;
+  }
+}
diff --git a/lib/xpdf/Outline.h b/lib/xpdf/Outline.h
new file mode 100644 (file)
index 0000000..f38f8d1
--- /dev/null
@@ -0,0 +1,76 @@
+//========================================================================
+//
+// Outline.h
+//
+// Copyright 2002-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef OUTLINE_H
+#define OUTLINE_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#include "Object.h"
+#include "CharTypes.h"
+
+class GString;
+class GList;
+class XRef;
+class LinkAction;
+
+//------------------------------------------------------------------------
+
+class Outline {
+public:
+
+  Outline(Object *outlineObj, XRef *xref);
+  ~Outline();
+
+  GList *getItems() { return items; }
+
+private:
+
+  GList *items;                        // NULL if document has no outline
+                               //   [OutlineItem]
+};
+
+//------------------------------------------------------------------------
+
+class OutlineItem {
+public:
+
+  OutlineItem(Dict *dict, XRef *xrefA);
+  ~OutlineItem();
+
+  static GList *readItemList(Object *firstItemRef, Object *lastItemRef,
+                            XRef *xrefA);
+
+  void open();
+  void close();
+
+  Unicode *getTitle() { return title; }
+  int getTitleLength() { return titleLen; }
+  LinkAction *getAction() { return action; }
+  GBool isOpen() { return startsOpen; }
+  GBool hasKids() { return firstRef.isRef(); }
+  GList *getKids() { return kids; }
+
+private:
+
+  XRef *xref;
+  Unicode *title;
+  int titleLen;
+  LinkAction *action;
+  Object firstRef;
+  Object lastRef;
+  Object nextRef;
+  GBool startsOpen;
+  GList *kids;                 // NULL unless this item is open [OutlineItem]
+};
+
+#endif
diff --git a/lib/xpdf/OutputDev.cc b/lib/xpdf/OutputDev.cc
new file mode 100644 (file)
index 0000000..cfa4161
--- /dev/null
@@ -0,0 +1,129 @@
+//========================================================================
+//
+// OutputDev.cc
+//
+// Copyright 1996-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <stddef.h>
+#include "Object.h"
+#include "Stream.h"
+#include "GfxState.h"
+#include "OutputDev.h"
+
+//------------------------------------------------------------------------
+// OutputDev
+//------------------------------------------------------------------------
+
+void OutputDev::setDefaultCTM(double *ctm) {
+  int i;
+  double 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(double dx, double dy, double *ux, double *uy) {
+  *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)(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) {
+  updateLineDash(state);
+  updateFlatness(state);
+  updateLineJoin(state);
+  updateLineCap(state);
+  updateMiterLimit(state);
+  updateLineWidth(state);
+  updateFillColorSpace(state);
+  updateFillColor(state);
+  updateStrokeColorSpace(state);
+  updateStrokeColor(state);
+  updateBlendMode(state);
+  updateFillOpacity(state);
+  updateStrokeOpacity(state);
+  updateFillOverprint(state);
+  updateStrokeOverprint(state);
+  updateFont(state);
+}
+
+GBool OutputDev::beginType3Char(GfxState *state, double x, double y,
+                               double dx, double dy,
+                               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) {
+  int i, j;
+
+  if (inlineImg) {
+    str->reset();
+    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,
+                         int *maskColors, GBool inlineImg) {
+  int i, j;
+
+  if (inlineImg) {
+    str->reset();
+    j = height * ((width * colorMap->getNumPixelComps() *
+                  colorMap->getBits() + 7) / 8);
+    for (i = 0; i < j; ++i)
+      str->getChar();
+    str->close();
+  }
+}
+
+void OutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str,
+                               int width, int height,
+                               GfxImageColorMap *colorMap,
+                               Stream *maskStr,
+                               int maskWidth, int maskHeight,
+                               GBool maskInvert) {
+  drawImage(state, ref, str, width, height, colorMap, NULL, gFalse);
+}
+
+void OutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str,
+                                   int width, int height,
+                                   GfxImageColorMap *colorMap,
+                                   Stream *maskStr,
+                                   int maskWidth, int maskHeight,
+                                   GfxImageColorMap *maskColorMap) {
+  drawImage(state, ref, str, width, height, colorMap, NULL, gFalse);
+}
+
+#if OPI_SUPPORT
+void OutputDev::opiBegin(GfxState *state, Dict *opiDict) {
+}
+
+void OutputDev::opiEnd(GfxState *state, Dict *opiDict) {
+}
+#endif
diff --git a/lib/xpdf/OutputDev.h b/lib/xpdf/OutputDev.h
new file mode 100644 (file)
index 0000000..8d2c9f3
--- /dev/null
@@ -0,0 +1,205 @@
+//========================================================================
+//
+// OutputDev.h
+//
+// Copyright 1996-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef OUTPUTDEV_H
+#define OUTPUTDEV_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#include "gtypes.h"
+#include "CharTypes.h"
+
+class GString;
+class GfxState;
+class GfxColorSpace;
+class GfxImageColorMap;
+class GfxFunctionShading;
+class GfxAxialShading;
+class GfxRadialShading;
+class Stream;
+class Link;
+class Catalog;
+
+//------------------------------------------------------------------------
+// OutputDev
+//------------------------------------------------------------------------
+
+class OutputDev {
+public:
+
+  // Constructor.
+  OutputDev() {}
+
+  // Destructor.
+  virtual ~OutputDev() {}
+
+  //----- get info about output device
+
+  // Does this device use upside-down coordinates?
+  // (Upside-down means (0,0) is the top left corner of the page.)
+  virtual GBool upsideDown() = 0;
+
+  // Does this device use drawChar() or drawString()?
+  virtual GBool useDrawChar() = 0;
+
+  // Does this device use tilingPatternFill()?  If this returns false,
+  // tiling pattern fills will be reduced to a series of other drawing
+  // operations.
+  virtual GBool useTilingPatternFill() { return gFalse; }
+
+  // Does this device use functionShadedFill(), axialShadedFill(), and
+  // radialShadedFill()?  If this returns false, these shaded fills
+  // will be reduced to a series of other drawing operations.
+  virtual GBool useShadedFills() { return gFalse; }
+
+  // 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 *ctm);
+
+  // Start a page.
+  virtual void startPage(int pageNum, GfxState *state, double x1,double y1,double x2,double y2) {}
+
+  // End a page.
+  virtual void endPage() {}
+
+  // Dump page contents to display.
+  virtual void dump() {}
+
+  //----- coordinate conversion
+
+  // Convert between device and user coordinates.
+  virtual void cvtDevToUser(double dx, double dy, double *ux, double *uy);
+  virtual void cvtUserToDev(double ux, double uy, int *dx, int *dy);
+
+  double *getDefCTM() { return defCTM; }
+  double *getDefICTM() { return defICTM; }
+
+  //----- link borders
+  virtual void drawLink(Link *link, Catalog *catalog) {}
+
+  //----- save/restore graphics state
+  virtual void saveState(GfxState *state) {}
+  virtual void restoreState(GfxState *state) {}
+
+  //----- update graphics state
+  virtual void updateAll(GfxState *state);
+  virtual void updateCTM(GfxState *state, double m11, double m12,
+                        double m21, double m22, double m31, double m32) {}
+  virtual void updateLineDash(GfxState *state) {}
+  virtual void updateFlatness(GfxState *state) {}
+  virtual void updateLineJoin(GfxState *state) {}
+  virtual void updateLineCap(GfxState *state) {}
+  virtual void updateMiterLimit(GfxState *state) {}
+  virtual void updateLineWidth(GfxState *state) {}
+  virtual void updateFillColorSpace(GfxState *state) {}
+  virtual void updateStrokeColorSpace(GfxState *state) {}
+  virtual void updateFillColor(GfxState *state) {}
+  virtual void updateStrokeColor(GfxState *state) {}
+  virtual void updateBlendMode(GfxState *state) {}
+  virtual void updateFillOpacity(GfxState *state) {}
+  virtual void updateStrokeOpacity(GfxState *state) {}
+  virtual void updateFillOverprint(GfxState *state) {}
+  virtual void updateStrokeOverprint(GfxState *state) {}
+
+  //----- update text state
+  virtual void updateFont(GfxState *state) {}
+  virtual void updateTextMat(GfxState *state) {}
+  virtual void updateCharSpace(GfxState *state) {}
+  virtual void updateRender(GfxState *state) {}
+  virtual void updateRise(GfxState *state) {}
+  virtual void updateWordSpace(GfxState *state) {}
+  virtual void updateHorizScaling(GfxState *state) {}
+  virtual void updateTextPos(GfxState *state) {}
+  virtual void updateTextShift(GfxState *state, double shift) {}
+
+  //----- path painting
+  virtual void stroke(GfxState *state) {}
+  virtual void fill(GfxState *state) {}
+  virtual void eoFill(GfxState *state) {}
+  virtual void tilingPatternFill(GfxState *state, Object *str,
+                                int paintType, Dict *resDict,
+                                double *mat, double *bbox,
+                                int x0, int y0, int x1, int y1,
+                                double xStep, double yStep) {}
+  virtual void functionShadedFill(GfxState *state,
+                                 GfxFunctionShading *shading) {}
+  virtual void axialShadedFill(GfxState *state, GfxAxialShading *shading) {}
+  virtual void radialShadedFill(GfxState *state, GfxRadialShading *shading) {}
+
+  //----- path clipping
+  virtual void clip(GfxState *state) {}
+  virtual void eoClip(GfxState *state) {}
+
+  //----- text drawing
+  virtual void beginStringOp(GfxState *state) {}
+  virtual void endStringOp(GfxState *state) {}
+  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,
+                       double originX, double originY,
+                       CharCode code, int nBytes, Unicode *u, int uLen) {}
+  virtual void drawString(GfxState *state, GString *s) {}
+  virtual GBool beginType3Char(GfxState *state, double x, double y,
+                              double dx, double dy,
+                              CharCode code, Unicode *u, int uLen);
+  virtual void endType3Char(GfxState *state) {}
+  virtual void endTextObject(GfxState *state) {}
+
+  //----- image drawing
+  virtual void drawImageMask(GfxState *state, Object *ref, Stream *str,
+                            int width, int height, GBool invert,
+                            GBool inlineImg);
+  virtual void drawImage(GfxState *state, Object *ref, Stream *str,
+                        int width, int height, GfxImageColorMap *colorMap,
+                        int *maskColors, GBool inlineImg);
+  virtual void drawMaskedImage(GfxState *state, Object *ref, Stream *str,
+                              int width, int height,
+                              GfxImageColorMap *colorMap,
+                              Stream *maskStr, int maskWidth, int maskHeight,
+                              GBool maskInvert);
+  virtual void drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str,
+                                  int width, int height,
+                                  GfxImageColorMap *colorMap,
+                                  Stream *maskStr,
+                                  int maskWidth, int maskHeight,
+                                  GfxImageColorMap *maskColorMap);
+
+#if OPI_SUPPORT
+  //----- OPI functions
+  virtual void opiBegin(GfxState *state, Dict *opiDict);
+  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 defCTM[6];            // default coordinate transform matrix
+  double defICTM[6];           // inverse of default CTM
+};
+
+#endif
diff --git a/lib/xpdf/PDFDoc.cc b/lib/xpdf/PDFDoc.cc
new file mode 100644 (file)
index 0000000..92863e4
--- /dev/null
@@ -0,0 +1,431 @@
+//========================================================================
+//
+// PDFDoc.cc
+//
+// Copyright 1996-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <string.h>
+#ifdef WIN32
+#  include <windows.h>
+#endif
+#include "GString.h"
+#include "config.h"
+#include "GlobalParams.h"
+#include "Page.h"
+#include "Catalog.h"
+#include "Stream.h"
+#include "XRef.h"
+#include "Link.h"
+#include "OutputDev.h"
+#include "Error.h"
+#include "ErrorCodes.h"
+#include "Lexer.h"
+#include "Parser.h"
+#include "SecurityHandler.h"
+#ifndef DISABLE_OUTLINE
+#include "Outline.h"
+#endif
+#include "PDFDoc.h"
+
+//------------------------------------------------------------------------
+
+#define headerSearchSize 1024  // read this many bytes at beginning of
+                               //   file to look for '%PDF'
+
+//------------------------------------------------------------------------
+// PDFDoc
+//------------------------------------------------------------------------
+
+PDFDoc::PDFDoc(GString *fileNameA, GString *ownerPassword,
+              GString *userPassword, void *guiDataA) {
+  Object obj;
+  GString *fileName1, *fileName2;
+
+  ok = gFalse;
+  errCode = errNone;
+
+  guiData = guiDataA;
+
+  file = NULL;
+  str = NULL;
+  xref = NULL;
+  catalog = NULL;
+  links = NULL;
+#ifndef DISABLE_OUTLINE
+  outline = NULL;
+#endif
+
+  fileName = fileNameA;
+  fileName1 = fileName;
+
+
+  // try to open file
+  fileName2 = NULL;
+#ifdef VMS
+  if (!(file = fopen(fileName1->getCString(), "rb", "ctx=stm"))) {
+    error(-1, "Couldn't open file '%s'", fileName1->getCString());
+    errCode = errOpenFile;
+    return;
+  }
+#else
+  if (!(file = fopen(fileName1->getCString(), "rb"))) {
+    fileName2 = fileName->copy();
+    fileName2->lowerCase();
+    if (!(file = fopen(fileName2->getCString(), "rb"))) {
+      fileName2->upperCase();
+      if (!(file = fopen(fileName2->getCString(), "rb"))) {
+       error(-1, "Couldn't open file '%s'", fileName->getCString());
+       delete fileName2;
+       errCode = errOpenFile;
+       return;
+      }
+    }
+    delete fileName2;
+  }
+#endif
+
+  // create stream
+  obj.initNull();
+  str = new FileStream(file, 0, gFalse, 0, &obj);
+
+  ok = setup(ownerPassword, userPassword);
+}
+
+#ifdef WIN32
+PDFDoc::PDFDoc(wchar_t *fileNameA, int fileNameLen, GString *ownerPassword,
+              GString *userPassword, void *guiDataA) {
+  OSVERSIONINFO version;
+  wchar_t fileName2[_MAX_PATH + 1];
+  Object obj;
+  int i;
+
+  ok = gFalse;
+  errCode = errNone;
+
+  guiData = guiDataA;
+
+  file = NULL;
+  str = NULL;
+  xref = NULL;
+  catalog = NULL;
+  links = NULL;
+#ifndef DISABLE_OUTLINE
+  outline = NULL;
+#endif
+
+  //~ file name should be stored in Unicode (?)
+  fileName = new GString();
+  for (i = 0; i < fileNameLen; ++i) {
+    fileName->append((char)fileNameA[i]);
+  }
+
+  // zero-terminate the file name string
+  for (i = 0; i < fileNameLen && i < _MAX_PATH; ++i) {
+    fileName2[i] = fileNameA[i];
+  }
+  fileName2[i] = 0;
+
+  // try to open file
+  // NB: _wfopen is only available in NT
+  version.dwOSVersionInfoSize = sizeof(version);
+  GetVersionEx(&version);
+  if (version.dwPlatformId == VER_PLATFORM_WIN32_NT) {
+    file = _wfopen(fileName2, L"rb");
+  } else {
+    file = fopen(fileName->getCString(), "rb");
+  }
+  if (!file) {
+    error(-1, "Couldn't open file '%s'", fileName->getCString());
+    errCode = errOpenFile;
+    return;
+  }
+
+  // create stream
+  obj.initNull();
+  str = new FileStream(file, 0, gFalse, 0, &obj);
+
+  ok = setup(ownerPassword, userPassword);
+}
+#endif
+
+PDFDoc::PDFDoc(BaseStream *strA, GString *ownerPassword,
+              GString *userPassword, void *guiDataA) {
+  ok = gFalse;
+  errCode = errNone;
+  guiData = guiDataA;
+  fileName = NULL;
+  file = NULL;
+  str = strA;
+  xref = NULL;
+  catalog = NULL;
+  links = NULL;
+#ifndef DISABLE_OUTLINE
+  outline = NULL;
+#endif
+  ok = setup(ownerPassword, userPassword);
+}
+
+GBool PDFDoc::setup(GString *ownerPassword, GString *userPassword) {
+  str->reset();
+
+  // check header
+  checkHeader();
+
+  // read xref table
+  xref = new XRef(str);
+  if (!xref->isOk()) {
+    error(-1, "Couldn't read xref table");
+    errCode = xref->getErrorCode();
+    return gFalse;
+  }
+
+  // check for encryption
+  if (!checkEncryption(ownerPassword, userPassword)) {
+    errCode = errEncrypted;
+    return gFalse;
+  }
+
+  // read catalog
+  catalog = new Catalog(xref);
+  if (!catalog->isOk()) {
+    error(-1, "Couldn't read page catalog");
+    errCode = errBadCatalog;
+    return gFalse;
+  }
+
+#ifndef DISABLE_OUTLINE
+  // read outline
+  outline = new Outline(catalog->getOutline(), xref);
+#endif
+
+  // done
+  return gTrue;
+}
+
+PDFDoc::~PDFDoc() {
+#ifndef DISABLE_OUTLINE
+  if (outline) {
+    delete outline;
+  }
+#endif
+  if (catalog) {
+    delete catalog;
+  }
+  if (xref) {
+    delete xref;
+  }
+  if (str) {
+    delete str;
+  }
+  if (file) {
+    fclose(file);
+  }
+  if (fileName) {
+    delete fileName;
+  }
+  if (links) {
+    delete links;
+  }
+}
+
+// Check for a PDF header on this stream.  Skip past some garbage
+// if necessary.
+void PDFDoc::checkHeader() {
+  char hdrBuf[headerSearchSize+1];
+  char *p;
+  int i;
+
+  pdfVersion = 0;
+  for (i = 0; i < headerSearchSize; ++i) {
+    hdrBuf[i] = str->getChar();
+  }
+  hdrBuf[headerSearchSize] = '\0';
+  for (i = 0; i < headerSearchSize - 5; ++i) {
+    if (!strncmp(&hdrBuf[i], "%PDF-", 5)) {
+      break;
+    }
+  }
+  if (i >= headerSearchSize - 5) {
+    error(-1, "May not be a PDF file (continuing anyway)");
+    return;
+  }
+  str->moveStart(i);
+  if (!(p = strtok(&hdrBuf[i+5], " \t\n\r"))) {
+    error(-1, "May not be a PDF file (continuing anyway)");
+    return;
+  }
+  pdfVersion = atof(p);
+  if (!(hdrBuf[i+5] >= '0' && hdrBuf[i+5] <= '9') ||
+      pdfVersion > supportedPDFVersionNum + 0.0001) {
+    error(-1, "PDF version %s -- xpdf supports version %s"
+         " (continuing anyway)", p, supportedPDFVersionStr);
+  }
+}
+
+GBool PDFDoc::checkEncryption(GString *ownerPassword, GString *userPassword) {
+  Object encrypt;
+  GBool encrypted;
+  SecurityHandler *secHdlr;
+  GBool ret;
+
+  xref->getTrailerDict()->dictLookup("Encrypt", &encrypt);
+  if ((encrypted = encrypt.isDict())) {
+    if ((secHdlr = SecurityHandler::make(this, &encrypt))) {
+      if (secHdlr->checkEncryption(ownerPassword, userPassword)) {
+       // authorization succeeded
+               xref->setEncryption(secHdlr->getPermissionFlags(),
+                           secHdlr->getOwnerPasswordOk(),
+                           secHdlr->getFileKey(),
+                           secHdlr->getFileKeyLength(),
+                           secHdlr->getEncVersion());
+       ret = gTrue;
+      } else {
+       // authorization failed
+       ret = gFalse;
+      }
+      delete secHdlr;
+    } else {
+      // couldn't find the matching security handler
+      ret = gFalse;
+    }
+  } else {
+    // document is not encrypted
+    ret = gTrue;
+  }
+  encrypt.free();
+  return ret;
+}
+
+void PDFDoc::displayPage(OutputDev *out, int page, double hDPI, double vDPI,
+                        int rotate, GBool useMediaBox, GBool crop,
+                        GBool doLinks,
+                        GBool (*abortCheckCbk)(void *data),
+                        void *abortCheckCbkData) {
+  Page *p;
+
+  if (globalParams->getPrintCommands()) {
+    printf("***** page %d *****\n", page);
+  }
+  p = catalog->getPage(page);
+  if (doLinks) {
+    if (links) {
+      delete links;
+    }
+    getLinks(p);
+    p->display(out, hDPI, vDPI, rotate, useMediaBox, crop, links, catalog,
+              abortCheckCbk, abortCheckCbkData);
+  } else {
+    p->display(out, hDPI, vDPI, rotate, useMediaBox, crop, NULL, catalog,
+              abortCheckCbk, abortCheckCbkData);
+  }
+}
+
+void PDFDoc::displayPages(OutputDev *out, int firstPage, int lastPage,
+                         double hDPI, double vDPI, int rotate,
+                         GBool useMediaBox, GBool crop, GBool doLinks,
+                         GBool (*abortCheckCbk)(void *data),
+                         void *abortCheckCbkData) {
+  int page;
+
+  for (page = firstPage; page <= lastPage; ++page) {
+    displayPage(out, page, hDPI, vDPI, rotate, useMediaBox, crop, doLinks,
+               abortCheckCbk, abortCheckCbkData);
+  }
+}
+
+void PDFDoc::displayPageSlice(OutputDev *out, int page,
+                             double hDPI, double vDPI, int rotate,
+                             GBool useMediaBox, GBool crop, GBool doLinks,
+                             int sliceX, int sliceY, int sliceW, int sliceH,
+                             GBool (*abortCheckCbk)(void *data),
+                             void *abortCheckCbkData) {
+  Page *p;
+
+  p = catalog->getPage(page);
+  if (doLinks) {
+    if (links) {
+      delete links;
+    }
+    getLinks(p);
+    p->displaySlice(out, hDPI, vDPI, rotate, useMediaBox, crop,
+                   sliceX, sliceY, sliceW, sliceH,
+                   links, catalog, abortCheckCbk, abortCheckCbkData);
+  } else {
+    p->displaySlice(out, hDPI, vDPI, rotate, useMediaBox, crop,
+                   sliceX, sliceY, sliceW, sliceH,
+                   NULL, catalog, abortCheckCbk, abortCheckCbkData);
+  }
+}
+
+Links *PDFDoc::takeLinks() {
+  Links *ret;
+
+  ret = links;
+  links = NULL;
+  return ret;
+}
+
+GBool PDFDoc::isLinearized() {
+  Parser *parser;
+  Object obj1, obj2, obj3, obj4, obj5;
+  GBool lin;
+
+  lin = gFalse;
+  obj1.initNull();
+  parser = new Parser(xref,
+            new Lexer(xref,
+              str->makeSubStream(str->getStart(), gFalse, 0, &obj1)));
+  parser->getObj(&obj1);
+  parser->getObj(&obj2);
+  parser->getObj(&obj3);
+  parser->getObj(&obj4);
+  if (obj1.isInt() && obj2.isInt() && obj3.isCmd("obj") &&
+      obj4.isDict()) {
+    obj4.dictLookup("Linearized", &obj5);
+    if (obj5.isNum() && obj5.getNum() > 0) {
+      lin = gTrue;
+    }
+    obj5.free();
+  }
+  obj4.free();
+  obj3.free();
+  obj2.free();
+  obj1.free();
+  delete parser;
+  return lin;
+}
+
+GBool PDFDoc::saveAs(GString *name) {
+  FILE *f;
+  int c;
+
+  if (!(f = fopen(name->getCString(), "wb"))) {
+    error(-1, "Couldn't open file '%s'", name->getCString());
+    return gFalse;
+  }
+  str->reset();
+  while ((c = str->getChar()) != EOF) {
+    fputc(c, f);
+  }
+  str->close();
+  fclose(f);
+  return gTrue;
+}
+
+void PDFDoc::getLinks(Page *page) {
+  Object obj;
+
+  links = new Links(page->getAnnots(&obj), catalog->getBaseURI());
+  obj.free();
+}
diff --git a/lib/xpdf/PDFDoc.h b/lib/xpdf/PDFDoc.h
new file mode 100644 (file)
index 0000000..a1f42c0
--- /dev/null
@@ -0,0 +1,182 @@
+//========================================================================
+//
+// PDFDoc.h
+//
+// Copyright 1996-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef PDFDOC_H
+#define PDFDOC_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#include <stdio.h>
+#include "XRef.h"
+#include "Catalog.h"
+#include "Page.h"
+
+class GString;
+class BaseStream;
+class OutputDev;
+class Links;
+class LinkAction;
+class LinkDest;
+class Outline;
+
+//------------------------------------------------------------------------
+// PDFDoc
+//------------------------------------------------------------------------
+
+class PDFDoc {
+public:
+
+  PDFDoc(GString *fileNameA, GString *ownerPassword = NULL,
+        GString *userPassword = NULL, void *guiDataA = NULL);
+#ifdef WIN32
+  PDFDoc(wchar_t *fileNameA, int fileNameLen, GString *ownerPassword = NULL,
+        GString *userPassword = NULL, void *guiDataA = NULL);
+#endif
+  PDFDoc(BaseStream *strA, GString *ownerPassword = NULL,
+        GString *userPassword = NULL, void *guiDataA = NULL);
+  ~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; }
+
+  // Get base stream.
+  BaseStream *getBaseStream() { return str; }
+
+  // Get page parameters.
+  double getPageMediaWidth(int page)
+    { return catalog->getPage(page)->getMediaWidth(); }
+  double getPageMediaHeight(int page)
+    { return catalog->getPage(page)->getMediaHeight(); }
+  double getPageCropWidth(int page)
+    { return catalog->getPage(page)->getCropWidth(); }
+  double getPageCropHeight(int page)
+    { return catalog->getPage(page)->getCropHeight(); }
+  int getPageRotate(int page)
+    { return catalog->getPage(page)->getRotate(); }
+
+  // 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 hDPI, double vDPI,
+                  int rotate, GBool useMediaBox, GBool crop,
+                  GBool doLinks,
+                  GBool (*abortCheckCbk)(void *data) = NULL,
+                  void *abortCheckCbkData = NULL);
+
+  // Display a range of pages.
+  void displayPages(OutputDev *out, int firstPage, int lastPage,
+                   double hDPI, double vDPI, int rotate,
+                   GBool useMediaBox, GBool crop, GBool doLinks,
+                   GBool (*abortCheckCbk)(void *data) = NULL,
+                   void *abortCheckCbkData = NULL);
+
+  // Display part of a page.
+  void displayPageSlice(OutputDev *out, int page,
+                       double hDPI, double vDPI, int rotate,
+                       GBool useMediaBox, GBool crop, GBool doLinks,
+                       int sliceX, int sliceY, int sliceW, int sliceH,
+                       GBool (*abortCheckCbk)(void *data) = NULL,
+                       void *abortCheckCbkData = NULL);
+
+  // Find a page, given its object ID.  Returns page number, or 0 if
+  // not found.
+  int findPage(int num, int gen) { return catalog->findPage(num, gen); }
+
+  // Returns the links for the current page, transferring ownership to
+  // the caller.
+  Links *takeLinks();
+
+  // Find a named destination.  Returns the link destination, or
+  // NULL if <name> is not a destination.
+  LinkDest *findDest(GString *name)
+    { return catalog->findDest(name); }
+
+#ifndef DISABLE_OUTLINE
+  // Return the outline object.
+  Outline *getOutline() { return outline; }
+#endif
+
+  // Is the file encrypted?
+  GBool isEncrypted() { return xref->isEncrypted(); }
+
+  // Check various permissions.
+  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();
+
+  // Return the document's Info dictionary (if any).
+  Object *getDocInfo(Object *obj) { return xref->getDocInfo(obj); }
+  Object *getDocInfoNF(Object *obj) { return xref->getDocInfoNF(obj); }
+
+  // Return the PDF version specified by the file.
+  double getPDFVersion() { return pdfVersion; }
+
+  // Save this file with another name.
+  GBool saveAs(GString *name);
+
+  // Return a pointer to the GUI (XPDFCore or WinPDFCore object).
+  void *getGUIData() { return guiData; }
+
+
+private:
+
+  GBool setup(GString *ownerPassword, GString *userPassword);
+  void checkHeader();
+  GBool checkEncryption(GString *ownerPassword, GString *userPassword);
+  void getLinks(Page *page);
+
+  GString *fileName;
+  FILE *file;
+  BaseStream *str;
+  void *guiData;
+  double pdfVersion;
+  XRef *xref;
+  Catalog *catalog;
+  Links *links;
+#ifndef DISABLE_OUTLINE
+  Outline *outline;
+#endif
+
+
+  GBool ok;
+  int errCode;
+};
+
+#endif
diff --git a/lib/xpdf/PDFDocEncoding.cc b/lib/xpdf/PDFDocEncoding.cc
new file mode 100644 (file)
index 0000000..89dc382
--- /dev/null
@@ -0,0 +1,44 @@
+//========================================================================
+//
+// PDFDocEncoding.h
+//
+// Copyright 2002-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include "PDFDocEncoding.h"
+
+Unicode pdfDocEncoding[256] = {
+  0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, // 00
+  0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+  0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, // 10
+  0x02d8, 0x02c7, 0x02c6, 0x02d9, 0x02dd, 0x02db, 0x02da, 0x02dc,
+  0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, // 20
+  0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f,
+  0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, // 30
+  0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f,
+  0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, // 40
+  0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f,
+  0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, // 50
+  0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f,
+  0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, // 60
+  0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f,
+  0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, // 70
+  0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x0000,
+  0x2022, 0x2020, 0x2021, 0x2026, 0x2014, 0x2013, 0x0192, 0x2044, // 80
+  0x2039, 0x203a, 0x2212, 0x2030, 0x201e, 0x201c, 0x201d, 0x2018,
+  0x2019, 0x201a, 0x2122, 0xfb01, 0xfb02, 0x0141, 0x0152, 0x0160, // 90
+  0x0178, 0x017d, 0x0131, 0x0142, 0x0153, 0x0161, 0x017e, 0x0000,
+  0x20ac, 0x00a1, 0x00a2, 0x00a3, 0x00a4, 0x00a5, 0x00a6, 0x00a7, // a0
+  0x00a8, 0x00a9, 0x00aa, 0x00ab, 0x00ac, 0x0000, 0x00ae, 0x00af,
+  0x00b0, 0x00b1, 0x00b2, 0x00b3, 0x00b4, 0x00b5, 0x00b6, 0x00b7, // b0
+  0x00b8, 0x00b9, 0x00ba, 0x00bb, 0x00bc, 0x00bd, 0x00be, 0x00bf,
+  0x00c0, 0x00c1, 0x00c2, 0x00c3, 0x00c4, 0x00c5, 0x00c6, 0x00c7, // c0
+  0x00c8, 0x00c9, 0x00ca, 0x00cb, 0x00cc, 0x00cd, 0x00ce, 0x00cf,
+  0x00d0, 0x00d1, 0x00d2, 0x00d3, 0x00d4, 0x00d5, 0x00d6, 0x00d7, // d0
+  0x00d8, 0x00d9, 0x00da, 0x00db, 0x00dc, 0x00dd, 0x00de, 0x00df,
+  0x00e0, 0x00e1, 0x00e2, 0x00e3, 0x00e4, 0x00e5, 0x00e6, 0x00e7, // e0
+  0x00e8, 0x00e9, 0x00ea, 0x00eb, 0x00ec, 0x00ed, 0x00ee, 0x00ef,
+  0x00f0, 0x00f1, 0x00f2, 0x00f3, 0x00f4, 0x00f5, 0x00f6, 0x00f7, // f0
+  0x00f8, 0x00f9, 0x00fa, 0x00fb, 0x00fc, 0x00fd, 0x00fe, 0x00ff
+};
diff --git a/lib/xpdf/PDFDocEncoding.h b/lib/xpdf/PDFDocEncoding.h
new file mode 100644 (file)
index 0000000..3259d3e
--- /dev/null
@@ -0,0 +1,16 @@
+//========================================================================
+//
+// PDFDocEncoding.h
+//
+// Copyright 2002-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef PDFDOCENCODING_H
+#define PDFDOCENCODING_H
+
+#include "CharTypes.h"
+
+extern Unicode pdfDocEncoding[256];
+
+#endif
diff --git a/lib/xpdf/PSTokenizer.cc b/lib/xpdf/PSTokenizer.cc
new file mode 100644 (file)
index 0000000..a65c324
--- /dev/null
@@ -0,0 +1,135 @@
+//========================================================================
+//
+// PSTokenizer.cc
+//
+// Copyright 2002-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include "PSTokenizer.h"
+
+//------------------------------------------------------------------------
+
+// A '1' in this array means the character is white space.  A '1' or
+// '2' means the character ends a name or command.
+static char specialChars[256] = {
+  1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0,   // 0x
+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,   // 1x
+  1, 0, 0, 0, 0, 2, 0, 0, 2, 2, 0, 0, 0, 0, 0, 2,   // 2x
+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 2, 0,   // 3x
+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,   // 4x
+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 2, 0, 0,   // 5x
+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,   // 6x
+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 2, 0, 0,   // 7x
+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,   // 8x
+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,   // 9x
+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,   // ax
+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,   // bx
+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,   // cx
+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,   // dx
+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,   // ex
+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0    // fx
+};
+
+//------------------------------------------------------------------------
+
+PSTokenizer::PSTokenizer(int (*getCharFuncA)(void *), void *dataA) {
+  getCharFunc = getCharFuncA;
+  data = dataA;
+  charBuf = -1;
+}
+
+PSTokenizer::~PSTokenizer() {
+}
+
+GBool PSTokenizer::getToken(char *buf, int size, int *length) {
+  GBool comment, backslash;
+  int c;
+  int i;
+
+  // skip whitespace and comments
+  comment = gFalse;
+  while (1) {
+    if ((c = getChar()) == EOF) {
+      buf[0] = '\0';
+      *length = 0;
+      return gFalse;
+    }
+    if (comment) {
+      if (c == '\x0a' || c == '\x0d') {
+       comment = gFalse;
+      }
+    } else if (c == '%') {
+      comment = gTrue;
+    } else if (specialChars[c] != 1) {
+      break;
+    }
+  }
+
+  // read a token
+  i = 0;
+  buf[i++] = c;
+  if (c == '(') {
+    backslash = gFalse;
+    while ((c = lookChar()) != EOF) {
+      if (i < size - 1) {
+       buf[i++] = c;
+      }
+      getChar();
+      if (c == '\\') {
+       backslash = gTrue;
+      } else if (!backslash && c == ')') {
+       break;
+      } else {
+       backslash = gFalse;
+      }
+    }
+  } else if (c == '<') {
+    while ((c = lookChar()) != EOF) {
+      getChar();
+      if (i < size - 1) {
+       buf[i++] = c;
+      }
+      if (c == '>') {
+       break;
+      }
+    }
+  } else if (c != '[' && c != ']') {
+    while ((c = lookChar()) != EOF && !specialChars[c]) {
+      getChar();
+      if (i < size - 1) {
+       buf[i++] = c;
+      }
+    }
+  }
+  buf[i] = '\0';
+  *length = i;
+
+  return gTrue;
+}
+
+int PSTokenizer::lookChar() {
+  if (charBuf < 0) {
+    charBuf = (*getCharFunc)(data);
+  }
+  return charBuf;
+}
+
+int PSTokenizer::getChar() {
+  int c;
+
+  if (charBuf < 0) {
+    charBuf = (*getCharFunc)(data);
+  }
+  c = charBuf;
+  charBuf = -1;
+  return c;
+}
diff --git a/lib/xpdf/PSTokenizer.h b/lib/xpdf/PSTokenizer.h
new file mode 100644 (file)
index 0000000..4d5ee97
--- /dev/null
@@ -0,0 +1,41 @@
+//========================================================================
+//
+// PSTokenizer.h
+//
+// Copyright 2002-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef PSTOKENIZER_H
+#define PSTOKENIZER_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#include "gtypes.h"
+
+//------------------------------------------------------------------------
+
+class PSTokenizer {
+public:
+
+  PSTokenizer(int (*getCharFuncA)(void *), void *dataA);
+  ~PSTokenizer();
+
+  // Get the next PostScript token.  Returns false at end-of-stream.
+  GBool getToken(char *buf, int size, int *length);
+
+private:
+
+  int lookChar();
+  int getChar();
+
+  int (*getCharFunc)(void *);
+  void *data;
+  int charBuf;
+};
+
+#endif
diff --git a/lib/xpdf/Page.cc b/lib/xpdf/Page.cc
new file mode 100644 (file)
index 0000000..5c2ae8b
--- /dev/null
@@ -0,0 +1,380 @@
+//========================================================================
+//
+// Page.cc
+//
+// Copyright 1996-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <stddef.h>
+#include "GlobalParams.h"
+#include "Object.h"
+#include "Array.h"
+#include "Dict.h"
+#include "XRef.h"
+#include "Link.h"
+#include "OutputDev.h"
+#ifndef PDF_PARSER_ONLY
+#include "Gfx.h"
+#include "GfxState.h"
+#include "Annot.h"
+#endif
+#include "Error.h"
+#include "Page.h"
+
+//------------------------------------------------------------------------
+// PageAttrs
+//------------------------------------------------------------------------
+
+PageAttrs::PageAttrs(PageAttrs *attrs, Dict *dict) {
+  Object obj1;
+
+  // get old/default values
+  if (attrs) {
+    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
+    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
+  readBox(dict, "MediaBox", &mediaBox);
+
+  // crop box
+  if (readBox(dict, "CropBox", &cropBox)) {
+    haveCropBox = gTrue;
+  }
+  if (!haveCropBox) {
+    cropBox = mediaBox;
+  }
+
+  /* if the crop box is larger than the media box, cut it down to 
+     media box size */
+  if(haveCropBox &&
+     mediaBox.x1 <= cropBox.x2 &&
+     mediaBox.y1 <= cropBox.y2 &&
+     cropBox.x1 <= mediaBox.x2 &&
+     cropBox.y1 <= mediaBox.y2) {
+      if(mediaBox.x1 >= cropBox.x1) cropBox.x1 = mediaBox.x1;
+      if(mediaBox.y1 >= cropBox.y1) cropBox.y1 = mediaBox.y1;
+      if(mediaBox.x2 <= cropBox.x2) cropBox.x2 = mediaBox.x2;
+      if(mediaBox.y2 <= cropBox.y2) cropBox.y2 = mediaBox.y2;
+  }
+
+  // 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()) {
+    rotate = obj1.getInt();
+  }
+  obj1.free();
+  while (rotate < 0) {
+    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);
+  if (obj1.isDict()) {
+    resources.free();
+    obj1.copy(&resources);
+  }
+  obj1.free();
+}
+
+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;
+  double t;
+  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) {
+      if (tmp.x1 > tmp.x2) {
+       t = tmp.x1; tmp.x1 = tmp.x2; tmp.x2 = t;
+      }
+      if (tmp.y1 > tmp.y2) {
+       t = tmp.y1; tmp.y1 = tmp.y2; tmp.y2 = t;
+      }
+      *box = tmp;
+    }
+  } else {
+    ok = gFalse;
+  }
+  obj1.free();
+  return ok;
+}
+
+//------------------------------------------------------------------------
+// Page
+//------------------------------------------------------------------------
+
+Page::Page(XRef *xrefA, int numA, Dict *pageDict, PageAttrs *attrsA) {
+  ok = gTrue;
+  xref = xrefA;
+  num = numA;
+
+  // get attributes
+  attrs = attrsA;
+
+  // annotations
+  pageDict->lookupNF("Annots", &annots);
+  if (!(annots.isRef() || annots.isArray() || annots.isNull())) {
+    error(-1, "Page annotations object (page %d) is wrong type (%s)",
+         num, annots.getTypeName());
+    annots.free();
+    goto err2;
+  }
+
+  // contents
+  pageDict->lookupNF("Contents", &contents);
+  if (!(contents.isRef() || contents.isArray() ||
+       contents.isNull())) {
+    error(-1, "Page contents object (page %d) is wrong type (%s)",
+         num, contents.getTypeName());
+    contents.free();
+    goto err1;
+  }
+
+  return;
+
+ err2:
+  annots.initNull();
+ err1:
+  contents.initNull();
+  ok = gFalse;
+}
+
+Page::~Page() {
+  delete attrs;
+  annots.free();
+  contents.free();
+}
+
+void Page::display(OutputDev *out, double hDPI, double vDPI,
+                  int rotate, GBool useMediaBox, GBool crop,
+                  Links *links, Catalog *catalog,
+                  GBool (*abortCheckCbk)(void *data),
+                  void *abortCheckCbkData) {
+  displaySlice(out, hDPI, vDPI, rotate, useMediaBox, crop,
+              -1, -1, -1, -1, links, catalog,
+              abortCheckCbk, abortCheckCbkData);
+}
+
+void Page::displaySlice(OutputDev *out, double hDPI, double vDPI,
+                       int rotate, GBool useMediaBox, GBool crop,
+                       int sliceX, int sliceY, int sliceW, int sliceH,
+                       Links *links, Catalog *catalog,
+                       GBool (*abortCheckCbk)(void *data),
+                       void *abortCheckCbkData) {
+#ifndef PDF_PARSER_ONLY
+  PDFRectangle *mediaBox, *cropBox, *baseBox;
+  PDFRectangle box;
+  Gfx *gfx;
+  Object obj;
+  Link *link;
+  Annots *annotList;
+  double kx, ky;
+  int i;
+
+  rotate += getRotate();
+  if (rotate >= 360) {
+    rotate -= 360;
+  } else if (rotate < 0) {
+    rotate += 360;
+  }
+
+  mediaBox = getMediaBox();
+  cropBox = getCropBox();
+  if (sliceW >= 0 && sliceH >= 0) {
+    baseBox = useMediaBox ? mediaBox : cropBox;
+    kx = 72.0 / hDPI;
+    ky = 72.0 / vDPI;
+    if (rotate == 90) {
+      if (out->upsideDown()) {
+       box.x1 = baseBox->x1 + ky * sliceY;
+       box.x2 = baseBox->x1 + ky * (sliceY + sliceH);
+      } else {
+       box.x1 = baseBox->x2 - ky * (sliceY + sliceH);
+       box.x2 = baseBox->x2 - ky * sliceY;
+      }
+      box.y1 = baseBox->y1 + kx * sliceX;
+      box.y2 = baseBox->y1 + kx * (sliceX + sliceW);
+    } else if (rotate == 180) {
+      box.x1 = baseBox->x2 - kx * (sliceX + sliceW);
+      box.x2 = baseBox->x2 - kx * sliceX;
+      if (out->upsideDown()) {
+       box.y1 = baseBox->y1 + ky * sliceY;
+       box.y2 = baseBox->y1 + ky * (sliceY + sliceH);
+      } else {
+       box.y1 = baseBox->y2 - ky * (sliceY + sliceH);
+       box.y2 = baseBox->y2 - ky * sliceY;
+      }
+    } else if (rotate == 270) {
+      if (out->upsideDown()) {
+       box.x1 = baseBox->x2 - ky * (sliceY + sliceH);
+       box.x2 = baseBox->x2 - ky * sliceY;
+      } else {
+       box.x1 = baseBox->x1 + ky * sliceY;
+       box.x2 = baseBox->x1 + ky * (sliceY + sliceH);
+      }
+      box.y1 = baseBox->y2 - kx * (sliceX + sliceW);
+      box.y2 = baseBox->y2 - kx * sliceX;
+    } else {
+      box.x1 = baseBox->x1 + kx * sliceX;
+      box.x2 = baseBox->x1 + kx * (sliceX + sliceW);
+      if (out->upsideDown()) {
+       box.y1 = baseBox->y2 - ky * (sliceY + sliceH);
+       box.y2 = baseBox->y2 - ky * sliceY;
+      } else {
+       box.y1 = baseBox->y1 + ky * sliceY;
+       box.y2 = baseBox->y1 + ky * (sliceY + sliceH);
+      }
+    }
+  } else if (useMediaBox) {
+    box = *mediaBox;
+  } else {
+    box = *cropBox;
+    crop = gFalse;
+  }
+
+  if (globalParams->getPrintCommands()) {
+    printf("***** MediaBox = ll:%g,%g ur:%g,%g\n",
+          mediaBox->x1, mediaBox->y1, mediaBox->x2, mediaBox->y2);
+    printf("***** CropBox = ll:%g,%g ur:%g,%g\n",
+          cropBox->x1, cropBox->y1, cropBox->x2, cropBox->y2);
+    printf("***** Rotate = %d\n", attrs->getRotate());
+  }
+
+  gfx = new Gfx(xref, out, num, attrs->getResourceDict(),
+               hDPI, vDPI, &box, crop ? cropBox : (PDFRectangle *)NULL,
+               rotate, abortCheckCbk, abortCheckCbkData);
+  contents.fetch(xref, &obj);
+  if (!obj.isNull()) {
+    gfx->saveState();
+    gfx->display(&obj);
+    gfx->restoreState();
+  }
+  obj.free();
+
+  // draw links
+  if (links) {
+    gfx->saveState();
+    for (i = 0; i < links->getNumLinks(); ++i) {
+      link = links->getLink(i);
+      out->drawLink(link, catalog);
+    }
+    gfx->restoreState();
+    out->dump();
+  }
+
+  // draw non-link annotations
+  annotList = new Annots(xref, catalog, annots.fetch(xref, &obj));
+  obj.free();
+  if (annotList->getNumAnnots() > 0) {
+    if (globalParams->getPrintCommands()) {
+      printf("***** Annotations\n");
+    }
+    for (i = 0; i < annotList->getNumAnnots(); ++i) {
+      annotList->getAnnot(i)->draw(gfx);
+    }
+    out->dump();
+  }
+  delete annotList;
+
+  delete gfx;
+#endif
+}
+
+void Page::getDefaultCTM(double *ctm, double hDPI, double vDPI,
+                        int rotate, GBool upsideDown) {
+  GfxState *state;
+  int i;
+
+  rotate += getRotate();
+  if (rotate >= 360) {
+    rotate -= 360;
+  } else if (rotate < 0) {
+    rotate += 360;
+  }
+  state = new GfxState(hDPI, vDPI, getMediaBox(), rotate, upsideDown);
+  for (i = 0; i < 6; ++i) {
+    ctm[i] = state->getCTM()[i];
+  }
+  delete state;
+}
diff --git a/lib/xpdf/Page.h b/lib/xpdf/Page.h
new file mode 100644 (file)
index 0000000..36f96e1
--- /dev/null
@@ -0,0 +1,175 @@
+//========================================================================
+//
+// Page.h
+//
+// Copyright 1996-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef PAGE_H
+#define PAGE_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#include "Object.h"
+
+class Dict;
+class XRef;
+class OutputDev;
+class Links;
+class Catalog;
+
+//------------------------------------------------------------------------
+
+class PDFRectangle {
+public:
+  double x1, y1, x2, y2;
+
+  PDFRectangle() { x1 = y1 = x2 = y2 = 0; }
+  PDFRectangle(double x1A, double y1A, double x2A, double y2A)
+    { x1 = x1A; y1 = y1A; x2 = x2A; y2 = y2A; }
+  GBool isValid() { return x1 != 0 || y1 != 0 || x2 != 0 || y2 != 0; }
+};
+
+//------------------------------------------------------------------------
+// PageAttrs
+//------------------------------------------------------------------------
+
+class PageAttrs {
+public:
+
+  // Construct a new PageAttrs object by merging a dictionary
+  // (of type Pages or Page) into another PageAttrs object.  If
+  // <attrs> is NULL, uses defaults.
+  PageAttrs(PageAttrs *attrs, Dict *dict);
+
+  // Destructor.
+  ~PageAttrs();
+
+  // Accessors.
+  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:
+
+  GBool readBox(Dict *dict, char *key, PDFRectangle *box);
+
+  PDFRectangle mediaBox;
+  PDFRectangle cropBox;
+  GBool haveCropBox;
+  PDFRectangle bleedBox;
+  PDFRectangle trimBox;
+  PDFRectangle artBox;
+  int rotate;
+  Object lastModified;
+  Object boxColorInfo;
+  Object group;
+  Object metadata;
+  Object pieceInfo;
+  Object separationInfo;
+  Object resources;
+};
+
+//------------------------------------------------------------------------
+// Page
+//------------------------------------------------------------------------
+
+class Page {
+public:
+
+  // Constructor.
+  Page(XRef *xrefA, int numA, Dict *pageDict, PageAttrs *attrsA);
+
+  // Destructor.
+  ~Page();
+
+  // Is page valid?
+  GBool isOk() { return ok; }
+
+  // Get page parameters.
+  PDFRectangle *getMediaBox() { return attrs->getMediaBox(); }
+  PDFRectangle *getCropBox() { return attrs->getCropBox(); }
+  GBool isCropped() { return attrs->isCropped(); }
+  double getMediaWidth() 
+    { return attrs->getMediaBox()->x2 - attrs->getMediaBox()->x1; }
+  double getMediaHeight()
+    { return attrs->getMediaBox()->y2 - attrs->getMediaBox()->y1; }
+  double getCropWidth() 
+    { return attrs->getCropBox()->x2 - attrs->getCropBox()->x1; }
+  double getCropHeight()
+    { return attrs->getCropBox()->y2 - attrs->getCropBox()->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(xref, obj); }
+
+  // Get contents.
+  Object *getContents(Object *obj) { return contents.fetch(xref, obj); }
+
+  // Display a page.
+  void display(OutputDev *out, double hDPI, double vDPI,
+              int rotate, GBool useMediaBox, GBool crop,
+              Links *links, Catalog *catalog,
+              GBool (*abortCheckCbk)(void *data) = NULL,
+              void *abortCheckCbkData = NULL);
+
+  // Display part of a page.
+  void displaySlice(OutputDev *out, double hDPI, double vDPI,
+                   int rotate, GBool useMediaBox, GBool crop,
+                   int sliceX, int sliceY, int sliceW, int sliceH,
+                   Links *links, Catalog *catalog,
+                   GBool (*abortCheckCbk)(void *data) = NULL,
+                   void *abortCheckCbkData = NULL);
+
+  // Get the page's default CTM.
+  void getDefaultCTM(double *ctm, double hDPI, double vDPI,
+                    int rotate, GBool upsideDown);
+
+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 ok;                    // true if page is valid
+};
+
+#endif
diff --git a/lib/xpdf/Parser.cc b/lib/xpdf/Parser.cc
new file mode 100644 (file)
index 0000000..af7c933
--- /dev/null
@@ -0,0 +1,214 @@
+//========================================================================
+//
+// Parser.cc
+//
+// Copyright 1996-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <stddef.h>
+#include "Object.h"
+#include "Array.h"
+#include "Dict.h"
+#include "Parser.h"
+#include "XRef.h"
+#include "Error.h"
+#include "Decrypt.h"
+
+Parser::Parser(XRef *xrefA, Lexer *lexerA) {
+  xref = xrefA;
+  lexer = lexerA;
+  inlineImg = 0;
+  lexer->getObj(&buf1);
+  lexer->getObj(&buf2);
+}
+
+Parser::~Parser() {
+  buf1.free();
+  buf2.free();
+  delete lexer;
+}
+
+Object *Parser::getObj(Object *obj,
+                      Guchar *fileKey, int keyLength,
+                      int objNum, int objGen) {
+  char *key;
+  Stream *str;
+  Object obj2;
+  int num;
+  Decrypt *decrypt;
+  GString *s;
+  char *p;
+  int i;
+
+  // refill buffer after inline image data
+  if (inlineImg == 2) {
+    buf1.free();
+    buf2.free();
+    lexer->getObj(&buf1);
+    lexer->getObj(&buf2);
+    inlineImg = 0;
+  }
+
+  // array
+  if (buf1.isCmd("[")) {
+    shift();
+    obj->initArray(xref);
+    while (!buf1.isCmd("]") && !buf1.isEOF())
+      obj->arrayAdd(getObj(&obj2, fileKey, keyLength, objNum, objGen));
+    if (buf1.isEOF())
+      error(getPos(), "End of file inside array");
+    shift();
+
+  // dictionary or stream
+  } else if (buf1.isCmd("<<")) {
+    shift();
+    obj->initDict(xref);
+    while (!buf1.isCmd(">>") && !buf1.isEOF()) {
+      if (!buf1.isName()) {
+       error(getPos(), "Dictionary key must be a name object");
+       shift();
+      } else {
+       key = copyString(buf1.getName());
+       shift();
+       if (buf1.isEOF() || buf1.isError()) {
+         gfree(key);
+         break;
+       }
+       obj->dictAdd(key, getObj(&obj2, fileKey, keyLength, objNum, objGen));
+      }
+    }
+    if (buf1.isEOF())
+      error(getPos(), "End of file inside dictionary");
+    if (buf2.isCmd("stream")) {
+      if ((str = makeStream(obj))) {
+       obj->initStream(str);
+       if (fileKey) {
+         str->getBaseStream()->doDecryption(fileKey, keyLength,
+                                            objNum, objGen);
+       }
+      } else {
+       obj->free();
+       obj->initError();
+      }
+    } else {
+      shift();
+    }
+
+  // indirect reference or integer
+  } else if (buf1.isInt()) {
+    num = buf1.getInt();
+    shift();
+    if (buf1.isInt() && buf2.isCmd("R")) {
+      obj->initRef(num, buf1.getInt());
+      shift();
+      shift();
+    } else {
+      obj->initInt(num);
+    }
+
+  // string
+  } else if (buf1.isString() && fileKey) {
+    buf1.copy(obj);
+    s = obj->getString();
+    decrypt = new Decrypt(fileKey, keyLength, objNum, objGen);
+    for (i = 0, p = obj->getString()->getCString();
+        i < s->getLength();
+        ++i, ++p) {
+      *p = decrypt->decryptByte(*p);
+    }
+    delete decrypt;
+    shift();
+
+  // simple object
+  } else {
+    buf1.copy(obj);
+    shift();
+  }
+
+  return obj;
+}
+
+Stream *Parser::makeStream(Object *dict) {
+  Object obj;
+  BaseStream *baseStr;
+  Stream *str;
+  Guint pos, endPos, length;
+
+  // get stream start position
+  lexer->skipToNextLine();
+  pos = lexer->getPos();
+
+  // get length
+  dict->dictLookup("Length", &obj);
+  if (obj.isInt()) {
+    length = (Guint)obj.getInt();
+    obj.free();
+  } else {
+    error(getPos(), "Bad 'Length' attribute in stream");
+    obj.free();
+    return NULL;
+  }
+
+  // check for length in damaged file
+  if (xref && xref->getStreamEnd(pos, &endPos)) {
+    length = endPos - pos;
+  }
+
+  // in badly damaged PDF files, we can run off the end of the input
+  // stream immediately after the "stream" token
+  if (!lexer->getStream()) {
+    return NULL;
+  }
+  baseStr = lexer->getStream()->getBaseStream();
+
+  // skip over stream data
+  lexer->setPos(pos + length);
+
+  // refill token buffers and check for 'endstream'
+  shift();  // kill '>>'
+  shift();  // kill 'stream'
+  if (buf1.isCmd("endstream")) {
+    shift();
+  } else {
+    error(getPos(), "Missing 'endstream'");
+    // kludge for broken PDF files: just add 5k to the length, and
+    // hope its enough
+    length += 5000;
+  }
+
+  // make base stream
+  str = baseStr->makeSubStream(pos, gTrue, length, dict);
+
+  // get filters
+  str = str->addFilters(dict);
+
+  return str;
+}
+
+void Parser::shift() {
+  if (inlineImg > 0) {
+    if (inlineImg < 2) {
+      ++inlineImg;
+    } else {
+      // in a damaged content stream, if 'ID' shows up in the middle
+      // of a dictionary, we need to reset
+      inlineImg = 0;
+    }
+  } else if (buf2.isCmd("ID")) {
+    lexer->skipChar();         // skip char after 'ID' command
+    inlineImg = 1;
+  }
+  buf1.free();
+  buf1 = buf2;
+  if (inlineImg > 0)           // don't buffer inline image data
+    buf2.initNull();
+  else
+    lexer->getObj(&buf2);
+}
diff --git a/lib/xpdf/Parser.h b/lib/xpdf/Parser.h
new file mode 100644 (file)
index 0000000..b583baf
--- /dev/null
@@ -0,0 +1,56 @@
+//========================================================================
+//
+// Parser.h
+//
+// Copyright 1996-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef PARSER_H
+#define PARSER_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#include "Lexer.h"
+
+//------------------------------------------------------------------------
+// Parser
+//------------------------------------------------------------------------
+
+class Parser {
+public:
+
+  // Constructor.
+  Parser(XRef *xrefA, Lexer *lexerA);
+
+  // Destructor.
+  ~Parser();
+
+  // Get the next object from the input stream.
+  Object *getObj(Object *obj,
+                Guchar *fileKey = NULL, int keyLength = 0,
+                int objNum = 0, int objGen = 0);
+
+  // Get stream.
+  Stream *getStream() { return lexer->getStream(); }
+
+  // Get current position in file.
+  int getPos() { return lexer->getPos(); }
+
+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
+
+  Stream *makeStream(Object *dict);
+  void shift();
+};
+
+#endif
+
diff --git a/lib/xpdf/SWFOutputDev.h b/lib/xpdf/SWFOutputDev.h
new file mode 100644 (file)
index 0000000..5bf11bd
--- /dev/null
@@ -0,0 +1,85 @@
+/* pdfswf.h
+   Header file for pdfswf.cc.
+
+   Part of the swftools package.
+
+   Copyright (c) 2001 Matthias Kramm <kramm@quiss.org> 
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */
+
+#ifndef __pdf_h__
+#define __pdf_h__
+
+#include "../lib/gfxdevice.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void pdfswf_setparameter(char*name, char*value);
+
+typedef struct _pdf_doc
+{
+    char*info;
+    char*title;
+    int num_pages;
+    void*internal;
+} pdf_doc_t;
+
+pdf_doc_t*  pdf_init(char*filename, char*userPassword);
+void pdf_destroy(pdf_doc_t*doc);
+
+typedef struct _dev_output
+{
+    int num_pages;
+    void*internal;
+} dev_output_t;
+
+dev_output_t* dev_output_init(gfxdevice_t*dev);
+void dev_output_startframe(dev_output_t*, int width, int height);
+void dev_output_endframe(dev_output_t*);
+void dev_output_setparameter(dev_output_t*, char*name, char*value);
+void dev_output_finish(dev_output_t*);
+void dev_output_preparepage(dev_output_t*, int pdfpage, int outputpage);
+void dev_output_destroy(dev_output_t*);
+
+typedef struct _pdf_page
+{
+    pdf_doc_t*parent;
+    int nr;
+    void*internal;
+} pdf_page_t;
+
+pdf_page_t* pdf_getpage(pdf_doc_t*doc, int page);
+void pdf_page_render(pdf_page_t*page, dev_output_t*output);
+void pdf_page_rendersection(pdf_page_t*page, dev_output_t*output, int x, int y, int x1, int y1, int x2, int y2);
+void pdf_page_destroy(pdf_page_t*page);
+
+typedef struct _pdf_page_info
+{
+    int xMin, yMin, xMax, yMax;
+    int number_of_images;
+    int number_of_links;
+    int number_of_fonts;
+} pdf_page_info_t;
+
+pdf_page_info_t* pdf_page_getinfo(pdf_page_t*page);
+void pdf_page_info_destroy(pdf_page_info_t*info);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif //__pdf_h__
diff --git a/lib/xpdf/SecurityHandler.cc b/lib/xpdf/SecurityHandler.cc
new file mode 100644 (file)
index 0000000..8825ce8
--- /dev/null
@@ -0,0 +1,376 @@
+//========================================================================
+//
+// SecurityHandler.cc
+//
+// Copyright 2004 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include "GString.h"
+#include "PDFDoc.h"
+#include "Decrypt.h"
+#include "Error.h"
+#include "GlobalParams.h"
+#if HAVE_XPDFCORE
+#  include "XPDFCore.h"
+#elif HAVE_WINPDFCORE
+#  include "WinPDFCore.h"
+#endif
+#ifdef ENABLE_PLUGINS
+#  include "XpdfPluginAPI.h"
+#endif
+#include "SecurityHandler.h"
+
+//------------------------------------------------------------------------
+// SecurityHandler
+//------------------------------------------------------------------------
+
+SecurityHandler *SecurityHandler::make(PDFDoc *docA, Object *encryptDictA) {
+  Object filterObj;
+  SecurityHandler *secHdlr;
+  XpdfSecurityHandler *xsh;
+
+  encryptDictA->dictLookup("Filter", &filterObj);
+  if (filterObj.isName("Standard")) {
+    secHdlr = new StandardSecurityHandler(docA, encryptDictA);
+  } else if (filterObj.isName()) {
+#ifdef ENABLE_PLUGINS
+    if ((xsh = globalParams->getSecurityHandler(filterObj.getName()))) {
+      secHdlr = new ExternalSecurityHandler(docA, encryptDictA, xsh);
+    } else {
+#endif
+      error(-1, "Couldn't find the '%s' security handler",
+           filterObj.getName());
+      secHdlr = NULL;
+#ifdef ENABLE_PLUGINS
+    }
+#endif
+  } else {
+    error(-1, "Missing or invalid 'Filter' entry in encryption dictionary");
+    secHdlr = NULL;
+  }
+  filterObj.free();
+  return secHdlr;
+}
+
+SecurityHandler::SecurityHandler(PDFDoc *docA) {
+  doc = docA;
+}
+
+SecurityHandler::~SecurityHandler() {
+}
+
+GBool SecurityHandler::checkEncryption(GString *ownerPassword,
+                                      GString *userPassword) {
+  void *authData;
+  GBool ok;
+  int i;
+
+  if (ownerPassword || userPassword) {
+    authData = makeAuthData(ownerPassword, userPassword);
+  } else {
+    authData = NULL;
+  }
+  ok = authorize(authData);
+  if (authData) {
+    freeAuthData(authData);
+  }
+  for (i = 0; !ok && i < 3; ++i) {
+    if (!(authData = getAuthData())) {
+      break;
+    }
+    ok = authorize(authData);
+    if (authData) {
+      freeAuthData(authData);
+    }
+  }
+  if (!ok) {
+    error(-1, "Incorrect password");
+  }
+  return ok;
+}
+
+//------------------------------------------------------------------------
+// StandardSecurityHandler
+//------------------------------------------------------------------------
+
+class StandardAuthData {
+public:
+
+  StandardAuthData(GString *ownerPasswordA, GString *userPasswordA) {
+    ownerPassword = ownerPasswordA;
+    userPassword = userPasswordA;
+  }
+
+  ~StandardAuthData() {
+    if (ownerPassword) {
+      delete ownerPassword;
+    }
+    if (userPassword) {
+      delete userPassword;
+    }
+  }
+
+  GString *ownerPassword;
+  GString *userPassword;
+};
+
+StandardSecurityHandler::StandardSecurityHandler(PDFDoc *docA,
+                                                Object *encryptDictA):
+  SecurityHandler(docA)
+{
+  Object versionObj, revisionObj, lengthObj;
+  Object ownerKeyObj, userKeyObj, permObj, fileIDObj;
+  Object fileIDObj1;
+  Object cryptFiltersObj, streamFilterObj, stringFilterObj;
+  Object cryptFilterObj, cfmObj, cfLengthObj;
+  Object encryptMetadataObj;
+
+  ok = gFalse;
+  fileID = NULL;
+  ownerKey = NULL;
+  userKey = NULL;
+
+  encryptDictA->dictLookup("V", &versionObj);
+  encryptDictA->dictLookup("R", &revisionObj);
+  encryptDictA->dictLookup("Length", &lengthObj);
+  encryptDictA->dictLookup("O", &ownerKeyObj);
+  encryptDictA->dictLookup("U", &userKeyObj);
+  encryptDictA->dictLookup("P", &permObj);
+  doc->getXRef()->getTrailerDict()->dictLookup("ID", &fileIDObj);
+  if (versionObj.isInt() &&
+      revisionObj.isInt() &&
+      ownerKeyObj.isString() && ownerKeyObj.getString()->getLength() == 32 &&
+      userKeyObj.isString() && userKeyObj.getString()->getLength() == 32 &&
+      permObj.isInt()) {
+    encVersion = versionObj.getInt();
+    encRevision = revisionObj.getInt();
+    // revision 2 forces a 40-bit key - some buggy PDF generators
+    // set the Length value incorrectly
+    if (encRevision == 2 || !lengthObj.isInt()) {
+      fileKeyLength = 5;
+    } else {
+      fileKeyLength = lengthObj.getInt() / 8;
+    }
+    encryptMetadata = gTrue;
+    //~ this currently only handles a subset of crypt filter functionality
+    if (encVersion == 4 && encRevision == 4) {
+      encryptDictA->dictLookup("CF", &cryptFiltersObj);
+      encryptDictA->dictLookup("StmF", &streamFilterObj);
+      encryptDictA->dictLookup("StrF", &stringFilterObj);
+      if (cryptFiltersObj.isDict() &&
+         streamFilterObj.isName() &&
+         stringFilterObj.isName() &&
+         !strcmp(streamFilterObj.getName(), stringFilterObj.getName())) {
+       if (cryptFiltersObj.dictLookup(streamFilterObj.getName(),
+                                      &cryptFilterObj)->isDict()) {
+         if (cryptFilterObj.dictLookup("CFM", &cfmObj)->isName("V2")) {
+           encVersion = 2;
+           encRevision = 3;
+           if (cryptFilterObj.dictLookup("Length", &cfLengthObj)->isInt()) {
+             //~ according to the spec, this should be cfLengthObj / 8
+             fileKeyLength = cfLengthObj.getInt();
+           }
+           cfLengthObj.free();
+         }
+         cfmObj.free();
+       }
+       cryptFilterObj.free();
+      }
+      stringFilterObj.free();
+      streamFilterObj.free();
+      cryptFiltersObj.free();
+      if (encryptDictA->dictLookup("EncryptMetadata",
+                                  &encryptMetadataObj)->isBool()) {
+       encryptMetadata = encryptMetadataObj.getBool();
+      }
+      encryptMetadataObj.free();
+    }
+    permFlags = permObj.getInt();
+    ownerKey = ownerKeyObj.getString()->copy();
+    userKey = userKeyObj.getString()->copy();
+    if (encVersion >= 1 && encVersion <= 2 &&
+       encRevision >= 2 && encRevision <= 3) {
+      if (fileIDObj.isArray()) {
+       if (fileIDObj.arrayGet(0, &fileIDObj1)->isString()) {
+         fileID = fileIDObj1.getString()->copy();
+       } else {
+         fileID = new GString();
+       }
+       fileIDObj1.free();
+      } else {
+       fileID = new GString();
+      }
+      ok = gTrue;
+    } else {
+      error(-1, "Unsupported version/revision (%d/%d) of Standard security handler",
+           encVersion, encRevision);
+    }
+  } else {
+    error(-1, "Weird encryption info");
+  }
+  if (fileKeyLength > 16) {
+    fileKeyLength = 16;
+  }
+  fileIDObj.free();
+  permObj.free();
+  userKeyObj.free();
+  ownerKeyObj.free();
+  lengthObj.free();
+  revisionObj.free();
+  versionObj.free();
+}
+
+StandardSecurityHandler::~StandardSecurityHandler() {
+  if (fileID) {
+    delete fileID;
+  }
+  if (ownerKey) {
+    delete ownerKey;
+  }
+  if (userKey) {
+    delete userKey;
+  }
+}
+
+void *StandardSecurityHandler::makeAuthData(GString *ownerPassword,
+                                           GString *userPassword) {
+  return new StandardAuthData(ownerPassword ? ownerPassword->copy()
+                                           : (GString *)NULL,
+                             userPassword ? userPassword->copy()
+                                          : (GString *)NULL);
+}
+
+void *StandardSecurityHandler::getAuthData() {
+#if HAVE_XPDFCORE
+  XPDFCore *core;
+  GString *password;
+
+  if (!(core = (XPDFCore *)doc->getGUIData()) ||
+      !(password = core->getPassword())) {
+    return NULL;
+  }
+  return new StandardAuthData(password, password->copy());
+#elif HAVE_WINPDFCORE
+  WinPDFCore *core;
+  GString *password;
+
+  if (!(core = (WinPDFCore *)doc->getGUIData()) ||
+      !(password = core->getPassword())) {
+    return NULL;
+  }
+  return new StandardAuthData(password, password->copy());
+#else
+  return NULL;
+#endif
+}
+
+void StandardSecurityHandler::freeAuthData(void *authData) {
+  delete (StandardAuthData *)authData;
+}
+
+GBool StandardSecurityHandler::authorize(void *authData) {
+  GString *ownerPassword, *userPassword;
+
+  if (!ok) {
+    return gFalse;
+  }
+  if (authData) {
+    ownerPassword = ((StandardAuthData *)authData)->ownerPassword;
+    userPassword = ((StandardAuthData *)authData)->userPassword;
+  } else {
+    ownerPassword = NULL;
+    userPassword = NULL;
+  }
+  if (!Decrypt::makeFileKey(encVersion, encRevision, fileKeyLength,
+                           ownerKey, userKey, permFlags, fileID,
+                           ownerPassword, userPassword, fileKey,
+                           encryptMetadata, &ownerPasswordOk)) {
+    return gFalse;
+  }
+  return gTrue;
+}
+
+#ifdef ENABLE_PLUGINS
+
+//------------------------------------------------------------------------
+// ExternalSecurityHandler
+//------------------------------------------------------------------------
+
+ExternalSecurityHandler::ExternalSecurityHandler(PDFDoc *docA,
+                                                Object *encryptDictA,
+                                                XpdfSecurityHandler *xshA):
+  SecurityHandler(docA)
+{
+  encryptDictA->copy(&encryptDict);
+  xsh = xshA;
+  ok = gFalse;
+
+  if (!(*xsh->newDoc)(xsh->handlerData, (XpdfDoc)docA,
+                     (XpdfObject)encryptDictA, &docData)) {
+    return;
+  }
+
+  ok = gTrue;
+}
+
+ExternalSecurityHandler::~ExternalSecurityHandler() {
+  (*xsh->freeDoc)(xsh->handlerData, docData);
+  encryptDict.free();
+}
+
+void *ExternalSecurityHandler::makeAuthData(GString *ownerPassword,
+                                           GString *userPassword) {
+  char *opw, *upw;
+  void *authData;
+
+  opw = ownerPassword ? ownerPassword->getCString() : (char *)NULL;
+  upw = userPassword ? userPassword->getCString() : (char *)NULL;
+  if (!(*xsh->makeAuthData)(xsh->handlerData, docData, opw, upw, &authData)) {
+    return NULL;
+  }
+  return authData;
+}
+
+void *ExternalSecurityHandler::getAuthData() {
+  void *authData;
+
+  if (!(*xsh->getAuthData)(xsh->handlerData, docData, &authData)) {
+    return NULL;
+  }
+  return authData;
+}
+
+void ExternalSecurityHandler::freeAuthData(void *authData) {
+  (*xsh->freeAuthData)(xsh->handlerData, docData, authData);
+}
+
+GBool ExternalSecurityHandler::authorize(void *authData) {
+  char *key;
+  int length;
+
+  if (!ok) {
+    return gFalse;
+  }
+  permFlags = (*xsh->authorize)(xsh->handlerData, docData, authData);
+  if (!(permFlags & xpdfPermissionOpen)) {
+    return gFalse;
+  }
+  if (!(*xsh->getKey)(xsh->handlerData, docData, &key, &length, &encVersion)) {
+    return gFalse;
+  }
+  if ((fileKeyLength = length) > 16) {
+    fileKeyLength = 16;
+  }
+  memcpy(fileKey, key, fileKeyLength);
+  (*xsh->freeKey)(xsh->handlerData, docData, key, length);
+  return gTrue;
+}
+
+#endif // ENABLE_PLUGINS
diff --git a/lib/xpdf/SecurityHandler.h b/lib/xpdf/SecurityHandler.h
new file mode 100644 (file)
index 0000000..127acb7
--- /dev/null
@@ -0,0 +1,155 @@
+//========================================================================
+//
+// SecurityHandler.h
+//
+// Copyright 2004 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef SECURITYHANDLER_H
+#define SECURITYHANDLER_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#include "gtypes.h"
+#include "Object.h"
+
+class GString;
+class PDFDoc;
+struct XpdfSecurityHandler;
+
+//------------------------------------------------------------------------
+// SecurityHandler
+//------------------------------------------------------------------------
+
+class SecurityHandler {
+public:
+
+  static SecurityHandler *make(PDFDoc *docA, Object *encryptDictA);
+
+  SecurityHandler(PDFDoc *docA);
+  virtual ~SecurityHandler();
+
+  // Check the document's encryption.  If the document is encrypted,
+  // this will first try <ownerPassword> and <userPassword> (in
+  // "batch" mode), and if those fail, it will attempt to request a
+  // password from the user.  This is the high-level function that
+  // calls the lower level functions for the specific security handler
+  // (requesting a password three times, etc.).  Returns true if the
+  // document can be opened (if it's unencrypted, or if a correct
+  // password is obtained); false otherwise (encrypted and no correct
+  // password).
+  GBool checkEncryption(GString *ownerPassword,
+                       GString *userPassword);
+
+  // Create authorization data for the specified owner and user
+  // passwords.  If the security handler doesn't support "batch" mode,
+  // this function should return NULL.
+  virtual void *makeAuthData(GString *ownerPassword,
+                            GString *userPassword) = 0;
+
+  // Construct authorization data, typically by prompting the user for
+  // a password.  Returns an authorization data object, or NULL to
+  // cancel.
+  virtual void *getAuthData() = 0;
+
+  // Free the authorization data returned by makeAuthData or
+  // getAuthData.
+  virtual void freeAuthData(void *authData) = 0;
+
+  // Attempt to authorize the document, using the supplied
+  // authorization data (which may be NULL).  Returns true if
+  // successful (i.e., if at least the right to open the document was
+  // granted).
+  virtual GBool authorize(void *authData) = 0;
+
+  // Return the various authorization parameters.  These are only
+  // valid after authorize has returned true.
+  virtual int getPermissionFlags() = 0;
+  virtual GBool getOwnerPasswordOk() = 0;
+  virtual Guchar *getFileKey() = 0;
+  virtual int getFileKeyLength() = 0;
+  virtual int getEncVersion() = 0;
+
+protected:
+
+  PDFDoc *doc;
+};
+
+//------------------------------------------------------------------------
+// StandardSecurityHandler
+//------------------------------------------------------------------------
+
+class StandardSecurityHandler: public SecurityHandler {
+public:
+
+  StandardSecurityHandler(PDFDoc *docA, Object *encryptDictA);
+  virtual ~StandardSecurityHandler();
+
+  virtual void *makeAuthData(GString *ownerPassword,
+                            GString *userPassword);
+  virtual void *getAuthData();
+  virtual void freeAuthData(void *authData);
+  virtual GBool authorize(void *authData);
+  virtual int getPermissionFlags() { return permFlags; }
+  virtual GBool getOwnerPasswordOk() { return ownerPasswordOk; }
+  virtual Guchar *getFileKey() { return fileKey; }
+  virtual int getFileKeyLength() { return fileKeyLength; }
+  virtual int getEncVersion() { return encVersion; }
+
+private:
+
+  int permFlags;
+  GBool ownerPasswordOk;
+  Guchar fileKey[16];
+  int fileKeyLength;
+  int encVersion;
+  int encRevision;
+  GBool encryptMetadata;
+
+  GString *ownerKey, *userKey;
+  GString *fileID;
+  GBool ok;
+};
+
+#ifdef ENABLE_PLUGINS
+//------------------------------------------------------------------------
+// ExternalSecurityHandler
+//------------------------------------------------------------------------
+
+class ExternalSecurityHandler: public SecurityHandler {
+public:
+
+  ExternalSecurityHandler(PDFDoc *docA, Object *encryptDictA,
+                         XpdfSecurityHandler *xshA);
+  virtual ~ExternalSecurityHandler();
+
+  virtual void *makeAuthData(GString *ownerPassword,
+                            GString *userPassword);
+  virtual void *getAuthData();
+  virtual void freeAuthData(void *authData);
+  virtual GBool authorize(void *authData);
+  virtual int getPermissionFlags() { return permFlags; }
+  virtual GBool getOwnerPasswordOk() { return gFalse; }
+  virtual Guchar *getFileKey() { return fileKey; }
+  virtual int getFileKeyLength() { return fileKeyLength; }
+  virtual int getEncVersion() { return encVersion; }
+
+private:
+
+  Object encryptDict;
+  XpdfSecurityHandler *xsh;
+  void *docData;
+  int permFlags;
+  Guchar fileKey[16];
+  int fileKeyLength;
+  int encVersion;
+  GBool ok;
+};
+#endif // ENABLE_PLUGINS
+
+#endif
diff --git a/lib/xpdf/Stream-CCITT.h b/lib/xpdf/Stream-CCITT.h
new file mode 100644 (file)
index 0000000..c4458fe
--- /dev/null
@@ -0,0 +1,459 @@
+//========================================================================
+//
+// Stream-CCITT.h
+//
+// Tables for CCITT Fax decoding.
+//
+// Copyright 1996-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+struct CCITTCode {
+  short bits;
+  short n;
+};
+
+#define ccittEOL -2
+
+//------------------------------------------------------------------------
+// 2D codes
+//------------------------------------------------------------------------
+
+#define twoDimPass   0
+#define twoDimHoriz  1
+#define twoDimVert0  2
+#define twoDimVertR1 3
+#define twoDimVertL1 4
+#define twoDimVertR2 5
+#define twoDimVertL2 6
+#define twoDimVertR3 7
+#define twoDimVertL3 8
+
+// 1-7 bit codes
+static CCITTCode twoDimTab1[128] = {
+  {-1, -1}, {-1, -1},                  // 000000x
+  {7, twoDimVertL3},                   // 0000010
+  {7, twoDimVertR3},                   // 0000011
+  {6, twoDimVertL2}, {6, twoDimVertL2},        // 000010x
+  {6, twoDimVertR2}, {6, twoDimVertR2},        // 000011x
+  {4, twoDimPass}, {4, twoDimPass},     // 0001xxx
+    {4, twoDimPass}, {4, twoDimPass},
+    {4, twoDimPass}, {4, twoDimPass},
+    {4, twoDimPass}, {4, twoDimPass},
+  {3, twoDimHoriz}, {3, twoDimHoriz},  // 001xxxx
+    {3, twoDimHoriz}, {3, twoDimHoriz},
+    {3, twoDimHoriz}, {3, twoDimHoriz},
+    {3, twoDimHoriz}, {3, twoDimHoriz},
+    {3, twoDimHoriz}, {3, twoDimHoriz},
+    {3, twoDimHoriz}, {3, twoDimHoriz},
+    {3, twoDimHoriz}, {3, twoDimHoriz},
+    {3, twoDimHoriz}, {3, twoDimHoriz},
+  {3, twoDimVertL1}, {3, twoDimVertL1},        // 010xxxx
+    {3, twoDimVertL1}, {3, twoDimVertL1},
+    {3, twoDimVertL1}, {3, twoDimVertL1},
+    {3, twoDimVertL1}, {3, twoDimVertL1},
+    {3, twoDimVertL1}, {3, twoDimVertL1},
+    {3, twoDimVertL1}, {3, twoDimVertL1},
+    {3, twoDimVertL1}, {3, twoDimVertL1},
+    {3, twoDimVertL1}, {3, twoDimVertL1},
+  {3, twoDimVertR1}, {3, twoDimVertR1},        // 011xxxx
+    {3, twoDimVertR1}, {3, twoDimVertR1},
+    {3, twoDimVertR1}, {3, twoDimVertR1},
+    {3, twoDimVertR1}, {3, twoDimVertR1},
+    {3, twoDimVertR1}, {3, twoDimVertR1},
+    {3, twoDimVertR1}, {3, twoDimVertR1},
+    {3, twoDimVertR1}, {3, twoDimVertR1},
+    {3, twoDimVertR1}, {3, twoDimVertR1},
+  {1, twoDimVert0}, {1, twoDimVert0},  // 1xxxxxx
+    {1, twoDimVert0}, {1, twoDimVert0},
+    {1, twoDimVert0}, {1, twoDimVert0},
+    {1, twoDimVert0}, {1, twoDimVert0},
+    {1, twoDimVert0}, {1, twoDimVert0},
+    {1, twoDimVert0}, {1, twoDimVert0},
+    {1, twoDimVert0}, {1, twoDimVert0},
+    {1, twoDimVert0}, {1, twoDimVert0},
+    {1, twoDimVert0}, {1, twoDimVert0},
+    {1, twoDimVert0}, {1, twoDimVert0},
+    {1, twoDimVert0}, {1, twoDimVert0},
+    {1, twoDimVert0}, {1, twoDimVert0},
+    {1, twoDimVert0}, {1, twoDimVert0},
+    {1, twoDimVert0}, {1, twoDimVert0},
+    {1, twoDimVert0}, {1, twoDimVert0},
+    {1, twoDimVert0}, {1, twoDimVert0},
+    {1, twoDimVert0}, {1, twoDimVert0},
+    {1, twoDimVert0}, {1, twoDimVert0},
+    {1, twoDimVert0}, {1, twoDimVert0},
+    {1, twoDimVert0}, {1, twoDimVert0},
+    {1, twoDimVert0}, {1, twoDimVert0},
+    {1, twoDimVert0}, {1, twoDimVert0},
+    {1, twoDimVert0}, {1, twoDimVert0},
+    {1, twoDimVert0}, {1, twoDimVert0},
+    {1, twoDimVert0}, {1, twoDimVert0},
+    {1, twoDimVert0}, {1, twoDimVert0},
+    {1, twoDimVert0}, {1, twoDimVert0},
+    {1, twoDimVert0}, {1, twoDimVert0},
+    {1, twoDimVert0}, {1, twoDimVert0},
+    {1, twoDimVert0}, {1, twoDimVert0},
+    {1, twoDimVert0}, {1, twoDimVert0},
+    {1, twoDimVert0}, {1, twoDimVert0}
+};
+
+//------------------------------------------------------------------------
+// white run lengths
+//------------------------------------------------------------------------
+
+// 11-12 bit codes (upper 7 bits are 0)
+static CCITTCode whiteTab1[32] = {
+  {-1, -1},                                    // 00000
+  {12, ccittEOL},                              // 00001
+  {-1, -1}, {-1, -1},                          // 0001x
+  {-1, -1}, {-1, -1}, {-1, -1}, {-1, -1},      // 001xx
+  {-1, -1}, {-1, -1}, {-1, -1}, {-1, -1},      // 010xx
+  {-1, -1}, {-1, -1}, {-1, -1}, {-1, -1},      // 011xx
+  {11, 1792}, {11, 1792},                      // 1000x
+  {12, 1984},                                  // 10010
+  {12, 2048},                                  // 10011
+  {12, 2112},                                  // 10100
+  {12, 2176},                                  // 10101
+  {12, 2240},                                  // 10110
+  {12, 2304},                                  // 10111
+  {11, 1856}, {11, 1856},                      // 1100x
+  {11, 1920}, {11, 1920},                      // 1101x
+  {12, 2368},                                  // 11100
+  {12, 2432},                                  // 11101
+  {12, 2496},                                  // 11110
+  {12, 2560}                                   // 11111
+};
+
+// 1-9 bit codes
+static CCITTCode whiteTab2[512] = {
+  {-1, -1}, {-1, -1}, {-1, -1}, {-1, -1},      // 0000000xx
+  {8, 29}, {8, 29},                            // 00000010x
+  {8, 30}, {8, 30},                            // 00000011x
+  {8, 45}, {8, 45},                            // 00000100x
+  {8, 46}, {8, 46},                            // 00000101x
+  {7, 22}, {7, 22}, {7, 22}, {7, 22},          // 0000011xx
+  {7, 23}, {7, 23}, {7, 23}, {7, 23},          // 0000100xx
+  {8, 47}, {8, 47},                            // 00001010x
+  {8, 48}, {8, 48},                            // 00001011x
+  {6, 13}, {6, 13}, {6, 13}, {6, 13},          // 000011xxx
+    {6, 13}, {6, 13}, {6, 13}, {6, 13},
+  {7, 20}, {7, 20}, {7, 20}, {7, 20},          // 0001000xx
+  {8, 33}, {8, 33},                            // 00010010x
+  {8, 34}, {8, 34},                            // 00010011x
+  {8, 35}, {8, 35},                            // 00010100x
+  {8, 36}, {8, 36},                            // 00010101x
+  {8, 37}, {8, 37},                            // 00010110x
+  {8, 38}, {8, 38},                            // 00010111x
+  {7, 19}, {7, 19}, {7, 19}, {7, 19},          // 0001100xx
+  {8, 31}, {8, 31},                            // 00011010x
+  {8, 32}, {8, 32},                            // 00011011x
+  {6, 1}, {6, 1}, {6, 1}, {6, 1},              // 000111xxx
+    {6, 1}, {6, 1}, {6, 1}, {6, 1},
+  {6, 12}, {6, 12}, {6, 12}, {6, 12},          // 001000xxx
+    {6, 12}, {6, 12}, {6, 12}, {6, 12},
+  {8, 53}, {8, 53},                            // 00100100x
+  {8, 54}, {8, 54},                            // 00100101x
+  {7, 26}, {7, 26}, {7, 26}, {7, 26},          // 0010011xx
+  {8, 39}, {8, 39},                            // 00101000x
+  {8, 40}, {8, 40},                            // 00101001x
+  {8, 41}, {8, 41},                            // 00101010x
+  {8, 42}, {8, 42},                            // 00101011x
+  {8, 43}, {8, 43},                            // 00101100x
+  {8, 44}, {8, 44},                            // 00101101x
+  {7, 21}, {7, 21}, {7, 21}, {7, 21},          // 0010111xx
+  {7, 28}, {7, 28}, {7, 28}, {7, 28},          // 0011000xx
+  {8, 61}, {8, 61},                            // 00110010x
+  {8, 62}, {8, 62},                            // 00110011x
+  {8, 63}, {8, 63},                            // 00110100x
+  {8, 0}, {8, 0},                              // 00110101x
+  {8, 320}, {8, 320},                          // 00110110x
+  {8, 384}, {8, 384},                          // 00110111x
+  {5, 10}, {5, 10}, {5, 10}, {5, 10},          // 00111xxxx
+    {5, 10}, {5, 10}, {5, 10}, {5, 10},
+    {5, 10}, {5, 10}, {5, 10}, {5, 10},
+    {5, 10}, {5, 10}, {5, 10}, {5, 10},
+  {5, 11}, {5, 11}, {5, 11}, {5, 11},          // 01000xxxx
+    {5, 11}, {5, 11}, {5, 11}, {5, 11},
+    {5, 11}, {5, 11}, {5, 11}, {5, 11},
+    {5, 11}, {5, 11}, {5, 11}, {5, 11},
+  {7, 27}, {7, 27}, {7, 27}, {7, 27},          // 0100100xx
+  {8, 59}, {8, 59},                            // 01001010x
+  {8, 60}, {8, 60},                            // 01001011x
+  {9, 1472},                                   // 010011000
+  {9, 1536},                                   // 010011001
+  {9, 1600},                                   // 010011010
+  {9, 1728},                                   // 010011011
+  {7, 18}, {7, 18}, {7, 18}, {7, 18},          // 0100111xx
+  {7, 24}, {7, 24}, {7, 24}, {7, 24},          // 0101000xx
+  {8, 49}, {8, 49},                            // 01010010x
+  {8, 50}, {8, 50},                            // 01010011x
+  {8, 51}, {8, 51},                            // 01010100x
+  {8, 52}, {8, 52},                            // 01010101x
+  {7, 25}, {7, 25}, {7, 25}, {7, 25},          // 0101011xx
+  {8, 55}, {8, 55},                            // 01011000x
+  {8, 56}, {8, 56},                            // 01011001x
+  {8, 57}, {8, 57},                            // 01011010x
+  {8, 58}, {8, 58},                            // 01011011x
+  {6, 192}, {6, 192}, {6, 192}, {6, 192},      // 010111xxx
+    {6, 192}, {6, 192}, {6, 192}, {6, 192},
+  {6, 1664}, {6, 1664}, {6, 1664}, {6, 1664},  // 011000xxx
+    {6, 1664}, {6, 1664}, {6, 1664}, {6, 1664},
+  {8, 448}, {8, 448},                          // 01100100x
+  {8, 512}, {8, 512},                          // 01100101x
+  {9, 704},                                    // 011001100
+  {9, 768},                                    // 011001101
+  {8, 640}, {8, 640},                          // 01100111x
+  {8, 576}, {8, 576},                          // 01101000x
+  {9, 832},                                    // 011010010
+  {9, 896},                                    // 011010011
+  {9, 960},                                    // 011010100
+  {9, 1024},                                   // 011010101
+  {9, 1088},                                   // 011010110
+  {9, 1152},                                   // 011010111
+  {9, 1216},                                   // 011011000
+  {9, 1280},                                   // 011011001
+  {9, 1344},                                   // 011011010
+  {9, 1408},                                   // 011011011
+  {7, 256}, {7, 256}, {7, 256}, {7, 256},      // 0110111xx
+  {4, 2}, {4, 2}, {4, 2}, {4, 2},              // 0111xxxxx
+    {4, 2}, {4, 2}, {4, 2}, {4, 2},
+    {4, 2}, {4, 2}, {4, 2}, {4, 2},
+    {4, 2}, {4, 2}, {4, 2}, {4, 2},
+    {4, 2}, {4, 2}, {4, 2}, {4, 2},
+    {4, 2}, {4, 2}, {4, 2}, {4, 2},
+    {4, 2}, {4, 2}, {4, 2}, {4, 2},
+    {4, 2}, {4, 2}, {4, 2}, {4, 2},
+  {4, 3}, {4, 3}, {4, 3}, {4, 3},              // 1000xxxxx
+    {4, 3}, {4, 3}, {4, 3}, {4, 3},
+    {4, 3}, {4, 3}, {4, 3}, {4, 3},
+    {4, 3}, {4, 3}, {4, 3}, {4, 3},
+    {4, 3}, {4, 3}, {4, 3}, {4, 3},
+    {4, 3}, {4, 3}, {4, 3}, {4, 3},
+    {4, 3}, {4, 3}, {4, 3}, {4, 3},
+    {4, 3}, {4, 3}, {4, 3}, {4, 3},
+  {5, 128}, {5, 128}, {5, 128}, {5, 128},      // 10010xxxx
+    {5, 128}, {5, 128}, {5, 128}, {5, 128},
+    {5, 128}, {5, 128}, {5, 128}, {5, 128},
+    {5, 128}, {5, 128}, {5, 128}, {5, 128},
+  {5, 8}, {5, 8}, {5, 8}, {5, 8},              // 10011xxxx
+    {5, 8}, {5, 8}, {5, 8}, {5, 8},
+    {5, 8}, {5, 8}, {5, 8}, {5, 8},
+    {5, 8}, {5, 8}, {5, 8}, {5, 8},
+  {5, 9}, {5, 9}, {5, 9}, {5, 9},              // 10100xxxx
+    {5, 9}, {5, 9}, {5, 9}, {5, 9},
+    {5, 9}, {5, 9}, {5, 9}, {5, 9},
+    {5, 9}, {5, 9}, {5, 9}, {5, 9},
+  {6, 16}, {6, 16}, {6, 16}, {6, 16},          // 101010xxx
+    {6, 16}, {6, 16}, {6, 16}, {6, 16},
+  {6, 17}, {6, 17}, {6, 17}, {6, 17},          // 101011xxx
+    {6, 17}, {6, 17}, {6, 17}, {6, 17},
+  {4, 4}, {4, 4}, {4, 4}, {4, 4},              // 1011xxxxx
+    {4, 4}, {4, 4}, {4, 4}, {4, 4},
+    {4, 4}, {4, 4}, {4, 4}, {4, 4},
+    {4, 4}, {4, 4}, {4, 4}, {4, 4},
+    {4, 4}, {4, 4}, {4, 4}, {4, 4},
+    {4, 4}, {4, 4}, {4, 4}, {4, 4},
+    {4, 4}, {4, 4}, {4, 4}, {4, 4},
+    {4, 4}, {4, 4}, {4, 4}, {4, 4},
+  {4, 5}, {4, 5}, {4, 5}, {4, 5},              // 1100xxxxx
+    {4, 5}, {4, 5}, {4, 5}, {4, 5},
+    {4, 5}, {4, 5}, {4, 5}, {4, 5},
+    {4, 5}, {4, 5}, {4, 5}, {4, 5},
+    {4, 5}, {4, 5}, {4, 5}, {4, 5},
+    {4, 5}, {4, 5}, {4, 5}, {4, 5},
+    {4, 5}, {4, 5}, {4, 5}, {4, 5},
+    {4, 5}, {4, 5}, {4, 5}, {4, 5},
+  {6, 14}, {6, 14}, {6, 14}, {6, 14},          // 110100xxx
+    {6, 14}, {6, 14}, {6, 14}, {6, 14},
+  {6, 15}, {6, 15}, {6, 15}, {6, 15},          // 110101xxx
+    {6, 15}, {6, 15}, {6, 15}, {6, 15},
+  {5, 64}, {5, 64}, {5, 64}, {5, 64},          // 11011xxxx
+    {5, 64}, {5, 64}, {5, 64}, {5, 64},
+    {5, 64}, {5, 64}, {5, 64}, {5, 64},
+    {5, 64}, {5, 64}, {5, 64}, {5, 64},
+  {4, 6}, {4, 6}, {4, 6}, {4, 6},              // 1110xxxxx
+    {4, 6}, {4, 6}, {4, 6}, {4, 6},
+    {4, 6}, {4, 6}, {4, 6}, {4, 6},
+    {4, 6}, {4, 6}, {4, 6}, {4, 6},
+    {4, 6}, {4, 6}, {4, 6}, {4, 6},
+    {4, 6}, {4, 6}, {4, 6}, {4, 6},
+    {4, 6}, {4, 6}, {4, 6}, {4, 6},
+    {4, 6}, {4, 6}, {4, 6}, {4, 6},
+  {4, 7}, {4, 7}, {4, 7}, {4, 7},              // 1111xxxxx
+    {4, 7}, {4, 7}, {4, 7}, {4, 7},
+    {4, 7}, {4, 7}, {4, 7}, {4, 7},
+    {4, 7}, {4, 7}, {4, 7}, {4, 7},
+    {4, 7}, {4, 7}, {4, 7}, {4, 7},
+    {4, 7}, {4, 7}, {4, 7}, {4, 7},
+    {4, 7}, {4, 7}, {4, 7}, {4, 7},
+    {4, 7}, {4, 7}, {4, 7}, {4, 7}
+};
+
+//------------------------------------------------------------------------
+// black run lengths
+//------------------------------------------------------------------------
+
+// 10-13 bit codes (upper 6 bits are 0)
+static CCITTCode blackTab1[128] = {
+  {-1, -1}, {-1, -1},                                  // 000000000000x
+  {12, ccittEOL}, {12, ccittEOL},                      // 000000000001x
+  {-1, -1}, {-1, -1}, {-1, -1}, {-1, -1},              // 00000000001xx
+  {-1, -1}, {-1, -1}, {-1, -1}, {-1, -1},              // 00000000010xx
+  {-1, -1}, {-1, -1}, {-1, -1}, {-1, -1},              // 00000000011xx
+  {-1, -1}, {-1, -1}, {-1, -1}, {-1, -1},              // 00000000100xx
+  {-1, -1}, {-1, -1}, {-1, -1}, {-1, -1},              // 00000000101xx
+  {-1, -1}, {-1, -1}, {-1, -1}, {-1, -1},              // 00000000110xx
+  {-1, -1}, {-1, -1}, {-1, -1}, {-1, -1},              // 00000000111xx
+  {11, 1792}, {11, 1792}, {11, 1792}, {11, 1792},      // 00000001000xx
+  {12, 1984}, {12, 1984},                              // 000000010010x
+  {12, 2048}, {12, 2048},                              // 000000010011x
+  {12, 2112}, {12, 2112},                              // 000000010100x
+  {12, 2176}, {12, 2176},                              // 000000010101x
+  {12, 2240}, {12, 2240},                              // 000000010110x
+  {12, 2304}, {12, 2304},                              // 000000010111x
+  {11, 1856}, {11, 1856}, {11, 1856}, {11, 1856},      // 00000001100xx
+  {11, 1920}, {11, 1920}, {11, 1920}, {11, 1920},      // 00000001101xx
+  {12, 2368}, {12, 2368},                              // 000000011100x
+  {12, 2432}, {12, 2432},                              // 000000011101x
+  {12, 2496}, {12, 2496},                              // 000000011110x
+  {12, 2560}, {12, 2560},                              // 000000011111x
+  {10, 18}, {10, 18}, {10, 18}, {10, 18},              // 0000001000xxx
+    {10, 18}, {10, 18}, {10, 18}, {10, 18},
+  {12, 52}, {12, 52},                                  // 000000100100x
+  {13, 640},                                           // 0000001001010
+  {13, 704},                                           // 0000001001011
+  {13, 768},                                           // 0000001001100
+  {13, 832},                                           // 0000001001101
+  {12, 55}, {12, 55},                                  // 000000100111x
+  {12, 56}, {12, 56},                                  // 000000101000x
+  {13, 1280},                                          // 0000001010010
+  {13, 1344},                                          // 0000001010011
+  {13, 1408},                                          // 0000001010100
+  {13, 1472},                                          // 0000001010101
+  {12, 59}, {12, 59},                                  // 000000101011x
+  {12, 60}, {12, 60},                                  // 000000101100x
+  {13, 1536},                                          // 0000001011010
+  {13, 1600},                                          // 0000001011011
+  {11, 24}, {11, 24}, {11, 24}, {11, 24},              // 00000010111xx
+  {11, 25}, {11, 25}, {11, 25}, {11, 25},              // 00000011000xx
+  {13, 1664},                                          // 0000001100100
+  {13, 1728},                                          // 0000001100101
+  {12, 320}, {12, 320},                                        // 000000110011x
+  {12, 384}, {12, 384},                                        // 000000110100x
+  {12, 448}, {12, 448},                                        // 000000110101x
+  {13, 512},                                           // 0000001101100
+  {13, 576},                                           // 0000001101101
+  {12, 53}, {12, 53},                                  // 000000110111x
+  {12, 54}, {12, 54},                                  // 000000111000x
+  {13, 896},                                           // 0000001110010
+  {13, 960},                                           // 0000001110011
+  {13, 1024},                                          // 0000001110100
+  {13, 1088},                                          // 0000001110101
+  {13, 1152},                                          // 0000001110110
+  {13, 1216},                                          // 0000001110111
+  {10, 64}, {10, 64}, {10, 64}, {10, 64},              // 0000001111xxx
+    {10, 64}, {10, 64}, {10, 64}, {10, 64}
+};
+
+// 7-12 bit codes (upper 4 bits are 0)
+static CCITTCode blackTab2[192] = {
+  {8, 13}, {8, 13}, {8, 13}, {8, 13},                  // 00000100xxxx
+    {8, 13}, {8, 13}, {8, 13}, {8, 13},
+    {8, 13}, {8, 13}, {8, 13}, {8, 13},
+    {8, 13}, {8, 13}, {8, 13}, {8, 13},
+  {11, 23}, {11, 23},                                  // 00000101000x
+  {12, 50},                                            // 000001010010
+  {12, 51},                                            // 000001010011
+  {12, 44},                                            // 000001010100
+  {12, 45},                                            // 000001010101
+  {12, 46},                                            // 000001010110
+  {12, 47},                                            // 000001010111
+  {12, 57},                                            // 000001011000
+  {12, 58},                                            // 000001011001
+  {12, 61},                                            // 000001011010
+  {12, 256},                                           // 000001011011
+  {10, 16}, {10, 16}, {10, 16}, {10, 16},              // 0000010111xx
+  {10, 17}, {10, 17}, {10, 17}, {10, 17},              // 0000011000xx
+  {12, 48},                                            // 000001100100
+  {12, 49},                                            // 000001100101
+  {12, 62},                                            // 000001100110
+  {12, 63},                                            // 000001100111
+  {12, 30},                                            // 000001101000
+  {12, 31},                                            // 000001101001
+  {12, 32},                                            // 000001101010
+  {12, 33},                                            // 000001101011
+  {12, 40},                                            // 000001101100
+  {12, 41},                                            // 000001101101
+  {11, 22}, {11, 22},                                  // 00000110111x
+  {8, 14}, {8, 14}, {8, 14}, {8, 14},                  // 00000111xxxx
+    {8, 14}, {8, 14}, {8, 14}, {8, 14},
+    {8, 14}, {8, 14}, {8, 14}, {8, 14},
+    {8, 14}, {8, 14}, {8, 14}, {8, 14},
+  {7, 10}, {7, 10}, {7, 10}, {7, 10},                  // 0000100xxxxx
+    {7, 10}, {7, 10}, {7, 10}, {7, 10},
+    {7, 10}, {7, 10}, {7, 10}, {7, 10},
+    {7, 10}, {7, 10}, {7, 10}, {7, 10},
+    {7, 10}, {7, 10}, {7, 10}, {7, 10},
+    {7, 10}, {7, 10}, {7, 10}, {7, 10},
+    {7, 10}, {7, 10}, {7, 10}, {7, 10},
+    {7, 10}, {7, 10}, {7, 10}, {7, 10},
+  {7, 11}, {7, 11}, {7, 11}, {7, 11},                  // 0000101xxxxx
+    {7, 11}, {7, 11}, {7, 11}, {7, 11},
+    {7, 11}, {7, 11}, {7, 11}, {7, 11},
+    {7, 11}, {7, 11}, {7, 11}, {7, 11},
+    {7, 11}, {7, 11}, {7, 11}, {7, 11},
+    {7, 11}, {7, 11}, {7, 11}, {7, 11},
+    {7, 11}, {7, 11}, {7, 11}, {7, 11},
+    {7, 11}, {7, 11}, {7, 11}, {7, 11},
+  {9, 15}, {9, 15}, {9, 15}, {9, 15},                  // 000011000xxx
+    {9, 15}, {9, 15}, {9, 15}, {9, 15},
+  {12, 128},                                           // 000011001000
+  {12, 192},                                           // 000011001001
+  {12, 26},                                            // 000011001010
+  {12, 27},                                            // 000011001011
+  {12, 28},                                            // 000011001100
+  {12, 29},                                            // 000011001101
+  {11, 19}, {11, 19},                                  // 00001100111x
+  {11, 20}, {11, 20},                                  // 00001101000x
+  {12, 34},                                            // 000011010010
+  {12, 35},                                            // 000011010011
+  {12, 36},                                            // 000011010100
+  {12, 37},                                            // 000011010101
+  {12, 38},                                            // 000011010110
+  {12, 39},                                            // 000011010111
+  {11, 21}, {11, 21},                                  // 00001101100x
+  {12, 42},                                            // 000011011010
+  {12, 43},                                            // 000011011011
+  {10, 0}, {10, 0}, {10, 0}, {10, 0},                  // 0000110111xx
+  {7, 12}, {7, 12}, {7, 12}, {7, 12},                  // 0000111xxxxx
+    {7, 12}, {7, 12}, {7, 12}, {7, 12},
+    {7, 12}, {7, 12}, {7, 12}, {7, 12},
+    {7, 12}, {7, 12}, {7, 12}, {7, 12},
+    {7, 12}, {7, 12}, {7, 12}, {7, 12},
+    {7, 12}, {7, 12}, {7, 12}, {7, 12},
+    {7, 12}, {7, 12}, {7, 12}, {7, 12},
+    {7, 12}, {7, 12}, {7, 12}, {7, 12}
+};
+
+// 2-6 bit codes
+static CCITTCode blackTab3[64] = {
+  {-1, -1}, {-1, -1}, {-1, -1}, {-1, -1},              // 0000xx
+  {6, 9},                                              // 000100
+  {6, 8},                                              // 000101
+  {5, 7}, {5, 7},                                      // 00011x
+  {4, 6}, {4, 6}, {4, 6}, {4, 6},                      // 0010xx
+  {4, 5}, {4, 5}, {4, 5}, {4, 5},                      // 0011xx
+  {3, 1}, {3, 1}, {3, 1}, {3, 1},                      // 010xxx
+    {3, 1}, {3, 1}, {3, 1}, {3, 1},
+  {3, 4}, {3, 4}, {3, 4}, {3, 4},                      // 011xxx
+    {3, 4}, {3, 4}, {3, 4}, {3, 4},
+  {2, 3}, {2, 3}, {2, 3}, {2, 3},                      // 10xxxx
+    {2, 3}, {2, 3}, {2, 3}, {2, 3},
+    {2, 3}, {2, 3}, {2, 3}, {2, 3},
+    {2, 3}, {2, 3}, {2, 3}, {2, 3},
+  {2, 2}, {2, 2}, {2, 2}, {2, 2},                      // 11xxxx
+    {2, 2}, {2, 2}, {2, 2}, {2, 2},
+    {2, 2}, {2, 2}, {2, 2}, {2, 2},
+    {2, 2}, {2, 2}, {2, 2}, {2, 2}
+};
diff --git a/lib/xpdf/Stream.h b/lib/xpdf/Stream.h
new file mode 100644 (file)
index 0000000..e1e7bae
--- /dev/null
@@ -0,0 +1,846 @@
+//========================================================================
+//
+// Stream.h
+//
+// Copyright 1996-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef STREAM_H
+#define STREAM_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#include <stdio.h>
+#include "gtypes.h"
+#include "Object.h"
+
+class Decrypt;
+class BaseStream;
+
+//------------------------------------------------------------------------
+
+enum StreamKind {
+  strFile,
+  strASCIIHex,
+  strASCII85,
+  strLZW,
+  strRunLength,
+  strCCITTFax,
+  strDCT,
+  strFlate,
+  strJBIG2,
+  strJPX,
+  strWeird                     // internal-use stream types
+};
+
+enum StreamColorSpaceMode {
+  streamCSNone,
+  streamCSDeviceGray,
+  streamCSDeviceRGB,
+  streamCSDeviceCMYK
+};
+
+//------------------------------------------------------------------------
+// Stream (base class)
+//------------------------------------------------------------------------
+
+class Stream {
+public:
+
+  // Constructor.
+  Stream();
+
+  // Destructor.
+  virtual ~Stream();
+
+  // Reference counting.
+  int incRef() { return ++ref; }
+  int decRef() { return --ref; }
+
+  // Get kind of stream.
+  virtual StreamKind getKind() = 0;
+
+  // Reset stream to beginning.
+  virtual void reset() = 0;
+
+  // Close down the stream.
+  virtual void close();
+
+  // Get next char from stream.
+  virtual int getChar() = 0;
+
+  // Peek at next char in stream.
+  virtual int lookChar() = 0;
+
+  // Get next char from stream without using the predictor.
+  // This is only used by StreamPredictor.
+  virtual int getRawChar();
+
+  // Get next line from stream.
+  virtual char *getLine(char *buf, int size);
+
+  // Get current position in file.
+  virtual int getPos() = 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(int psLevel, char *indent);
+
+  // Does this stream type potentially contain non-printable chars?
+  virtual GBool isBinary(GBool last = gTrue) = 0;
+
+  // Get the BaseStream of this stream.
+  virtual BaseStream *getBaseStream() = 0;
+
+  // Get the dictionary associated with this stream.
+  virtual Dict *getDict() = 0;
+
+  // Is this an encoding filter?
+  virtual GBool isEncoder() { return gFalse; }
+
+  // Get image parameters which are defined by the stream contents.
+  virtual void getImageParams(int *bitsPerComponent,
+                             StreamColorSpaceMode *csMode) {}
+
+  // Add filters to this stream according to the parameters in <dict>.
+  // Returns the new stream.
+  Stream *addFilters(Object *dict);
+
+private:
+
+  Stream *makeFilter(char *name, Stream *str, Object *params);
+
+  int ref;                     // reference count
+};
+
+//------------------------------------------------------------------------
+// BaseStream
+//
+// This is the base class for all streams that read directly from a file.
+//------------------------------------------------------------------------
+
+class BaseStream: public Stream {
+public:
+
+  BaseStream(Object *dictA);
+  virtual ~BaseStream();
+  virtual Stream *makeSubStream(Guint start, GBool limited,
+                               Guint length, Object *dict) = 0;
+  virtual void setPos(Guint pos, int dir = 0) = 0;
+  virtual GBool isBinary(GBool last = gTrue) { return last; }
+  virtual BaseStream *getBaseStream() { return this; }
+  virtual Dict *getDict() { return dict.getDict(); }
+
+  // Get/set position of first byte of stream within the file.
+  virtual Guint getStart() = 0;
+  virtual void moveStart(int delta) = 0;
+
+  // Set decryption for this stream.
+  virtual void doDecryption(Guchar *fileKey, int keyLength,
+                           int objNum, int objGen);
+
+protected:
+
+  Decrypt *decrypt;
+
+private:
+
+  Object dict;
+};
+
+//------------------------------------------------------------------------
+// FilterStream
+//
+// This is the base class for all streams that filter another stream.
+//------------------------------------------------------------------------
+
+class FilterStream: public Stream {
+public:
+
+  FilterStream(Stream *strA);
+  virtual ~FilterStream();
+  virtual void close();
+  virtual int getPos() { return str->getPos(); }
+  virtual void setPos(Guint pos, int dir = 0);
+  virtual BaseStream *getBaseStream() { return str->getBaseStream(); }
+  virtual Dict *getDict() { return str->getDict(); }
+
+protected:
+
+  Stream *str;
+};
+
+//------------------------------------------------------------------------
+// ImageStream
+//------------------------------------------------------------------------
+
+class ImageStream {
+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 *strA, int widthA, int nCompsA, int nBitsA);
+
+  ~ImageStream();
+
+  // Reset the stream.
+  void reset();
+
+  // Gets the next pixel from the stream.  <pix> should be able to hold
+  // at least nComps elements.  Returns false at end of file.
+  GBool getPixel(Guchar *pix);
+
+  // Returns a pointer to the next line of pixels.  Returns NULL at
+  // end of file.
+  Guchar *getLine();
+
+  // Skip an entire line from the image.
+  void skipLine();
+
+private:
+
+  Stream *str;                 // base stream
+  int width;                   // pixels per line
+  int nComps;                  // components per pixel
+  int nBits;                   // bits per component
+  int nVals;                   // components per line
+  Guchar *imgLine;             // line buffer
+  int imgIdx;                  // current index in imgLine
+};
+
+//------------------------------------------------------------------------
+// StreamPredictor
+//------------------------------------------------------------------------
+
+class StreamPredictor {
+public:
+
+  // Create a predictor object.  Note that the parameters are for the
+  // predictor, and may not match the actual image parameters.
+  StreamPredictor(Stream *strA, int predictorA,
+                 int widthA, int nCompsA, int nBitsA);
+
+  ~StreamPredictor();
+
+  GBool isOk() { return ok; }
+
+  int lookChar();
+  int getChar();
+
+private:
+
+  GBool getNextLine();
+
+  Stream *str;                 // base stream
+  int predictor;               // predictor
+  int width;                   // pixels per line
+  int nComps;                  // components per pixel
+  int nBits;                   // bits per component
+  int nVals;                   // components per line
+  int pixBytes;                        // bytes per pixel
+  int rowBytes;                        // bytes per line
+  Guchar *predLine;            // line buffer
+  int predIdx;                 // current index in predLine
+  GBool ok;
+};
+
+//------------------------------------------------------------------------
+// FileStream
+//------------------------------------------------------------------------
+
+#define fileStreamBufSize 256
+
+class FileStream: public BaseStream {
+public:
+
+  FileStream(FILE *fA, Guint startA, GBool limitedA,
+            Guint lengthA, Object *dictA);
+  virtual ~FileStream();
+  virtual Stream *makeSubStream(Guint startA, GBool limitedA,
+                               Guint lengthA, Object *dictA);
+  virtual StreamKind getKind() { return strFile; }
+  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 int getPos() { return bufPos + (bufPtr - buf); }
+  virtual void setPos(Guint pos, int dir = 0);
+  virtual Guint getStart() { return start; }
+  virtual void moveStart(int delta);
+
+private:
+
+  GBool fillBuf();
+
+  FILE *f;
+  Guint start;
+  GBool limited;
+  Guint length;
+  char buf[fileStreamBufSize];
+  char *bufPtr;
+  char *bufEnd;
+  Guint bufPos;
+  int savePos;
+  GBool saved;
+};
+
+//------------------------------------------------------------------------
+// MemStream
+//------------------------------------------------------------------------
+
+class MemStream: public BaseStream {
+public:
+
+  MemStream(char *bufA, Guint startA, 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 (int)(bufPtr - buf); }
+  virtual void setPos(Guint pos, int dir = 0);
+  virtual Guint getStart() { return start; }
+  virtual void moveStart(int delta);
+  virtual void doDecryption(Guchar *fileKey, int keyLength,
+                           int objNum, int objGen);
+
+private:
+
+  char *buf;
+  Guint start;
+  Guint length;
+  char *bufEnd;
+  char *bufPtr;
+  GBool needFree;
+};
+
+//------------------------------------------------------------------------
+// EmbedStream
+//
+// This is a special stream type used for embedded streams (inline
+// images).  It reads directly from the base stream -- after the
+// EmbedStream is deleted, reads from the base stream will proceed where
+// the BaseStream left off.  Note that this is very different behavior
+// that creating a new FileStream (using makeSubStream).
+//------------------------------------------------------------------------
+
+class EmbedStream: public BaseStream {
+public:
+
+  EmbedStream(Stream *strA, Object *dictA, GBool limitedA, Guint lengthA);
+  virtual ~EmbedStream();
+  virtual Stream *makeSubStream(Guint start, GBool limitedA,
+                               Guint lengthA, Object *dictA);
+  virtual StreamKind getKind() { return str->getKind(); }
+  virtual void reset() {}
+  virtual int getChar();
+  virtual int lookChar();
+  virtual int getPos() { return str->getPos(); }
+  virtual void setPos(Guint pos, int dir = 0);
+  virtual Guint getStart();
+  virtual void moveStart(int delta);
+
+private:
+
+  Stream *str;
+  GBool limited;
+  Guint length;
+};
+
+//------------------------------------------------------------------------
+// ASCIIHexStream
+//------------------------------------------------------------------------
+
+class ASCIIHexStream: public FilterStream {
+public:
+
+  ASCIIHexStream(Stream *strA);
+  virtual ~ASCIIHexStream();
+  virtual StreamKind getKind() { return strASCIIHex; }
+  virtual void reset();
+  virtual int getChar()
+    { int c = lookChar(); buf = EOF; return c; }
+  virtual int lookChar();
+  virtual GString *getPSFilter(int psLevel, char *indent);
+  virtual GBool isBinary(GBool last = gTrue);
+
+private:
+
+  int buf;
+  GBool eof;
+};
+
+//------------------------------------------------------------------------
+// ASCII85Stream
+//------------------------------------------------------------------------
+
+class ASCII85Stream: public FilterStream {
+public:
+
+  ASCII85Stream(Stream *strA);
+  virtual ~ASCII85Stream();
+  virtual StreamKind getKind() { return strASCII85; }
+  virtual void reset();
+  virtual int getChar()
+    { int ch = lookChar(); ++index; return ch; }
+  virtual int lookChar();
+  virtual GString *getPSFilter(int psLevel, char *indent);
+  virtual GBool isBinary(GBool last = gTrue);
+
+private:
+
+  int c[5];
+  int b[4];
+  int index, n;
+  GBool eof;
+};
+
+//------------------------------------------------------------------------
+// LZWStream
+//------------------------------------------------------------------------
+
+class LZWStream: public FilterStream {
+public:
+
+  LZWStream(Stream *strA, int predictor, int columns, int colors,
+           int bits, int earlyA);
+  virtual ~LZWStream();
+  virtual StreamKind getKind() { return strLZW; }
+  virtual void reset();
+  virtual int getChar();
+  virtual int lookChar();
+  virtual int getRawChar();
+  virtual GString *getPSFilter(int psLevel, char *indent);
+  virtual GBool isBinary(GBool last = gTrue);
+
+private:
+
+  StreamPredictor *pred;       // predictor
+  int early;                   // early parameter
+  GBool eof;                   // true if at eof
+  int inputBuf;                        // input buffer
+  int inputBits;               // number of bits in input buffer
+  struct {                     // decoding table
+    int length;
+    int head;
+    Guchar tail;
+  } table[4097];
+  int nextCode;                        // next code to be used
+  int nextBits;                        // number of bits in next code word
+  int prevCode;                        // previous code used in stream
+  int newChar;                 // next char to be added to table
+  Guchar seqBuf[4097];         // buffer for current sequence
+  int seqLength;               // length of current sequence
+  int seqIndex;                        // index into current sequence
+  GBool first;                 // first code after a table clear
+
+  GBool processNextCode();
+  void clearTable();
+  int getCode();
+};
+
+//------------------------------------------------------------------------
+// RunLengthStream
+//------------------------------------------------------------------------
+
+class RunLengthStream: public FilterStream {
+public:
+
+  RunLengthStream(Stream *strA);
+  virtual ~RunLengthStream();
+  virtual StreamKind getKind() { return strRunLength; }
+  virtual void reset();
+  virtual int getChar()
+    { return (bufPtr >= bufEnd && !fillBuf()) ? EOF : (*bufPtr++ & 0xff); }
+  virtual int lookChar()
+    { return (bufPtr >= bufEnd && !fillBuf()) ? EOF : (*bufPtr & 0xff); }
+  virtual GString *getPSFilter(int psLevel, char *indent);
+  virtual GBool isBinary(GBool last = gTrue);
+
+private:
+
+  char buf[128];               // buffer
+  char *bufPtr;                        // next char to read
+  char *bufEnd;                        // end of buffer
+  GBool eof;
+
+  GBool fillBuf();
+};
+
+//------------------------------------------------------------------------
+// CCITTFaxStream
+//------------------------------------------------------------------------
+
+struct CCITTCodeTable;
+
+class CCITTFaxStream: public FilterStream {
+public:
+
+  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();
+  virtual int getChar()
+    { int c = lookChar(); buf = EOF; return c; }
+  virtual int lookChar();
+  virtual GString *getPSFilter(int psLevel, char *indent);
+  virtual GBool isBinary(GBool last = gTrue);
+
+private:
+
+  int encoding;                        // 'K' parameter
+  GBool endOfLine;             // 'EndOfLine' parameter
+  GBool byteAlign;             // 'EncodedByteAlign' parameter
+  int columns;                 // 'Columns' parameter
+  int rows;                    // 'Rows' parameter
+  GBool endOfBlock;            // 'EndOfBlock' parameter
+  GBool black;                 // 'BlackIs1' parameter
+  GBool eof;                   // true if at eof
+  GBool nextLine2D;            // true if next line uses 2D encoding
+  int row;                     // current row
+  int inputBuf;                        // input buffer
+  int inputBits;               // number of bits in input buffer
+  short *refLine;              // reference line changing elements
+  int b1;                      // index into refLine
+  short *codingLine;           // coding line changing elements
+  int a0;                      // index into codingLine
+  int outputBits;              // remaining ouput bits
+  int buf;                     // character buffer
+
+  short getTwoDimCode();
+  short getWhiteCode();
+  short getBlackCode();
+  short lookBits(int n);
+  void eatBits(int n) { inputBits -= n; }
+};
+
+//------------------------------------------------------------------------
+// DCTStream
+//------------------------------------------------------------------------
+
+// DCT component info
+struct DCTCompInfo {
+  int id;                      // component ID
+  int hSample, vSample;                // horiz/vert sampling resolutions
+  int quantTable;              // quantization table number
+  int prevDC;                  // DC coefficient accumulator
+};
+
+struct DCTScanInfo {
+  GBool comp[4];               // comp[i] is set if component i is
+                               //   included in this scan
+  int numComps;                        // number of components in the scan
+  int dcHuffTable[4];          // DC Huffman table numbers
+  int acHuffTable[4];          // AC Huffman table numbers
+  int firstCoeff, lastCoeff;   // first and last DCT coefficient
+  int ah, al;                  // successive approximation parameters
+};
+
+// DCT Huffman decoding table
+struct DCTHuffTable {
+  Guchar firstSym[17];         // first symbol for this bit length
+  Gushort firstCode[17];       // first code for this bit length
+  Gushort numCodes[17];                // number of codes of this bit length
+  Guchar sym[256];             // symbols
+};
+
+class DCTStream: public FilterStream {
+public:
+
+  DCTStream(Stream *strA);
+  virtual ~DCTStream();
+  virtual StreamKind getKind() { return strDCT; }
+  virtual void reset();
+  virtual int getChar();
+  virtual int lookChar();
+  virtual GString *getPSFilter(int psLevel, char *indent);
+  virtual GBool isBinary(GBool last = gTrue);
+  Stream *getRawStream() { return str; }
+
+private:
+
+  GBool progressive;           // set if in progressive mode
+  GBool interleaved;           // set if in interleaved mode
+  int width, height;           // image size
+  int mcuWidth, mcuHeight;     // size of min coding unit, in data units
+  int bufWidth, bufHeight;     // frameBuf size
+  DCTCompInfo compInfo[4];     // info for each component
+  DCTScanInfo scanInfo;                // info for the current scan
+  int numComps;                        // number of components in image
+  int colorXform;              // need YCbCr-to-RGB transform?
+  GBool gotJFIFMarker;         // set if APP0 JFIF marker was present
+  GBool gotAdobeMarker;                // set if APP14 Adobe marker was present
+  int restartInterval;         // restart interval, in MCUs
+  Gushort quantTables[4][64];  // quantization tables
+  int numQuantTables;          // number of quantization tables
+  DCTHuffTable dcHuffTables[4];        // DC Huffman tables
+  DCTHuffTable acHuffTables[4];        // AC Huffman tables
+  int numDCHuffTables;         // number of DC Huffman tables
+  int numACHuffTables;         // number of AC Huffman tables
+  Guchar *rowBuf[4][32];       // buffer for one MCU (non-progressive mode)
+  int *frameBuf[4];            // buffer for frame (progressive mode)
+  int comp, x, y, dy;          // current position within image/MCU
+  int restartCtr;              // MCUs left until restart
+  int restartMarker;           // next restart marker
+  int eobRun;                  // number of EOBs left in the current run
+  int inputBuf;                        // input buffer for variable length codes
+  int inputBits;               // number of valid bits in input buffer
+
+  void restart();
+  GBool readMCURow();
+  void readScan();
+  GBool readDataUnit(DCTHuffTable *dcHuffTable,
+                    DCTHuffTable *acHuffTable,
+                    int *prevDC, int data[64]);
+  GBool readProgressiveDataUnit(DCTHuffTable *dcHuffTable,
+                               DCTHuffTable *acHuffTable,
+                               int *prevDC, int data[64]);
+  void decodeImage();
+  void transformDataUnit(Gushort *quantTable,
+                        int dataIn[64], Guchar dataOut[64]);
+  int readHuffSym(DCTHuffTable *table);
+  int readAmp(int size);
+  int readBit();
+  GBool readHeader();
+  GBool readBaselineSOF();
+  GBool readProgressiveSOF();
+  GBool readScanInfo();
+  GBool readQuantTables();
+  GBool readHuffmanTables();
+  GBool readRestartInterval();
+  GBool readJFIFMarker();
+  GBool readAdobeMarker();
+  GBool readTrailer();
+  int readMarker();
+  int read16();
+};
+
+//------------------------------------------------------------------------
+// FlateStream
+//------------------------------------------------------------------------
+
+#define flateWindow          32768    // buffer size
+#define flateMask            (flateWindow-1)
+#define flateMaxHuffman         15    // max Huffman code length
+#define flateMaxCodeLenCodes    19    // max # code length codes
+#define flateMaxLitCodes       288    // max # literal codes
+#define flateMaxDistCodes       30    // max # distance codes
+
+// Huffman code table entry
+struct FlateCode {
+  Gushort len;                 // code length, in bits
+  Gushort val;                 // value represented by this code
+};
+
+struct FlateHuffmanTab {
+  FlateCode *codes;
+  int maxLen;
+};
+
+// Decoding info for length and distance code words
+struct FlateDecode {
+  int bits;                    // # extra bits
+  int first;                   // first length/distance
+};
+
+class FlateStream: public FilterStream {
+public:
+
+  FlateStream(Stream *strA, int predictor, int columns,
+             int colors, int bits);
+  virtual ~FlateStream();
+  virtual StreamKind getKind() { return strFlate; }
+  virtual void reset();
+  virtual int getChar();
+  virtual int lookChar();
+  virtual int getRawChar();
+  virtual GString *getPSFilter(int psLevel, char *indent);
+  virtual GBool isBinary(GBool last = gTrue);
+
+private:
+
+  StreamPredictor *pred;       // predictor
+  Guchar buf[flateWindow];     // output data buffer
+  int index;                   // current index into output buffer
+  int remain;                  // number valid bytes in output buffer
+  int codeBuf;                 // input buffer
+  int codeSize;                        // number of bits in input buffer
+  int                          // literal and distance code lengths
+    codeLengths[flateMaxLitCodes + flateMaxDistCodes];
+  FlateHuffmanTab litCodeTab;  // literal code table
+  FlateHuffmanTab distCodeTab; // distance code table
+  GBool compressedBlock;       // set if reading a compressed block
+  int blockLen;                        // remaining length of uncompressed block
+  GBool endOfBlock;            // set when end of block is reached
+  GBool eof;                   // set when end of stream is reached
+
+  static int                   // code length code reordering
+    codeLenCodeMap[flateMaxCodeLenCodes];
+  static FlateDecode           // length decoding info
+    lengthDecode[flateMaxLitCodes-257];
+  static FlateDecode           // distance decoding info
+    distDecode[flateMaxDistCodes];
+  static FlateHuffmanTab       // fixed literal code table
+    fixedLitCodeTab;
+  static FlateHuffmanTab       // fixed distance code table
+    fixedDistCodeTab;
+
+  void readSome();
+  GBool startBlock();
+  void loadFixedCodes();
+  GBool readDynamicCodes();
+  void compHuffmanCodes(int *lengths, int n, FlateHuffmanTab *tab);
+  int getHuffmanCodeWord(FlateHuffmanTab *tab);
+  int getCodeWord(int bits);
+};
+
+//------------------------------------------------------------------------
+// EOFStream
+//------------------------------------------------------------------------
+
+class EOFStream: public FilterStream {
+public:
+
+  EOFStream(Stream *strA);
+  virtual ~EOFStream();
+  virtual StreamKind getKind() { return strWeird; }
+  virtual void reset() {}
+  virtual int getChar() { return EOF; }
+  virtual int lookChar() { return EOF; }
+  virtual GString *getPSFilter(int psLevel, char *indent)  { return NULL; }
+  virtual GBool isBinary(GBool last = gTrue) { return gFalse; }
+};
+
+//------------------------------------------------------------------------
+// FixedLengthEncoder
+//------------------------------------------------------------------------
+
+class FixedLengthEncoder: public FilterStream {
+public:
+
+  FixedLengthEncoder(Stream *strA, int lengthA);
+  ~FixedLengthEncoder();
+  virtual StreamKind getKind() { return strWeird; }
+  virtual void reset();
+  virtual int getChar();
+  virtual int lookChar();
+  virtual GString *getPSFilter(int psLevel, char *indent) { return NULL; }
+  virtual GBool isBinary(GBool last = gTrue);
+  virtual GBool isEncoder() { return gTrue; }
+
+private:
+
+  int length;
+  int count;
+};
+
+//------------------------------------------------------------------------
+// ASCIIHexEncoder
+//------------------------------------------------------------------------
+
+class ASCIIHexEncoder: public FilterStream {
+public:
+
+  ASCIIHexEncoder(Stream *strA);
+  virtual ~ASCIIHexEncoder();
+  virtual StreamKind getKind() { return strWeird; }
+  virtual void reset();
+  virtual int getChar()
+    { return (bufPtr >= bufEnd && !fillBuf()) ? EOF : (*bufPtr++ & 0xff); }
+  virtual int lookChar()
+    { return (bufPtr >= bufEnd && !fillBuf()) ? EOF : (*bufPtr & 0xff); }
+  virtual GString *getPSFilter(int psLevel, 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 *strA);
+  virtual ~ASCII85Encoder();
+  virtual StreamKind getKind() { return strWeird; }
+  virtual void reset();
+  virtual int getChar()
+    { return (bufPtr >= bufEnd && !fillBuf()) ? EOF : (*bufPtr++ & 0xff); }
+  virtual int lookChar()
+    { return (bufPtr >= bufEnd && !fillBuf()) ? EOF : (*bufPtr & 0xff); }
+  virtual GString *getPSFilter(int psLevel, char *indent) { return NULL; }
+  virtual GBool isBinary(GBool last = gTrue) { return gFalse; }
+  virtual GBool isEncoder() { return gTrue; }
+
+private:
+
+  char buf[8];
+  char *bufPtr;
+  char *bufEnd;
+  int lineLen;
+  GBool eof;
+
+  GBool fillBuf();
+};
+
+//------------------------------------------------------------------------
+// RunLengthEncoder
+//------------------------------------------------------------------------
+
+class RunLengthEncoder: public FilterStream {
+public:
+
+  RunLengthEncoder(Stream *strA);
+  virtual ~RunLengthEncoder();
+  virtual StreamKind getKind() { return strWeird; }
+  virtual void reset();
+  virtual int getChar()
+    { return (bufPtr >= bufEnd && !fillBuf()) ? EOF : (*bufPtr++ & 0xff); }
+  virtual int lookChar()
+    { return (bufPtr >= bufEnd && !fillBuf()) ? EOF : (*bufPtr & 0xff); }
+  virtual GString *getPSFilter(int psLevel, char *indent) { return NULL; }
+  virtual GBool isBinary(GBool last = gTrue) { return gTrue; }
+  virtual GBool isEncoder() { return gTrue; }
+
+private:
+
+  char buf[131];
+  char *bufPtr;
+  char *bufEnd;
+  char *nextEnd;
+  GBool eof;
+
+  GBool fillBuf();
+};
+
+#endif
diff --git a/lib/xpdf/UTF8.h b/lib/xpdf/UTF8.h
new file mode 100644 (file)
index 0000000..8536dbf
--- /dev/null
@@ -0,0 +1,56 @@
+//========================================================================
+//
+// UTF8.h
+//
+// Copyright 2001-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+static int mapUTF8(Unicode u, char *buf, int bufSize) {
+  if        (u <= 0x0000007f) {
+    if (bufSize < 1) {
+      return 0;
+    }
+    buf[0] = (char)u;
+    return 1;
+  } else if (u <= 0x000007ff) {
+    if (bufSize < 2) {
+      return 0;
+    }
+    buf[0] = (char)(0xc0 + (u >> 6));
+    buf[1] = (char)(0x80 + (u & 0x3f));
+    return 2;
+  } else if (u <= 0x0000ffff) {
+    if (bufSize < 3) {
+      return 0;
+    }
+    buf[0] = (char)(0xe0 + (u >> 12));
+    buf[1] = (char)(0x80 + ((u >> 6) & 0x3f));
+    buf[2] = (char)(0x80 + (u & 0x3f));
+    return 3;
+  } else if (u <= 0x0010ffff) {
+    if (bufSize < 4) {
+      return 0;
+    }
+    buf[0] = (char)(0xf0 + (u >> 18));
+    buf[1] = (char)(0x80 + ((u >> 12) & 0x3f));
+    buf[2] = (char)(0x80 + ((u >> 6) & 0x3f));
+    buf[3] = (char)(0x80 + (u & 0x3f));
+    return 4;
+  } else {
+    return 0;
+  }
+}
+
+static int mapUCS2(Unicode u, char *buf, int bufSize) {
+  if (u <= 0xffff) {
+    if (bufSize < 2) {
+      return 0;
+    }
+    buf[0] = (char)((u >> 8) & 0xff);
+    buf[1] = (char)(u & 0xff);
+    return 2;
+  } else {
+    return 0;
+  }
+}
diff --git a/lib/xpdf/UnicodeMap.cc b/lib/xpdf/UnicodeMap.cc
new file mode 100644 (file)
index 0000000..2b8cb1f
--- /dev/null
@@ -0,0 +1,293 @@
+//========================================================================
+//
+// UnicodeMap.cc
+//
+// Copyright 2001-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include "gmem.h"
+#include "gfile.h"
+#include "GString.h"
+#include "GList.h"
+#include "Error.h"
+#include "GlobalParams.h"
+#include "UnicodeMap.h"
+
+//------------------------------------------------------------------------
+
+#define maxExtCode 16
+
+struct UnicodeMapExt {
+  Unicode u;                   // Unicode char
+  char code[maxExtCode];
+  Guint nBytes;
+};
+
+//------------------------------------------------------------------------
+
+UnicodeMap *UnicodeMap::parse(GString *encodingNameA) {
+  FILE *f;
+  UnicodeMap *map;
+  UnicodeMapRange *range;
+  UnicodeMapExt *eMap;
+  int size, eMapsSize;
+  char buf[256];
+  int line, nBytes, i, x;
+  char *tok1, *tok2, *tok3;
+
+  if (!(f = globalParams->getUnicodeMapFile(encodingNameA))) {
+    error(-1, "Couldn't find unicodeMap file for the '%s' encoding",
+         encodingNameA->getCString());
+    return NULL;
+  }
+
+  map = new UnicodeMap(encodingNameA->copy());
+
+  size = 8;
+  map->ranges = (UnicodeMapRange *)gmallocn(size, sizeof(UnicodeMapRange));
+  eMapsSize = 0;
+
+  line = 1;
+  while (getLine(buf, sizeof(buf), f)) {
+    if ((tok1 = strtok(buf, " \t\r\n")) &&
+       (tok2 = strtok(NULL, " \t\r\n"))) {
+      if (!(tok3 = strtok(NULL, " \t\r\n"))) {
+       tok3 = tok2;
+       tok2 = tok1;
+      }
+      nBytes = strlen(tok3) / 2;
+      if (nBytes <= 4) {
+       if (map->len == size) {
+         size *= 2;
+         map->ranges = (UnicodeMapRange *)
+           greallocn(map->ranges, size, sizeof(UnicodeMapRange));
+       }
+       range = &map->ranges[map->len];
+       sscanf(tok1, "%x", &range->start);
+       sscanf(tok2, "%x", &range->end);
+       sscanf(tok3, "%x", &range->code);
+       range->nBytes = nBytes;
+       ++map->len;
+      } else if (tok2 == tok1) {
+       if (map->eMapsLen == eMapsSize) {
+         eMapsSize += 16;
+         map->eMaps = (UnicodeMapExt *)
+           greallocn(map->eMaps, eMapsSize, sizeof(UnicodeMapExt));
+       }
+       eMap = &map->eMaps[map->eMapsLen];
+       sscanf(tok1, "%x", &eMap->u);
+       for (i = 0; i < nBytes; ++i) {
+         sscanf(tok3 + i*2, "%2x", &x);
+         eMap->code[i] = (char)x;
+       }
+       eMap->nBytes = nBytes;
+       ++map->eMapsLen;
+      } else {
+       error(-1, "Bad line (%d) in unicodeMap file for the '%s' encoding",
+             line, encodingNameA->getCString());
+      }
+    } else {
+      error(-1, "Bad line (%d) in unicodeMap file for the '%s' encoding",
+           line, encodingNameA->getCString());
+    }
+    ++line;
+  }
+
+  fclose(f);
+
+  return map;
+}
+
+UnicodeMap::UnicodeMap(GString *encodingNameA) {
+  encodingName = encodingNameA;
+  unicodeOut = gFalse;
+  kind = unicodeMapUser;
+  ranges = NULL;
+  len = 0;
+  eMaps = NULL;
+  eMapsLen = 0;
+  refCnt = 1;
+#if MULTITHREADED
+  gInitMutex(&mutex);
+#endif
+}
+
+UnicodeMap::UnicodeMap(char *encodingNameA, GBool unicodeOutA,
+                      UnicodeMapRange *rangesA, int lenA) {
+  encodingName = new GString(encodingNameA);
+  unicodeOut = unicodeOutA;
+  kind = unicodeMapResident;
+  ranges = rangesA;
+  len = lenA;
+  eMaps = NULL;
+  eMapsLen = 0;
+  refCnt = 1;
+#if MULTITHREADED
+  gInitMutex(&mutex);
+#endif
+}
+
+UnicodeMap::UnicodeMap(char *encodingNameA, GBool unicodeOutA,
+                      UnicodeMapFunc funcA) {
+  encodingName = new GString(encodingNameA);
+  unicodeOut = unicodeOutA;
+  kind = unicodeMapFunc;
+  func = funcA;
+  eMaps = NULL;
+  eMapsLen = 0;
+  refCnt = 1;
+#if MULTITHREADED
+  gInitMutex(&mutex);
+#endif
+}
+
+UnicodeMap::~UnicodeMap() {
+  delete encodingName;
+  if (kind == unicodeMapUser && ranges) {
+    gfree(ranges);
+  }
+  if (eMaps) {
+    gfree(eMaps);
+  }
+#if MULTITHREADED
+  gDestroyMutex(&mutex);
+#endif
+}
+
+void UnicodeMap::incRefCnt() {
+#if MULTITHREADED
+  gLockMutex(&mutex);
+#endif
+  ++refCnt;
+#if MULTITHREADED
+  gUnlockMutex(&mutex);
+#endif
+}
+
+void UnicodeMap::decRefCnt() {
+  GBool done;
+
+#if MULTITHREADED
+  gLockMutex(&mutex);
+#endif
+  done = --refCnt == 0;
+#if MULTITHREADED
+  gUnlockMutex(&mutex);
+#endif
+  if (done) {
+    delete this;
+  }
+}
+
+GBool UnicodeMap::match(GString *encodingNameA) {
+  return !encodingName->cmp(encodingNameA);
+}
+
+int UnicodeMap::mapUnicode(Unicode u, char *buf, int bufSize) {
+  int a, b, m, n, i, j;
+  Guint code;
+
+  if (kind == unicodeMapFunc) {
+    return (*func)(u, buf, bufSize);
+  }
+
+  a = 0;
+  b = len;
+  if (u >= ranges[a].start) {
+    // invariant: ranges[a].start <= u < ranges[b].start
+    while (b - a > 1) {
+      m = (a + b) / 2;
+      if (u >= ranges[m].start) {
+       a = m;
+      } else if (u < ranges[m].start) {
+       b = m;
+      }
+    }
+    if (u <= ranges[a].end) {
+      n = ranges[a].nBytes;
+      if (n > bufSize) {
+       return 0;
+      }
+      code = ranges[a].code + (u - ranges[a].start);
+      for (i = n - 1; i >= 0; --i) {
+       buf[i] = (char)(code & 0xff);
+       code >>= 8;
+      }
+      return n;
+    }
+  }
+
+  for (i = 0; i < eMapsLen; ++i) {
+    if (eMaps[i].u == u) {
+      n = eMaps[i].nBytes;
+      for (j = 0; j < n; ++j) {
+       buf[j] = eMaps[i].code[j];
+      }
+      return n;
+    }
+  }
+
+  return 0;
+}
+
+//------------------------------------------------------------------------
+
+UnicodeMapCache::UnicodeMapCache() {
+  int i;
+
+  for (i = 0; i < unicodeMapCacheSize; ++i) {
+    cache[i] = NULL;
+  }
+}
+
+UnicodeMapCache::~UnicodeMapCache() {
+  int i;
+
+  for (i = 0; i < unicodeMapCacheSize; ++i) {
+    if (cache[i]) {
+      cache[i]->decRefCnt();
+    }
+  }
+}
+
+UnicodeMap *UnicodeMapCache::getUnicodeMap(GString *encodingName) {
+  UnicodeMap *map;
+  int i, j;
+
+  if (cache[0] && cache[0]->match(encodingName)) {
+    cache[0]->incRefCnt();
+    return cache[0];
+  }
+  for (i = 1; i < unicodeMapCacheSize; ++i) {
+    if (cache[i] && cache[i]->match(encodingName)) {
+      map = cache[i];
+      for (j = i; j >= 1; --j) {
+       cache[j] = cache[j - 1];
+      }
+      cache[0] = map;
+      map->incRefCnt();
+      return map;
+    }
+  }
+  if ((map = UnicodeMap::parse(encodingName))) {
+    if (cache[unicodeMapCacheSize - 1]) {
+      cache[unicodeMapCacheSize - 1]->decRefCnt();
+    }
+    for (j = unicodeMapCacheSize - 1; j >= 1; --j) {
+      cache[j] = cache[j - 1];
+    }
+    cache[0] = map;
+    map->incRefCnt();
+    return map;
+  }
+  return NULL;
+}