upgrade to xpdf 3.00. xpdf-3-00
authorkramm <kramm>
Fri, 17 Sep 2004 16:24:55 +0000 (16:24 +0000)
committerkramm <kramm>
Fri, 17 Sep 2004 16:24:55 +0000 (16:24 +0000)
95 files changed:
pdf2swf/xpdf/Annot.cc
pdf2swf/xpdf/Annot.h
pdf2swf/xpdf/Array.cc
pdf2swf/xpdf/Array.h
pdf2swf/xpdf/BuiltinFont.cc
pdf2swf/xpdf/BuiltinFont.h
pdf2swf/xpdf/BuiltinFontTables.cc
pdf2swf/xpdf/BuiltinFontTables.h
pdf2swf/xpdf/CMap.cc
pdf2swf/xpdf/CMap.h
pdf2swf/xpdf/Catalog.cc
pdf2swf/xpdf/Catalog.h
pdf2swf/xpdf/CharCodeToUnicode.cc
pdf2swf/xpdf/CharCodeToUnicode.h
pdf2swf/xpdf/CharTypes.h
pdf2swf/xpdf/Decrypt.cc
pdf2swf/xpdf/Decrypt.h
pdf2swf/xpdf/Dict.cc
pdf2swf/xpdf/Dict.h
pdf2swf/xpdf/Error.cc
pdf2swf/xpdf/Error.h
pdf2swf/xpdf/ErrorCodes.h
pdf2swf/xpdf/FoFiBase.cc [new file with mode: 0644]
pdf2swf/xpdf/FoFiBase.h [new file with mode: 0644]
pdf2swf/xpdf/FoFiEncodings.cc [new file with mode: 0644]
pdf2swf/xpdf/FoFiEncodings.h [new file with mode: 0644]
pdf2swf/xpdf/FoFiTrueType.cc [new file with mode: 0644]
pdf2swf/xpdf/FoFiTrueType.h [new file with mode: 0644]
pdf2swf/xpdf/FoFiType1.cc [new file with mode: 0644]
pdf2swf/xpdf/FoFiType1.h [new file with mode: 0644]
pdf2swf/xpdf/FoFiType1C.cc [new file with mode: 0644]
pdf2swf/xpdf/FoFiType1C.h [new file with mode: 0644]
pdf2swf/xpdf/FontEncodingTables.cc
pdf2swf/xpdf/FontEncodingTables.h
pdf2swf/xpdf/Function.cc
pdf2swf/xpdf/Function.h
pdf2swf/xpdf/GHash.cc
pdf2swf/xpdf/GHash.h
pdf2swf/xpdf/GList.cc
pdf2swf/xpdf/GList.h
pdf2swf/xpdf/GMutex.h [new file with mode: 0644]
pdf2swf/xpdf/GString.cc
pdf2swf/xpdf/GString.h
pdf2swf/xpdf/Gfx.cc
pdf2swf/xpdf/Gfx.h
pdf2swf/xpdf/GfxFont.cc
pdf2swf/xpdf/GfxFont.h
pdf2swf/xpdf/GfxState.cc
pdf2swf/xpdf/GfxState.h
pdf2swf/xpdf/GlobalParams.cc
pdf2swf/xpdf/GlobalParams.h
pdf2swf/xpdf/JArithmeticDecoder.cc [new file with mode: 0644]
pdf2swf/xpdf/JArithmeticDecoder.h [new file with mode: 0644]
pdf2swf/xpdf/JBIG2Stream.cc [new file with mode: 0644]
pdf2swf/xpdf/JBIG2Stream.h [new file with mode: 0644]
pdf2swf/xpdf/JPXStream.cc [new file with mode: 0644]
pdf2swf/xpdf/JPXStream.h [new file with mode: 0644]
pdf2swf/xpdf/Lexer.cc
pdf2swf/xpdf/Lexer.h
pdf2swf/xpdf/Link.cc
pdf2swf/xpdf/Link.h
pdf2swf/xpdf/NameToCharCode.cc
pdf2swf/xpdf/NameToCharCode.h
pdf2swf/xpdf/NameToUnicodeTable.h
pdf2swf/xpdf/Object.cc
pdf2swf/xpdf/Object.h
pdf2swf/xpdf/Outline.cc [new file with mode: 0644]
pdf2swf/xpdf/Outline.h [new file with mode: 0644]
pdf2swf/xpdf/OutputDev.cc
pdf2swf/xpdf/OutputDev.h
pdf2swf/xpdf/PDFDoc.cc
pdf2swf/xpdf/PDFDoc.h
pdf2swf/xpdf/PDFDocEncoding.cc [new file with mode: 0644]
pdf2swf/xpdf/PDFDocEncoding.h [new file with mode: 0644]
pdf2swf/xpdf/PSTokenizer.cc
pdf2swf/xpdf/PSTokenizer.h
pdf2swf/xpdf/Page.cc
pdf2swf/xpdf/Page.h
pdf2swf/xpdf/Parser.cc
pdf2swf/xpdf/Parser.h
pdf2swf/xpdf/Stream-CCITT.h
pdf2swf/xpdf/Stream.cc
pdf2swf/xpdf/Stream.h
pdf2swf/xpdf/UTF8.h
pdf2swf/xpdf/UnicodeMap.cc
pdf2swf/xpdf/UnicodeMap.h
pdf2swf/xpdf/UnicodeMapTables.h
pdf2swf/xpdf/XRef.cc
pdf2swf/xpdf/XRef.h
pdf2swf/xpdf/aconf.h
pdf2swf/xpdf/config.h
pdf2swf/xpdf/gfile.cc
pdf2swf/xpdf/gfile.h
pdf2swf/xpdf/gmem.h
pdf2swf/xpdf/gtypes.h

index b9c606f..245780d 100644 (file)
@@ -2,15 +2,16 @@
 //
 // Annot.cc
 //
-// Copyright 2000-2002 Glyph & Cog, LLC
+// Copyright 2000-2003 Glyph & Cog, LLC
 //
 //========================================================================
 
-#ifdef __GNUC__
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
 #pragma implementation
 #endif
 
-#include <aconf.h>
 #include "gmem.h"
 #include "Object.h"
 #include "Gfx.h"
@@ -96,7 +97,7 @@ void Annot::draw(Gfx *gfx) {
 
 Annots::Annots(XRef *xref, Object *annotsObj) {
   Annot *annot;
-  Object obj1, obj2;
+  Object obj1;
   int size;
   int i;
 
@@ -107,21 +108,16 @@ Annots::Annots(XRef *xref, Object *annotsObj) {
   if (annotsObj->isArray()) {
     for (i = 0; i < annotsObj->arrayGetLength(); ++i) {
       if (annotsObj->arrayGet(i, &obj1)->isDict()) {
-       obj1.dictLookup("Subtype", &obj2);
-       if (obj2.isName("Widget") ||
-           obj2.isName("Stamp")) {
-         annot = new Annot(xref, obj1.getDict());
-         if (annot->isOk()) {
-           if (nAnnots >= size) {
-             size += 16;
-             annots = (Annot **)grealloc(annots, size * sizeof(Annot *));
-           }
-           annots[nAnnots++] = annot;
-         } else {
-           delete annot;
+       annot = new Annot(xref, obj1.getDict());
+       if (annot->isOk()) {
+         if (nAnnots >= size) {
+           size += 16;
+           annots = (Annot **)grealloc(annots, size * sizeof(Annot *));
          }
+         annots[nAnnots++] = annot;
+       } else {
+         delete annot;
        }
-       obj2.free();
       }
       obj1.free();
     }
index 4113a0b..89dde0f 100644 (file)
@@ -2,14 +2,16 @@
 //
 // Annot.h
 //
-// Copyright 2000-2002 Glyph & Cog, LLC
+// Copyright 2000-2003 Glyph & Cog, LLC
 //
 //========================================================================
 
 #ifndef ANNOT_H
 #define ANNOT_H
 
-#ifdef __GNUC__
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
 #pragma interface
 #endif
 
index fbdde49..a6c6db1 100644 (file)
@@ -2,15 +2,17 @@
 //
 // Array.cc
 //
-// Copyright 1996-2002 Glyph & Cog, LLC
+// Copyright 1996-2003 Glyph & Cog, LLC
 //
 //========================================================================
 
-#ifdef __GNUC__
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
 #pragma implementation
 #endif
 
-#include <aconf.h>
+#include <stdlib.h>
 #include <stddef.h>
 #include "gmem.h"
 #include "Object.h"
@@ -36,8 +38,12 @@ Array::~Array() {
 }
 
 void Array::add(Object *elem) {
-  if (length + 1 > size) {
-    size += 8;
+  if (length == size) {
+    if (length == 0) {
+      size = 8;
+    } else {
+      size *= 2;
+    }
     elems = (Object *)grealloc(elems, size * sizeof(Object));
   }
   elems[length] = *elem;
@@ -45,9 +51,23 @@ void Array::add(Object *elem) {
 }
 
 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);
 }
index a118f68..20ae05f 100644 (file)
@@ -2,14 +2,16 @@
 //
 // Array.h
 //
-// Copyright 1996-2002 Glyph & Cog, LLC
+// Copyright 1996-2003 Glyph & Cog, LLC
 //
 //========================================================================
 
 #ifndef ARRAY_H
 #define ARRAY_H
 
-#ifdef __GNUC__
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
 #pragma interface
 #endif
 
index 1b07064..a687e73 100644 (file)
@@ -2,15 +2,16 @@
 //
 // BuiltinFont.cc
 //
-// Copyright 2001-2002 Glyph & Cog, LLC
+// Copyright 2001-2003 Glyph & Cog, LLC
 //
 //========================================================================
 
-#ifdef __GNUC__
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
 #pragma implementation
 #endif
 
-#include <aconf.h>
 #include <stdlib.h>
 #include <string.h>
 #include "gmem.h"
index a795311..903ed19 100644 (file)
@@ -2,14 +2,16 @@
 //
 // BuiltinFont.h
 //
-// Copyright 2001-2002 Glyph & Cog, LLC
+// Copyright 2001-2003 Glyph & Cog, LLC
 //
 //========================================================================
 
 #ifndef BUILTINFONT_H
 #define BUILTINFONT_H
 
-#ifdef __GNUC__
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
 #pragma interface
 #endif
 
index 845abcd..9c36238 100644 (file)
@@ -2,7 +2,7 @@
 //
 // BuiltinFontTables.cc
 //
-// Copyright 2001-2002 Glyph & Cog, LLC
+// Copyright 2001-2003 Glyph & Cog, LLC
 //
 //========================================================================
 
 
 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 },
-  { "arrowup",                           600, NULL },
   { "circumflex",                        600, NULL },
   { "dotaccent",                         600, NULL },
-  { "LL",                                600, NULL },
+  { "edotaccent",                        600, NULL },
   { "asciitilde",                        600, NULL },
   { "colon",                             600, NULL },
   { "onehalf",                           600, NULL },
   { "dollar",                            600, NULL },
+  { "Lcaron",                            600, NULL },
   { "ntilde",                            600, NULL },
-  { "left",                              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 },
@@ -46,14 +56,18 @@ static BuiltinFontWidth courierWidthsTab[] = {
   { "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 },
@@ -62,9 +76,7 @@ static BuiltinFontWidth courierWidthsTab[] = {
   { "U",                                 600, NULL },
   { "agrave",                            600, NULL },
   { "V",                                 600, NULL },
-  { "tab",                               600, NULL },
   { "W",                                 600, NULL },
-  { "ll",                                600, NULL },
   { "equal",                             600, NULL },
   { "question",                          600, NULL },
   { "X",                                 600, NULL },
@@ -72,6 +84,7 @@ static BuiltinFontWidth courierWidthsTab[] = {
   { "Z",                                 600, NULL },
   { "four",                              600, NULL },
   { "a",                                 600, NULL },
+  { "Gcommaaccent",                      600, NULL },
   { "b",                                 600, NULL },
   { "c",                                 600, NULL },
   { "d",                                 600, NULL },
@@ -88,44 +101,57 @@ static BuiltinFontWidth courierWidthsTab[] = {
   { "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 },
-  { "largebullet",                       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 },
-  { "lira",                              600, NULL },
   { "quotedblleft",                      600, NULL },
   { "hyphen",                            600, NULL },
   { "guilsinglright",                    600, NULL },
@@ -134,9 +160,11 @@ static BuiltinFontWidth courierWidthsTab[] = {
   { "exclamdown",                        600, NULL },
   { "endash",                            600, NULL },
   { "oe",                                600, NULL },
+  { "Abreve",                            600, NULL },
+  { "Umacron",                           600, NULL },
   { "ecircumflex",                       600, NULL },
-  { "copyright",                         600, NULL },
   { "Adieresis",                         600, NULL },
+  { "copyright",                         600, NULL },
   { "Egrave",                            600, NULL },
   { "slash",                             600, NULL },
   { "Edieresis",                         600, NULL },
@@ -144,14 +172,17 @@ static BuiltinFontWidth courierWidthsTab[] = {
   { "Idieresis",                         600, NULL },
   { "parenleft",                         600, NULL },
   { "one",                               600, NULL },
-  { "ucircumflex",                       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 },
@@ -167,139 +198,173 @@ static BuiltinFontWidth courierWidthsTab[] = {
   { "nine",                              600, NULL },
   { "udieresis",                         600, NULL },
   { "Zcaron",                            600, NULL },
+  { "Scommaaccent",                      600, NULL },
   { "threequarters",                     600, NULL },
   { "guillemotright",                    600, NULL },
-  { "ydieresis",                         600, NULL },
   { "Ccedilla",                          600, NULL },
+  { "ydieresis",                         600, NULL },
   { "tilde",                             600, NULL },
   { "at",                                600, NULL },
   { "eacute",                            600, NULL },
-  { "Gcaron",                            600, NULL },
   { "underscore",                        600, NULL },
+  { "Euro",                              600, NULL },
+  { "Dcroat",                            600, NULL },
   { "zero",                              600, NULL },
   { "multiply",                          600, NULL },
-  { "Scedilla",                          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 },
-  { "gcaron",                            600, NULL },
+  { "Dcaron",                            600, NULL },
+  { "dcroat",                            600, NULL },
+  { "scedilla",                          600, NULL },
   { "Oacute",                            600, NULL },
   { "Ocircumflex",                       600, NULL },
-  { "scedilla",                          600, NULL },
   { "ogonek",                            600, NULL },
-  { "arrowdown",                         600, NULL },
   { "ograve",                            600, NULL },
+  { "racute",                            600, NULL },
+  { "Tcaron",                            600, NULL },
+  { "Eogonek",                           600, NULL },
   { "thorn",                             600, NULL },
   { "degree",                            600, NULL },
   { "registered",                        600, NULL },
-  { "percent",                           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 },
-  { "oacute",                            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 },
-  { "square",                            600, NULL },
   { "grave",                             600, NULL },
+  { "uogonek",                           600, NULL },
   { "bracketright",                      600, NULL },
   { "ampersand",                         600, NULL },
   { "Iacute",                            600, NULL },
+  { "lacute",                            600, NULL },
   { "igrave",                            600, NULL },
-  { "return",                            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 },
-  { "notegraphic",                       600, NULL },
   { "section",                           600, NULL },
-  { "arrowleft",                         600, NULL },
   { "dieresis",                          600, NULL },
   { "quotedblbase",                      600, NULL },
   { "iacute",                            600, NULL },
-  { "up",                                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 },
-  { "indent",                            600, NULL },
-  { "prescription",                      600, NULL },
-  { "dectab",                            600, NULL },
+  { "Amacron",                           600, NULL },
+  { "Sacute",                            600, NULL },
   { "ordmasculine",                      600, NULL },
   { "dotlessi",                          600, NULL },
   { "sterling",                          600, NULL },
-  { "IJ",                                600, NULL },
+  { "notequal",                          600, NULL },
+  { "Imacron",                           600, NULL },
+  { "rcommaaccent",                      600, NULL },
+  { "Zdotaccent",                        600, NULL },
   { "acircumflex",                       600, NULL },
-  { "overscore",                         600, NULL },
+  { "cacute",                            600, NULL },
+  { "Ecaron",                            600, NULL },
   { "braceright",                        600, NULL },
   { "icircumflex",                       600, NULL },
-  { "graybox",                           600, NULL },
   { "quotedblright",                     600, NULL },
-  { "center",                            600, NULL },
-  { "stop",                              600, NULL },
+  { "amacron",                           600, NULL },
+  { "sacute",                            600, NULL },
+  { "imacron",                           600, NULL },
   { "cent",                              600, NULL },
   { "currency",                          600, NULL },
   { "logicalnot",                        600, NULL },
-  { "Idot",                              600, NULL },
-  { "merge",                             600, NULL },
+  { "zdotaccent",                        600, NULL },
   { "Atilde",                            600, NULL },
   { "breve",                             600, NULL },
   { "bar",                               600, NULL },
   { "fraction",                          600, NULL },
   { "less",                              600, NULL },
-  { "down",                              600, NULL },
+  { "ecaron",                            600, NULL },
   { "guilsinglleft",                     600, NULL },
   { "exclam",                            600, NULL },
   { "period",                            600, NULL },
-  { "arrowright",                        600, NULL },
-  { "format",                            600, NULL },
+  { "Rcaron",                            600, NULL },
+  { "Kcommaaccent",                      600, NULL },
   { "greater",                           600, NULL },
   { "atilde",                            600, NULL },
-  { "ij",                                600, NULL },
   { "brokenbar",                         600, NULL },
-  { "arrowboth",                         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 },
-  { "arrowup",                           600, NULL },
   { "circumflex",                        600, NULL },
   { "dotaccent",                         600, NULL },
-  { "LL",                                600, NULL },
+  { "edotaccent",                        600, NULL },
   { "asciitilde",                        600, NULL },
   { "colon",                             600, NULL },
   { "onehalf",                           600, NULL },
   { "dollar",                            600, NULL },
+  { "Lcaron",                            600, NULL },
   { "ntilde",                            600, NULL },
-  { "left",                              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 },
@@ -309,14 +374,18 @@ static BuiltinFontWidth courierBoldWidthsTab[] = {
   { "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 },
@@ -325,9 +394,7 @@ static BuiltinFontWidth courierBoldWidthsTab[] = {
   { "U",                                 600, NULL },
   { "agrave",                            600, NULL },
   { "V",                                 600, NULL },
-  { "tab",                               600, NULL },
   { "W",                                 600, NULL },
-  { "ll",                                600, NULL },
   { "X",                                 600, NULL },
   { "question",                          600, NULL },
   { "equal",                             600, NULL },
@@ -335,6 +402,7 @@ static BuiltinFontWidth courierBoldWidthsTab[] = {
   { "Z",                                 600, NULL },
   { "four",                              600, NULL },
   { "a",                                 600, NULL },
+  { "Gcommaaccent",                      600, NULL },
   { "b",                                 600, NULL },
   { "c",                                 600, NULL },
   { "d",                                 600, NULL },
@@ -351,44 +419,57 @@ static BuiltinFontWidth courierBoldWidthsTab[] = {
   { "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 },
-  { "largebullet",                       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 },
-  { "lira",                              600, NULL },
   { "quotedblleft",                      600, NULL },
   { "guilsinglright",                    600, NULL },
   { "hyphen",                            600, NULL },
@@ -397,9 +478,11 @@ static BuiltinFontWidth courierBoldWidthsTab[] = {
   { "exclamdown",                        600, NULL },
   { "endash",                            600, NULL },
   { "oe",                                600, NULL },
+  { "Abreve",                            600, NULL },
+  { "Umacron",                           600, NULL },
   { "ecircumflex",                       600, NULL },
-  { "copyright",                         600, NULL },
   { "Adieresis",                         600, NULL },
+  { "copyright",                         600, NULL },
   { "Egrave",                            600, NULL },
   { "slash",                             600, NULL },
   { "Edieresis",                         600, NULL },
@@ -407,14 +490,17 @@ static BuiltinFontWidth courierBoldWidthsTab[] = {
   { "Idieresis",                         600, NULL },
   { "parenleft",                         600, NULL },
   { "one",                               600, NULL },
-  { "ucircumflex",                       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 },
@@ -430,139 +516,173 @@ static BuiltinFontWidth courierBoldWidthsTab[] = {
   { "five",                              600, NULL },
   { "udieresis",                         600, NULL },
   { "Zcaron",                            600, NULL },
+  { "Scommaaccent",                      600, NULL },
   { "threequarters",                     600, NULL },
   { "guillemotright",                    600, NULL },
-  { "ydieresis",                         600, NULL },
   { "Ccedilla",                          600, NULL },
+  { "ydieresis",                         600, NULL },
   { "tilde",                             600, NULL },
   { "at",                                600, NULL },
   { "eacute",                            600, NULL },
-  { "Gcaron",                            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 },
-  { "gcaron",                            600, NULL },
-  { "scedilla",                          600, NULL },
+  { "Dcaron",                            600, NULL },
+  { "dcroat",                            600, NULL },
   { "Ocircumflex",                       600, NULL },
   { "Oacute",                            600, NULL },
-  { "arrowdown",                         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 },
-  { "square",                            600, NULL },
   { "aring",                             600, NULL },
   { "grave",                             600, NULL },
+  { "uogonek",                           600, NULL },
   { "bracketright",                      600, NULL },
   { "Iacute",                            600, NULL },
   { "ampersand",                         600, NULL },
   { "igrave",                            600, NULL },
-  { "return",                            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 },
-  { "notegraphic",                       600, NULL },
   { "section",                           600, NULL },
-  { "arrowleft",                         600, NULL },
   { "dieresis",                          600, NULL },
   { "iacute",                            600, NULL },
   { "quotedblbase",                      600, NULL },
-  { "up",                                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 },
-  { "prescription",                      600, NULL },
-  { "indent",                            600, NULL },
-  { "dectab",                            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 },
-  { "IJ",                                600, NULL },
-  { "overscore",                         600, NULL },
+  { "cacute",                            600, NULL },
+  { "Ecaron",                            600, NULL },
   { "icircumflex",                       600, NULL },
   { "braceright",                        600, NULL },
-  { "graybox",                           600, NULL },
   { "quotedblright",                     600, NULL },
-  { "center",                            600, NULL },
-  { "stop",                              600, NULL },
+  { "amacron",                           600, NULL },
+  { "sacute",                            600, NULL },
+  { "imacron",                           600, NULL },
   { "cent",                              600, NULL },
   { "currency",                          600, NULL },
   { "logicalnot",                        600, NULL },
-  { "merge",                             600, NULL },
-  { "Idot",                              600, NULL },
+  { "zdotaccent",                        600, NULL },
   { "Atilde",                            600, NULL },
   { "breve",                             600, NULL },
   { "bar",                               600, NULL },
   { "fraction",                          600, NULL },
   { "less",                              600, NULL },
-  { "down",                              600, NULL },
+  { "ecaron",                            600, NULL },
   { "guilsinglleft",                     600, NULL },
   { "exclam",                            600, NULL },
   { "period",                            600, NULL },
-  { "format",                            600, NULL },
-  { "arrowright",                        600, NULL },
+  { "Rcaron",                            600, NULL },
+  { "Kcommaaccent",                      600, NULL },
   { "greater",                           600, NULL },
-  { "ij",                                600, NULL },
   { "atilde",                            600, NULL },
   { "brokenbar",                         600, NULL },
-  { "arrowboth",                         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 },
-  { "arrowup",                           600, NULL },
   { "circumflex",                        600, NULL },
   { "dotaccent",                         600, NULL },
-  { "LL",                                600, NULL },
+  { "edotaccent",                        600, NULL },
   { "asciitilde",                        600, NULL },
   { "colon",                             600, NULL },
   { "onehalf",                           600, NULL },
   { "dollar",                            600, NULL },
+  { "Lcaron",                            600, NULL },
   { "ntilde",                            600, NULL },
-  { "left",                              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 },
@@ -572,14 +692,18 @@ static BuiltinFontWidth courierBoldObliqueWidthsTab[] = {
   { "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 },
@@ -588,9 +712,7 @@ static BuiltinFontWidth courierBoldObliqueWidthsTab[] = {
   { "U",                                 600, NULL },
   { "agrave",                            600, NULL },
   { "V",                                 600, NULL },
-  { "tab",                               600, NULL },
   { "W",                                 600, NULL },
-  { "ll",                                600, NULL },
   { "X",                                 600, NULL },
   { "question",                          600, NULL },
   { "equal",                             600, NULL },
@@ -598,6 +720,7 @@ static BuiltinFontWidth courierBoldObliqueWidthsTab[] = {
   { "Z",                                 600, NULL },
   { "four",                              600, NULL },
   { "a",                                 600, NULL },
+  { "Gcommaaccent",                      600, NULL },
   { "b",                                 600, NULL },
   { "c",                                 600, NULL },
   { "d",                                 600, NULL },
@@ -614,44 +737,57 @@ static BuiltinFontWidth courierBoldObliqueWidthsTab[] = {
   { "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 },
-  { "largebullet",                       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 },
-  { "lira",                              600, NULL },
   { "quotedblleft",                      600, NULL },
   { "guilsinglright",                    600, NULL },
   { "hyphen",                            600, NULL },
@@ -660,9 +796,11 @@ static BuiltinFontWidth courierBoldObliqueWidthsTab[] = {
   { "exclamdown",                        600, NULL },
   { "endash",                            600, NULL },
   { "oe",                                600, NULL },
+  { "Abreve",                            600, NULL },
+  { "Umacron",                           600, NULL },
   { "ecircumflex",                       600, NULL },
-  { "copyright",                         600, NULL },
   { "Adieresis",                         600, NULL },
+  { "copyright",                         600, NULL },
   { "Egrave",                            600, NULL },
   { "slash",                             600, NULL },
   { "Edieresis",                         600, NULL },
@@ -670,14 +808,17 @@ static BuiltinFontWidth courierBoldObliqueWidthsTab[] = {
   { "Idieresis",                         600, NULL },
   { "parenleft",                         600, NULL },
   { "one",                               600, NULL },
-  { "ucircumflex",                       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 },
@@ -693,139 +834,173 @@ static BuiltinFontWidth courierBoldObliqueWidthsTab[] = {
   { "five",                              600, NULL },
   { "udieresis",                         600, NULL },
   { "Zcaron",                            600, NULL },
+  { "Scommaaccent",                      600, NULL },
   { "threequarters",                     600, NULL },
   { "guillemotright",                    600, NULL },
-  { "ydieresis",                         600, NULL },
   { "Ccedilla",                          600, NULL },
+  { "ydieresis",                         600, NULL },
   { "tilde",                             600, NULL },
   { "at",                                600, NULL },
   { "eacute",                            600, NULL },
-  { "Gcaron",                            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 },
-  { "gcaron",                            600, NULL },
-  { "scedilla",                          600, NULL },
+  { "Dcaron",                            600, NULL },
+  { "dcroat",                            600, NULL },
   { "Ocircumflex",                       600, NULL },
   { "Oacute",                            600, NULL },
-  { "arrowdown",                         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 },
-  { "square",                            600, NULL },
   { "aring",                             600, NULL },
   { "grave",                             600, NULL },
+  { "uogonek",                           600, NULL },
   { "bracketright",                      600, NULL },
   { "Iacute",                            600, NULL },
   { "ampersand",                         600, NULL },
   { "igrave",                            600, NULL },
-  { "return",                            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 },
-  { "notegraphic",                       600, NULL },
   { "section",                           600, NULL },
-  { "arrowleft",                         600, NULL },
   { "dieresis",                          600, NULL },
   { "iacute",                            600, NULL },
   { "quotedblbase",                      600, NULL },
-  { "up",                                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 },
-  { "prescription",                      600, NULL },
-  { "indent",                            600, NULL },
-  { "dectab",                            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 },
-  { "IJ",                                600, NULL },
-  { "overscore",                         600, NULL },
+  { "cacute",                            600, NULL },
+  { "Ecaron",                            600, NULL },
   { "icircumflex",                       600, NULL },
   { "braceright",                        600, NULL },
-  { "graybox",                           600, NULL },
   { "quotedblright",                     600, NULL },
-  { "center",                            600, NULL },
-  { "stop",                              600, NULL },
+  { "amacron",                           600, NULL },
+  { "sacute",                            600, NULL },
+  { "imacron",                           600, NULL },
   { "cent",                              600, NULL },
   { "currency",                          600, NULL },
   { "logicalnot",                        600, NULL },
-  { "merge",                             600, NULL },
-  { "Idot",                              600, NULL },
+  { "zdotaccent",                        600, NULL },
   { "Atilde",                            600, NULL },
   { "breve",                             600, NULL },
   { "bar",                               600, NULL },
   { "fraction",                          600, NULL },
   { "less",                              600, NULL },
-  { "down",                              600, NULL },
+  { "ecaron",                            600, NULL },
   { "guilsinglleft",                     600, NULL },
   { "exclam",                            600, NULL },
   { "period",                            600, NULL },
-  { "format",                            600, NULL },
-  { "arrowright",                        600, NULL },
+  { "Rcaron",                            600, NULL },
+  { "Kcommaaccent",                      600, NULL },
   { "greater",                           600, NULL },
-  { "ij",                                600, NULL },
   { "atilde",                            600, NULL },
   { "brokenbar",                         600, NULL },
-  { "arrowboth",                         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 },
-  { "arrowup",                           600, NULL },
   { "circumflex",                        600, NULL },
   { "dotaccent",                         600, NULL },
-  { "LL",                                600, NULL },
+  { "edotaccent",                        600, NULL },
   { "asciitilde",                        600, NULL },
   { "colon",                             600, NULL },
   { "onehalf",                           600, NULL },
   { "dollar",                            600, NULL },
+  { "Lcaron",                            600, NULL },
   { "ntilde",                            600, NULL },
-  { "left",                              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 },
@@ -835,14 +1010,18 @@ static BuiltinFontWidth courierObliqueWidthsTab[] = {
   { "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 },
@@ -851,9 +1030,7 @@ static BuiltinFontWidth courierObliqueWidthsTab[] = {
   { "U",                                 600, NULL },
   { "agrave",                            600, NULL },
   { "V",                                 600, NULL },
-  { "tab",                               600, NULL },
   { "W",                                 600, NULL },
-  { "ll",                                600, NULL },
   { "X",                                 600, NULL },
   { "question",                          600, NULL },
   { "equal",                             600, NULL },
@@ -861,6 +1038,7 @@ static BuiltinFontWidth courierObliqueWidthsTab[] = {
   { "Z",                                 600, NULL },
   { "four",                              600, NULL },
   { "a",                                 600, NULL },
+  { "Gcommaaccent",                      600, NULL },
   { "b",                                 600, NULL },
   { "c",                                 600, NULL },
   { "d",                                 600, NULL },
@@ -877,44 +1055,57 @@ static BuiltinFontWidth courierObliqueWidthsTab[] = {
   { "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 },
-  { "largebullet",                       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 },
-  { "lira",                              600, NULL },
   { "quotedblleft",                      600, NULL },
   { "guilsinglright",                    600, NULL },
   { "hyphen",                            600, NULL },
@@ -923,9 +1114,11 @@ static BuiltinFontWidth courierObliqueWidthsTab[] = {
   { "exclamdown",                        600, NULL },
   { "endash",                            600, NULL },
   { "oe",                                600, NULL },
+  { "Abreve",                            600, NULL },
+  { "Umacron",                           600, NULL },
   { "ecircumflex",                       600, NULL },
-  { "copyright",                         600, NULL },
   { "Adieresis",                         600, NULL },
+  { "copyright",                         600, NULL },
   { "Egrave",                            600, NULL },
   { "slash",                             600, NULL },
   { "Edieresis",                         600, NULL },
@@ -933,14 +1126,17 @@ static BuiltinFontWidth courierObliqueWidthsTab[] = {
   { "Idieresis",                         600, NULL },
   { "parenleft",                         600, NULL },
   { "one",                               600, NULL },
-  { "ucircumflex",                       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 },
@@ -956,136 +1152,173 @@ static BuiltinFontWidth courierObliqueWidthsTab[] = {
   { "five",                              600, NULL },
   { "udieresis",                         600, NULL },
   { "Zcaron",                            600, NULL },
+  { "Scommaaccent",                      600, NULL },
   { "threequarters",                     600, NULL },
   { "guillemotright",                    600, NULL },
-  { "ydieresis",                         600, NULL },
   { "Ccedilla",                          600, NULL },
+  { "ydieresis",                         600, NULL },
   { "tilde",                             600, NULL },
   { "at",                                600, NULL },
   { "eacute",                            600, NULL },
-  { "Gcaron",                            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 },
-  { "gcaron",                            600, NULL },
-  { "scedilla",                          600, NULL },
+  { "Dcaron",                            600, NULL },
+  { "dcroat",                            600, NULL },
   { "Ocircumflex",                       600, NULL },
   { "Oacute",                            600, NULL },
-  { "arrowdown",                         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 },
-  { "square",                            600, NULL },
   { "aring",                             600, NULL },
   { "grave",                             600, NULL },
+  { "uogonek",                           600, NULL },
   { "bracketright",                      600, NULL },
   { "Iacute",                            600, NULL },
   { "ampersand",                         600, NULL },
   { "igrave",                            600, NULL },
-  { "return",                            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 },
-  { "notegraphic",                       600, NULL },
   { "section",                           600, NULL },
-  { "arrowleft",                         600, NULL },
   { "dieresis",                          600, NULL },
   { "iacute",                            600, NULL },
   { "quotedblbase",                      600, NULL },
-  { "up",                                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 },
-  { "prescription",                      600, NULL },
-  { "indent",                            600, NULL },
-  { "dectab",                            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 },
-  { "IJ",                                600, NULL },
-  { "overscore",                         600, NULL },
+  { "cacute",                            600, NULL },
+  { "Ecaron",                            600, NULL },
   { "icircumflex",                       600, NULL },
   { "braceright",                        600, NULL },
-  { "graybox",                           600, NULL },
   { "quotedblright",                     600, NULL },
-  { "center",                            600, NULL },
-  { "stop",                              600, NULL },
+  { "amacron",                           600, NULL },
+  { "sacute",                            600, NULL },
+  { "imacron",                           600, NULL },
   { "cent",                              600, NULL },
   { "currency",                          600, NULL },
   { "logicalnot",                        600, NULL },
-  { "merge",                             600, NULL },
-  { "Idot",                              600, NULL },
+  { "zdotaccent",                        600, NULL },
   { "Atilde",                            600, NULL },
   { "breve",                             600, NULL },
   { "bar",                               600, NULL },
   { "fraction",                          600, NULL },
   { "less",                              600, NULL },
-  { "down",                              600, NULL },
+  { "ecaron",                            600, NULL },
   { "guilsinglleft",                     600, NULL },
   { "exclam",                            600, NULL },
   { "period",                            600, NULL },
-  { "format",                            600, NULL },
-  { "arrowright",                        600, NULL },
+  { "Rcaron",                            600, NULL },
+  { "Kcommaaccent",                      600, NULL },
   { "greater",                           600, NULL },
-  { "ij",                                600, NULL },
   { "atilde",                            600, NULL },
   { "brokenbar",                         600, NULL },
-  { "arrowboth",                         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 },
@@ -1095,14 +1328,18 @@ static BuiltinFontWidth helveticaWidthsTab[] = {
   { "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 },
@@ -1119,6 +1356,7 @@ static BuiltinFontWidth helveticaWidthsTab[] = {
   { "Z",                                 611, NULL },
   { "four",                              556, NULL },
   { "a",                                 556, NULL },
+  { "Gcommaaccent",                      778, NULL },
   { "b",                                 556, NULL },
   { "c",                                 500, NULL },
   { "d",                                 556, NULL },
@@ -1135,11 +1373,13 @@ static BuiltinFontWidth helveticaWidthsTab[] = {
   { "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 },
@@ -1148,24 +1388,37 @@ static BuiltinFontWidth helveticaWidthsTab[] = {
   { "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 },
@@ -1179,9 +1432,11 @@ static BuiltinFontWidth helveticaWidthsTab[] = {
   { "exclamdown",                        333, NULL },
   { "endash",                            556, NULL },
   { "oe",                                944, NULL },
+  { "Abreve",                            667, NULL },
+  { "Umacron",                           722, NULL },
   { "ecircumflex",                       556, NULL },
-  { "copyright",                         737, NULL },
   { "Adieresis",                         667, NULL },
+  { "copyright",                         737, NULL },
   { "Egrave",                            667, NULL },
   { "slash",                             278, NULL },
   { "Edieresis",                         667, NULL },
@@ -1189,14 +1444,17 @@ static BuiltinFontWidth helveticaWidthsTab[] = {
   { "Idieresis",                         278, NULL },
   { "parenleft",                         333, NULL },
   { "one",                               556, NULL },
-  { "ucircumflex",                       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 },
@@ -1212,111 +1470,173 @@ static BuiltinFontWidth helveticaWidthsTab[] = {
   { "five",                              556, NULL },
   { "udieresis",                         556, NULL },
   { "Zcaron",                            611, NULL },
+  { "Scommaaccent",                      667, NULL },
   { "threequarters",                     834, NULL },
   { "guillemotright",                    556, NULL },
-  { "ydieresis",                         500, 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 },
@@ -1326,14 +1646,18 @@ static BuiltinFontWidth helveticaBoldWidthsTab[] = {
   { "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 },
@@ -1350,6 +1674,7 @@ static BuiltinFontWidth helveticaBoldWidthsTab[] = {
   { "Z",                                 611, NULL },
   { "four",                              556, NULL },
   { "a",                                 556, NULL },
+  { "Gcommaaccent",                      778, NULL },
   { "b",                                 611, NULL },
   { "c",                                 556, NULL },
   { "d",                                 611, NULL },
@@ -1366,11 +1691,13 @@ static BuiltinFontWidth helveticaBoldWidthsTab[] = {
   { "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 },
@@ -1379,24 +1706,37 @@ static BuiltinFontWidth helveticaBoldWidthsTab[] = {
   { "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 },
@@ -1410,9 +1750,11 @@ static BuiltinFontWidth helveticaBoldWidthsTab[] = {
   { "exclamdown",                        333, NULL },
   { "endash",                            556, NULL },
   { "oe",                                944, NULL },
+  { "Abreve",                            722, NULL },
+  { "Umacron",                           722, NULL },
   { "ecircumflex",                       556, NULL },
-  { "copyright",                         737, NULL },
   { "Adieresis",                         722, NULL },
+  { "copyright",                         737, NULL },
   { "Egrave",                            667, NULL },
   { "slash",                             278, NULL },
   { "Edieresis",                         667, NULL },
@@ -1420,14 +1762,17 @@ static BuiltinFontWidth helveticaBoldWidthsTab[] = {
   { "Idieresis",                         278, NULL },
   { "parenleft",                         333, NULL },
   { "one",                               556, NULL },
-  { "ucircumflex",                       611, 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 },
@@ -1443,111 +1788,174 @@ static BuiltinFontWidth helveticaBoldWidthsTab[] = {
   { "five",                              556, NULL },
   { "udieresis",                         611, NULL },
   { "Zcaron",                            611, NULL },
+  { "Scommaaccent",                      667, NULL },
   { "threequarters",                     834, NULL },
   { "guillemotright",                    556, NULL },
-  { "ydieresis",                         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 },
@@ -1557,14 +1965,18 @@ static BuiltinFontWidth helveticaBoldObliqueWidthsTab[] = {
   { "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 },
@@ -1581,6 +1993,7 @@ static BuiltinFontWidth helveticaBoldObliqueWidthsTab[] = {
   { "Z",                                 611, NULL },
   { "four",                              556, NULL },
   { "a",                                 556, NULL },
+  { "Gcommaaccent",                      778, NULL },
   { "b",                                 611, NULL },
   { "c",                                 556, NULL },
   { "d",                                 611, NULL },
@@ -1597,11 +2010,13 @@ static BuiltinFontWidth helveticaBoldObliqueWidthsTab[] = {
   { "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 },
@@ -1610,24 +2025,37 @@ static BuiltinFontWidth helveticaBoldObliqueWidthsTab[] = {
   { "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 },
@@ -1641,9 +2069,11 @@ static BuiltinFontWidth helveticaBoldObliqueWidthsTab[] = {
   { "exclamdown",                        333, NULL },
   { "endash",                            556, NULL },
   { "oe",                                944, NULL },
+  { "Abreve",                            722, NULL },
+  { "Umacron",                           722, NULL },
   { "ecircumflex",                       556, NULL },
-  { "copyright",                         737, NULL },
   { "Adieresis",                         722, NULL },
+  { "copyright",                         737, NULL },
   { "Egrave",                            667, NULL },
   { "slash",                             278, NULL },
   { "Edieresis",                         667, NULL },
@@ -1651,14 +2081,17 @@ static BuiltinFontWidth helveticaBoldObliqueWidthsTab[] = {
   { "Idieresis",                         278, NULL },
   { "parenleft",                         333, NULL },
   { "one",                               556, NULL },
-  { "ucircumflex",                       611, 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 },
@@ -1674,111 +2107,173 @@ static BuiltinFontWidth helveticaBoldObliqueWidthsTab[] = {
   { "five",                              556, NULL },
   { "udieresis",                         611, NULL },
   { "Zcaron",                            611, NULL },
+  { "Scommaaccent",                      667, NULL },
   { "threequarters",                     834, NULL },
   { "guillemotright",                    556, NULL },
-  { "ydieresis",                         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 },
@@ -1788,14 +2283,18 @@ static BuiltinFontWidth helveticaObliqueWidthsTab[] = {
   { "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 },
@@ -1812,6 +2311,7 @@ static BuiltinFontWidth helveticaObliqueWidthsTab[] = {
   { "Z",                                 611, NULL },
   { "four",                              556, NULL },
   { "a",                                 556, NULL },
+  { "Gcommaaccent",                      778, NULL },
   { "b",                                 556, NULL },
   { "c",                                 500, NULL },
   { "d",                                 556, NULL },
@@ -1828,11 +2328,13 @@ static BuiltinFontWidth helveticaObliqueWidthsTab[] = {
   { "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 },
@@ -1841,24 +2343,37 @@ static BuiltinFontWidth helveticaObliqueWidthsTab[] = {
   { "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 },
@@ -1872,9 +2387,11 @@ static BuiltinFontWidth helveticaObliqueWidthsTab[] = {
   { "exclamdown",                        333, NULL },
   { "endash",                            556, NULL },
   { "oe",                                944, NULL },
+  { "Abreve",                            667, NULL },
+  { "Umacron",                           722, NULL },
   { "ecircumflex",                       556, NULL },
-  { "copyright",                         737, NULL },
   { "Adieresis",                         667, NULL },
+  { "copyright",                         737, NULL },
   { "Egrave",                            667, NULL },
   { "slash",                             278, NULL },
   { "Edieresis",                         667, NULL },
@@ -1882,14 +2399,17 @@ static BuiltinFontWidth helveticaObliqueWidthsTab[] = {
   { "Idieresis",                         278, NULL },
   { "parenleft",                         333, NULL },
   { "one",                               556, NULL },
-  { "ucircumflex",                       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 },
@@ -1905,85 +2425,134 @@ static BuiltinFontWidth helveticaObliqueWidthsTab[] = {
   { "five",                              556, NULL },
   { "udieresis",                         556, NULL },
   { "Zcaron",                            611, NULL },
+  { "Scommaaccent",                      667, NULL },
   { "threequarters",                     834, NULL },
   { "guillemotright",                    556, NULL },
-  { "ydieresis",                         500, 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 }
 };
 
@@ -2098,6 +2667,7 @@ static BuiltinFontWidth symbolWidthsTab[] = {
   { "parenrighttp",                      384, NULL },
   { "eta",                               603, NULL },
   { "underscore",                        500, NULL },
+  { "Euro",                              750, NULL },
   { "multiply",                          549, NULL },
   { "zero",                              500, NULL },
   { "partialdiff",                       494, NULL },
@@ -2181,27 +2751,40 @@ static BuiltinFontWidth symbolWidthsTab[] = {
 
 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 },
@@ -2211,14 +2794,18 @@ static BuiltinFontWidth timesBoldWidthsTab[] = {
   { "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 },
@@ -2235,6 +2822,7 @@ static BuiltinFontWidth timesBoldWidthsTab[] = {
   { "Z",                                 667, NULL },
   { "four",                              500, NULL },
   { "a",                                 500, NULL },
+  { "Gcommaaccent",                      778, NULL },
   { "b",                                 556, NULL },
   { "c",                                 444, NULL },
   { "d",                                 556, NULL },
@@ -2251,11 +2839,13 @@ static BuiltinFontWidth timesBoldWidthsTab[] = {
   { "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 },
@@ -2264,24 +2854,37 @@ static BuiltinFontWidth timesBoldWidthsTab[] = {
   { "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 },
@@ -2295,9 +2898,11 @@ static BuiltinFontWidth timesBoldWidthsTab[] = {
   { "exclamdown",                        333, NULL },
   { "endash",                            500, NULL },
   { "oe",                                722, NULL },
+  { "Abreve",                            722, NULL },
+  { "Umacron",                           722, NULL },
   { "ecircumflex",                       444, NULL },
-  { "copyright",                         747, NULL },
   { "Adieresis",                         722, NULL },
+  { "copyright",                         747, NULL },
   { "Egrave",                            667, NULL },
   { "slash",                             278, NULL },
   { "Edieresis",                         667, NULL },
@@ -2305,14 +2910,17 @@ static BuiltinFontWidth timesBoldWidthsTab[] = {
   { "Idieresis",                         389, NULL },
   { "parenleft",                         333, NULL },
   { "one",                               500, NULL },
-  { "ucircumflex",                       556, 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 },
@@ -2328,111 +2936,173 @@ static BuiltinFontWidth timesBoldWidthsTab[] = {
   { "five",                              500, NULL },
   { "udieresis",                         556, NULL },
   { "Zcaron",                            667, NULL },
+  { "Scommaaccent",                      556, NULL },
   { "threequarters",                     750, NULL },
   { "guillemotright",                    500, NULL },
-  { "ydieresis",                         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 },
@@ -2442,14 +3112,18 @@ static BuiltinFontWidth timesBoldItalicWidthsTab[] = {
   { "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 },
@@ -2466,6 +3140,7 @@ static BuiltinFontWidth timesBoldItalicWidthsTab[] = {
   { "Z",                                 611, NULL },
   { "four",                              500, NULL },
   { "a",                                 500, NULL },
+  { "Gcommaaccent",                      722, NULL },
   { "b",                                 500, NULL },
   { "c",                                 444, NULL },
   { "d",                                 500, NULL },
@@ -2482,11 +3157,13 @@ static BuiltinFontWidth timesBoldItalicWidthsTab[] = {
   { "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 },
@@ -2495,24 +3172,37 @@ static BuiltinFontWidth timesBoldItalicWidthsTab[] = {
   { "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 },
@@ -2526,9 +3216,11 @@ static BuiltinFontWidth timesBoldItalicWidthsTab[] = {
   { "exclamdown",                        389, NULL },
   { "endash",                            500, NULL },
   { "oe",                                722, NULL },
+  { "Abreve",                            667, NULL },
+  { "Umacron",                           722, NULL },
   { "ecircumflex",                       444, NULL },
-  { "copyright",                         747, NULL },
   { "Adieresis",                         667, NULL },
+  { "copyright",                         747, NULL },
   { "Egrave",                            667, NULL },
   { "slash",                             278, NULL },
   { "Edieresis",                         667, NULL },
@@ -2536,14 +3228,17 @@ static BuiltinFontWidth timesBoldItalicWidthsTab[] = {
   { "Idieresis",                         389, NULL },
   { "parenleft",                         333, NULL },
   { "one",                               500, NULL },
-  { "ucircumflex",                       556, 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 },
@@ -2559,111 +3254,173 @@ static BuiltinFontWidth timesBoldItalicWidthsTab[] = {
   { "five",                              500, NULL },
   { "udieresis",                         556, NULL },
   { "Zcaron",                            611, NULL },
+  { "Scommaaccent",                      556, NULL },
   { "threequarters",                     750, NULL },
   { "guillemotright",                    500, NULL },
-  { "ydieresis",                         444, 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 },
@@ -2673,14 +3430,18 @@ static BuiltinFontWidth timesItalicWidthsTab[] = {
   { "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 },
@@ -2697,6 +3458,7 @@ static BuiltinFontWidth timesItalicWidthsTab[] = {
   { "Z",                                 556, NULL },
   { "four",                              500, NULL },
   { "a",                                 500, NULL },
+  { "Gcommaaccent",                      722, NULL },
   { "b",                                 500, NULL },
   { "c",                                 444, NULL },
   { "d",                                 500, NULL },
@@ -2713,11 +3475,13 @@ static BuiltinFontWidth timesItalicWidthsTab[] = {
   { "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 },
@@ -2726,24 +3490,37 @@ static BuiltinFontWidth timesItalicWidthsTab[] = {
   { "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 },
@@ -2757,9 +3534,11 @@ static BuiltinFontWidth timesItalicWidthsTab[] = {
   { "exclamdown",                        389, NULL },
   { "endash",                            500, NULL },
   { "oe",                                667, NULL },
+  { "Abreve",                            611, NULL },
+  { "Umacron",                           722, NULL },
   { "ecircumflex",                       444, NULL },
-  { "copyright",                         760, NULL },
   { "Adieresis",                         611, NULL },
+  { "copyright",                         760, NULL },
   { "Egrave",                            611, NULL },
   { "slash",                             278, NULL },
   { "Edieresis",                         611, NULL },
@@ -2767,14 +3546,17 @@ static BuiltinFontWidth timesItalicWidthsTab[] = {
   { "Idieresis",                         333, NULL },
   { "parenleft",                         333, NULL },
   { "one",                               500, NULL },
-  { "ucircumflex",                       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 },
@@ -2790,111 +3572,173 @@ static BuiltinFontWidth timesItalicWidthsTab[] = {
   { "five",                              500, NULL },
   { "udieresis",                         500, NULL },
   { "Zcaron",                            556, NULL },
+  { "Scommaaccent",                      500, NULL },
   { "threequarters",                     750, NULL },
   { "guillemotright",                    500, NULL },
-  { "ydieresis",                         444, 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 },
@@ -2904,14 +3748,18 @@ static BuiltinFontWidth timesRomanWidthsTab[] = {
   { "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 },
@@ -2928,6 +3776,7 @@ static BuiltinFontWidth timesRomanWidthsTab[] = {
   { "Z",                                 611, NULL },
   { "four",                              500, NULL },
   { "a",                                 444, NULL },
+  { "Gcommaaccent",                      722, NULL },
   { "b",                                 500, NULL },
   { "c",                                 444, NULL },
   { "d",                                 500, NULL },
@@ -2944,11 +3793,13 @@ static BuiltinFontWidth timesRomanWidthsTab[] = {
   { "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 },
@@ -2957,24 +3808,37 @@ static BuiltinFontWidth timesRomanWidthsTab[] = {
   { "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 },
@@ -2988,9 +3852,11 @@ static BuiltinFontWidth timesRomanWidthsTab[] = {
   { "exclamdown",                        333, NULL },
   { "endash",                            500, NULL },
   { "oe",                                722, NULL },
+  { "Abreve",                            722, NULL },
+  { "Umacron",                           722, NULL },
   { "ecircumflex",                       444, NULL },
-  { "copyright",                         760, NULL },
   { "Adieresis",                         722, NULL },
+  { "copyright",                         760, NULL },
   { "Egrave",                            611, NULL },
   { "slash",                             278, NULL },
   { "Edieresis",                         611, NULL },
@@ -2998,14 +3864,17 @@ static BuiltinFontWidth timesRomanWidthsTab[] = {
   { "Idieresis",                         333, NULL },
   { "parenleft",                         333, NULL },
   { "one",                               500, NULL },
-  { "ucircumflex",                       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 },
@@ -3021,85 +3890,134 @@ static BuiltinFontWidth timesRomanWidthsTab[] = {
   { "five",                              500, NULL },
   { "udieresis",                         500, NULL },
   { "Zcaron",                            611, NULL },
+  { "Scommaaccent",                      556, NULL },
   { "threequarters",                     750, NULL },
   { "guillemotright",                    500, NULL },
-  { "ydieresis",                         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 }
 };
 
@@ -3278,10 +4196,10 @@ static BuiltinFontWidth zapfDingbatsWidthsTab[] = {
   { "a203",                              762, NULL },
   { "a123",                              788, NULL },
   { "a204",                              759, NULL },
-  { "a205",                              509, NULL },
   { "a124",                              788, NULL },
-  { "a206",                              410, NULL },
+  { "a205",                              509, NULL },
   { "a125",                              788, NULL },
+  { "a206",                              410, NULL },
   { "a126",                              788, NULL },
   { "a127",                              788, NULL },
   { "a128",                              788, NULL },
@@ -3309,19 +4227,19 @@ static BuiltinFontWidth zapfDingbatsWidthsTab[] = {
 };
 
 BuiltinFont builtinFonts[] = {
-  { "Courier",               standardEncoding,            624, -207, { -40, -290,  640,  795}, NULL },
-  { "Courier-Bold",          standardEncoding,            674, -257, {-100, -350,  700,  855}, NULL },
-  { "Courier-BoldOblique",   standardEncoding,            674, -257, {-145, -350,  817,  855}, NULL },
-  { "Courier-Oblique",       standardEncoding,            624, -207, { -85, -290,  759,  795}, NULL },
-  { "Helvetica",             standardEncoding,            729, -219, {-174, -220, 1001,  944}, NULL },
-  { "Helvetica-Bold",        standardEncoding,            729, -219, {-173, -221, 1003,  936}, NULL },
-  { "Helvetica-BoldOblique", standardEncoding,            729, -219, {-177, -221, 1107,  936}, NULL },
-  { "Helvetica-Oblique",     standardEncoding,            729, -219, {-178, -220, 1108,  944}, NULL },
+  { "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,            670, -210, {-172, -256, 1008,  965}, NULL },
-  { "Times-BoldItalic",      standardEncoding,            682, -203, {-168, -232, 1014,  894}, NULL },
-  { "Times-Italic",          standardEncoding,            684, -206, {-176, -252,  990,  930}, NULL },
-  { "Times-Roman",           standardEncoding,            682, -217, {-170, -223, 1024,  896}, 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 }
 };
 
@@ -3341,19 +4259,19 @@ BuiltinFont *builtinFontSubst[] = {
 };
 
 void initBuiltinFontTables() {
-  builtinFonts[0].widths = new BuiltinFontWidths(courierWidthsTab, 260);
-  builtinFonts[1].widths = new BuiltinFontWidths(courierBoldWidthsTab, 260);
-  builtinFonts[2].widths = new BuiltinFontWidths(courierBoldObliqueWidthsTab, 260);
-  builtinFonts[3].widths = new BuiltinFontWidths(courierObliqueWidthsTab, 260);
-  builtinFonts[4].widths = new BuiltinFontWidths(helveticaWidthsTab, 228);
-  builtinFonts[5].widths = new BuiltinFontWidths(helveticaBoldWidthsTab, 228);
-  builtinFonts[6].widths = new BuiltinFontWidths(helveticaBoldObliqueWidthsTab, 228);
-  builtinFonts[7].widths = new BuiltinFontWidths(helveticaObliqueWidthsTab, 228);
-  builtinFonts[8].widths = new BuiltinFontWidths(symbolWidthsTab, 189);
-  builtinFonts[9].widths = new BuiltinFontWidths(timesBoldWidthsTab, 228);
-  builtinFonts[10].widths = new BuiltinFontWidths(timesBoldItalicWidthsTab, 228);
-  builtinFonts[11].widths = new BuiltinFontWidths(timesItalicWidthsTab, 228);
-  builtinFonts[12].widths = new BuiltinFontWidths(timesRomanWidthsTab, 228);
+  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);
 }
 
index 3a8892e..eb45549 100644 (file)
@@ -2,7 +2,7 @@
 //
 // BuiltinFontTables.h
 //
-// Copyright 2001-2002 Glyph & Cog, LLC
+// Copyright 2001-2003 Glyph & Cog, LLC
 //
 //========================================================================
 
index b49cffd..25f3af7 100644 (file)
@@ -2,15 +2,16 @@
 //
 // CMap.cc
 //
-// Copyright 2001-2002 Glyph & Cog, LLC
+// Copyright 2001-2003 Glyph & Cog, LLC
 //
 //========================================================================
 
-#ifdef __GNUC__
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
 #pragma implementation
 #endif
 
-#include <aconf.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -143,6 +144,9 @@ CMap::CMap(GString *collectionA, GString *cMapNameA) {
     vector[i].cid = 0;
   }
   refCnt = 1;
+#if MULTITHREADED
+  gInitMutex(&mutex);
+#endif
 }
 
 CMap::CMap(GString *collectionA, GString *cMapNameA, int wModeA) {
@@ -151,6 +155,9 @@ CMap::CMap(GString *collectionA, GString *cMapNameA, int wModeA) {
   wMode = wModeA;
   vector = NULL;
   refCnt = 1;
+#if MULTITHREADED
+  gInitMutex(&mutex);
+#endif
 }
 
 void CMap::useCMap(CMapCache *cache, char *useName) {
@@ -251,6 +258,9 @@ CMap::~CMap() {
   if (vector) {
     freeCMapVector(vector);
   }
+#if MULTITHREADED
+  gDestroyMutex(&mutex);
+#endif
 }
 
 void CMap::freeCMapVector(CMapVectorEntry *vec) {
@@ -265,11 +275,26 @@ void CMap::freeCMapVector(CMapVectorEntry *vec) {
 }
 
 void CMap::incRefCnt() {
+#if MULTITHREADED
+  gLockMutex(&mutex);
+#endif
   ++refCnt;
+#if MULTITHREADED
+  gUnlockMutex(&mutex);
+#endif
 }
 
 void CMap::decRefCnt() {
-  if (--refCnt == 0) {
+  GBool done;
+
+#if MULTITHREADED
+  gLockMutex(&mutex);
+#endif
+  done = --refCnt == 0;
+#if MULTITHREADED
+  gUnlockMutex(&mutex);
+#endif
+  if (done) {
     delete this;
   }
 }
index fe49acf..eff2a81 100644 (file)
@@ -2,20 +2,26 @@
 //
 // CMap.h
 //
-// Copyright 2001-2002 Glyph & Cog, LLC
+// Copyright 2001-2003 Glyph & Cog, LLC
 //
 //========================================================================
 
 #ifndef CMAP_H
 #define CMAP_H
 
-#ifdef __GNUC__
+#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;
@@ -67,6 +73,9 @@ private:
   CMapVectorEntry *vector;     // vector for first byte (NULL for
                                //   identity CMap)
   int refCnt;
+#ifdef MULTITHREADED
+  GMutex mutex;
+#endif
 };
 
 //------------------------------------------------------------------------
index 1212e2e..c645fd0 100644 (file)
@@ -2,15 +2,16 @@
 //
 // Catalog.cc
 //
-// Copyright 1996-2002 Glyph & Cog, LLC
+// Copyright 1996-2003 Glyph & Cog, LLC
 //
 //========================================================================
 
-#ifdef __GNUC__
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
 #pragma implementation
 #endif
 
-#include <aconf.h>
 #include <stddef.h>
 #include "gmem.h"
 #include "Object.h"
@@ -26,7 +27,7 @@
 // Catalog
 //------------------------------------------------------------------------
 
-Catalog::Catalog(XRef *xrefA, GBool printCommands) {
+Catalog::Catalog(XRef *xrefA) {
   Object catDict, pagesDict;
   Object obj, obj2;
   int numPages0;
@@ -55,12 +56,13 @@ Catalog::Catalog(XRef *xrefA, GBool printCommands) {
     goto err2;
   }
   pagesDict.dictLookup("Count", &obj);
-  if (!obj.isInt()) {
+  // 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 = obj.getInt();
+  pagesSize = numPages0 = (int)obj.getNum();
   obj.free();
   pages = (Page **)gmalloc(pagesSize * sizeof(Page *));
   pageRefs = (Ref *)gmalloc(pagesSize * sizeof(Ref));
@@ -69,7 +71,7 @@ Catalog::Catalog(XRef *xrefA, GBool printCommands) {
     pageRefs[i].num = -1;
     pageRefs[i].gen = -1;
   }
-  numPages = readPageTree(pagesDict.getDict(), NULL, 0, printCommands);
+  numPages = readPageTree(pagesDict.getDict(), NULL, 0);
   if (numPages != numPages0) {
     error(-1, "Page count in top-level pages object is incorrect");
   }
@@ -100,6 +102,9 @@ Catalog::Catalog(XRef *xrefA, GBool printCommands) {
   // get the structure tree root
   catDict.dictLookup("StructTreeRoot", &structTreeRoot);
 
+  // get the outline dictionary
+  catDict.dictLookup("Outlines", &outline);
+
   catDict.free();
   return;
 
@@ -133,6 +138,7 @@ Catalog::~Catalog() {
   }
   metadata.free();
   structTreeRoot.free();
+  outline.free();
 }
 
 GString *Catalog::readMetadata() {
@@ -159,8 +165,7 @@ GString *Catalog::readMetadata() {
   return s;
 }
 
-int Catalog::readPageTree(Dict *pagesDict, PageAttrs *attrs, int start,
-                         GBool printCommands) {
+int Catalog::readPageTree(Dict *pagesDict, PageAttrs *attrs, int start) {
   Object kids;
   Object kid;
   Object kidRef;
@@ -179,7 +184,7 @@ int Catalog::readPageTree(Dict *pagesDict, PageAttrs *attrs, int start,
     kids.arrayGet(i, &kid);
     if (kid.isDict("Page")) {
       attrs2 = new PageAttrs(attrs1, kid.getDict());
-      page = new Page(xref, start+1, kid.getDict(), attrs2, printCommands);
+      page = new Page(xref, start+1, kid.getDict(), attrs2);
       if (!page->isOk()) {
        ++start;
        goto err3;
@@ -205,7 +210,7 @@ int Catalog::readPageTree(Dict *pagesDict, PageAttrs *attrs, int start,
     // This should really be isDict("Pages"), but I've seen at least one
     // PDF file where the /Type entry is missing.
     } else if (kid.isDict()) {
-      if ((start = readPageTree(kid.getDict(), attrs1, start, printCommands))
+      if ((start = readPageTree(kid.getDict(), attrs1, start))
          < 0)
        goto err2;
     } else {
@@ -276,6 +281,10 @@ LinkDest *Catalog::findDest(GString *name) {
     error(-1, "Bad named destination value");
   }
   obj1.free();
+  if (dest && !dest->isOk()) {
+    delete dest;
+    dest = NULL;
+  }
 
   return dest;
 }
@@ -299,8 +308,8 @@ Object *Catalog::findDestInTree(Object *tree, GString *name, Object *obj) {
        } else if (cmp < 0) {
          done = gTrue;
        }
-       name1.free();
       }
+      name1.free();
     }
     names.free();
     if (!found)
index afad803..8ab7c61 100644 (file)
@@ -2,14 +2,16 @@
 //
 // Catalog.h
 //
-// Copyright 1996-2002 Glyph & Cog, LLC
+// Copyright 1996-2003 Glyph & Cog, LLC
 //
 //========================================================================
 
 #ifndef CATALOG_H
 #define CATALOG_H
 
-#ifdef __GNUC__
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
 #pragma interface
 #endif
 
@@ -28,7 +30,7 @@ class Catalog {
 public:
 
   // Constructor.
-  Catalog(XRef *xrefA, GBool printCommands = gFalse);
+  Catalog(XRef *xrefA);
 
   // Destructor.
   ~Catalog();
@@ -63,6 +65,8 @@ public:
   // NULL if <name> is not a destination.
   LinkDest *findDest(GString *name);
 
+  Object *getOutline() { return &outline; }
+
 private:
 
   XRef *xref;                  // the xref table for this PDF file
@@ -75,10 +79,10 @@ private:
   GString *baseURI;            // base URI for URI-type links
   Object metadata;             // metadata stream
   Object structTreeRoot;       // structure tree root dictionary
+  Object outline;              // outline dictionary
   GBool ok;                    // true if catalog is valid
 
-  int readPageTree(Dict *pages, PageAttrs *attrs, int start,
-                  GBool printCommands);
+  int readPageTree(Dict *pages, PageAttrs *attrs, int start);
   Object *findDestInTree(Object *tree, GString *name, Object *obj);
 };
 
index 912981e..2e2ad47 100644 (file)
@@ -2,15 +2,16 @@
 //
 // CharCodeToUnicode.cc
 //
-// Copyright 2001-2002 Glyph & Cog, LLC
+// Copyright 2001-2003 Glyph & Cog, LLC
 //
 //========================================================================
 
-#ifdef __GNUC__
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
 #pragma implementation
 #endif
 
-#include <aconf.h>
 #include <stdio.h>
 #include <string.h>
 #include "gmem.h"
@@ -53,7 +54,8 @@ static int getCharFromFile(void *data) {
 
 //------------------------------------------------------------------------
 
-CharCodeToUnicode *CharCodeToUnicode::parseCIDToUnicode(GString *collectionA) {
+CharCodeToUnicode *CharCodeToUnicode::parseCIDToUnicode(GString *fileName,
+                                                       GString *collection) {
   FILE *f;
   Unicode *mapA;
   CharCode size, mapLenA;
@@ -61,9 +63,9 @@ CharCodeToUnicode *CharCodeToUnicode::parseCIDToUnicode(GString *collectionA) {
   Unicode u;
   CharCodeToUnicode *ctu;
 
-  if (!(f = globalParams->getCIDToUnicodeFile(collectionA))) {
-    error(-1, "Couldn't find cidToUnicode file for the '%s' collection",
-         collectionA->getCString());
+  if (!(f = fopen(fileName->getCString(), "r"))) {
+    error(-1, "Couldn't open cidToUnicode file '%s'",
+         fileName->getCString());
     return NULL;
   }
 
@@ -79,21 +81,110 @@ CharCodeToUnicode *CharCodeToUnicode::parseCIDToUnicode(GString *collectionA) {
     if (sscanf(buf, "%x", &u) == 1) {
       mapA[mapLenA] = u;
     } else {
-      error(-1, "Bad line (%d) in cidToUnicode file for the '%s' collection",
-           (int)(mapLenA + 1), collectionA->getCString());
+      error(-1, "Bad line (%d) in cidToUnicode file '%s'",
+           (int)(mapLenA + 1), fileName->getCString());
       mapA[mapLenA] = 0;
     }
     ++mapLenA;
   }
+  fclose(f);
 
-  ctu = new CharCodeToUnicode(collectionA->copy(), mapA, mapLenA, gTrue,
-                             NULL, 0);
+  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 *)gmalloc(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 *)grealloc(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 *)
+                 grealloc(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);
+  return new CharCodeToUnicode(NULL, toUnicode, 256, gTrue, NULL, 0, 0);
 }
 
 CharCodeToUnicode *CharCodeToUnicode::parseCMap(GString *buf, int nBits) {
@@ -106,16 +197,20 @@ CharCodeToUnicode *CharCodeToUnicode::parseCMap(GString *buf, int 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 oldLen, i;
+  CharCode i;
   CharCode code1, code2;
-  Unicode u;
-  char uHex[5];
-  int j;
   GString *name;
   FILE *f;
 
@@ -156,38 +251,7 @@ void CharCodeToUnicode::parseCMap1(int (*getCharFunc)(void *), void *data,
          error(-1, "Illegal entry in bfchar block in ToUnicode CMap");
          continue;
        }
-       if (code1 >= mapLen) {
-         oldLen = mapLen;
-         mapLen = (code1 + 256) & ~255;
-         map = (Unicode *)grealloc(map, mapLen * sizeof(Unicode));
-         for (i = oldLen; i < mapLen; ++i) {
-           map[i] = 0;
-         }
-       }
-       if (n2 == 6) {
-         if (sscanf(tok2 + 1, "%x", &u) != 1) {
-           error(-1, "Illegal entry in bfchar block in ToUnicode CMap");
-           continue;
-         }
-         map[code1] = u;
-       } else {
-         map[code1] = 0;
-         if (sMapLen == sMapSize) {
-           sMapSize += 8;
-           sMap = (CharCodeToUnicodeString *)
-               grealloc(sMap, sMapSize * sizeof(CharCodeToUnicodeString));
-         }
-         sMap[sMapLen].c = code1;
-         sMap[sMapLen].len = (n2 - 2) / 4;
-         for (j = 0; j < sMap[sMapLen].len && j < maxUnicodeString; ++j) {
-           strncpy(uHex, tok2 + 1 + j*4, 4);
-           uHex[4] = '\0';
-           if (sscanf(uHex, "%x", &sMap[sMapLen].u[j]) != 1) {
-             error(-1, "Illegal entry in bfchar block in ToUnicode CMap");
-           }
-         }
-         ++sMapLen;
-       }
+       addMapping(code1, tok2 + 1, n2 - 1, 0);
       }
       pst->getToken(tok1, sizeof(tok1), &n1);
     } else if (!strcmp(tok2, "beginbfrange")) {
@@ -203,53 +267,39 @@ void CharCodeToUnicode::parseCMap1(int (*getCharFunc)(void *), void *data,
          break;
        }
        if (!(n1 == 2 + nDigits && tok1[0] == '<' && tok1[n1 - 1] == '>' &&
-             n2 == 2 + nDigits && tok2[0] == '<' && tok2[n2 - 1] == '>' &&
-             tok3[0] == '<' && tok3[n3 - 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] = tok3[n3 - 1] = '\0';
+       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 (code2 >= mapLen) {
-         oldLen = mapLen;
-         mapLen = (code2 + 256) & ~255;
-         map = (Unicode *)grealloc(map, mapLen * sizeof(Unicode));
-         for (i = oldLen; i < mapLen; ++i) {
-           map[i] = 0;
-         }
-       }
-       if (n3 == 6) {
-         if (sscanf(tok3 + 1, "%x", &u) != 1) {
-           error(-1, "Illegal entry in bfrange block in ToUnicode CMap");
-           continue;
-         }
-         for (; code1 <= code2; ++code1) {
-           map[code1] = u++;
-         }
-       } else {
-         if (sMapLen + (int)(code2 - code1 + 1) > sMapSize) {
-           sMapSize = (sMapSize + (code2 - code1 + 1) + 7) & ~7;
-           sMap = (CharCodeToUnicodeString *)
-               grealloc(sMap, sMapSize * sizeof(CharCodeToUnicodeString));
+       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) {
-           map[code1] = 0;
-           sMap[sMapLen].c = code1;
-           sMap[sMapLen].len = (n3 - 2) / 4;
-           for (j = 0; j < sMap[sMapLen].len && j < maxUnicodeString; ++j) {
-             strncpy(uHex, tok3 + 1 + j*4, 4);
-             uHex[4] = '\0';
-             if (sscanf(uHex, "%x", &sMap[sMapLen].u[j]) != 1) {
-               error(-1, "Illegal entry in bfrange block in ToUnicode CMap");
-             }
-           }
-           sMap[sMapLen].u[sMap[sMapLen].len - 1] += i;
-           ++sMapLen;
+           addMapping(code1, tok3 + 1, n3 - 2, i);
          }
+
+       } else {
+         error(-1, "Illegal entry in bfrange block in ToUnicode CMap");
        }
       }
       pst->getToken(tok1, sizeof(tok1), &n1);
@@ -260,10 +310,52 @@ void CharCodeToUnicode::parseCMap1(int (*getCharFunc)(void *), void *data,
   delete pst;
 }
 
-CharCodeToUnicode::CharCodeToUnicode(GString *collectionA) {
+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 *)grealloc(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 *)
+              grealloc(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;
 
-  collection = collectionA;
+  tag = tagA;
   mapLen = 256;
   map = (Unicode *)gmalloc(mapLen * sizeof(Unicode));
   for (i = 0; i < mapLen; ++i) {
@@ -272,13 +364,16 @@ CharCodeToUnicode::CharCodeToUnicode(GString *collectionA) {
   sMap = NULL;
   sMapLen = sMapSize = 0;
   refCnt = 1;
+#if MULTITHREADED
+  gInitMutex(&mutex);
+#endif
 }
 
-CharCodeToUnicode::CharCodeToUnicode(GString *collectionA, Unicode *mapA,
+CharCodeToUnicode::CharCodeToUnicode(GString *tagA, Unicode *mapA,
                                     CharCode mapLenA, GBool copyMap,
                                     CharCodeToUnicodeString *sMapA,
-                                    int sMapLenA) {
-  collection = collectionA;
+                                    int sMapLenA, int sMapSizeA) {
+  tag = tagA;
   mapLen = mapLenA;
   if (copyMap) {
     map = (Unicode *)gmalloc(mapLen * sizeof(Unicode));
@@ -287,32 +382,75 @@ CharCodeToUnicode::CharCodeToUnicode(GString *collectionA, Unicode *mapA,
     map = mapA;
   }
   sMap = sMapA;
-  sMapLen = sMapSize = sMapLenA;
+  sMapLen = sMapLenA;
+  sMapSize = sMapSizeA;
   refCnt = 1;
+#if MULTITHREADED
+  gInitMutex(&mutex);
+#endif
 }
 
 CharCodeToUnicode::~CharCodeToUnicode() {
-  if (collection) {
-    delete collection;
+  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() {
-  if (--refCnt == 0) {
+  GBool done;
+
+#if MULTITHREADED
+  gLockMutex(&mutex);
+#endif
+  done = --refCnt == 0;
+#if MULTITHREADED
+  gUnlockMutex(&mutex);
+#endif
+  if (done) {
     delete this;
   }
 }
 
-GBool CharCodeToUnicode::match(GString *collectionA) {
-  return collection && !collection->cmp(collectionA);
+GBool CharCodeToUnicode::match(GString *tagA) {
+  return tag && !tag->cmp(tagA);
+}
+
+void CharCodeToUnicode::setMapping(CharCode c, Unicode *u, int len) {
+  int i;
+
+  if (len == 1) {
+    map[c] = u[0];
+  } else {
+    map[c] = 0;
+    if (sMapLen == sMapSize) {
+      sMapSize += 8;
+      sMap = (CharCodeToUnicodeString *)
+              grealloc(sMap, sMapSize * sizeof(CharCodeToUnicodeString));
+    }
+    sMap[sMapLen].c = c;
+    sMap[sMapLen].len = len;
+    for (i = 0; i < len && i < maxUnicodeString; ++i) {
+      sMap[sMapLen].u[i] = u[i];
+    }
+    ++sMapLen;
+  }
 }
 
 int CharCodeToUnicode::mapToUnicode(CharCode c, Unicode *u, int size) {
@@ -338,34 +476,37 @@ int CharCodeToUnicode::mapToUnicode(CharCode c, Unicode *u, int size) {
 
 //------------------------------------------------------------------------
 
-CIDToUnicodeCache::CIDToUnicodeCache() {
+CharCodeToUnicodeCache::CharCodeToUnicodeCache(int sizeA) {
   int i;
 
-  for (i = 0; i < cidToUnicodeCacheSize; ++i) {
+  size = sizeA;
+  cache = (CharCodeToUnicode **)gmalloc(size * sizeof(CharCodeToUnicode *));
+  for (i = 0; i < size; ++i) {
     cache[i] = NULL;
   }
 }
 
-CIDToUnicodeCache::~CIDToUnicodeCache() {
+CharCodeToUnicodeCache::~CharCodeToUnicodeCache() {
   int i;
 
-  for (i = 0; i < cidToUnicodeCacheSize; ++i) {
+  for (i = 0; i < size; ++i) {
     if (cache[i]) {
       cache[i]->decRefCnt();
     }
   }
+  gfree(cache);
 }
 
-CharCodeToUnicode *CIDToUnicodeCache::getCIDToUnicode(GString *collection) {
+CharCodeToUnicode *CharCodeToUnicodeCache::getCharCodeToUnicode(GString *tag) {
   CharCodeToUnicode *ctu;
   int i, j;
 
-  if (cache[0] && cache[0]->match(collection)) {
+  if (cache[0] && cache[0]->match(tag)) {
     cache[0]->incRefCnt();
     return cache[0];
   }
-  for (i = 1; i < cidToUnicodeCacheSize; ++i) {
-    if (cache[i] && cache[i]->match(collection)) {
+  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];
@@ -375,16 +516,18 @@ CharCodeToUnicode *CIDToUnicodeCache::getCIDToUnicode(GString *collection) {
       return ctu;
     }
   }
-  if ((ctu = CharCodeToUnicode::parseCIDToUnicode(collection))) {
-    if (cache[cidToUnicodeCacheSize - 1]) {
-      cache[cidToUnicodeCacheSize - 1]->decRefCnt();
-    }
-    for (j = cidToUnicodeCacheSize - 1; 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();
+}
index 06916c8..605e2bd 100644 (file)
@@ -4,19 +4,25 @@
 //
 // Mapping from character codes to Unicode.
 //
-// Copyright 2001-2002 Glyph & Cog, LLC
+// Copyright 2001-2003 Glyph & Cog, LLC
 //
 //========================================================================
 
 #ifndef CHARCODETOUNICODE_H
 #define CHARCODETOUNICODE_H
 
-#ifdef __GNUC__
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
 #pragma interface
 #endif
 
 #include "CharTypes.h"
 
+#if MULTITHREADED
+#include "GMutex.h"
+#endif
+
 struct CharCodeToUnicodeString;
 
 //------------------------------------------------------------------------
@@ -24,10 +30,16 @@ struct CharCodeToUnicodeString;
 class CharCodeToUnicode {
 public:
 
-  // Create the CID-to-Unicode mapping specified by <collection>.
-  // This reads a .cidToUnicode file from disk.  Sets the initial
-  // reference count to 1.  Returns NULL on failure.
-  static CharCodeToUnicode *parseCIDToUnicode(GString *collectionA);
+  // 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
@@ -37,13 +49,20 @@ public:
   // 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 <collectionA>.
-  GBool match(GString *collectionA);
+  // 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);
@@ -51,38 +70,44 @@ public:
 private:
 
   void parseCMap1(int (*getCharFunc)(void *), void *data, int nBits);
-  CharCodeToUnicode(GString *collectionA);
-  CharCodeToUnicode(GString *collectionA, Unicode *mapA,
+  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);
+                   CharCodeToUnicodeString *sMapA,
+                   int sMapLenA, int sMapSizeA);
 
-  GString *collection;
+  GString *tag;
   Unicode *map;
   CharCode mapLen;
   CharCodeToUnicodeString *sMap;
   int sMapLen, sMapSize;
   int refCnt;
+#ifdef MULTITHREADED
+  GMutex mutex;
+#endif
 };
 
 //------------------------------------------------------------------------
 
-#define cidToUnicodeCacheSize 4
-
-class CIDToUnicodeCache {
+class CharCodeToUnicodeCache {
 public:
 
-  CIDToUnicodeCache();
-  ~CIDToUnicodeCache();
+  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);
 
-  // Get the CharCodeToUnicode object for <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.
-  CharCodeToUnicode *getCIDToUnicode(GString *collection);
+  // Insert <ctu> into the cache, in the most-recently-used position.
+  void add(CharCodeToUnicode *ctu);
 
 private:
 
-  CharCodeToUnicode *cache[cidToUnicodeCacheSize];
+  CharCodeToUnicode **cache;
+  int size;
 };
 
 #endif
index bae2f26..d0df630 100644 (file)
@@ -2,7 +2,7 @@
 //
 // CharTypes.h
 //
-// Copyright 2001-2002 Glyph & Cog, LLC
+// Copyright 2001-2003 Glyph & Cog, LLC
 //
 //========================================================================
 
index 8de4091..dab0750 100644 (file)
@@ -2,15 +2,16 @@
 //
 // Decrypt.cc
 //
-// Copyright 1996-2002 Glyph & Cog, LLC
+// Copyright 1996-2003 Glyph & Cog, LLC
 //
 //========================================================================
 
-#ifdef __GNUC__
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
 #pragma implementation
 #endif
 
-#include <aconf.h>
 #include "gmem.h"
 #include "Decrypt.h"
 
@@ -65,13 +66,15 @@ GBool Decrypt::makeFileKey(int encVersion, int encRevision, int keyLength,
                           int permissions, GString *fileID,
                           GString *ownerPassword, GString *userPassword,
                           Guchar *fileKey, GBool *ownerPasswordOk) {
-  Guchar test[32];
+  Guchar test[32], test2[32];
   GString *userPassword2;
   Guchar fState[256];
+  Guchar tmpKey[16];
   Guchar fx, fy;
-  int len, i;
+  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) {
@@ -80,29 +83,40 @@ GBool Decrypt::makeFileKey(int encVersion, int encRevision, int keyLength,
     } else {
       memcpy(test, ownerPassword->getCString(), 32);
     }
-  } else {
-    memcpy(test, passwordPad, 32);
-  }
-  md5(test, 32, test);
-  if (encRevision == 3) {
-    for (i = 0; i < 50; ++i) {
-      md5(test, 16, test);
+    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)) {
+      *ownerPasswordOk = gTrue;
+      delete userPassword2;
+      return gTrue;
     }
-  }
-  rc4InitKey(test, keyLength, fState);
-  fx = fy = 0;
-  for (i = 0; i < 32; ++i) {
-    test[i] = rc4DecryptByte(fState, &fx, &fy, ownerKey->getChar(i));
-  }
-  userPassword2 = new GString((char *)test, 32);
-  if (makeFileKey2(encVersion, encRevision, keyLength, ownerKey, userKey,
-                  permissions, fileID, userPassword2, fileKey)) {
-    *ownerPasswordOk = gTrue;
     delete userPassword2;
-    return gTrue;
   }
-  *ownerPasswordOk = gFalse;
-  delete userPassword2;
 
   // try using the supplied user password
   return makeFileKey2(encVersion, encRevision, keyLength, ownerKey, userKey,
@@ -143,7 +157,7 @@ GBool Decrypt::makeFileKey2(int encVersion, int encRevision, int keyLength,
   md5(buf, 68 + fileID->getLength(), fileKey);
   if (encRevision == 3) {
     for (i = 0; i < 50; ++i) {
-      md5(fileKey, 16, fileKey);
+      md5(fileKey, keyLength, fileKey);
     }
   }
 
@@ -366,20 +380,20 @@ static void md5(Guchar *msg, int msgLen, Guchar *digest) {
   }
 
   // break digest into bytes
-  digest[0] = a & 0xff;
-  digest[1] = (a >>= 8) & 0xff;
-  digest[2] = (a >>= 8) & 0xff;
-  digest[3] = (a >>= 8) & 0xff;
-  digest[4] = b & 0xff;
-  digest[5] = (b >>= 8) & 0xff;
-  digest[6] = (b >>= 8) & 0xff;
-  digest[7] = (b >>= 8) & 0xff;
-  digest[8] = c & 0xff;
-  digest[9] = (c >>= 8) & 0xff;
-  digest[10] = (c >>= 8) & 0xff;
-  digest[11] = (c >>= 8) & 0xff;
-  digest[12] = d & 0xff;
-  digest[13] = (d >>= 8) & 0xff;
-  digest[14] = (d >>= 8) & 0xff;
-  digest[15] = (d >>= 8) & 0xff;
+  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);
 }
index 52afb2f..71f9457 100644 (file)
@@ -2,14 +2,16 @@
 //
 // Decrypt.h
 //
-// Copyright 1996-2002 Glyph & Cog, LLC
+// Copyright 1996-2003 Glyph & Cog, LLC
 //
 //========================================================================
 
 #ifndef DECRYPT_H
 #define DECRYPT_H
 
-#ifdef __GNUC__
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
 #pragma interface
 #endif
 
index 5eb077e..6274590 100644 (file)
@@ -2,15 +2,16 @@
 //
 // Dict.cc
 //
-// Copyright 1996-2002 Glyph & Cog, LLC
+// Copyright 1996-2003 Glyph & Cog, LLC
 //
 //========================================================================
 
-#ifdef __GNUC__
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
 #pragma implementation
 #endif
 
-#include <aconf.h>
 #include <stddef.h>
 #include <string.h>
 #include "gmem.h"
@@ -40,8 +41,12 @@ Dict::~Dict() {
 }
 
 void Dict::add(char *key, Object *val) {
-  if (length + 1 > size) {
-    size += 8;
+  if (length == size) {
+    if (length == 0) {
+      size = 8;
+    } else {
+      size *= 2;
+    }
     entries = (DictEntry *)grealloc(entries, size * sizeof(DictEntry));
   }
   entries[length].key = key;
index b994514..08f55ec 100644 (file)
@@ -2,14 +2,16 @@
 //
 // Dict.h
 //
-// Copyright 1996-2002 Glyph & Cog, LLC
+// Copyright 1996-2003 Glyph & Cog, LLC
 //
 //========================================================================
 
 #ifndef DICT_H
 #define DICT_H
 
-#ifdef __GNUC__
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
 #pragma interface
 #endif
 
index 3eae5c9..c03f75f 100644 (file)
@@ -2,15 +2,16 @@
 //
 // Error.cc
 //
-// Copyright 1996-2002 Glyph & Cog, LLC
+// Copyright 1996-2003 Glyph & Cog, LLC
 //
 //========================================================================
 
-#ifdef __GNUC__
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
 #pragma implementation
 #endif
 
-#include <aconf.h>
 #include <stdio.h>
 #include <stddef.h>
 #include <stdarg.h>
index 77801c5..0ce55e9 100644 (file)
@@ -2,14 +2,16 @@
 //
 // Error.h
 //
-// Copyright 1996-2002 Glyph & Cog, LLC
+// Copyright 1996-2003 Glyph & Cog, LLC
 //
 //========================================================================
 
 #ifndef ERROR_H
 #define ERROR_H
 
-#ifdef __GNUC__
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
 #pragma interface
 #endif
 
index 4e0d38a..b28528d 100644 (file)
@@ -2,7 +2,7 @@
 //
 // ErrorCodes.h
 //
-// Copyright 2002 Glyph & Cog, LLC
+// Copyright 2002-2003 Glyph & Cog, LLC
 //
 //========================================================================
 
 #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/pdf2swf/xpdf/FoFiBase.cc b/pdf2swf/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/pdf2swf/xpdf/FoFiBase.h b/pdf2swf/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/pdf2swf/xpdf/FoFiEncodings.cc b/pdf2swf/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/pdf2swf/xpdf/FoFiEncodings.h b/pdf2swf/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/pdf2swf/xpdf/FoFiTrueType.cc b/pdf2swf/xpdf/FoFiTrueType.cc
new file mode 100644 (file)
index 0000000..a4cf43c
--- /dev/null
@@ -0,0 +1,1438 @@
+//========================================================================
+//
+// FoFiTrueType.cc
+//
+// Copyright 1999-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <stdlib.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
+//
+
+//------------------------------------------------------------------------
+
+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 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
+
+//------------------------------------------------------------------------
+
+// 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);
+  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);
+
+  // 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,
+                                    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);
+
+  // 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,
+                                 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);
+  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) {
+  static char cmapTab[20] = {
+    0, 0,                      // table version number
+    0, 1,                      // number of encoding tables
+    0, 1,                      // platform ID
+    0, 0,                      // encoding ID
+    0, 0, 0, 12,               // offset of subtable
+    0, 0,                      // subtable format
+    0, 1,                      // subtable length
+    0, 1,                      // subtable version
+    0,                         // map char 0 -> glyph 0
+    0                          // pad to multiple of four bytes
+  };
+  static char nameTab[8] = {
+    0, 0,                      // format
+    0, 0,                      // number of name records
+    0, 6,                      // offset to start of string storage
+    0, 0                       // pad to multiple of four bytes
+  };
+  static char postTab[32] = {
+    0, 1, 0, 0,                        // format
+    0, 0, 0, 0,                        // italic angle
+    0, 0,                      // underline position
+    0, 0,                      // underline thickness
+    0, 0, 0, 0,                        // fixed pitch
+    0, 0, 0, 0,                        // min Type 42 memory
+    0, 0, 0, 0,                        // max Type 42 memory
+    0, 0, 0, 0,                        // min Type 1 memory
+    0, 0, 0, 0                 // max Type 1 memory
+  };
+  GBool missingCmap, missingName, missingPost, unsortedLoca, badCmapLen;
+  int nZeroLengthTables;
+  TrueTypeLoca *locaTable;
+  TrueTypeTable *newTables;
+  int nNewTables, cmapIdx, cmapLen, glyfLen;
+  char *tableDir;
+  char locaBuf[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 *)gmalloc((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) {
+    (*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;
+  }
+
+  // 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
+  nNewTables = nTables - nZeroLengthTables +
+               (missingCmap ? 1 : 0) + (missingName ? 1 : 0) +
+               (missingPost ? 1 : 0);
+  newTables = (TrueTypeTable *)gmalloc(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 (newTables[j].tag == cmapTag && badCmapLen) {
+       newTables[j].len = cmapLen;
+      } else if (newTables[j].tag == locaTag && unsortedLoca) {
+       newTables[j].len = (nGlyphs + 1) * (locaFmt ? 4 : 2);
+      } else if (newTables[j].tag == glyfTag && unsortedLoca) {
+       newTables[j].len = glyfLen;
+      }
+      ++j;
+    }
+  }
+  if (missingCmap) {
+    newTables[j].tag = cmapTag;
+    newTables[j].checksum = 0; //~ should compute the checksum
+    newTables[j].len = sizeof(cmapTab);
+    ++j;
+  }
+  if (missingName) {
+    newTables[j].tag = nameTag;
+    newTables[j].checksum = 0; //~ should compute the checksum
+    newTables[j].len = sizeof(nameTab);
+    ++j;
+  }
+  if (missingPost) {
+    newTables[j].tag = postTag;
+    newTables[j].checksum = 0; //~ should compute the checksum
+    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);
+
+  // write the tables
+  for (i = 0; i < nNewTables; ++i) {
+    if (newTables[i].tag == cmapTag && missingCmap) {
+      (*outputFunc)(outputStream, cmapTab, 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(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) {
+  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;
+
+  // 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 *)gmalloc((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 *)gmalloc((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;
+    }
+  }
+
+  // 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 (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);
+       }
+      }
+    }
+  }
+
+  // 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() {
+  int pos, i, j;
+
+  parsedOk = gTrue;
+
+  // read the table directory
+  nTables = getU16BE(4, &parsedOk);
+  if (!parsedOk) {
+    return;
+  }
+  tables = (TrueTypeTable *)gmalloc(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 *)gmalloc(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;
+  }
+
+  // read the post table
+  readPostTable();
+  if (!parsedOk) {
+    return;
+  }
+}
+
+void FoFiTrueType::readPostTable() {
+  GString *name;
+  int tablePos, postFmt, stringIdx, stringPos;
+  int i, j, n, m;
+
+  if ((i = seekTable("post")) < 0) {
+    return;
+  }
+  tablePos = tables[i].offset;
+  postFmt = getU32BE(tablePos, &parsedOk);
+  if (!parsedOk) {
+    return;
+  }
+  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, &parsedOk);
+    if (!parsedOk) {
+      return;
+    }
+    if (n > nGlyphs) {
+      n = nGlyphs;
+    }
+    stringIdx = 0;
+    stringPos = tablePos + 34 + 2*n;
+    for (i = 0; i < n; ++i) {
+      j = getU16BE(tablePos + 34 + 2*i, &parsedOk);
+      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, &parsedOk)) ;
+         if (!parsedOk) {
+           return;
+         }
+       }
+       m = getU8(stringPos, &parsedOk);
+       if (!parsedOk || !checkRegion(stringPos + 1, m)) {
+         parsedOk = gFalse;
+         return;
+       }
+       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, &parsedOk);
+      if (!parsedOk) {
+       return;
+      }
+      if (j < 258) {
+       nameToGID->removeInt(macGlyphNames[j]);
+       nameToGID->add(new GString(macGlyphNames[j]), i);
+      }
+    }
+  }
+}
+
+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/pdf2swf/xpdf/FoFiTrueType.h b/pdf2swf/xpdf/FoFiTrueType.h
new file mode 100644 (file)
index 0000000..ea05eec
--- /dev/null
@@ -0,0 +1,133 @@
+//========================================================================
+//
+// 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 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,
+                        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,
+                     FoFiOutputFunc outputFunc, void *outputStream);
+
+  // Write a clean TTF file, filling in missing tables and correcting
+  // various other errors.  If the font is complete and correct, it
+  // will be written unmodified.
+  void writeTTF(FoFiOutputFunc outputFunc, void *outputStream);
+
+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);
+  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/pdf2swf/xpdf/FoFiType1.cc b/pdf2swf/xpdf/FoFiType1.cc
new file mode 100644 (file)
index 0000000..fe54a63
--- /dev/null
@@ -0,0 +1,207 @@
+//========================================================================
+//
+// 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) {
+    (*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 **)gmalloc(256 * sizeof(char *));
+      for (j = 0; j < 256; ++j) {
+       encoding[j] = NULL;
+      }
+      line = getNextLine(line);
+      for (j = 0; j < 300 && line; ++j) {
+       line1 = getNextLine(line);
+       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 {
+         if (strtok(buf, " \t") &&
+             (p = strtok(NULL, " \t\n\r")) && !strcmp(p, "def")) {
+           break;
+         }
+       }
+       line = line1;
+      }
+      //~ check for getinterval/putinterval junk
+
+    } else {
+      line = getNextLine(line);
+    }
+
+    ++i;
+  }
+
+  parsed = gTrue;
+}
diff --git a/pdf2swf/xpdf/FoFiType1.h b/pdf2swf/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/pdf2swf/xpdf/FoFiType1C.cc b/pdf2swf/xpdf/FoFiType1C.cc
new file mode 100644 (file)
index 0000000..91a21ad
--- /dev/null
@@ -0,0 +1,2385 @@
+//========================================================================
+//
+// 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 *)gmalloc(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;
+    for (i = 0; i < 256; ++i) {
+      if (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 *)gmalloc(nCIDs * sizeof(int));
+  for (i = 0; i < nCIDs; ++i) {
+    cidMap[i] = -1;
+  }
+  for (i = 0; i < nGlyphs; ++i) {
+    cidMap[charset[i]] = i;
+  }
+
+  // build the charstrings
+  charStrings = new GString();
+  charStringOffsets = (int *)gmalloc((nCIDs + 1) * sizeof(int));
+  for (i = 0; i < nCIDs; ++i) {
+    charStringOffsets[i] = charStrings->getLength();
+    if ((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);
+  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));
+  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);
+    (*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 *)gmalloc(nCIDs * sizeof(int));
+  for (i = 0; i < nCIDs; ++i) {
+    cidMap[i] = -1;
+  }
+  for (i = 0; i < nGlyphs; ++i) {
+    cidMap[charset[i]] = i;
+  }
+
+  // write the descendant Type 1 fonts
+  for (i = 0; i < nCIDs; i += 256) {
+
+    //~ this assumes that all CIDs in this block have the same FD --
+    //~ to handle multiple FDs correctly, need to somehow divide the
+    //~ font up by FD
+    fd = 0;
+    for (j = 0; j < 256 && i+j < nCIDs; ++j) {
+      if (cidMap[i+j] >= 0) {
+       fd = fdSelect[cidMap[i+j]];
+       break;
+      }
+    }
+
+    // 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);
+    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));
+    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);
+  (*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;
+  }
+
+  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) {
+         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) {
+         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 (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;
+       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;
+       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;
+       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;
+       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 (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 (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 (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;
+       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;
+       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;
+       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;
+       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;
+       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;
+       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;
+       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;
+       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;
+       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;
+       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 *)
+                        gmalloc(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.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; 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.
+void FoFiType1C::readFD(int offset, int length, Type1CPrivateDict *pDict) {
+  int pos, pSize, pOffset;
+
+  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;
+      }
+      nOps = 0;
+    }
+  }
+  readPrivateDict(pOffset, pSize, pDict);
+}
+
+void FoFiType1C::readPrivateDict(int offset, int length,
+                                Type1CPrivateDict *pDict) {
+  int pos;
+
+  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 = 0;
+  pDict->nominalWidthX = 0;
+  pDict->nominalWidthXFP = 0;
+
+  // 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;
+       break;
+      case 0x0015:
+       pDict->nominalWidthX = ops[0].num;
+       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 **)gmalloc(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 *)gmalloc(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
+    idx->offSize = 0;
+    idx->startPos = idx->endPos = 0;
+  } 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/pdf2swf/xpdf/FoFiType1C.h b/pdf2swf/xpdf/FoFiType1C.h
new file mode 100644 (file)
index 0000000..e6f2b64
--- /dev/null
@@ -0,0 +1,226 @@
+//========================================================================
+//
+// 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];
+  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 {
+  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
+};
+
+#endif
index bd5f9cf..f3b9280 100644 (file)
@@ -2,7 +2,7 @@
 //
 // FontEncodingTables.cc
 //
-// Copyright 2001-2002 Glyph & Cog, LLC
+// Copyright 2001-2003 Glyph & Cog, LLC
 //
 //========================================================================
 
@@ -184,32 +184,32 @@ char *macRomanEncoding[256] = {
   "trademark",
   "acute",
   "dieresis",
-  NULL,
+  "notequal",
   "AE",
   "Oslash",
-  NULL,
+  "infinity",
   "plusminus",
-  NULL,
-  NULL,
+  "lessequal",
+  "greaterequal",
   "yen",
   "mu",
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
+  "partialdiff",
+  "summation",
+  "product",
+  "pi",
+  "integral",
   "ordfeminine",
   "ordmasculine",
-  NULL,
+  "Omega",
   "ae",
   "oslash",
   "questiondown",
   "exclamdown",
   "logicalnot",
-  NULL,
+  "radical",
   "florin",
-  NULL,
-  NULL,
+  "approxequal",
+  "Delta",
   "guillemotleft",
   "guillemotright",
   "ellipsis",
@@ -226,7 +226,7 @@ char *macRomanEncoding[256] = {
   "quoteleft",
   "quoteright",
   "divide",
-  NULL,
+  "lozenge",
   "ydieresis",
   "Ydieresis",
   "fraction",
@@ -251,7 +251,7 @@ char *macRomanEncoding[256] = {
   "Igrave",
   "Oacute",
   "Ocircumflex",
-  NULL,
+  "apple",
   "Ograve",
   "Uacute",
   "Ucircumflex",
index deee0a8..8b0a1e7 100644 (file)
@@ -2,7 +2,7 @@
 //
 // FontEncodingTables.h
 //
-// Copyright 2001-2002 Glyph & Cog, LLC
+// Copyright 2001-2003 Glyph & Cog, LLC
 //
 //========================================================================
 
index 64ea60c..46b1912 100644 (file)
@@ -2,15 +2,16 @@
 //
 // Function.cc
 //
-// Copyright 2001-2002 Glyph & Cog, LLC
+// Copyright 2001-2003 Glyph & Cog, LLC
 //
 //========================================================================
 
-#ifdef __GNUC__
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
 #pragma implementation
 #endif
 
-#include <aconf.h>
 #include <stdlib.h>
 #include <string.h>
 #include <ctype.h>
@@ -379,8 +380,8 @@ void SampledFunction::transform(double *in, double *out) {
 
     // pull 2^m values out of the sample array
     for (j = 0; j < (1<<m); ++j) {
-      idx = e[j & 1][m - 1];
-      for (k = m - 2; k >= 0; --k) {
+      idx = 0;
+      for (k = m - 1; k >= 0; --k) {
        idx = idx * sampleSize[k] + e[(j >> k) & 1][k];
       }
       idx = idx * n + i;
@@ -411,7 +412,6 @@ void SampledFunction::transform(double *in, double *out) {
 
 ExponentialFunction::ExponentialFunction(Object *funcObj, Dict *dict) {
   Object obj1, obj2;
-  GBool hasN;
   int i;
 
   ok = gFalse;
@@ -424,23 +424,14 @@ ExponentialFunction::ExponentialFunction(Object *funcObj, Dict *dict) {
     error(-1, "Exponential function with more than one input");
     goto err1;
   }
-  hasN = hasRange;
-
-  //----- default values
-  for (i = 0; i < funcMaxOutputs; ++i) {
-    c0[i] = 0;
-    c1[i] = 1;
-  }
 
   //----- C0
   if (dict->lookup("C0", &obj1)->isArray()) {
-    if (!hasN) {
-      n = obj1.arrayGetLength();
-      hasN = gTrue;
-    } else if (obj1.arrayGetLength() != n) {
+    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()) {
@@ -450,15 +441,19 @@ ExponentialFunction::ExponentialFunction(Object *funcObj, Dict *dict) {
       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 (!hasN) {
-      n = obj1.arrayGetLength();
-      hasN = gTrue;
-    } else if (obj1.arrayGetLength() != n) {
+    if (obj1.arrayGetLength() != n) {
       error(-1, "Function's C1 array is wrong length");
       goto err2;
     }
@@ -471,6 +466,12 @@ ExponentialFunction::ExponentialFunction(Object *funcObj, Dict *dict) {
       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();
 
@@ -482,13 +483,6 @@ ExponentialFunction::ExponentialFunction(Object *funcObj, Dict *dict) {
   e = obj1.getNum();
   obj1.free();
 
-  // this isn't supposed to happen, but I've run into (broken) PDF
-  // files where it does
-  if (!hasN) {
-    error(-1, "Exponential function does not define number of output values");
-    n = 1;
-  }
-
   ok = gTrue;
   return;
 
@@ -622,9 +616,13 @@ StitchingFunction::StitchingFunction(Object *funcObj, Dict *dict) {
 }
 
 StitchingFunction::StitchingFunction(StitchingFunction *func) {
+  int i;
+
   k = func->k;
   funcs = (Function **)gmalloc(k * sizeof(Function *));
-  memcpy(funcs, func->funcs, k * sizeof(Function *));
+  for (i = 0; i < k; ++i) {
+    funcs[i] = func->funcs[i]->copy();
+  }
   bounds = (double *)gmalloc((k + 1) * sizeof(double));
   memcpy(bounds, func->bounds, (k + 1) * sizeof(double));
   encode = (double *)gmalloc(2 * k * sizeof(double));
@@ -635,9 +633,11 @@ StitchingFunction::StitchingFunction(StitchingFunction *func) {
 StitchingFunction::~StitchingFunction() {
   int i;
 
-  for (i = 0; i < k; ++i) {
-    if (funcs[i]) {
-      delete funcs[i];
+  if (funcs) {
+    for (i = 0; i < k; ++i) {
+      if (funcs[i]) {
+       delete funcs[i];
+      }
     }
   }
   gfree(funcs);
@@ -1095,14 +1095,14 @@ GBool PostScriptFunction::parseCode(Stream *str, int *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;
       }
-      delete tok;
-      if (!(tok = getToken(str))) {
-       error(-1, "Unexpected end of PostScript function stream");
-       return gFalse;
-      }
       if (!tok->cmp("if")) {
        if (elsePtr >= 0) {
          error(-1, "Got 'if' operator with two blocks in PostScript function");
@@ -1247,7 +1247,7 @@ void PostScriptFunction::exec(PSStack *stack, int codePtr) {
        } else {
          b2 = stack->popBool();
          b1 = stack->popBool();
-         stack->pushReal(b1 && b2);
+         stack->pushBool(b1 && b2);
        }
        break;
       case psOpAtan:
@@ -1314,8 +1314,8 @@ void PostScriptFunction::exec(PSStack *stack, int codePtr) {
        stack->roll(2, 1);
        break;
       case psOpExp:
-       r2 = stack->popInt();
-       r1 = stack->popInt();
+       r2 = stack->popNum();
+       r1 = stack->popNum();
        stack->pushReal(pow(r1, r2));
        break;
       case psOpFalse:
@@ -1427,7 +1427,7 @@ void PostScriptFunction::exec(PSStack *stack, int codePtr) {
        if (stack->topIsInt()) {
          stack->pushInt(~stack->popInt());
        } else {
-         stack->pushReal(!stack->popBool());
+         stack->pushBool(!stack->popBool());
        }
        break;
       case psOpOr:
@@ -1438,7 +1438,7 @@ void PostScriptFunction::exec(PSStack *stack, int codePtr) {
        } else {
          b2 = stack->popBool();
          b1 = stack->popBool();
-         stack->pushReal(b1 || b2);
+         stack->pushBool(b1 || b2);
        }
        break;
       case psOpPop:
@@ -1456,7 +1456,7 @@ void PostScriptFunction::exec(PSStack *stack, int codePtr) {
        }
        break;
       case psOpSin:
-       stack->pushReal(cos(stack->popNum()));
+       stack->pushReal(sin(stack->popNum()));
        break;
       case psOpSqrt:
        stack->pushReal(sqrt(stack->popNum()));
@@ -1489,7 +1489,7 @@ void PostScriptFunction::exec(PSStack *stack, int codePtr) {
        } else {
          b2 = stack->popBool();
          b1 = stack->popBool();
-         stack->pushReal(b1 ^ b2);
+         stack->pushBool(b1 ^ b2);
        }
        break;
       case psOpIf:
index 9b0879f..0ceb035 100644 (file)
@@ -2,14 +2,16 @@
 //
 // Function.h
 //
-// Copyright 2001-2002 Glyph & Cog, LLC
+// Copyright 2001-2003 Glyph & Cog, LLC
 //
 //========================================================================
 
 #ifndef FUNCTION_H
 #define FUNCTION_H
 
-#ifdef __GNUC__
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
 #pragma interface
 #endif
 
@@ -25,8 +27,8 @@ class PSStack;
 // Function
 //------------------------------------------------------------------------
 
-#define funcMaxInputs  8
-#define funcMaxOutputs 8
+#define funcMaxInputs   8
+#define funcMaxOutputs 32
 
 class Function {
 public:
index dc09f71..1dd0e26 100644 (file)
@@ -2,15 +2,16 @@
 //
 // GHash.cc
 //
-// Copyright 2001-2002 Glyph & Cog, LLC
+// Copyright 2001-2003 Glyph & Cog, LLC
 //
 //========================================================================
 
-#ifdef __GNUC__
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
 #pragma implementation
 #endif
 
-#include <aconf.h>
 #include "gmem.h"
 #include "GString.h"
 #include "GHash.h"
 
 struct GHashBucket {
   GString *key;
-  void *val;
+  union {
+    void *p;
+    int i;
+  } val;
   GHashBucket *next;
 };
 
@@ -60,35 +64,37 @@ GHash::~GHash() {
 }
 
 void GHash::add(GString *key, void *val) {
-  GHashBucket **oldTab;
   GHashBucket *p;
-  int oldSize, i, h;
+  int h;
 
   // expand the table if necessary
   if (len >= size) {
-    oldSize = size;
-    oldTab = tab;
-    size = 2*size + 1;
-    tab = (GHashBucket **)gmalloc(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);
+    expand();
   }
 
   // add the new symbol
   p = new GHashBucket;
   p->key = key;
-  p->val = val;
+  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;
@@ -102,7 +108,17 @@ void *GHash::lookup(GString *key) {
   if (!(p = find(key, &h))) {
     return NULL;
   }
-  return p->val;
+  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) {
@@ -112,7 +128,17 @@ void *GHash::lookup(char *key) {
   if (!(p = find(key, &h))) {
     return NULL;
   }
-  return p->val;
+  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) {
@@ -132,7 +158,30 @@ void *GHash::remove(GString *key) {
   if (deleteKeys) {
     delete p->key;
   }
-  val = p->val;
+  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;
@@ -155,7 +204,30 @@ void *GHash::remove(char *key) {
   if (deleteKeys) {
     delete p->key;
   }
-  val = p->val;
+  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;
@@ -183,7 +255,27 @@ GBool GHash::getNext(GHashIter **iter, GString **key, void **val) {
     (*iter)->p = tab[(*iter)->h];
   }
   *key = (*iter)->p->key;
-  *val = (*iter)->p->val;
+  *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;
 }
 
@@ -192,6 +284,30 @@ void GHash::killIter(GHashIter **iter) {
   *iter = NULL;
 }
 
+void GHash::expand() {
+  GHashBucket **oldTab;
+  GHashBucket *p;
+  int oldSize, h, i;
+
+  oldSize = size;
+  oldTab = tab;
+  size = 2*size + 1;
+  tab = (GHashBucket **)gmalloc(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;
 
index 91d9700..4a6e08d 100644 (file)
@@ -2,14 +2,16 @@
 //
 // GHash.h
 //
-// Copyright 2001-2002 Glyph & Cog, LLC
+// Copyright 2001-2003 Glyph & Cog, LLC
 //
 //========================================================================
 
 #ifndef GHASH_H
 #define GHASH_H
 
-#ifdef __GNUC__
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
 #pragma interface
 #endif
 
@@ -27,17 +29,24 @@ public:
   GHash(GBool deleteKeysA = gFalse);
   ~GHash();
   void add(GString *key, void *val);
+  void add(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);
index f52bc26..9534232 100644 (file)
@@ -2,15 +2,16 @@
 //
 // GList.cc
 //
-// Copyright 2001-2002 Glyph & Cog, LLC
+// Copyright 2001-2003 Glyph & Cog, LLC
 //
 //========================================================================
 
-#ifdef __GNUC__
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
 #pragma implementation
 #endif
 
-#include <aconf.h>
 #include <string.h>
 #include "gmem.h"
 #include "GList.h"
index 0ef4fd7..4c52489 100644 (file)
@@ -2,14 +2,16 @@
 //
 // GList.h
 //
-// Copyright 2001-2002 Glyph & Cog, LLC
+// Copyright 2001-2003 Glyph & Cog, LLC
 //
 //========================================================================
 
 #ifndef GLIST_H
 #define GLIST_H
 
-#ifdef __GNUC__
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
 #pragma interface
 #endif
 
diff --git a/pdf2swf/xpdf/GMutex.h b/pdf2swf/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
index 3bf626a..7653fd0 100644 (file)
@@ -4,15 +4,16 @@
 //
 // Simple variable-length string type.
 //
-// Copyright 1996-2002 Glyph & Cog, LLC
+// Copyright 1996-2003 Glyph & Cog, LLC
 //
 //========================================================================
 
-#ifdef __GNUC__
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
 #pragma implementation
 #endif
 
-#include <aconf.h>
 #include <stdlib.h>
 #include <stddef.h>
 #include <string.h>
@@ -203,8 +204,12 @@ GString *GString::del(int i, int n) {
   int j;
 
   if (n > 0) {
-    for (j = i; j <= length - n; ++j)
+    if (i + n > length) {
+      n = length - i;
+    }
+    for (j = i; j <= length - n; ++j) {
       s[j] = s[j + n];
+    }
     resize(length -= n);
   }
   return this;
index 93796cb..2083802 100644 (file)
@@ -4,14 +4,16 @@
 //
 // Simple variable-length string type.
 //
-// Copyright 1996-2002 Glyph & Cog, LLC
+// Copyright 1996-2003 Glyph & Cog, LLC
 //
 //========================================================================
 
 #ifndef GSTRING_H
 #define GSTRING_H
 
-#ifdef __GNUC__
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
 #pragma interface
 #endif
 
index 81d164e..a52aa02 100644 (file)
@@ -2,20 +2,22 @@
 //
 // Gfx.cc
 //
-// Copyright 1996-2002 Glyph & Cog, LLC
+// Copyright 1996-2003 Glyph & Cog, LLC
 //
 //========================================================================
 
-#ifdef __GNUC__
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
 #pragma implementation
 #endif
 
-#include <aconf.h>
 #include <stdio.h>
 #include <stddef.h>
 #include <string.h>
 #include <math.h>
 #include "gmem.h"
+#include "GlobalParams.h"
 #include "CharTypes.h"
 #include "Object.h"
 #include "Array.h"
 // 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 (1 / 256.0)
+
 // Max number of splits along the t axis for an axial shading fill.
 #define axialMaxSplits 256
 
 // 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},
@@ -210,6 +222,10 @@ Operator Gfx::opTab[] = {
           &Gfx::opCurveTo2},
 };
 
+#ifdef WIN32 // this works around a bug in the VC7 compiler
+#  pragma optimize("",on)
+#endif
+
 #define numOps (sizeof(opTab) / sizeof(Operator))
 
 //------------------------------------------------------------------------
@@ -217,15 +233,23 @@ Operator Gfx::opTab[] = {
 //------------------------------------------------------------------------
 
 GfxResources::GfxResources(XRef *xref, Dict *resDict, GfxResources *nextA) {
-  Object obj1;
+  Object obj1, obj2;
+  Ref r;
 
   if (resDict) {
 
     // build font dictionary
     fonts = NULL;
-    resDict->lookup("Font", &obj1);
-    if (obj1.isDict()) {
-      fonts = new GfxFontDict(xref, obj1.getDict());
+    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();
 
@@ -249,6 +273,7 @@ GfxResources::GfxResources(XRef *xref, Dict *resDict, GfxResources *nextA) {
     xObjDict.initNull();
     colorSpaceDict.initNull();
     patternDict.initNull();
+    shadingDict.initNull();
     gStateDict.initNull();
   }
 
@@ -379,33 +404,38 @@ GBool GfxResources::lookupGState(char *name, Object *obj) {
 // Gfx
 //------------------------------------------------------------------------
 
-Gfx::Gfx(XRef *xrefA, OutputDev *outA, int pageNum, Dict *resDict, double dpi,
-        PDFRectangle *box, GBool crop, PDFRectangle *cropBox, int rotate,
-        GBool printCommandsA) {
+Gfx::Gfx(XRef *xrefA, OutputDev *outA, int pageNum, Dict *resDict,
+        double hDPI, double vDPI, PDFRectangle *box, GBool crop,
+        PDFRectangle *cropBox, int rotate,
+        GBool (*abortCheckCbkA)(void *data),
+        void *abortCheckCbkDataA) {
   int i;
 
   xref = xrefA;
   subPage = gFalse;
-  printCommands = printCommandsA;
+  printCommands = globalParams->getPrintCommands();
 
   // start the resource stack
   res = new GfxResources(xref, resDict, NULL);
 
   // initialize
   out = outA;
-  state = new GfxState(dpi, box, rotate, out->upsideDown());
+  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->startPage(pageNum, state);
   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 (crop) {
+  if (crop) {
     state->moveTo(cropBox->x1, cropBox->y1);
     state->lineTo(cropBox->x2, cropBox->y1);
     state->lineTo(cropBox->x2, cropBox->y2);
@@ -414,29 +444,34 @@ Gfx::Gfx(XRef *xrefA, OutputDev *outA, int pageNum, Dict *resDict, double dpi,
     state->clip();
     out->clip(state);
     state->clearPath();
-  }*/
+  }
 }
 
 Gfx::Gfx(XRef *xrefA, OutputDev *outA, Dict *resDict,
-        PDFRectangle *box, GBool crop, PDFRectangle *cropBox) {
+        PDFRectangle *box, GBool crop, PDFRectangle *cropBox,
+        GBool (*abortCheckCbkA)(void *data),
+        void *abortCheckCbkDataA) {
   int i;
 
   xref = xrefA;
   subPage = gTrue;
-  printCommands = gFalse;
+  printCommands = globalParams->getPrintCommands();
 
   // start the resource stack
   res = new GfxResources(xref, resDict, NULL);
 
   // initialize
   out = outA;
-  state = new GfxState(72, box, 0, gFalse);
+  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 (crop) {
@@ -453,8 +488,7 @@ Gfx::Gfx(XRef *xrefA, OutputDev *outA, Dict *resDict,
 
 Gfx::~Gfx() {
   while (state->hasSaves()) {
-    state = state->restore();
-    out->restoreState(state);
+    restoreState();
   }
   if (!subPage) {
     out->endPage();
@@ -494,11 +528,11 @@ void Gfx::display(Object *obj, GBool topLevel) {
 void Gfx::go(GBool topLevel) {
   Object obj;
   Object args[maxArgs];
-  int numArgs;
-  int i;
+  int numArgs, i;
+  int lastAbortCheck;
 
   // scan a sequence of objects
-  updateLevel = 0;
+  updateLevel = lastAbortCheck = 0;
   numArgs = 0;
   parser->getObj(&obj);
   while (!obj.isEOF()) {
@@ -526,6 +560,16 @@ void Gfx::go(GBool topLevel) {
        updateLevel = 0;
       }
 
+      // check for an abort
+      if (abortCheckCbk) {
+       if (updateLevel - lastAbortCheck > 10) {
+         if ((*abortCheckCbk)(abortCheckCbkData)) {
+           break;
+         }
+         lastAbortCheck = updateLevel;
+       }
+      }
+
     // got an argument - save it
     } else if (numArgs < maxArgs) {
       args[numArgs++] = obj;
@@ -572,10 +616,11 @@ void Gfx::go(GBool topLevel) {
 void Gfx::execOp(Object *cmd, Object args[], int numArgs) {
   Operator *op;
   char *name;
+  Object *argPtr;
   int i;
 
   // find operator
-  name = cmd->getName();
+  name = cmd->getCmd();
   if (!(op = findOp(name))) {
     if (ignoreUndef == 0)
       error(getPos(), "Unknown operator '%s'", name);
@@ -583,12 +628,19 @@ void Gfx::execOp(Object *cmd, Object args[], int numArgs) {
   }
 
   // type check args
+  argPtr = args;
   if (op->numArgs >= 0) {
-    if (numArgs != op->numArgs) {
-      error(getPos(), "Wrong number (%d) of args to '%s' operator",
-           numArgs, name);
+    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",
@@ -597,15 +649,15 @@ void Gfx::execOp(Object *cmd, Object args[], int numArgs) {
     }
   }
   for (i = 0; i < numArgs; ++i) {
-    if (!checkArg(&args[i], op->tchk[i])) {
+    if (!checkArg(&argPtr[i], op->tchk[i])) {
       error(getPos(), "Arg #%d to '%s' operator is wrong type (%s)",
-           i, name, args[i].getTypeName());
+           i, name, argPtr[i].getTypeName());
       return;
     }
   }
 
   // do it
-  (this->*op->func)(args, numArgs);
+  (this->*op->func)(argPtr, numArgs);
 }
 
 Operator *Gfx::findOp(char *name) {
@@ -653,13 +705,11 @@ int Gfx::getPos() {
 //------------------------------------------------------------------------
 
 void Gfx::opSave(Object args[], int numArgs) {
-  out->saveState(state);
-  state = state->save();
+  saveState();
 }
 
 void Gfx::opRestore(Object args[], int numArgs) {
-  state = state->restore();
-  out->restoreState(state);
+  restoreState();
 }
 
 void Gfx::opConcat(Object args[], int numArgs) {
@@ -1175,18 +1225,7 @@ void Gfx::opCloseEOFillStroke(Object args[], int numArgs) {
 }
 
 void Gfx::doPatternFill(GBool eoFill) {
-  GfxPatternColorSpace *patCS;
   GfxPattern *pattern;
-  GfxTilingPattern *tPat;
-  GfxColorSpace *cs;
-  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;
 
   // 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
@@ -1195,17 +1234,38 @@ void Gfx::doPatternFill(GBool eoFill) {
     return;
   }
 
-  // get color space
-  patCS = (GfxPatternColorSpace *)state->getFillColorSpace();
-
-  // get pattern
   if (!(pattern = state->getFillPattern())) {
     return;
   }
-  if (pattern->getType() != 1) {
-    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;
   }
-  tPat = (GfxTilingPattern *)pattern;
+}
+
+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();
@@ -1244,17 +1304,26 @@ void Gfx::doPatternFill(GBool eoFill) {
   imb[5] = (m1[1] * m1[4] - m1[0] * m1[5]) * det;
 
   // save current graphics state
-  out->saveState(state);
-  state = state->save();
+  savedPath = state->getPath()->copy();
+  saveState();
 
-  // set underlying color space (for uncolored tiling patterns)
+  // 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());
+    state->setStrokeColorSpace(cs->copy());
+    state->setStrokeColor(state->getFillColor());
   } else {
     state->setFillColorSpace(new GfxDeviceGrayColorSpace());
+    state->setStrokeColorSpace(new GfxDeviceGrayColorSpace());
   }
   state->setFillPattern(NULL);
   out->updateFillColor(state);
+  state->setStrokePattern(NULL);
+  out->updateStrokeColor(state);
+  state->setLineWidth(0);
+  out->updateLineWidth(state);
 
   // clip to current path
   state->clip();
@@ -1311,10 +1380,10 @@ void Gfx::doPatternFill(GBool eoFill) {
   //~ edge instead of left/bottom (?)
   xstep = fabs(tPat->getXStep());
   ystep = fabs(tPat->getYStep());
-  xi0 = (int)floor(xMin / xstep);
-  xi1 = (int)ceil(xMax / xstep);
-  yi0 = (int)floor(yMin / ystep);
-  yi1 = (int)ceil(yMax / ystep);
+  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];
   }
@@ -1330,12 +1399,101 @@ void Gfx::doPatternFill(GBool eoFill) {
   }
 
   // restore graphics state
-  state = state->restore();
-  out->restoreState(state);
+  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->clearPath();
+  }
+
+  // clip to current path
+  state->clip();
+  if (eoFill) {
+    out->eoClip(state);
+  } else {
+    out->clip(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]);
+
+  // set the color space
+  state->setFillColorSpace(shading->getColorSpace()->copy());
+
+  // 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;
+  }
+
+  // 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()))) {
@@ -1343,8 +1501,8 @@ void Gfx::opShFill(Object args[], int numArgs) {
   }
 
   // save current graphics state
-  out->saveState(state);
-  state = state->save();
+  savedPath = state->getPath()->copy();
+  saveState();
 
   // clip to bbox
   if (shading->getHasBBox()) {
@@ -1364,6 +1522,9 @@ void Gfx::opShFill(Object args[], int numArgs) {
 
   // do shading type-specific operations
   switch (shading->getType()) {
+  case 1:
+    doFunctionShFill((GfxFunctionShading *)shading);
+    break;
   case 2:
     doAxialShFill((GfxAxialShading *)shading);
     break;
@@ -1373,12 +1534,132 @@ void Gfx::opShFill(Object args[], int numArgs) {
   }
 
   // restore graphics state
-  state = state->restore();
-  out->restoreState(state);
+  restoreState();
+  state->setPath(savedPath);
 
   delete shading;
 }
 
+void Gfx::doFunctionShFill(GfxFunctionShading *shading) {
+  double x0, y0, x1, y1;
+  GfxColor colors[4];
+
+  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 (fabs(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;
@@ -1461,11 +1742,14 @@ void Gfx::doAxialShFill(GfxAxialShading *shading) {
   // difference across a region is small enough, and then the region
   // is painted with a single color.
 
-  // set up
+  // 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;
-  next[0] = axialMaxSplits;
 
   // compute the color at t = tMin
   if (tMin < 0) {
@@ -1730,7 +2014,9 @@ void Gfx::doRadialShFill(GfxRadialShading *shading) {
     // 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
+    // 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);
@@ -1747,7 +2033,7 @@ void Gfx::doRadialShFill(GfxRadialShading *shading) {
          break;
        }
       }
-      if (k == nComps) {
+      if (k == nComps && ib < radialMaxSplits) {
        break;
       }
       ib = (ia + ib) / 2;
@@ -1806,7 +2092,7 @@ void Gfx::doRadialShFill(GfxRadialShading *shading) {
 }
 
 void Gfx::doEndPath() {
-  if (state->isPath() && clip != clipNone) {
+  if (state->isCurPt() && clip != clipNone) {
     state->clip();
     if (clip == clipNormal) {
       out->clip(state);
@@ -1843,6 +2129,7 @@ void Gfx::opBeginText(Object args[], int numArgs) {
 }
 
 void Gfx::opEndText(Object args[], int numArgs) {
+  out->endTextObject(state);
 }
 
 //------------------------------------------------------------------------
@@ -2019,7 +2306,7 @@ void Gfx::doShowText(GString *s) {
   double riseX, riseY;
   CharCode code;
   Unicode u[8];
-  double x, y, dx, dy, dx2, dy2, curX, curY, tdx, tdy;
+  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;
@@ -2057,12 +2344,16 @@ void Gfx::doShowText(GString *s) {
     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();
@@ -2078,11 +2369,11 @@ void Gfx::doShowText(GString *s) {
       dy *= state->getFontSize();
       state->textTransformDelta(dx, dy, &tdx, &tdy);
       state->transform(curX + riseX, curY + riseY, &x, &y);
-      out->saveState(state);
-      state = state->save();
+      saveState();
       state->setCTM(newCTM[0], newCTM[1], newCTM[2], newCTM[3], x, y);
       //~ out->updateCTM(???)
-      if (!out->beginType3Char(state, code, u, uLen)) {
+      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);
@@ -2098,13 +2389,13 @@ void Gfx::doShowText(GString *s) {
        }
        charProc.free();
       }
-      state = state->restore();
-      out->restoreState(state);
+      restoreState();
       // GfxState::restore() does *not* restore the current position,
-      // so we track it here with (curX, curY)
+      // 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;
     }
@@ -2291,9 +2582,13 @@ void Gfx::doImage(Object *ref, Stream *str, GBool inlineImg) {
     obj1.free();
     dict->lookup("BPC", &obj1);
   }
-  if (!obj1.isInt())
+  if (obj1.isInt()) {
+    bits = obj1.getInt();
+  } else if (mask) {
+    bits = 1;
+  } else {
     goto err2;
-  bits = obj1.getInt();
+  }
   obj1.free();
 
   // display a mask
@@ -2397,6 +2692,11 @@ void Gfx::doForm(Object *str) {
   Object obj1;
   int i;
 
+  // check for excessive recursion
+  if (formDepth > 20) {
+    return;
+  }
+
   // get stream dict
   dict = str->streamGetDict();
 
@@ -2442,7 +2742,9 @@ void Gfx::doForm(Object *str) {
   resDict = resObj.isDict() ? resObj.getDict() : (Dict *)NULL;
 
   // draw it
+  ++formDepth;
   doForm1(str, resDict, m, bbox);
+  --formDepth;
 
   resObj.free();
 }
@@ -2570,8 +2872,10 @@ void Gfx::doForm1(Object *str, Dict *resDict, double *matrix, double *bbox) {
   pushResources(resDict);
 
   // save current graphics state
-  out->saveState(state);
-  state = state->save();
+  saveState();
+
+  // kill any pre-existing path
+  state->clearPath();
 
   // save current parser
   oldParser = parser;
@@ -2610,8 +2914,7 @@ void Gfx::doForm1(Object *str, Dict *resDict, double *matrix, double *bbox) {
   parser = oldParser;
 
   // restore graphics state
-  state = state->restore();
-  out->restoreState(state);
+  restoreState();
 
   // pop resource stack
   popResources();
@@ -2619,18 +2922,6 @@ void Gfx::doForm1(Object *str, Dict *resDict, double *matrix, double *bbox) {
   return;
 }
 
-void Gfx::pushResources(Dict *resDict) {
-  res = new GfxResources(xref, resDict, res);
-}
-
-void Gfx::popResources() {
-  GfxResources *resPtr;
-
-  resPtr = res->getNext();
-  delete res;
-  res = resPtr;
-}
-
 //------------------------------------------------------------------------
 // in-line image operators
 //------------------------------------------------------------------------
@@ -2691,7 +2982,7 @@ Stream *Gfx::buildImageStream() {
   obj.free();
 
   // make stream
-  str = new EmbedStream(parser->getStream(), &dict);
+  str = new EmbedStream(parser->getStream(), &dict, gFalse, 0);
   str = str->addFilters(&dict);
 
   return str;
@@ -2758,3 +3049,29 @@ void Gfx::opMarkPoint(Object args[], int numArgs) {
     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;
+}
index b4da531..2e40a57 100644 (file)
@@ -2,14 +2,16 @@
 //
 // Gfx.h
 //
-// Copyright 1996-2002 Glyph & Cog, LLC
+// Copyright 1996-2003 Glyph & Cog, LLC
 //
 //========================================================================
 
 #ifndef GFX_H
 #define GFX_H
 
-#ifdef __GNUC__
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
 #pragma interface
 #endif
 
@@ -25,12 +27,16 @@ class OutputDev;
 class GfxFontDict;
 class GfxFont;
 class GfxPattern;
+class GfxTilingPattern;
+class GfxShadingPattern;
 class GfxShading;
+class GfxFunctionShading;
 class GfxAxialShading;
 class GfxRadialShading;
 class GfxState;
+struct GfxColor;
 class Gfx;
-struct PDFRectangle;
+class PDFRectangle;
 
 //------------------------------------------------------------------------
 // Gfx
@@ -94,13 +100,17 @@ class Gfx {
 public:
 
   // Constructor for regular output.
-  Gfx(XRef *xrefA, OutputDev *outA, int pageNum, Dict *resDict, double dpi,
-      PDFRectangle *box, GBool crop, PDFRectangle *cropBox, int rotate,
-      GBool printCommandsA);
+  Gfx(XRef *xrefA, OutputDev *outA, int pageNum, Dict *resDict,
+      double hDPI, double vDPI, PDFRectangle *box, GBool crop,
+      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, GBool crop, PDFRectangle *cropBox);
+      PDFRectangle *box, GBool crop, PDFRectangle *cropBox,
+      GBool (*abortCheckCbkA)(void *data) = NULL,
+      void *abortCheckCbkDataA = NULL);
 
   ~Gfx();
 
@@ -112,8 +122,11 @@ public:
   void doAnnot(Object *str, double xMin, double yMin,
               double xMax, double yMax);
 
-  void pushResources(Dict *resDict);
-  void popResources();
+  // Save graphics state.
+  void saveState();
+
+  // Restore graphics state.
+  void restoreState();
 
 private:
 
@@ -130,9 +143,14 @@ private:
   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);
@@ -188,7 +206,14 @@ private:
   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 doEndPath();
@@ -247,6 +272,9 @@ private:
   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
index 8dcd8e7..ed9f076 100644 (file)
@@ -2,15 +2,16 @@
 //
 // GfxFont.cc
 //
-// Copyright 1996-2002 Glyph & Cog, LLC
+// Copyright 1996-2003 Glyph & Cog, LLC
 //
 //========================================================================
 
-#ifdef __GNUC__
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
 #pragma implementation
 #endif
 
-#include <aconf.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -24,7 +25,9 @@
 #include "CharCodeToUnicode.h"
 #include "FontEncodingTables.h"
 #include "BuiltinFontTables.h"
-#include "FontFile.h"
+#include "FoFiType1.h"
+#include "FoFiType1C.h"
+#include "FoFiTrueType.h"
 #include "GfxFont.h"
 
 //------------------------------------------------------------------------
@@ -34,6 +37,11 @@ struct StdFontMapEntry {
   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" },
@@ -65,6 +73,9 @@ static StdFontMapEntry stdFontMap[] = {
   { "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" },
@@ -79,7 +90,10 @@ static StdFontMapEntry stdFontMap[] = {
   { "TimesNewRomanPS-BoldMT",       "Times-Bold" },
   { "TimesNewRomanPS-Italic",       "Times-Italic" },
   { "TimesNewRomanPS-ItalicMT",     "Times-Italic" },
-  { "TimesNewRomanPSMT",            "Times-Roman" }
+  { "TimesNewRomanPSMT",            "Times-Roman" },
+  { "TimesNewRomanPSMT,Bold",       "Times-Bold" },
+  { "TimesNewRomanPSMT,BoldItalic", "Times-BoldItalic" },
+  { "TimesNewRomanPSMT,Italic",     "Times-Italic" }
 };
 
 //------------------------------------------------------------------------
@@ -127,12 +141,16 @@ GfxFont::GfxFont(char *tagA, Ref idA, GString *nameA) {
   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;
   }
@@ -255,6 +273,10 @@ void GfxFont::readFontDescriptor(XRef *xref, Dict *fontDict) {
       if (t != 0) {
        descent = t;
       }
+      // some broken font descriptors specify a positive descent
+      if (descent > 0) {
+       descent = -descent;
+      }
     }
     obj2.free();
 
@@ -273,8 +295,8 @@ void GfxFont::readFontDescriptor(XRef *xref, Dict *fontDict) {
   obj1.free();
 }
 
-CharCodeToUnicode *GfxFont::readToUnicodeCMap(Dict *fontDict, int nBits) {
-  CharCodeToUnicode *ctu;
+CharCodeToUnicode *GfxFont::readToUnicodeCMap(Dict *fontDict, int nBits,
+                                             CharCodeToUnicode *ctu) {
   GString *buf;
   Object obj1;
   int c;
@@ -290,17 +312,24 @@ CharCodeToUnicode *GfxFont::readToUnicodeCMap(Dict *fontDict, int nBits) {
   }
   obj1.streamClose();
   obj1.free();
-  ctu = CharCodeToUnicode::parseCMap(buf, nBits);
+  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, ".pfa", ".pfb");
+      extFontFile = globalParams->findFontFile(name, type1Exts);
     } else if (type == fontTrueType) {
-      extFontFile = globalParams->findFontFile(name, ".ttf", NULL);
+      extFontFile = globalParams->findFontFile(name, ttExts);
     }
   }
 }
@@ -318,7 +347,8 @@ char *GfxFont::readExtFontFile(int *len) {
   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);
+    error(-1, "Error reading external font file '%s'",
+         extFontFile->getCString());
   }
   fclose(f);
   return buf;
@@ -374,11 +404,14 @@ Gfx8BitFont::Gfx8BitFont(XRef *xref, char *tagA, Ref idA, GString *nameA,
   GBool baseEncFromFontFile;
   char *buf;
   int len;
-  FontFile *fontFile;
+  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;
@@ -388,10 +421,8 @@ Gfx8BitFont::Gfx8BitFont(XRef *xref, char *tagA, Ref idA, GString *nameA,
   type = typeA;
   ctu = NULL;
 
-  // 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.)
+  // do font name substitution for various aliases of the Base 14 font
+  // names
   if (name) {
     a = 0;
     b = sizeof(stdFontMap) / sizeof(StdFontMapEntry);
@@ -405,7 +436,6 @@ Gfx8BitFont::Gfx8BitFont(XRef *xref, char *tagA, Ref idA, GString *nameA,
       }
     }
     if (!name->cmp(stdFontMap[a].altName)) {
-      delete name;
       name = new GString(stdFontMap[a].properName);
     }
   }
@@ -490,6 +520,7 @@ Gfx8BitFont::Gfx8BitFont(XRef *xref, char *tagA, Ref idA, GString *nameA,
 
   // check FontDict for base encoding
   hasEncoding = gFalse;
+  usesMacRomanEnc = gFalse;
   baseEnc = NULL;
   baseEncFromFontFile = gFalse;
   fontDict->lookup("Encoding", &obj1);
@@ -497,6 +528,7 @@ Gfx8BitFont::Gfx8BitFont(XRef *xref, char *tagA, Ref idA, GString *nameA,
     obj1.dictLookup("BaseEncoding", &obj2);
     if (obj2.isName("MacRomanEncoding")) {
       hasEncoding = gTrue;
+      usesMacRomanEnc = gTrue;
       baseEnc = macRomanEncoding;
     } else if (obj2.isName("MacExpertEncoding")) {
       hasEncoding = gTrue;
@@ -511,6 +543,7 @@ Gfx8BitFont::Gfx8BitFont(XRef *xref, char *tagA, Ref idA, GString *nameA,
     obj2.free();
   } else if (obj1.isName("MacRomanEncoding")) {
     hasEncoding = gTrue;
+    usesMacRomanEnc = gTrue;
     baseEnc = macRomanEncoding;
   } else if (obj1.isName("MacExpertEncoding")) {
     hasEncoding = gTrue;
@@ -526,46 +559,59 @@ Gfx8BitFont::Gfx8BitFont(XRef *xref, char *tagA, Ref idA, GString *nameA,
   // check embedded or external font file for base encoding
   // (only for Type 1 fonts - trying to get an encoding out of a
   // TrueType font is a losing proposition)
-  fontFile = NULL;
+  ffT1 = NULL;
+  ffT1C = NULL;
   buf = NULL;
-  if ((type == fontType1 || type == fontType1C) &&
-      (extFontFile || embFontID.num >= 0)) {
+  if (type == fontType1 && (extFontFile || embFontID.num >= 0)) {
     if (extFontFile) {
-      buf = readExtFontFile(&len);
+      ffT1 = FoFiType1::load(extFontFile->getCString());
     } else {
       buf = readEmbFontFile(xref, &len);
+      ffT1 = FoFiType1::make(buf, len);
     }
-    if (buf) {
-      if (type == fontType1C && !strncmp(buf, "%!", 2)) {
-       // various tools (including Adobe's) occasionally embed Type 1
-       // fonts but label them Type 1C
-       type = fontType1;
+    if (ffT1) {
+      if (ffT1->getName()) {
+       if (embFontName) {
+         delete embFontName;
+       }
+       embFontName = new GString(ffT1->getName());
       }
-      if (type == fontType1) {
-       fontFile = new Type1FontFile(buf, len);
-      } else {
-       fontFile = new Type1CFontFile(buf, len);
+      if (!baseEnc) {
+       baseEnc = ffT1->getEncoding();
+       baseEncFromFontFile = gTrue;
       }
-      if (fontFile->getName()) {
+    }
+  } 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(fontFile->getName());
+       embFontName = new GString(ffT1C->getName());
       }
       if (!baseEnc) {
-       baseEnc = fontFile->getEncoding();
+       baseEnc = ffT1C->getEncoding();
        baseEncFromFontFile = gTrue;
       }
-      gfree(buf);
     }
   }
+  if (buf) {
+    gfree(buf);
+  }
 
   // get default base encoding
   if (!baseEnc) {
     if (builtinFont) {
       baseEnc = builtinFont->defaultBaseEnc;
+      hasEncoding = gTrue;
     } else if (type == fontTrueType) {
-      baseEnc = macRomanEncoding;
+      baseEnc = winAnsiEncoding;
     } else {
       baseEnc = standardEncoding;
     }
@@ -579,6 +625,20 @@ Gfx8BitFont::Gfx8BitFont(XRef *xref, char *tagA, Ref idA, GString *nameA,
     }
   }
 
+  // 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);
@@ -590,7 +650,7 @@ Gfx8BitFont::Gfx8BitFont(XRef *xref, char *tagA, Ref idA, GString *nameA,
        if (obj3.isInt()) {
          code = obj3.getInt();
        } else if (obj3.isName()) {
-         if (code < 256) {
+         if (code >= 0 && code < 256) {
            if (encFree[code]) {
              gfree(enc[code]);
            }
@@ -608,82 +668,106 @@ Gfx8BitFont::Gfx8BitFont(XRef *xref, char *tagA, Ref idA, GString *nameA,
     obj2.free();
   }
   obj1.free();
-  if (fontFile) {
-    delete fontFile;
+  if (ffT1) {
+    delete ffT1;
+  }
+  if (ffT1C) {
+    delete ffT1C;
   }
 
   //----- build the mapping to Unicode -----
 
-  // look for a ToUnicode CMap
-  if (!(ctu = readToUnicodeCMap(fontDict, 8))) {
-
-    // no ToUnicode CMap, so use the char names
+  // pass 1: use the name-to-Unicode mapping table
+  missing = hex = gFalse;
+  for (code = 0; code < 256; ++code) {
+    if ((charName = enc[code])) {
+      if (!(toUnicode[code] = globalParams->mapNameToUnicode(charName)) &&
+         strcmp(charName, ".notdef")) {
+       // if it wasn't in the name-to-Unicode table, check for a
+       // name that looks like 'Axx' or 'xx', where 'A' is any letter
+       // and 'xx' is two hex digits
+       if ((strlen(charName) == 3 &&
+            isalpha(charName[0]) &&
+            isxdigit(charName[1]) && isxdigit(charName[2]) &&
+            ((charName[1] >= 'a' && charName[1] <= 'f') ||
+             (charName[1] >= 'A' && charName[1] <= 'F') ||
+             (charName[2] >= 'a' && charName[2] <= 'f') ||
+             (charName[2] >= 'A' && charName[2] <= 'F'))) ||
+           (strlen(charName) == 2 &&
+            isxdigit(charName[0]) && isxdigit(charName[1]) &&
+            ((charName[0] >= 'a' && charName[0] <= 'f') ||
+             (charName[0] >= 'A' && charName[0] <= 'F') ||
+             (charName[1] >= 'a' && charName[1] <= 'f') ||
+             (charName[1] >= 'A' && charName[1] <= 'F')))) {
+         hex = gTrue;
+       }
+       missing = gTrue;
+      }
+    } else {
+      toUnicode[code] = 0;
+    }
+  }
 
-    // pass 1: use the name-to-Unicode mapping table
-    missing = hex = gFalse;
+  // 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])) {
-       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;
+      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;
        }
-      } 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);
        }
       }
     }
-
-    ctu = CharCodeToUnicode::make8BitToUnicode(toUnicode);
+    utu->decRefCnt();
+    delete ctu;
+    ctu = ctu2;
   }
 
   //----- get the character widths -----
@@ -697,13 +781,22 @@ Gfx8BitFont::Gfx8BitFont(XRef *xref, char *tagA, Ref idA, GString *nameA,
   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()) {
@@ -797,12 +890,126 @@ CharCodeToUnicode *Gfx8BitFont::getToUnicode() {
   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 *)gmalloc(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:
+  //    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 (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 ((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 (charProcs.isDict()) {
+  if (enc[code] && charProcs.isDict()) {
     charProcs.dictLookup(enc[code], proc);
   } else {
     proc->initNull();
@@ -818,12 +1025,12 @@ Dict *Gfx8BitFont::getResources() {
 // GfxCIDFont
 //------------------------------------------------------------------------
 
-static int cmpWidthExcep(const void *w1, const void *w2) {
+static int CDECL cmpWidthExcep(const void *w1, const void *w2) {
   return ((GfxFontCIDWidthExcep *)w1)->first -
          ((GfxFontCIDWidthExcep *)w2)->first;
 }
 
-static int cmpWidthExcepV(const void *w1, const void *w2) {
+static int CDECL cmpWidthExcepV(const void *w1, const void *w2) {
   return ((GfxFontCIDWidthExcepV *)w1)->first -
          ((GfxFontCIDWidthExcepV *)w2)->first;
 }
@@ -908,7 +1115,7 @@ GfxCIDFont::GfxCIDFont(XRef *xref, char *tagA, Ref idA, GString *nameA,
   obj1.free();
 
   // look for a ToUnicode CMap
-  if (!(ctu = readToUnicodeCMap(fontDict, 16))) {
+  if (!(ctu = readToUnicodeCMap(fontDict, 16, NULL))) {
 
     // the "Adobe-Identity" and "Adobe-UCS" collections don't have
     // cidToUnicode files
@@ -947,7 +1154,7 @@ GfxCIDFont::GfxCIDFont(XRef *xref, char *tagA, Ref idA, GString *nameA,
 
   // CIDToGIDMap (for embedded TrueType fonts)
   if (type == fontCIDType2) {
-    fontDict->lookup("CIDToGIDMap", &obj1);
+    desFontDict->lookup("CIDToGIDMap", &obj1);
     if (obj1.isStream()) {
       cidToGIDLen = 0;
       i = 64;
@@ -1036,11 +1243,11 @@ GfxCIDFont::GfxCIDFont(XRef *xref, char *tagA, Ref idA, GString *nameA,
   if (desFontDict->lookup("DW2", &obj1)->isArray() &&
       obj1.arrayGetLength() == 2) {
     if (obj1.arrayGet(0, &obj2)->isNum()) {
-      widths.defVY = obj1.getNum() * 0.001;
+      widths.defVY = obj2.getNum() * 0.001;
     }
     obj2.free();
     if (obj1.arrayGet(1, &obj2)->isNum()) {
-      widths.defHeight = obj1.getNum() * 0.001;
+      widths.defHeight = obj2.getNum() * 0.001;
     }
     obj2.free();
   }
@@ -1051,8 +1258,8 @@ GfxCIDFont::GfxCIDFont(XRef *xref, char *tagA, Ref idA, GString *nameA,
     excepsSize = 0;
     i = 0;
     while (i + 1 < obj1.arrayGetLength()) {
-      obj1.arrayGet(0, &obj2);
-      obj2.arrayGet(0, &obj3);
+      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() &&
@@ -1085,10 +1292,10 @@ GfxCIDFont::GfxCIDFont(XRef *xref, char *tagA, Ref idA, GString *nameA,
                     excepsSize * sizeof(GfxFontCIDWidthExcepV));
        }
        j = obj2.getInt();
-       for (k = 0; k < obj3.arrayGetLength(); ++k) {
+       for (k = 0; k < obj3.arrayGetLength(); k += 3) {
          if (obj3.arrayGet(k, &obj4)->isNum() &&
-             obj3.arrayGet(k, &obj5)->isNum() &&
-             obj3.arrayGet(k, &obj6)->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;
@@ -1225,7 +1432,9 @@ int GfxCIDFont::getWMode() {
 }
 
 CharCodeToUnicode *GfxCIDFont::getToUnicode() {
-  ctu->incRefCnt();
+  if (ctu) {
+    ctu->incRefCnt();
+  }
   return ctu;
 }
 
@@ -1237,24 +1446,38 @@ GString *GfxCIDFont::getCollection() {
 // GfxFontDict
 //------------------------------------------------------------------------
 
-GfxFontDict::GfxFontDict(XRef *xref, Dict *fontDict) {
+GfxFontDict::GfxFontDict(XRef *xref, Ref *fontDictRef, Dict *fontDict) {
   int i;
   Object obj1, obj2;
+  Ref r;
 
   numFonts = fontDict->getLength();
   fonts = (GfxFont **)gmalloc(numFonts * sizeof(GfxFont *));
   for (i = 0; i < numFonts; ++i) {
     fontDict->getValNF(i, &obj1);
     obj1.fetch(xref, &obj2);
-    if (obj1.isRef() && obj2.isDict()) {
+    if (obj2.isDict()) {
+      if (obj1.isRef()) {
+       r = obj1.getRef();
+      } else {
+       // no indirect reference for this font, so invent a unique one
+       // (legal generation numbers are five digits, so any 6-digit
+       // number would be safe)
+       r.num = i;
+       if (fontDictRef) {
+         r.gen = 100000 + fontDictRef->num;
+       } else {
+         r.gen = 999999;
+       }
+      }
       fonts[i] = GfxFont::makeFont(xref, fontDict->getKey(i),
-                                  obj1.getRef(), obj2.getDict());
+                                  r, obj2.getDict());
       if (fonts[i] && !fonts[i]->isOk()) {
        delete fonts[i];
        fonts[i] = NULL;
       }
     } else {
-      error(-1, "font resource is not a dictionary reference");
+      error(-1, "font resource is not a dictionary");
       fonts[i] = NULL;
     }
     obj1.free();
index edd26e5..62dfd08 100644 (file)
@@ -2,14 +2,16 @@
 //
 // GfxFont.h
 //
-// Copyright 1996-2002 Glyph & Cog, LLC
+// Copyright 1996-2003 Glyph & Cog, LLC
 //
 //========================================================================
 
 #ifndef GFXFONT_H
 #define GFXFONT_H
 
-#ifdef __GNUC__
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
 #pragma interface
 #endif
 
@@ -21,6 +23,7 @@
 class Dict;
 class CMap;
 class CharCodeToUnicode;
+class FoFiTrueType;
 struct GfxFontCIDWidths;
 
 //------------------------------------------------------------------------
@@ -102,6 +105,10 @@ public:
   // 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; }
@@ -156,12 +163,14 @@ public:
 protected:
 
   void readFontDescriptor(XRef *xref, Dict *fontDict);
-  CharCodeToUnicode *readToUnicodeCMap(Dict *fontDict, int nBits);
+  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
@@ -198,14 +207,21 @@ public:
   CharCodeToUnicode *getToUnicode();
 
   // Return the character name associated with <code>.
-  char *getCharName(int code) { return code>=256?0:enc[code]; }
+  char *getCharName(int code) { return enc[code]; }
 
   // Returns true if the PDF font specified an encoding.
   GBool getHasEncoding() { return hasEncoding; }
 
-  // Get width of a character or string.
+  // 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();
 
@@ -222,6 +238,7 @@ private:
                                //   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
@@ -277,7 +294,7 @@ class GfxFontDict {
 public:
 
   // Build the font dictionary, given the PDF font dictionary.
-  GfxFontDict(XRef *xref, Dict *fontDict);
+  GfxFontDict(XRef *xref, Ref *fontDictRef, Dict *fontDict);
 
   // Destructor.
   ~GfxFontDict();
index d65bbba..65a1da8 100644 (file)
@@ -2,15 +2,16 @@
 //
 // GfxState.cc
 //
-// Copyright 1996-2002 Glyph & Cog, LLC
+// Copyright 1996-2003 Glyph & Cog, LLC
 //
 //========================================================================
 
-#ifdef __GNUC__
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
 #pragma implementation
 #endif
 
-#include <aconf.h>
 #include <stddef.h>
 #include <math.h>
 #include <string.h> // for memcpy()
@@ -28,6 +29,24 @@ static inline double clip01(double x) {
 }
 
 //------------------------------------------------------------------------
+
+static char *gfxColorSpaceModeNames[] = {
+  "DeviceGray",
+  "CalGray",
+  "DeviceRGB",
+  "CalRGB",
+  "DeviceCMYK",
+  "Lab",
+  "ICCBased",
+  "Indexed",
+  "Separation",
+  "DeviceN",
+  "Pattern"
+};
+
+#define nGfxColorSpaceModes ((sizeof(gfxColorSpaceModeNames) / sizeof(char *)))
+
+//------------------------------------------------------------------------
 // GfxColorSpace
 //------------------------------------------------------------------------
 
@@ -79,7 +98,7 @@ GfxColorSpace *GfxColorSpace::parse(Object *csObj) {
     } else if (obj1.isName("Pattern")) {
       cs = GfxPatternColorSpace::parse(csObj->getArray());
     } else {
-      error(-1, "Bad color space '%s'", csObj->getName());
+      error(-1, "Bad color space");
     }
     obj1.free();
   } else {
@@ -98,6 +117,14 @@ void GfxColorSpace::getDefaultRanges(double *decodeLow, double *decodeRange,
   }
 }
 
+int GfxColorSpace::getNumColorSpaceModes() {
+  return nGfxColorSpaceModes;
+}
+
+char *GfxColorSpace::getColorSpaceModeName(int idx) {
+  return gfxColorSpaceModeNames[idx];
+}
+
 //------------------------------------------------------------------------
 // GfxDeviceGrayColorSpace
 //------------------------------------------------------------------------
@@ -733,12 +760,18 @@ void GfxICCBasedColorSpace::getCMYK(GfxColor *color, GfxCMYK *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
 }
 
 //------------------------------------------------------------------------
@@ -788,9 +821,19 @@ GfxColorSpace *GfxIndexedColorSpace::parse(Array *arr) {
   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);
@@ -833,43 +876,37 @@ GfxColorSpace *GfxIndexedColorSpace::parse(Array *arr) {
   return NULL;
 }
 
-void GfxIndexedColorSpace::getGray(GfxColor *color, double *gray) {
+GfxColor *GfxIndexedColorSpace::mapColorToBase(GfxColor *color,
+                                              GfxColor *baseColor) {
   Guchar *p;
-  GfxColor color2;
+  double low[gfxColorMaxComps], range[gfxColorMaxComps];
   int n, i;
 
   n = base->getNComps();
+  base->getDefaultRanges(low, range, indexHigh);
   p = &lookup[(int)(color->c[0] + 0.5) * n];
   for (i = 0; i < n; ++i) {
-    color2.c[i] = p[i] / 255.0;
+    baseColor->c[i] = low[i] + (p[i] / 255.0) * range[i];
   }
-  base->getGray(&color2, gray);
+  return baseColor;
+}
+
+void GfxIndexedColorSpace::getGray(GfxColor *color, double *gray) {
+  GfxColor color2;
+
+  base->getGray(mapColorToBase(color, &color2), gray);
 }
 
 void GfxIndexedColorSpace::getRGB(GfxColor *color, GfxRGB *rgb) {
-  Guchar *p;
   GfxColor color2;
-  int n, i;
 
-  n = base->getNComps();
-  p = &lookup[(int)(color->c[0] + 0.5) * n];
-  for (i = 0; i < n; ++i) {
-    color2.c[i] = p[i] / 255.0;
-  }
-  base->getRGB(&color2, rgb);
+  base->getRGB(mapColorToBase(color, &color2), rgb);
 }
 
 void GfxIndexedColorSpace::getCMYK(GfxColor *color, GfxCMYK *cmyk) {
-  Guchar *p;
   GfxColor color2;
-  int n, i;
 
-  n = base->getNComps();
-  p = &lookup[(int)(color->c[0] + 0.5) * n];
-  for (i = 0; i < n; ++i) {
-    color2.c[i] = p[i] / 255.0;
-  }
-  base->getCMYK(&color2, cmyk);
+  base->getCMYK(mapColorToBase(color, &color2), cmyk);
 }
 
 void GfxIndexedColorSpace::getDefaultRanges(double *decodeLow,
@@ -1016,6 +1053,11 @@ GfxColorSpace *GfxDeviceNColorSpace::parse(Array *arr) {
     goto err2;
   }
   nCompsA = obj1.arrayGetLength();
+  if (nCompsA > gfxColorMaxComps) {
+    error(-1, "DeviceN color space with more than %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)");
@@ -1144,18 +1186,22 @@ GfxPattern::~GfxPattern() {
 
 GfxPattern *GfxPattern::parse(Object *obj) {
   GfxPattern *pattern;
-  Dict *dict;
   Object obj1;
 
+  if (obj->isDict()) {
+    obj->dictLookup("PatternType", &obj1);
+  } else if (obj->isStream()) {
+    obj->streamGetDict()->lookup("PatternType", &obj1);
+  } else {
+    return NULL;
+  }
   pattern = NULL;
-  if (obj->isStream()) {
-    dict = obj->streamGetDict();
-    dict->lookup("PatternType", &obj1);
-    if (obj1.isInt() && obj1.getInt() == 1) {
-      pattern = new GfxTilingPattern(dict, obj);
-    }
-    obj1.free();
+  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;
 }
 
@@ -1163,33 +1209,42 @@ GfxPattern *GfxPattern::parse(Object *obj) {
 // GfxTilingPattern
 //------------------------------------------------------------------------
 
-GfxTilingPattern::GfxTilingPattern(Dict *streamDict, Object *stream):
-  GfxPattern(1)
-{
+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 (streamDict->lookup("PaintType", &obj1)->isInt()) {
-    paintType = obj1.getInt();
+  if (!patObj->isStream()) {
+    return NULL;
+  }
+  dict = patObj->streamGetDict();
+
+  if (dict->lookup("PaintType", &obj1)->isInt()) {
+    paintTypeA = obj1.getInt();
   } else {
-    paintType = 1;
+    paintTypeA = 1;
     error(-1, "Invalid or missing PaintType in pattern");
   }
   obj1.free();
-  if (streamDict->lookup("TilingType", &obj1)->isInt()) {
-    tilingType = obj1.getInt();
+  if (dict->lookup("TilingType", &obj1)->isInt()) {
+    tilingTypeA = obj1.getInt();
   } else {
-    tilingType = 1;
+    tilingTypeA = 1;
     error(-1, "Invalid or missing TilingType in pattern");
   }
   obj1.free();
-  bbox[0] = bbox[1] = 0;
-  bbox[2] = bbox[3] = 1;
-  if (streamDict->lookup("BBox", &obj1)->isArray() &&
+  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()) {
-       bbox[i] = obj2.getNum();
+       bboxA[i] = obj2.getNum();
       }
       obj2.free();
     }
@@ -1197,39 +1252,65 @@ GfxTilingPattern::GfxTilingPattern(Dict *streamDict, Object *stream):
     error(-1, "Invalid or missing BBox in pattern");
   }
   obj1.free();
-  if (streamDict->lookup("XStep", &obj1)->isNum()) {
-    xStep = obj1.getNum();
+  if (dict->lookup("XStep", &obj1)->isNum()) {
+    xStepA = obj1.getNum();
   } else {
-    xStep = 1;
+    xStepA = 1;
     error(-1, "Invalid or missing XStep in pattern");
   }
   obj1.free();
-  if (streamDict->lookup("YStep", &obj1)->isNum()) {
-    yStep = obj1.getNum();
+  if (dict->lookup("YStep", &obj1)->isNum()) {
+    yStepA = obj1.getNum();
   } else {
-    yStep = 1;
+    yStepA = 1;
     error(-1, "Invalid or missing YStep in pattern");
   }
   obj1.free();
-  if (!streamDict->lookup("Resources", &resDict)->isDict()) {
-    resDict.free();
-    resDict.initNull();
+  if (!dict->lookup("Resources", &resDictA)->isDict()) {
+    resDictA.free();
+    resDictA.initNull();
     error(-1, "Invalid or missing Resources in pattern");
   }
-  matrix[0] = 1; matrix[1] = 0;
-  matrix[2] = 0; matrix[3] = 1;
-  matrix[4] = 0; matrix[5] = 0;
-  if (streamDict->lookup("Matrix", &obj1)->isArray() &&
+  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()) {
-       matrix[i] = obj2.getNum();
+       matrixA[i] = obj2.getNum();
       }
       obj2.free();
     }
   }
   obj1.free();
-  stream->copy(&contentStream);
+
+  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() {
@@ -1238,127 +1319,341 @@ GfxTilingPattern::~GfxTilingPattern() {
 }
 
 GfxPattern *GfxTilingPattern::copy() {
-  return new GfxTilingPattern(this);
+  return new GfxTilingPattern(paintType, tilingType, bbox, xStep, yStep,
+                             &resDict, matrix, &contentStream);
 }
 
-GfxTilingPattern::GfxTilingPattern(GfxTilingPattern *pat):
-  GfxPattern(1)
+//------------------------------------------------------------------------
+// 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)
 {
-  memcpy(this, pat, sizeof(GfxTilingPattern));
-  pat->resDict.copy(&resDict);
-  pat->contentStream.copy(&contentStream);
+  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() {
+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() {
-  delete colorSpace;
+  if (colorSpace) {
+    delete colorSpace;
+  }
 }
 
 GfxShading *GfxShading::parse(Object *obj) {
   GfxShading *shading;
+  Dict *dict;
   int typeA;
-  GfxColorSpace *colorSpaceA;
-  GfxColor backgroundA;
-  GBool hasBackgroundA;
-  double xMinA, yMinA, xMaxA, yMaxA;
-  GBool hasBBoxA;
-  Object obj1, obj2;
-  int i;
+  Object obj1;
 
-  shading = NULL;
   if (obj->isDict()) {
+    dict = obj->getDict();
+  } else if (obj->isStream()) {
+    dict = obj->streamGetDict();
+  } else {
+    return NULL;
+  }
 
-    if (!obj->dictLookup("ShadingType", &obj1)->isInt()) {
-      error(-1, "Invalid ShadingType in shading dictionary");
-      obj1.free();
-      goto err1;
-    }
-    typeA = obj1.getInt();
+  if (!dict->lookup("ShadingType", &obj1)->isInt()) {
+    error(-1, "Invalid ShadingType in shading dictionary");
     obj1.free();
+    return NULL;
+  }
+  typeA = obj1.getInt();
+  obj1.free();
 
-    obj->dictLookup("ColorSpace", &obj1);
-    if (!(colorSpaceA = GfxColorSpace::parse(&obj1))) {
-      error(-1, "Bad color space in shading dictionary");
-      obj1.free();
-      goto err1;
-    }
-    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;
+  default:
+    error(-1, "Unimplemented shading type %d", typeA);
+    goto err1;
+  }
 
-    for (i = 0; i < gfxColorMaxComps; ++i) {
-      backgroundA.c[i] = 0;
-    }
-    hasBackgroundA = gFalse;
-    if (obj->dictLookup("Background", &obj1)->isArray()) {
-      if (obj1.arrayGetLength() == colorSpaceA->getNComps()) {
-       hasBackgroundA = gTrue;
-       for (i = 0; i < colorSpaceA->getNComps(); ++i) {
-         backgroundA.c[i] = obj1.arrayGet(i, &obj2)->getNum();
-         obj2.free();
-       }
-      } else {
-       error(-1, "Bad Background in shading dictionary");
-      }
-    }
+  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();
 
-    xMinA = yMinA = xMaxA = yMaxA = 0;
-    hasBBoxA = gFalse;
-    if (obj->dictLookup("BBox", &obj1)->isArray()) {
-      if (obj1.arrayGetLength() == 4) {
-       hasBBoxA = gTrue;
-       xMinA = obj1.arrayGet(0, &obj2)->getNum();
-       obj2.free();
-       yMinA = obj1.arrayGet(1, &obj2)->getNum();
-       obj2.free();
-       xMaxA = obj1.arrayGet(2, &obj2)->getNum();
-       obj2.free();
-       yMaxA = obj1.arrayGet(3, &obj2)->getNum();
+  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] = obj1.arrayGet(i, &obj2)->getNum();
        obj2.free();
-      } else {
-       error(-1, "Bad BBox in shading dictionary");
       }
+    } else {
+      error(-1, "Bad Background in shading dictionary");
     }
-    obj1.free();
+  }
+  obj1.free();
 
-    switch (typeA) {
-    case 2:
-      shading = GfxAxialShading::parse(obj->getDict());
-      break;
-    case 3:
-      shading = GfxRadialShading::parse(obj->getDict());
-      break;
-    default:
-      error(-1, "Unimplemented shading type %d", typeA);
-      goto err1;
+  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();
 
-    if (shading) {
-      shading->type = typeA;
-      shading->colorSpace = colorSpaceA;
-      shading->background = backgroundA;
-      shading->hasBackground = hasBackgroundA;
-      shading->xMin = xMinA;
-      shading->yMin = yMinA;
-      shading->xMax = xMaxA;
-      shading->yMax = yMaxA;
-      shading->hasBBox = hasBBoxA;
-    } else {
-      delete colorSpaceA;
+  return gTrue;
+}
+
+//------------------------------------------------------------------------
+// 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];
+  int i;
+
+  in[0] = x;
+  in[1] = y;
+  for (i = 0; i < nFuncs; ++i) {
+    funcs[i]->transform(in, &color->c[i]);
+  }
+}
+
 //------------------------------------------------------------------------
 // GfxAxialShading
 //------------------------------------------------------------------------
@@ -1367,7 +1662,9 @@ GfxAxialShading::GfxAxialShading(double x0A, double y0A,
                                 double x1A, double y1A,
                                 double t0A, double t1A,
                                 Function **funcsA, int nFuncsA,
-                                GBool extend0A, GBool extend1A) {
+                                GBool extend0A, GBool extend1A):
+  GfxShading(2)
+{
   int i;
 
   x0 = x0A;
@@ -1384,6 +1681,25 @@ GfxAxialShading::GfxAxialShading(double x0A, double y0A,
   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;
 
@@ -1393,6 +1709,7 @@ GfxAxialShading::~GfxAxialShading() {
 }
 
 GfxAxialShading *GfxAxialShading::parse(Dict *dict) {
+  GfxAxialShading *shading;
   double x0A, y0A, x1A, y1A;
   double t0A, t1A;
   Function *funcsA[gfxColorMaxComps];
@@ -1432,6 +1749,10 @@ GfxAxialShading *GfxAxialShading::parse(Dict *dict) {
   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))) {
@@ -1460,16 +1781,27 @@ GfxAxialShading *GfxAxialShading::parse(Dict *dict) {
   }
   obj1.free();
 
-  return new GfxAxialShading(x0A, y0A, x1A, y1A, t0A, t1A,
-                            funcsA, nFuncsA, extend0A, extend1A);
+  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) {
   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 < nFuncs; ++i) {
     funcs[i]->transform(&t, &color->c[i]);
   }
@@ -1483,7 +1815,9 @@ 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) {
+                                  GBool extend0A, GBool extend1A):
+  GfxShading(3)
+{
   int i;
 
   x0 = x0A;
@@ -1502,6 +1836,27 @@ GfxRadialShading::GfxRadialShading(double x0A, double y0A, double r0A,
   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;
 
@@ -1511,6 +1866,7 @@ GfxRadialShading::~GfxRadialShading() {
 }
 
 GfxRadialShading *GfxRadialShading::parse(Dict *dict) {
+  GfxRadialShading *shading;
   double x0A, y0A, r0A, x1A, y1A, r1A;
   double t0A, t1A;
   Function *funcsA[gfxColorMaxComps];
@@ -1554,6 +1910,10 @@ GfxRadialShading *GfxRadialShading::parse(Dict *dict) {
   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))) {
@@ -1582,16 +1942,27 @@ GfxRadialShading *GfxRadialShading::parse(Dict *dict) {
   }
   obj1.free();
 
-  return new GfxRadialShading(x0A, y0A, r0A, x1A, y1A, r1A, t0A, t1A,
-                             funcsA, nFuncsA, extend0A, extend1A);
+  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) {
   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 < nFuncs; ++i) {
     funcs[i]->transform(&t, &color->c[i]);
   }
@@ -1612,14 +1983,12 @@ GfxImageColorMap::GfxImageColorMap(int bitsA, Object *decode,
   double x[gfxColorMaxComps];
   double y[gfxColorMaxComps];
   int i, j, k;
-  int maxPixelForAlloc;
 
   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
@@ -1666,19 +2035,25 @@ GfxImageColorMap::GfxImageColorMap(int bitsA, Object *decode,
     colorSpace2 = indexedCS->getBase();
     indexHigh = indexedCS->getIndexHigh();
     nComps2 = colorSpace2->getNComps();
-    lookup = (double *)gmalloc((maxPixelForAlloc + 1) * nComps2 * sizeof(double));
+    lookup = (double *)gmalloc((maxPixel + 1) * nComps2 * sizeof(double));
     lookup2 = indexedCS->getLookup();
-    for (i = 0; i <= indexHigh; ++i) {
-      j = (int)(decodeLow[0] +(i * decodeRange[0]) / maxPixel + 0.5);
+    colorSpace2->getDefaultRanges(x, y, indexHigh);
+    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;
+      }
       for (k = 0; k < nComps2; ++k) {
-       lookup[i*nComps2 + k] = lookup2[i*nComps2 + k] / 255.0;
+       lookup[i*nComps2 + k] = x[k] + (lookup2[j*nComps2 + k] / 255.0) * y[k];
       }
     }
   } else if (colorSpace->getMode() == csSeparation) {
     sepCS = (GfxSeparationColorSpace *)colorSpace;
     colorSpace2 = sepCS->getAlt();
     nComps2 = colorSpace2->getNComps();
-    lookup = (double *)gmalloc((maxPixelForAlloc + 1) * nComps2 * sizeof(double));
+    lookup = (double *)gmalloc((maxPixel + 1) * nComps2 * sizeof(double));
     sepFunc = sepCS->getFunc();
     for (i = 0; i <= maxPixel; ++i) {
       x[0] = decodeLow[0] + (i * decodeRange[0]) / maxPixel;
@@ -1688,7 +2063,7 @@ GfxImageColorMap::GfxImageColorMap(int bitsA, Object *decode,
       }
     }
   } else {
-    lookup = (double *)gmalloc((maxPixelForAlloc + 1) * nComps * sizeof(double));
+    lookup = (double *)gmalloc((maxPixel + 1) * nComps * sizeof(double));
     for (i = 0; i <= maxPixel; ++i) {
       for (k = 0; k < nComps; ++k) {
        lookup[i*nComps + k] = decodeLow[k] +
@@ -1705,6 +2080,34 @@ GfxImageColorMap::GfxImageColorMap(int bitsA, Object *decode,
   ok = gFalse;
 }
 
+GfxImageColorMap::GfxImageColorMap(GfxImageColorMap *colorMap) {
+  int n, i;
+
+  colorSpace = colorMap->colorSpace->copy();
+  bits = colorMap->bits;
+  nComps = colorMap->nComps;
+  nComps2 = colorMap->nComps2;
+  colorSpace2 = NULL;
+  lookup = NULL;
+  n = 1 << bits;
+  if (colorSpace->getMode() == csIndexed) {
+    colorSpace2 = ((GfxIndexedColorSpace *)colorSpace)->getBase();
+    n = n * nComps2 * sizeof(double);
+  } else if (colorSpace->getMode() == csSeparation) {
+    colorSpace2 = ((GfxSeparationColorSpace *)colorSpace)->getAlt();
+    n = n * nComps2 * sizeof(double);
+  } else {
+    n = n * nComps * sizeof(double);
+  }
+  lookup = (double *)gmalloc(n);
+  memcpy(lookup, colorMap->lookup, n);
+  for (i = 0; i < nComps; ++i) {
+    decodeLow[i] = colorMap->decodeLow[i];
+    decodeRange[i] = colorMap->decodeRange[i];
+  }
+  ok = gTrue;
+}
+
 GfxImageColorMap::~GfxImageColorMap() {
   delete colorSpace;
   gfree(lookup);
@@ -1730,20 +2133,17 @@ void GfxImageColorMap::getGray(Guchar *x, double *gray) {
 }
 
 void GfxImageColorMap::getRGB(Guchar *x, GfxRGB *rgb) {
-
   GfxColor color;
   double *p;
   int i;
 
   if (colorSpace2) {
-    //printf("lookup[%d] bits=%d\n",x[0],bits);fflush(stdout);
     p = &lookup[x[0] * nComps2];
     for (i = 0; i < nComps2; ++i) {
       color.c[i] = *p++;
     }
     colorSpace2->getRGB(&color, rgb);
   } else {
-    //printf("for i=0,i<%d, bits=%d\n",nComps,bits);fflush(stdout);
     for (i = 0; i < nComps; ++i) {
       color.c[i] = lookup[x[i] * nComps + i];
     }
@@ -1770,6 +2170,15 @@ void GfxImageColorMap::getCMYK(Guchar *x, GfxCMYK *cmyk) {
   }
 }
 
+void GfxImageColorMap::getColor(Guchar *x, GfxColor *color) {
+  int maxPixel, i;
+
+  maxPixel = (1 << bits) - 1;
+  for (i = 0; i < nComps; ++i) {
+    color->c[i] = decodeLow[i] + (x[i] * decodeRange[i]) / maxPixel;
+  }
+}
+
 //------------------------------------------------------------------------
 // GfxSubpath and GfxPath
 //------------------------------------------------------------------------
@@ -1844,6 +2253,15 @@ void GfxSubpath::close() {
   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;
@@ -1926,55 +2344,78 @@ void GfxPath::close() {
   subpaths[n-1]->close();
 }
 
+void GfxPath::append(GfxPath *path) {
+  int i;
+
+  if (n + path->n > size) {
+    size = n + path->n;
+    subpaths = (GfxSubpath **)
+                 grealloc(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 dpi, PDFRectangle *pageBox, int rotate,
-                  GBool upsideDown) {
-  double k;
+GfxState::GfxState(double hDPI, double vDPI, PDFRectangle *pageBox,
+                  int rotate, GBool upsideDown) {
+  double kx, ky;
 
   px1 = pageBox->x1;
   py1 = pageBox->y1;
   px2 = pageBox->x2;
   py2 = pageBox->y2;
-  k = dpi / 72.0;
+  kx = hDPI / 72.0;
+  ky = vDPI / 72.0;
   if (rotate == 90) {
     ctm[0] = 0;
-    ctm[1] = upsideDown ? k : -k;
-    ctm[2] = k;
+    ctm[1] = upsideDown ? ky : -ky;
+    ctm[2] = kx;
     ctm[3] = 0;
-    ctm[4] = -k * py1;
-    ctm[5] = k * (upsideDown ? -px1 : px2);
-    pageWidth = k * (py2 - py1);
-    pageHeight = k * (px2 - px1);
+    ctm[4] = -kx * py1;
+    ctm[5] = ky * (upsideDown ? -px1 : px2);
+    pageWidth = kx * (py2 - py1);
+    pageHeight = ky * (px2 - px1);
   } else if (rotate == 180) {
-    ctm[0] = -k;
+    ctm[0] = -kx;
     ctm[1] = 0;
     ctm[2] = 0;
-    ctm[3] = upsideDown ? k : -k;
-    ctm[4] = k * px2;
-    ctm[5] = k * (upsideDown ? -py1 : py2);
-    pageWidth = k * (px2 - px1);
-    pageHeight = k * (py2 - py1);
+    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 ? -k : k;
-    ctm[2] = -k;
+    ctm[1] = upsideDown ? -ky : ky;
+    ctm[2] = -kx;
     ctm[3] = 0;
-    ctm[4] = k * py2;
-    ctm[5] = k * (upsideDown ? px2 : -px1);
-    pageWidth = k * (py2 - py1);
-    pageHeight = k * (px2 - px1);
+    ctm[4] = kx * py2;
+    ctm[5] = ky * (upsideDown ? px2 : -px1);
+    pageWidth = kx * (py2 - py1);
+    pageHeight = ky * (px2 - px1);
   } else {
-    ctm[0] = k;
+    ctm[0] = kx;
     ctm[1] = 0;
     ctm[2] = 0;
-    ctm[3] = upsideDown ? -k : k;
-    ctm[4] = -k * px1;
-    ctm[5] = k * (upsideDown ? py2 : -py1);
-    pageWidth = k * (px2 - px1);
-    pageHeight = k * (py2 - py1);
+    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();
@@ -1990,7 +2431,7 @@ GfxState::GfxState(double dpi, PDFRectangle *pageBox, int rotate,
   lineDash = NULL;
   lineDashLength = 0;
   lineDashStart = 0;
-  flatness = 0;
+  flatness = 1;
   lineJoin = 0;
   lineCap = 0;
   miterLimit = 10;
@@ -2064,6 +2505,11 @@ GfxState::GfxState(GfxState *state) {
   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];
index b1f6f28..f747a83 100644 (file)
@@ -2,14 +2,16 @@
 //
 // GfxState.h
 //
-// Copyright 1996-2002 Glyph & Cog, LLC
+// Copyright 1996-2003 Glyph & Cog, LLC
 //
 //========================================================================
 
 #ifndef GFXSTATE_H
 #define GFXSTATE_H
 
-#ifdef __GNUC__
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
 #pragma interface
 #endif
 
@@ -19,7 +21,8 @@
 
 class Array;
 class GfxFont;
-struct PDFRectangle;
+class PDFRectangle;
+class GfxShading;
 
 //------------------------------------------------------------------------
 // GfxColor
@@ -51,6 +54,8 @@ struct GfxCMYK {
 // GfxColorSpace
 //------------------------------------------------------------------------
 
+// NB: The nGfxColorSpaceModes constant and the gfxColorSpaceModeNames
+// array defined in GfxState.cc must match this enum.
 enum GfxColorSpaceMode {
   csDeviceGray,
   csCalGray,
@@ -89,6 +94,12 @@ public:
   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:
 };
 
@@ -342,6 +353,7 @@ public:
   GfxColorSpace *getBase() { return base; }
   int getIndexHigh() { return indexHigh; }
   Guchar *getLookup() { return lookup; }
+  GfxColor *mapColorToBase(GfxColor *color, GfxColor *baseColor);
 
 private:
 
@@ -391,7 +403,7 @@ private:
 class GfxDeviceNColorSpace: public GfxColorSpace {
 public:
 
-  GfxDeviceNColorSpace(int nComps, GfxColorSpace *alt, Function *func);
+  GfxDeviceNColorSpace(int nCompsA, GfxColorSpace *alt, Function *func);
   virtual ~GfxDeviceNColorSpace();
   virtual GfxColorSpace *copy();
   virtual GfxColorSpaceMode getMode() { return csDeviceN; }
@@ -406,7 +418,9 @@ public:
   virtual int getNComps() { return nComps; }
 
   // DeviceN-specific access.
+  GString *getColorantName(int i) { return names[i]; }
   GfxColorSpace *getAlt() { return alt; }
+  Function *getTintTransformFunc() { return func; }
 
 private:
 
@@ -415,7 +429,6 @@ private:
     *names[gfxColorMaxComps];
   GfxColorSpace *alt;          // alternate color space
   Function *func;              // tint transform (into alternate color space)
-  
 };
 
 //------------------------------------------------------------------------
@@ -476,7 +489,7 @@ private:
 class GfxTilingPattern: public GfxPattern {
 public:
 
-  GfxTilingPattern(Dict *streamDict, Object *stream);
+  static GfxTilingPattern *parse(Object *patObj);
   virtual ~GfxTilingPattern();
 
   virtual GfxPattern *copy();
@@ -493,7 +506,10 @@ public:
 
 private:
 
-  GfxTilingPattern(GfxTilingPattern *pat);
+  GfxTilingPattern(int paintTypeA, int tilingTypeA,
+                  double *bboxA, double xStepA, double yStepA,
+                  Object *resDictA, double *matrixA,
+                  Object *contentStreamA);
 
   int paintType;
   int tilingType;
@@ -505,17 +521,43 @@ private:
 };
 
 //------------------------------------------------------------------------
+// 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();
+  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; }
@@ -524,7 +566,9 @@ public:
     { *xMinA = xMin; *yMinA = yMin; *xMaxA = xMax; *yMaxA = yMax; }
   GBool getHasBBox() { return hasBBox; }
 
-private:
+protected:
+
+  GBool init(Dict *dict);
 
   int type;
   GfxColorSpace *colorSpace;
@@ -535,6 +579,37 @@ private:
 };
 
 //------------------------------------------------------------------------
+// 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; }
+  void getColor(double x, double y, GfxColor *color);
+
+private:
+
+  double x0, y0, x1, y1;
+  double matrix[6];
+  Function *funcs[gfxColorMaxComps];
+  int nFuncs;
+};
+
+//------------------------------------------------------------------------
 // GfxAxialShading
 //------------------------------------------------------------------------
 
@@ -546,10 +621,13 @@ public:
                  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; }
@@ -579,10 +657,13 @@ public:
                   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; }
@@ -614,6 +695,9 @@ public:
   // Destructor.
   ~GfxImageColorMap();
 
+  // Return a copy of this color map.
+  GfxImageColorMap *copy() { return new GfxImageColorMap(this); }
+
   // Is color map valid?
   GBool isOk() { return ok; }
 
@@ -632,9 +716,12 @@ public:
   void getGray(Guchar *x, double *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
@@ -685,6 +772,9 @@ public:
   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
@@ -737,6 +827,12 @@ public:
   // 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
@@ -756,11 +852,11 @@ private:
 class GfxState {
 public:
 
-  // Construct a default GfxState, for a device with resolution <dpi>,
-  // page box <pageBox>, page rotation <rotate>, and coordinate system
-  // specified by <upsideDown>.
-  GfxState(double dpi, PDFRectangle *pageBox, int rotate,
-          GBool upsideDown);
+  // Construct a default GfxState, for a device with resolution <hDPI>
+  // x <vDPI>, page box <pageBox>, page rotation <rotate>, and
+  // coordinate system specified by <upsideDown>.
+  GfxState(double hDPI, double vDPI, PDFRectangle *pageBox,
+          int rotate, GBool upsideDown);
 
   // Destructor.
   ~GfxState();
@@ -781,7 +877,7 @@ public:
   void getFillGray(double *gray)
     { fillColorSpace->getGray(&fillColor, gray); }
   void getStrokeGray(double *gray)
-    { strokeColorSpace->getGray(&fillColor, gray); }
+    { strokeColorSpace->getGray(&strokeColor, gray); }
   void getFillRGB(GfxRGB *rgb)
     { fillColorSpace->getRGB(&fillColor, rgb); }
   void getStrokeRGB(GfxRGB *rgb)
@@ -813,6 +909,7 @@ public:
   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)
@@ -898,6 +995,7 @@ public:
   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);
index 0bc908e..c125430 100644 (file)
@@ -2,16 +2,18 @@
 //
 // GlobalParams.cc
 //
-// Copyright 2001-2002 Glyph & Cog, LLC
+// Copyright 2001-2003 Glyph & Cog, LLC
 //
 //========================================================================
 
-#ifdef __GNUC__
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
 #pragma implementation
 #endif
 
-#include <aconf.h>
 #include <string.h>
+#include <stdio.h>
 #include <ctype.h>
 #if HAVE_PAPER_H
 #include <paper.h>
 #include "FontEncodingTables.h"
 #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 "DisplayFontTable.h"
 #include "UTF8.h"
 
 //------------------------------------------------------------------------
 
+#define cidToUnicodeCacheSize     4
+#define unicodeToUnicodeCacheSize 4
+
+//------------------------------------------------------------------------
+
+static struct {
+  char *name;
+  char *fileName;
+} displayFontTab[] = {
+  {"Courier",               "n022003l.pfb"},
+  {"Courier-Bold",          "n022004l.pfb"},
+  {"Courier-BoldOblique",   "n022024l.pfb"},
+  {"Courier-Oblique",       "n022023l.pfb"},
+  {"Helvetica",             "n019003l.pfb"},
+  {"Helvetica-Bold",        "n019004l.pfb"},
+  {"Helvetica-BoldOblique", "n019024l.pfb"},
+  {"Helvetica-Oblique",     "n019023l.pfb"},
+  {"Symbol",                "s050000l.pfb"},
+  {"Times-Bold",            "n021004l.pfb"},
+  {"Times-BoldItalic",      "n021024l.pfb"},
+  {"Times-Italic",          "n021023l.pfb"},
+  {"Times-Roman",           "n021003l.pfb"},
+  {"ZapfDingbats",          "d050000l.pfb"},
+  {NULL}
+};
+
+static char *displayFontDirs[] = {
+  "/usr/share/ghostscript/fonts",
+  "/usr/local/share/ghostscript/fonts",
+  "/usr/share/fonts/default/Type1",
+  NULL
+};
+
+//------------------------------------------------------------------------
+
 GlobalParams *globalParams = NULL;
 
 //------------------------------------------------------------------------
@@ -48,10 +100,6 @@ DisplayFontParam::DisplayFontParam(GString *nameA,
   name = nameA;
   kind = kindA;
   switch (kind) {
-  case displayFontX:
-    x.xlfd = NULL;
-    x.encoding = NULL;
-    break;
   case displayFontT1:
     t1.fileName = NULL;
     break;
@@ -61,24 +109,9 @@ DisplayFontParam::DisplayFontParam(GString *nameA,
   }
 }
 
-DisplayFontParam::DisplayFontParam(char *nameA, char *xlfdA, char *encodingA) {
-  name = new GString(nameA);
-  kind = displayFontX;
-  x.xlfd = new GString(xlfdA);
-  x.encoding = new GString(encodingA);
-}
-
 DisplayFontParam::~DisplayFontParam() {
   delete name;
   switch (kind) {
-  case displayFontX:
-    if (x.xlfd) {
-      delete x.xlfd;
-    }
-    if (x.encoding) {
-      delete x.encoding;
-    }
-    break;
   case displayFontT1:
     if (t1.fileName) {
       delete t1.fileName;
@@ -118,11 +151,16 @@ PSFontParam::~PSFontParam() {
 
 GlobalParams::GlobalParams(char *cfgFileName) {
   UnicodeMap *map;
-  DisplayFontParam *dfp;
   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
@@ -136,6 +174,7 @@ GlobalParams::GlobalParams(char *cfgFileName) {
 
   nameToUnicode = new NameToCharCode();
   cidToUnicodes = new GHash(gTrue);
+  unicodeToUnicodes = new GHash(gTrue);
   residentUnicodeMaps = new GHash();
   unicodeMaps = new GHash(gTrue);
   cMapDirs = new GHash(gTrue);
@@ -144,16 +183,30 @@ GlobalParams::GlobalParams(char *cfgFileName) {
   displayCIDFonts = new GHash();
   displayNamedCIDFonts = new GHash();
 #if HAVE_PAPER_H
+  char *paperName;
   const struct paper *paperType;
   paperinit();
-  paperType = paperinfo(systempapername());
-  psPaperWidth = (int)paperpswidth(paperType);
-  psPaperHeight = (int)paperpsheight(paperType);
+  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;
@@ -174,15 +227,22 @@ GlobalParams::GlobalParams(char *cfgFileName) {
 #else
   textEOL = eolUnix;
 #endif
+  textPageBreaks = gTrue;
+  textKeepTinyChars = gFalse;
   fontDirs = new GList();
-  initialZoom = new GString("1");
-  t1libControl = fontRastAALow;
-  freetypeControl = fontRastAALow;
+  initialZoom = new GString("125");
+  enableT1lib = gTrue;
+  enableFreeType = gTrue;
+  antialias = gTrue;
   urlCommand = NULL;
+  movieCommand = NULL;
   mapNumericCharNames = gTrue;
+  printCommands = gFalse;
   errQuiet = gFalse;
 
-  cidToUnicodeCache = new CIDToUnicodeCache();
+  cidToUnicodeCache = new CharCodeToUnicodeCache(cidToUnicodeCacheSize);
+  unicodeToUnicodeCache =
+      new CharCodeToUnicodeCache(unicodeToUnicodeCacheSize);
   unicodeMapCache = new UnicodeMapCache();
   cMapCache = new CMapCache();
 
@@ -192,28 +252,23 @@ GlobalParams::GlobalParams(char *cfgFileName) {
   }
 
   // set up the residentUnicodeMaps table
-  map = new UnicodeMap("Latin1", latin1UnicodeMapRanges, latin1UnicodeMapLen);
+  map = new UnicodeMap("Latin1", gFalse,
+                      latin1UnicodeMapRanges, latin1UnicodeMapLen);
   residentUnicodeMaps->add(map->getEncodingName(), map);
-  map = new UnicodeMap("ASCII7", ascii7UnicodeMapRanges, ascii7UnicodeMapLen);
+  map = new UnicodeMap("ASCII7", gFalse,
+                      ascii7UnicodeMapRanges, ascii7UnicodeMapLen);
   residentUnicodeMaps->add(map->getEncodingName(), map);
-  map = new UnicodeMap("Symbol", symbolUnicodeMapRanges, symbolUnicodeMapLen);
+  map = new UnicodeMap("Symbol", gFalse,
+                      symbolUnicodeMapRanges, symbolUnicodeMapLen);
   residentUnicodeMaps->add(map->getEncodingName(), map);
-  map = new UnicodeMap("ZapfDingbats", zapfDingbatsUnicodeMapRanges,
+  map = new UnicodeMap("ZapfDingbats", gFalse, zapfDingbatsUnicodeMapRanges,
                       zapfDingbatsUnicodeMapLen);
   residentUnicodeMaps->add(map->getEncodingName(), map);
-  map = new UnicodeMap("UTF-8", &mapUTF8);
+  map = new UnicodeMap("UTF-8", gTrue, &mapUTF8);
   residentUnicodeMaps->add(map->getEncodingName(), map);
-  map = new UnicodeMap("UCS-2", &mapUCS2);
+  map = new UnicodeMap("UCS-2", gTrue, &mapUCS2);
   residentUnicodeMaps->add(map->getEncodingName(), map);
 
-  // default displayFonts table
-  for (i = 0; displayFontTab[i].name; ++i) {
-    dfp = new DisplayFontParam(displayFontTab[i].name,
-                              displayFontTab[i].xlfd,
-                              displayFontTab[i].encoding);
-    displayFonts->add(dfp->name, dfp);
-  }
-
   // look for a user config file, then a system-wide config file
   f = NULL;
   fileName = NULL;
@@ -235,7 +290,7 @@ GlobalParams::GlobalParams(char *cfgFileName) {
     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[i] = '\0';
+      buf[0] = '\0';
     }
     fileName = grabPath(buf);
     appendToPath(fileName, xpdfSysConfigFile);
@@ -249,6 +304,7 @@ GlobalParams::GlobalParams(char *cfgFileName) {
   if (f) {
     parseFile(fileName, f);
     delete fileName;
+    fclose(f);
   }
 }
 
@@ -261,7 +317,7 @@ void GlobalParams::parseFile(GString *fileName, FILE *f) {
   FILE *f2;
 
   line = 1;
-  while (fgets(buf, sizeof(buf) - 1, f)) {
+  while (getLine(buf, sizeof(buf) - 1, f)) {
 
     // break the line into tokens
     tokens = new GList();
@@ -278,7 +334,7 @@ void GlobalParams::parseFile(GString *fileName, FILE *f) {
        for (p2 = p1 + 1; *p2 && !isspace(*p2); ++p2) ;
       }
       tokens->append(new GString(p1, p2 - p1));
-      p1 = p2 + 1;
+      p1 = *p2 ? p2 + 1 : p2;
     }
 
     if (tokens->getLength() > 0 &&
@@ -302,24 +358,30 @@ void GlobalParams::parseFile(GString *fileName, FILE *f) {
        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("displayFontX")) {
-       parseDisplayFont(tokens, displayFonts, displayFontX, 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("displayCIDFontX")) {
+      } else if (!cmd->cmp("displayNamedCIDFontT1")) {
+       parseDisplayFont(tokens, displayNamedCIDFonts,
+                        displayFontT1, fileName, line);
+      } else if (!cmd->cmp("displayCIDFontT1")) {
        parseDisplayFont(tokens, displayCIDFonts,
-                        displayFontX, fileName, line);
-      } else if (!cmd->cmp("displayNamedCIDFontX")) {
+                        displayFontT1, fileName, line);
+      } else if (!cmd->cmp("displayNamedCIDFontTT")) {
        parseDisplayFont(tokens, displayNamedCIDFonts,
-                        displayFontX, fileName, line);
+                        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")) {
@@ -331,6 +393,17 @@ void GlobalParams::parseFile(GString *fileName, FILE *f) {
        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")) {
@@ -354,29 +427,46 @@ void GlobalParams::parseFile(GString *fileName, FILE *f) {
        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("t1libControl")) {
-       parseFontRastControl("t1libControl", &t1libControl,
-                            tokens, fileName, line);
-      } else if (!cmd->cmp("freetypeControl")) {
-       parseFontRastControl("freetypeControl", &freetypeControl,
-                            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")) {
-       parseURLCommand(tokens, fileName, line);
+       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 if (!cmd->cmp("fontpath") || !cmd->cmp("fontmap")) {
-       error(-1, "Unknown config file command");
-       error(-1, "-- the config file format has changed since Xpdf 0.9x");
       } 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");
+       }
       }
     }
 
@@ -406,7 +496,7 @@ void GlobalParams::parseNameToUnicode(GList *tokens, GString *fileName,
     return;
   }
   line2 = 1;
-  while (fgets(buf, sizeof(buf), f)) {
+  while (getLine(buf, sizeof(buf), f)) {
     tok1 = strtok(buf, " \t\r\n");
     tok2 = strtok(NULL, " \t\r\n");
     if (tok1 && tok2) {
@@ -437,6 +527,23 @@ void GlobalParams::parseCIDToUnicode(GList *tokens, GString *fileName,
   cidToUnicodes->add(collection->copy(), name->copy());
 }
 
+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(), file->copy());
+}
+
 void GlobalParams::parseUnicodeMap(GList *tokens, GString *fileName,
                                   int line) {
   GString *encodingName, *name, *old;
@@ -493,13 +600,6 @@ void GlobalParams::parseDisplayFont(GList *tokens, GHash *fontHash,
   param = new DisplayFontParam(((GString *)tokens->get(1))->copy(), kind);
   
   switch (kind) {
-  case displayFontX:
-    if (tokens->getLength() != 4) {
-      goto err2;
-    }
-    param->x.xlfd = ((GString *)tokens->get(2))->copy();
-    param->x.encoding = ((GString *)tokens->get(3))->copy();
-    break;
   case displayFontT1:
     if (tokens->getLength() != 3) {
       goto err2;
@@ -542,12 +642,28 @@ void GlobalParams::parsePSPaperSize(GList *tokens, GString *fileName,
     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;
 
@@ -680,34 +796,17 @@ void GlobalParams::parseInitialZoom(GList *tokens,
   initialZoom = ((GString *)tokens->get(1))->copy();
 }
 
-void GlobalParams::parseFontRastControl(char *cmdName, FontRastControl *val,
-                                       GList *tokens, GString *fileName,
-                                       int line) {
-  GString *tok;
-
+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;
   }
-  tok = (GString *)tokens->get(1);
-  if (!setFontRastControl(val, tok->getCString())) {
-    error(-1, "Bad '%s' config file command (%s:%d)",
-         cmdName, fileName->getCString(), line);
+  if (*val) {
+    delete *val;
   }
-}
-
-void GlobalParams::parseURLCommand(GList *tokens, GString *fileName,
-                                  int line) {
-  if (tokens->getLength() != 2) {
-    error(-1, "Bad 'urlCommand' config file command (%s:%d)",
-         fileName->getCString(), line);
-    return;
-  }
-  if (urlCommand) {
-    delete urlCommand;
-  }
-  urlCommand = ((GString *)tokens->get(1))->copy();
+  *val = ((GString *)tokens->get(1))->copy();
 }
 
 void GlobalParams::parseYesNo(char *cmdName, GBool *flag,
@@ -720,14 +819,21 @@ void GlobalParams::parseYesNo(char *cmdName, GBool *flag,
     return;
   }
   tok = (GString *)tokens->get(1);
-  if (!tok->cmp("yes")) {
+  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 (!tok->cmp("no")) {
+  } else if (!strcmp(token, "no")) {
     *flag = gFalse;
   } else {
-    error(-1, "Bad '%s' config file command (%s:%d)",
-         cmdName, fileName->getCString(), line);
+    return gFalse;
   }
+  return gTrue;
 }
 
 GlobalParams::~GlobalParams() {
@@ -741,6 +847,7 @@ GlobalParams::~GlobalParams() {
 
   delete nameToUnicode;
   deleteGHash(cidToUnicodes, GString);
+  deleteGHash(unicodeToUnicodes, GString);
   deleteGHash(residentUnicodeMaps, UnicodeMap);
   deleteGHash(unicodeMaps, GString);
   deleteGList(toUnicodeDirs, GString);
@@ -759,6 +866,9 @@ GlobalParams::~GlobalParams() {
   if (urlCommand) {
     delete urlCommand;
   }
+  if (movieCommand) {
+    delete movieCommand;
+  }
 
   cMapDirs->startIter(&iter);
   while (cMapDirs->getNext(&iter, &key, (void **)&list)) {
@@ -767,8 +877,63 @@ GlobalParams::~GlobalParams() {
   delete cMapDirs;
 
   delete cidToUnicodeCache;
+  delete unicodeToUnicodeCache;
   delete unicodeMapCache;
   delete cMapCache;
+
+#if MULTITHREADED
+  gDestroyMutex(&mutex);
+  gDestroyMutex(&unicodeMapCacheMutex);
+  gDestroyMutex(&cMapCacheMutex);
+#endif
+}
+
+//------------------------------------------------------------------------
+
+void GlobalParams::setupBaseFonts(char *dir) {
+  GString *fontName;
+  GString *fileName;
+  FILE *f;
+  DisplayFontParam *dfp;
+  int i, j;
+
+  for (i = 0; displayFontTab[i].name; ++i) {
+    fontName = new GString(displayFontTab[i].name);
+    if (getDisplayFont(fontName)) {
+      delete fontName;
+      continue;
+    }
+    fileName = NULL;
+    if (dir) {
+      fileName = appendToPath(new GString(dir), displayFontTab[i].fileName);
+      if ((f = fopen(fileName->getCString(), "rb"))) {
+       fclose(f);
+      } else {
+       delete fileName;
+       fileName = NULL;
+      }
+    }
+#ifndef WIN32
+    for (j = 0; !fileName && displayFontDirs[j]; ++j) {
+      fileName = appendToPath(new GString(displayFontDirs[j]),
+                             displayFontTab[i].fileName);
+      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, displayFontT1);
+    dfp->t1.fileName = fileName;
+    globalParams->addDisplayFont(dfp);
+  }
 }
 
 //------------------------------------------------------------------------
@@ -776,33 +941,39 @@ GlobalParams::~GlobalParams() {
 //------------------------------------------------------------------------
 
 CharCode GlobalParams::getMacRomanCharCode(char *charName) {
+  // no need to lock - macRomanReverseMap is constant
   return macRomanReverseMap->lookup(charName);
 }
 
 Unicode GlobalParams::mapNameToUnicode(char *charName) {
+  // no need to lock - nameToUnicode is constant
   return nameToUnicode->lookup(charName);
 }
 
-FILE *GlobalParams::getCIDToUnicodeFile(GString *collection) {
-  GString *fileName;
+UnicodeMap *GlobalParams::getResidentUnicodeMap(GString *encodingName) {
+  UnicodeMap *map;
 
-  if (!(fileName = (GString *)cidToUnicodes->lookup(collection))) {
-    return NULL;
+  lockGlobalParams;
+  map = (UnicodeMap *)residentUnicodeMaps->lookup(encodingName);
+  unlockGlobalParams;
+  if (map) {
+    map->incRefCnt();
   }
-  return fopen(fileName->getCString(), "r");
-}
-
-UnicodeMap *GlobalParams::getResidentUnicodeMap(GString *encodingName) {
-  return (UnicodeMap *)residentUnicodeMaps->lookup(encodingName);
+  return map;
 }
 
 FILE *GlobalParams::getUnicodeMapFile(GString *encodingName) {
   GString *fileName;
+  FILE *f;
 
-  if (!(fileName = (GString *)unicodeMaps->lookup(encodingName))) {
-    return NULL;
+  lockGlobalParams;
+  if ((fileName = (GString *)unicodeMaps->lookup(encodingName))) {
+    f = fopen(fileName->getCString(), "r");
+  } else {
+    f = NULL;
   }
-  return fopen(fileName->getCString(), "r");
+  unlockGlobalParams;
+  return f;
 }
 
 FILE *GlobalParams::findCMapFile(GString *collection, GString *cMapName) {
@@ -812,7 +983,9 @@ FILE *GlobalParams::findCMapFile(GString *collection, GString *cMapName) {
   FILE *f;
   int i;
 
+  lockGlobalParams;
   if (!(list = (GList *)cMapDirs->lookup(collection))) {
+    unlockGlobalParams;
     return NULL;
   }
   for (i = 0; i < list->getLength(); ++i) {
@@ -821,9 +994,11 @@ FILE *GlobalParams::findCMapFile(GString *collection, GString *cMapName) {
     f = fopen(fileName->getCString(), "r");
     delete fileName;
     if (f) {
+      unlockGlobalParams;
       return f;
     }
   }
+  unlockGlobalParams;
   return NULL;
 }
 
@@ -832,35 +1007,140 @@ FILE *GlobalParams::findToUnicodeFile(GString *name) {
   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) {
-  return (DisplayFontParam *)displayFonts->lookup(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) {
-  return (PSFontParam *)psFonts->lookup(fontName);
+  PSFontParam *p;
+
+  lockGlobalParams;
+  p = (PSFontParam *)psFonts->lookup(fontName);
+  unlockGlobalParams;
+  return p;
 }
 
 PSFontParam *GlobalParams::getPSFont16(GString *fontName,
@@ -868,6 +1148,7 @@ PSFontParam *GlobalParams::getPSFont16(GString *fontName,
   PSFontParam *p;
   int i;
 
+  lockGlobalParams;
   p = NULL;
   if (fontName) {
     for (i = 0; i < psNamedFonts16->getLength(); ++i) {
@@ -889,74 +1170,288 @@ PSFontParam *GlobalParams::getPSFont16(GString *fontName,
       p = NULL;
     }
   }
+  unlockGlobalParams;
   return p;
 }
 
-GString *GlobalParams::findFontFile(GString *fontName,
-                                   char *ext1, char *ext2) {
+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);
-    if (ext1) {
-      fileName = appendToPath(dir->copy(), fontName->getCString());
-      fileName->append(ext1);
-      if ((f = fopen(fileName->getCString(), "r"))) {
-       fclose(f);
-       return fileName;
-      }
-      delete fileName;
-    }
-    if (ext2) {
+    for (ext = exts; *ext; ++ext) {
       fileName = appendToPath(dir->copy(), fontName->getCString());
-      fileName->append(ext2);
-      if ((f = fopen(fileName->getCString(), "r"))) {
+      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::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) {
-  return cidToUnicodeCache->getCIDToUnicode(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))) {
-    map->incRefCnt();
-    return map;
+  if (!(map = getResidentUnicodeMap(encodingName))) {
+    lockUnicodeMapCache;
+    map = unicodeMapCache->getUnicodeMap(encodingName);
+    unlockUnicodeMapCache;
   }
-  return unicodeMapCache->getUnicodeMap(encodingName);
+  return map;
 }
 
 CMap *GlobalParams::getCMap(GString *collection, GString *cMapName) {
-  return cMapCache->getCMap(collection, cMapName);
+  CMap *cMap;
+
+  lockCMapCache;
+  cMap = cMapCache->getCMap(collection, cMapName);
+  unlockCMapCache;
+  return cMap;
 }
 
 UnicodeMap *GlobalParams::getTextEncoding() {
-  return getUnicodeMap(textEncoding);
+  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) {
-  if (!strcmp(size, "letter")) {
+  lockGlobalParams;
+  if (!strcmp(size, "match")) {
+    psPaperWidth = psPaperHeight = -1;
+  } else if (!strcmp(size, "letter")) {
     psPaperWidth = 612;
     psPaperHeight = 792;
   } else if (!strcmp(size, "legal")) {
@@ -969,57 +1464,122 @@ GBool GlobalParams::setPSPaperSize(char *size) {
     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")) {
@@ -1027,39 +1587,74 @@ GBool GlobalParams::setTextEOL(char *s) {
   } 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;
 }
 
-GBool GlobalParams::setT1libControl(char *s) {
-  return setFontRastControl(&t1libControl, s);
+GBool GlobalParams::setEnableT1lib(char *s) {
+  GBool ok;
+
+  lockGlobalParams;
+  ok = parseYesNo2(s, &enableT1lib);
+  unlockGlobalParams;
+  return ok;
 }
 
-GBool GlobalParams::setFreeTypeControl(char *s) {
-  return setFontRastControl(&freetypeControl, s);
+GBool GlobalParams::setEnableFreeType(char *s) {
+  GBool ok;
+
+  lockGlobalParams;
+  ok = parseYesNo2(s, &enableFreeType);
+  unlockGlobalParams;
+  return ok;
 }
 
-GBool GlobalParams::setFontRastControl(FontRastControl *val, char *s) {
-  if (!strcmp(s, "none")) {
-    *val = fontRastNone;
-  } else if (!strcmp(s, "plain")) {
-    *val = fontRastPlain;
-  } else if (!strcmp(s, "low")) {
-    *val = fontRastAALow;
-  } else if (!strcmp(s, "high")) {
-    *val = fontRastAAHigh;
-  } else {
-    return gFalse;
-  }
-  return gTrue;
+
+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;
 }
index b651110..93ec06a 100644 (file)
@@ -2,14 +2,16 @@
 //
 // GlobalParams.h
 //
-// Copyright 2001-2002 Glyph & Cog, LLC
+// Copyright 2001-2003 Glyph & Cog, LLC
 //
 //========================================================================
 
 #ifndef GLOBALPARAMS_H
 #define GLOBALPARAMS_H
 
-#ifdef __GNUC__
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
 #pragma interface
 #endif
 
 #include "gtypes.h"
 #include "CharTypes.h"
 
+#if MULTITHREADED
+#include "GMutex.h"
+#endif
+
 class GString;
 class GList;
 class GHash;
 class NameToCharCode;
 class CharCodeToUnicode;
-class CIDToUnicodeCache;
+class CharCodeToUnicodeCache;
 class UnicodeMap;
 class UnicodeMapCache;
 class CMap;
@@ -37,7 +43,6 @@ extern GlobalParams *globalParams;
 //------------------------------------------------------------------------
 
 enum DisplayFontParamKind {
-  displayFontX,
   displayFontT1,
   displayFontTT
 };
@@ -51,10 +56,6 @@ public:
   DisplayFontParamKind kind;
   union {
     struct {
-      GString *xlfd;
-      GString *encoding;
-    } x;
-    struct {
       GString *fileName;
     } t1;
     struct {
@@ -63,18 +64,9 @@ public:
   };
 
   DisplayFontParam(GString *nameA, DisplayFontParamKind kindA);
-  DisplayFontParam(char *nameA, char *xlfdA, char *encodingA);
   ~DisplayFontParam();
 };
 
-// Font rasterizer control.
-enum FontRastControl {
-  fontRastNone,                        // don't use this rasterizer
-  fontRastPlain,               // use it, without anti-aliasing
-  fontRastAALow,               // use it, with low-level anti-aliasing
-  fontRastAAHigh               // use it, with high-level anti-aliasing
-};
-
 //------------------------------------------------------------------------
 
 class PSFontParam {
@@ -123,53 +115,71 @@ public:
 
   ~GlobalParams();
 
+  void setupBaseFonts(char *dir);
+
   //----- accessors
 
   CharCode getMacRomanCharCode(char *charName);
 
   Unicode mapNameToUnicode(char *charName);
-  FILE *getCIDToUnicodeFile(GString *collection);
   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() { return psFile; }
-  int getPSPaperWidth() { return psPaperWidth; }
-  int getPSPaperHeight() { return psPaperHeight; }
-  GBool getPSDuplex() { return psDuplex; }
-  PSLevel getPSLevel() { return psLevel; }
+  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() { return psEmbedType1; }
-  GBool getPSEmbedTrueType() { return psEmbedTrueType; }
-  GBool getPSEmbedCIDPostScript() { return psEmbedCIDPostScript; }
-  GBool getPSEmbedCIDTrueType() { return psEmbedCIDTrueType; }
-  GBool getPSOPI() { return psOPI; }
-  GBool getPSASCIIHex() { return psASCIIHex; }
-  GString *getTextEncodingName() { return textEncoding; }
-  EndOfLineKind getTextEOL() { return textEOL; }
-  GString *findFontFile(GString *fontName, char *ext1, char *ext2);
-  GString *getInitialZoom() { return initialZoom; }
-  FontRastControl getT1libControl() { return t1libControl; }
-  FontRastControl getFreeTypeControl() { return freetypeControl; }
+  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 getEnableT1lib();
+  GBool getEnableFreeType();
+  GBool getAntialias();
   GString *getURLCommand() { return urlCommand; }
-  GBool getMapNumericCharNames() { return mapNumericCharNames; }
-  GBool getErrQuiet() { return errQuiet; }
+  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);
@@ -179,9 +189,14 @@ public:
   void setPSASCIIHex(GBool hex);
   void setTextEncoding(char *encodingName);
   GBool setTextEOL(char *s);
+  void setTextPageBreaks(GBool pageBreaks);
+  void setTextKeepTinyChars(GBool keep);
   void setInitialZoom(char *s);
-  GBool setT1libControl(char *s);
-  GBool setFreeTypeControl(char *s);
+  GBool setEnableT1lib(char *s);
+  GBool setEnableFreeType(char *s);
+  GBool setAntialias(char *s);
+  void setMapNumericCharNames(GBool map);
+  void setPrintCommands(GBool printCommandsA);
   void setErrQuiet(GBool errQuietA);
 
 private:
@@ -189,6 +204,7 @@ private:
   void parseFile(GString *fileName, FILE *f);
   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);
@@ -197,6 +213,7 @@ private:
                        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,
@@ -205,12 +222,12 @@ private:
   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 parseFontRastControl(char *cmdName, FontRastControl *val,
-                           GList *tokens, GString *fileName, int line);
-  void parseURLCommand(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 setFontRastControl(FontRastControl *val, char *s);
+  GBool parseYesNo2(char *token, GBool *flag);
+  UnicodeMap *getUnicodeMap2(GString *encodingName);
 
   //----- static tables
 
@@ -224,6 +241,8 @@ private:
   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
@@ -240,6 +259,14 @@ private:
   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
@@ -256,18 +283,29 @@ private:
                                //   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
-  FontRastControl t1libControl;        // t1lib rasterization mode
-  FontRastControl              // FreeType rasterization mode
-    freetypeControl;
+  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?
 
-  CIDToUnicodeCache *cidToUnicodeCache;
+  CharCodeToUnicodeCache *cidToUnicodeCache;
+  CharCodeToUnicodeCache *unicodeToUnicodeCache;
   UnicodeMapCache *unicodeMapCache;
   CMapCache *cMapCache;
+
+#if MULTITHREADED
+  GMutex mutex;
+  GMutex unicodeMapCacheMutex;
+  GMutex cMapCacheMutex;
+#endif
 };
 
 #endif
diff --git a/pdf2swf/xpdf/JArithmeticDecoder.cc b/pdf2swf/xpdf/JArithmeticDecoder.cc
new file mode 100644 (file)
index 0000000..fd29744
--- /dev/null
@@ -0,0 +1,300 @@
+//========================================================================
+//
+// 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 *)gmalloc(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;
+}
+
+JArithmeticDecoder::~JArithmeticDecoder() {
+  while (dataLen > 0) {
+    readByte();
+  }
+}
+
+inline Guint JArithmeticDecoder::readByte() {
+  if (dataLen == 0) {
+    return 0xff;
+  }
+  if (dataLen > 0) {
+    --dataLen;
+  }
+  return (Guint)str->getChar() & 0xff;
+}
+
+void JArithmeticDecoder::start() {
+  buf0 = readByte();
+  buf1 = readByte();
+
+  // INITDEC
+  c = (buf0 ^ 0xff) << 16;
+  byteIn();
+  c <<= 7;
+  ct -= 7;
+  a = 0x80000000;
+}
+
+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/pdf2swf/xpdf/JArithmeticDecoder.h b/pdf2swf/xpdf/JArithmeticDecoder.h
new file mode 100644 (file)
index 0000000..a348017
--- /dev/null
@@ -0,0 +1,91 @@
+//========================================================================
+//
+// 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 = -1; }
+  void setStream(Stream *strA, int dataLenA)
+    { str = strA; dataLen = dataLenA; }
+  void start();
+  int decodeBit(Guint context, JArithmeticDecoderStats *stats);
+  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;
+};
+
+#endif
diff --git a/pdf2swf/xpdf/JBIG2Stream.cc b/pdf2swf/xpdf/JBIG2Stream.cc
new file mode 100644 (file)
index 0000000..c1bf4f7
--- /dev/null
@@ -0,0 +1,3337 @@
+//========================================================================
+//
+// 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;
+  data = (Guchar *)gmalloc(h * line);
+}
+
+JBIG2Bitmap::JBIG2Bitmap(Guint segNumA, JBIG2Bitmap *bitmap):
+  JBIG2Segment(segNumA)
+{
+  w = bitmap->w;
+  h = bitmap->h;
+  line = bitmap->line;
+  data = (Guchar *)gmalloc(h * line);
+  memcpy(data, bitmap->data, h * line);
+}
+
+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;
+  }
+  data = (Guchar *)grealloc(data, newH * line);
+  if (pixel) {
+    memset(data + h * line, 0xff, (newH - h) * line);
+  } else {
+    memset(data + h * line, 0x00, (newH - h) * line);
+  }
+  h = newH;
+}
+
+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
+      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 **)gmalloc(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 **)gmalloc(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 *)gmalloc(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:
+      readSymbolDictSeg(segNum, segLength, refSegs, nRefSegs);
+      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;
+
+ eofError2:
+  gfree(refSegs);
+ eofError1:
+  error(getPos(), "Unexpected EOF in JBIG2 stream");
+}
+
+void 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 **)gmalloc((numInputSyms + numNewSyms) *
+                                   sizeof(JBIG2Bitmap *));
+  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 *)gmalloc(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);
+    }
+    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;
+       }
+      }
+      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;
+
+ eofError:
+  error(getPos(), "Unexpected EOF in JBIG2 stream");
+}
+
+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) {
+    seg = findSegment(refSegs[i]);
+    if (seg->getType() == jbig2SegSymbolDict) {
+      numSyms += ((JBIG2SymbolDict *)seg)->getSize();
+    } else if (seg->getType() == jbig2SegCodeTable) {
+      codeTables->append(seg);
+    }
+  }
+  symCodeLen = 0;
+  i = 1;
+  while (i < numSyms) {
+    ++symCodeLen;
+    i <<= 1;
+  }
+
+  // get the symbol bitmaps
+  syms = (JBIG2Bitmap **)gmalloc(numSyms * sizeof(JBIG2Bitmap *));
+  kk = 0;
+  for (i = 0; i < nRefSegs; ++i) {
+    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 *)gmalloc((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);
+      }
+
+      // 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 *)gmalloc(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 *)gmalloc((w + 2) * sizeof(int));
+    codingLine = (int *)gmalloc((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 *)
+                gmalloc(huffTabSize * sizeof(JBIG2HuffmanTable));
+  i = 0;
+  val = lowVal;
+  while (val < highVal) {
+    if (i == huffTabSize) {
+      huffTabSize *= 2;
+      huffTab = (JBIG2HuffmanTable *)
+                   grealloc(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 *)
+                  grealloc(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/pdf2swf/xpdf/JBIG2Stream.h b/pdf2swf/xpdf/JBIG2Stream.h
new file mode 100644 (file)
index 0000000..ed26d4e
--- /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();
+  void 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/pdf2swf/xpdf/JPXStream.cc b/pdf2swf/xpdf/JPXStream.cc
new file mode 100644 (file)
index 0000000..defa7d2
--- /dev/null
@@ -0,0 +1,2822 @@
+//========================================================================
+//
+// 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->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);
+}
+
+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 *)gmalloc(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 *)gmalloc(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 *)gmalloc(palette.nComps * sizeof(Guint));
+      palette.c =
+          (int *)gmalloc(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 *)gmalloc(compMap.nChannels * sizeof(Guint));
+      compMap.type = (Guint *)gmalloc(compMap.nChannels * sizeof(Guint));
+      compMap.pComp = (Guint *)gmalloc(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 *)gmalloc(channelDefn.nChannels * sizeof(Guint));
+      channelDefn.type =
+         (Guint *)gmalloc(channelDefn.nChannels * sizeof(Guint));
+      channelDefn.assoc =
+         (Guint *)gmalloc(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");
+       return gFalse;
+      }
+      if (!haveCS) {
+       error(getPos(), "JPX stream has no supported color spec");
+       return gFalse;
+      }
+      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 == 3 + 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 == 3) {
+       //~ 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, 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;
+      img.tiles = (JPXTile *)gmalloc(img.nXTiles * img.nYTiles *
+                                    sizeof(JPXTile));
+      for (i = 0; i < img.nXTiles * img.nYTiles; ++i) {
+       img.tiles[i].tileComps = (JPXTileComp *)gmalloc(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 *)gmalloc(
+                    (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 *)grealloc(
+                    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 *)grealloc(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 *)grealloc(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 *)grealloc(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 *)grealloc(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 *)grealloc(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 *)grealloc(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 *)grealloc(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 *)grealloc(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 *)gmalloc(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 *)grealloc(
+                    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 *)grealloc(
+                    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 *)grealloc(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 *)grealloc(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 *)grealloc(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 *)grealloc(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 *)grealloc(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 *)grealloc(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 *)grealloc(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 *)gmalloc(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 *)gmalloc((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 *)gmalloc((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 *)gmalloc(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 *)gmalloc(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 *)gmalloc(n * sizeof(JPXTagTreeNode));
+           subband->zeroBitPlane =
+               (JPXTagTreeNode *)gmalloc(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 *)gmalloc(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 *)gmalloc((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->stats = new JArithmeticDecoderStats(jpxNContexts);
+               cb->stats->setEntry(jpxContextSigProp, 4, 0);
+               cb->stats->setEntry(jpxContextRunLength, 3, 0);
+               cb->stats->setEntry(jpxContextUniform, 46, 0);
+               ++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;
+  JArithmeticDecoder *arithDecoder;
+  Guint horiz, vert, diag, all, cx, xorBit;
+  int horizSign, vertSign;
+  Guint i, x, y0, y1, y2;
+
+  arithDecoder = new JArithmeticDecoder();
+  arithDecoder->setStream(str, cb->dataLen);
+  arithDecoder->start();
+
+  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[-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[-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[-tileComp->cbW].flags & jpxCoeffSignificant) {
+                 ++vert;
+                 vertSign += (coeff[-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 (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 (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[-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[-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[-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) |
+                          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[-tileComp->cbW - 1].flags
+                & jpxCoeffSignificant)) &&
+             (y0 == cb->y0 ||
+              !(coeff1[-tileComp->cbW].flags & jpxCoeffSignificant)) &&
+             (x == cb->x1 - 1 || y0 == cb->y0 ||
+              !(coeff1[-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 (arithDecoder->decodeBit(jpxContextRunLength, cb->stats)) {
+             y1 = arithDecoder->decodeBit(jpxContextUniform, cb->stats);
+             y1 = (y1 << 1) |
+                  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 (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[-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[-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[-tileComp->cbW].flags & jpxCoeffSignificant) {
+                 ++vert;
+                 vertSign += (coeff[-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 (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 (arithDecoder->decodeBit(cx, cb->stats) ^ xorBit) {
+                 coeff->flags |= jpxCoeffSign;
+               }
+             }
+             ++coeff->len;
+           } else {
+             coeff->flags &= ~jpxCoeffTouched;
+           }
+         }
+       }
+      }
+      cb->nextPass = jpxPassSigProp;
+      break;
+    }
+  }
+
+  delete arithDecoder;
+  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, 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, shift2, t;
+  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, 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[0].data[j] = d0 - ((d2 + d1) >> 2);
+         tile->tileComps[1].data[j] = d2 - d1;
+         tile->tileComps[2].data[j] = d0 - d1;
+         ++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/pdf2swf/xpdf/JPXStream.h b/pdf2swf/xpdf/JPXStream.h
new file mode 100644 (file)
index 0000000..eb84fe6
--- /dev/null
@@ -0,0 +1,340 @@
+//========================================================================
+//
+// 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 JPXColorSpec {
+  Guint meth;                  // method
+  int prec;                    // precedence
+  union {
+    struct {
+      JPXColorSpaceType type;  // color space type
+      union {
+       struct {
+         Guint rl, ol, ra, oa, rb, ob, il;
+       } cieLab;
+      };
+    } 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
+  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);
+
+private:
+
+  void fillReadBuf();
+  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
index d037469..1fa166f 100644 (file)
@@ -2,15 +2,16 @@
 //
 // Lexer.cc
 //
-// Copyright 1996-2002 Glyph & Cog, LLC
+// Copyright 1996-2003 Glyph & Cog, LLC
 //
 //========================================================================
 
-#ifdef __GNUC__
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
 #pragma implementation
 #endif
 
-#include <aconf.h>
 #include <stdlib.h>
 #include <stddef.h>
 #include <string.h>
index 8a01ab2..398d27c 100644 (file)
@@ -2,14 +2,16 @@
 //
 // Lexer.h
 //
-// Copyright 1996-2002 Glyph & Cog, LLC
+// Copyright 1996-2003 Glyph & Cog, LLC
 //
 //========================================================================
 
 #ifndef LEXER_H
 #define LEXER_H
 
-#ifdef __GNUC__
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
 #pragma interface
 #endif
 
index af64c8b..2d146b5 100644 (file)
@@ -2,15 +2,16 @@
 //
 // Link.cc
 //
-// Copyright 1996-2002 Glyph & Cog, LLC
+// Copyright 1996-2003 Glyph & Cog, LLC
 //
 //========================================================================
 
-#ifdef __GNUC__
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
 #pragma implementation
 #endif
 
-#include <aconf.h>
 #include <stddef.h>
 #include <string.h>
 #include "gmem.h"
 #include "Link.h"
 
 //------------------------------------------------------------------------
+// LinkAction
+//------------------------------------------------------------------------
+
+LinkAction *LinkAction::parseDest(Object *obj) {
+  LinkAction *action;
+
+  action = new LinkGoTo(obj);
+  if (!action->isOk()) {
+    delete action;
+    return NULL;
+  }
+  return action;
+}
+
+LinkAction *LinkAction::parseAction(Object *obj, GString *baseURI) {
+  LinkAction *action;
+  Object obj2, obj3, obj4;
+
+  if (!obj->isDict()) {
+    error(-1, "Bad annotation action");
+    return NULL;
+  }
+
+  obj->dictLookup("S", &obj2);
+
+  // GoTo action
+  if (obj2.isName("GoTo")) {
+    obj->dictLookup("D", &obj3);
+    action = new LinkGoTo(&obj3);
+    obj3.free();
+
+  // GoToR action
+  } else if (obj2.isName("GoToR")) {
+    obj->dictLookup("F", &obj3);
+    obj->dictLookup("D", &obj4);
+    action = new LinkGoToR(&obj3, &obj4);
+    obj3.free();
+    obj4.free();
+
+  // Launch action
+  } else if (obj2.isName("Launch")) {
+    action = new LinkLaunch(obj);
+
+  // URI action
+  } else if (obj2.isName("URI")) {
+    obj->dictLookup("URI", &obj3);
+    action = new LinkURI(&obj3, baseURI);
+    obj3.free();
+
+  // Named action
+  } else if (obj2.isName("Named")) {
+    obj->dictLookup("N", &obj3);
+    action = new LinkNamed(&obj3);
+    obj3.free();
+
+  // Movie action
+  } else if (obj2.isName("Movie")) {
+    obj->dictLookupNF("Annot", &obj3);
+    obj->dictLookup("T", &obj4);
+    action = new LinkMovie(&obj3, &obj4);
+    obj3.free();
+    obj4.free();
+
+  // unknown action
+  } else if (obj2.isName()) {
+    action = new LinkUnknown(obj2.getName());
+
+  // action is missing or wrong type
+  } else {
+    error(-1, "Bad annotation action");
+    action = NULL;
+  }
+
+  obj2.free();
+
+  if (action && !action->isOk()) {
+    delete action;
+    return NULL;
+  }
+  return action;
+}
+
+GString *LinkAction::getFileSpecName(Object *fileSpecObj) {
+  GString *name;
+  Object obj1;
+
+  name = NULL;
+
+  // string
+  if (fileSpecObj->isString()) {
+    name = fileSpecObj->getString()->copy();
+
+  // dictionary
+  } else if (fileSpecObj->isDict()) {
+    if (!fileSpecObj->dictLookup("Unix", &obj1)->isString()) {
+      obj1.free();
+      fileSpecObj->dictLookup("F", &obj1);
+    }
+    if (obj1.isString())
+      name = obj1.getString()->copy();
+    else
+      error(-1, "Illegal file spec in link");
+    obj1.free();
+
+  // error
+  } else {
+    error(-1, "Illegal file spec in link");
+  }
 
-static GString *getFileSpecName(Object *fileSpecObj);
+  return name;
+}
 
 //------------------------------------------------------------------------
 // LinkDest
@@ -37,6 +147,10 @@ LinkDest::LinkDest(Array *a) {
   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;
@@ -57,46 +171,66 @@ LinkDest::LinkDest(Array *a) {
   // XYZ link
   if (obj1.isName("XYZ")) {
     kind = destXYZ;
-    a->get(2, &obj2);
-    if (obj2.isNull()) {
+    if (a->getLength() < 3) {
       changeLeft = gFalse;
-    } else if (obj2.isNum()) {
-      changeLeft = gTrue;
-      left = obj2.getNum();
     } else {
-      error(-1, "Bad annotation destination position");
-      goto err1;
+      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();
     }
-    obj2.free();
-    a->get(3, &obj2);
-    if (obj2.isNull()) {
+    if (a->getLength() < 4) {
       changeTop = gFalse;
-    } else if (obj2.isNum()) {
-      changeTop = gTrue;
-      top = obj2.getNum();
     } else {
-      error(-1, "Bad annotation destination position");
-      goto err1;
+      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();
     }
-    obj2.free();
-    a->get(4, &obj2);
-    if (obj2.isNull()) {
+    if (a->getLength() < 5) {
       changeZoom = gFalse;
-    } else if (obj2.isNum()) {
-      changeZoom = gTrue;
-      zoom = obj2.getNum();
     } else {
-      error(-1, "Bad annotation destination position");
-      goto err1;
+      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();
     }
-    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");
@@ -107,6 +241,10 @@ LinkDest::LinkDest(Array *a) {
 
   // 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");
@@ -117,6 +255,10 @@ LinkDest::LinkDest(Array *a) {
 
   // 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");
@@ -145,10 +287,18 @@ LinkDest::LinkDest(Array *a) {
 
   // 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");
@@ -159,6 +309,10 @@ LinkDest::LinkDest(Array *a) {
 
   // 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");
@@ -292,18 +446,33 @@ LinkLaunch::LinkLaunch(Object *actionObj) {
       fileName = getFileSpecName(&obj1);
     } else {
       obj1.free();
+#ifdef WIN32
+      if (actionObj->dictLookup("Win", &obj1)->isDict()) {
+       obj1.dictLookup("F", &obj2);
+       fileName = getFileSpecName(&obj2);
+       obj2.free();
+       if (obj1.dictLookup("P", &obj2)->isString()) {
+         params = obj2.getString()->copy();
+       }
+       obj2.free();
+      } else {
+       error(-1, "Bad launch-type link action");
+      }
+#else
       //~ This hasn't been defined by Adobe yet, so assume it looks
       //~ just like the Win dictionary until they say otherwise.
       if (actionObj->dictLookup("Unix", &obj1)->isDict()) {
        obj1.dictLookup("F", &obj2);
        fileName = getFileSpecName(&obj2);
        obj2.free();
-       if (obj1.dictLookup("P", &obj2)->isString())
+       if (obj1.dictLookup("P", &obj2)->isString()) {
          params = obj2.getString()->copy();
+       }
        obj2.free();
       } else {
        error(-1, "Bad launch-type link action");
       }
+#endif
     }
     obj1.free();
   }
@@ -378,6 +547,28 @@ LinkNamed::~LinkNamed() {
 }
 
 //------------------------------------------------------------------------
+// LinkMovie
+//------------------------------------------------------------------------
+
+LinkMovie::LinkMovie(Object *annotObj, Object *titleObj) {
+  annotRef.num = -1;
+  title = NULL;
+  if (annotObj->isRef()) {
+    annotRef = annotObj->getRef();
+  } else if (titleObj->isString()) {
+    title = titleObj->getString()->copy();
+  } else {
+    error(-1, "Movie action is missing both the Annot and T keys");
+  }
+}
+
+LinkMovie::~LinkMovie() {
+  if (title) {
+    delete title;
+  }
+}
+
+//------------------------------------------------------------------------
 // LinkUnknown
 //------------------------------------------------------------------------
 
@@ -390,13 +581,42 @@ LinkUnknown::~LinkUnknown() {
 }
 
 //------------------------------------------------------------------------
+// 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, obj4;
+  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;
 
@@ -441,82 +661,110 @@ Link::Link(Dict *dict, GString *baseURI) {
     y2 = t;
   }
 
-  // get border
-  borderW = 1;
-  if (!dict->lookup("Border", &obj1)->isNull()) {
-    if (obj1.isArray() && obj1.arrayGetLength() >= 3) {
-      if (obj1.arrayGet(2, &obj2)->isNum()) {
-       borderW = obj2.getNum();
-      } else {
-       error(-1, "Bad annotation border");
+  // 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 *)gmalloc(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 *)gmalloc(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();
+       }
       }
-      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 = new LinkGoTo(&obj1);
+    action = LinkAction::parseDest(&obj1);
 
   // look for action
   } else {
     obj1.free();
     if (dict->lookup("A", &obj1)->isDict()) {
-      obj1.dictLookup("S", &obj2);
-
-      // GoTo action
-      if (obj2.isName("GoTo")) {
-       obj1.dictLookup("D", &obj3);
-       action = new LinkGoTo(&obj3);
-       obj3.free();
-
-      // GoToR action
-      } else if (obj2.isName("GoToR")) {
-       obj1.dictLookup("F", &obj3);
-       obj1.dictLookup("D", &obj4);
-       action = new LinkGoToR(&obj3, &obj4);
-       obj3.free();
-       obj4.free();
-
-      // Launch action
-      } else if (obj2.isName("Launch")) {
-       action = new LinkLaunch(&obj1);
-
-      // URI action
-      } else if (obj2.isName("URI")) {
-       obj1.dictLookup("URI", &obj3);
-       action = new LinkURI(&obj3, baseURI);
-       obj3.free();
-
-      // Named action
-      } else if (obj2.isName("Named")) {
-       obj1.dictLookup("N", &obj3);
-       action = new LinkNamed(&obj3);
-       obj3.free();
-
-      // unknown action
-      } else if (obj2.isName()) {
-       action = new LinkUnknown(obj2.getName());
-
-      // action is missing or wrong type
-      } else {
-       error(-1, "Bad annotation action");
-       action = NULL;
-      }
-
-      obj2.free();
-
-    } else {
-      error(-1, "Missing annotation destination/action");
-      action = NULL;
+      action = LinkAction::parseAction(&obj1, baseURI);
     }
   }
   obj1.free();
 
   // check for bad action
-  if (action && action->isOk())
+  if (action) {
     ok = gTrue;
+  }
 
   return;
 
@@ -527,8 +775,12 @@ Link::Link(Dict *dict, GString *baseURI) {
 }
 
 Link::~Link() {
-  if (action)
+  if (borderStyle) {
+    delete borderStyle;
+  }
+  if (action) {
     delete action;
+  }
 }
 
 //------------------------------------------------------------------------
@@ -595,36 +847,3 @@ GBool Links::onLink(double x, double y) {
   }
   return gFalse;
 }
-
-//------------------------------------------------------------------------
-
-// Extract a file name from a file specification (string or dictionary).
-static GString *getFileSpecName(Object *fileSpecObj) {
-  GString *name;
-  Object obj1;
-
-  name = NULL;
-
-  // string
-  if (fileSpecObj->isString()) {
-    name = fileSpecObj->getString()->copy();
-
-  // dictionary
-  } else if (fileSpecObj->isDict()) {
-    if (!fileSpecObj->dictLookup("Unix", &obj1)->isString()) {
-      obj1.free();
-      fileSpecObj->dictLookup("F", &obj1);
-    }
-    if (obj1.isString())
-      name = obj1.getString()->copy();
-    else
-      error(-1, "Illegal file spec in link");
-    obj1.free();
-
-  // error
-  } else {
-    error(-1, "Illegal file spec in link");
-  }
-
-  return name;
-}
index 4c644b8..9f04420 100644 (file)
@@ -2,14 +2,16 @@
 //
 // Link.h
 //
-// Copyright 1996-2002 Glyph & Cog, LLC
+// Copyright 1996-2003 Glyph & Cog, LLC
 //
 //========================================================================
 
 #ifndef LINK_H
 #define LINK_H
 
-#ifdef __GNUC__
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
 #pragma interface
 #endif
 
@@ -29,6 +31,7 @@ enum LinkActionKind {
   actionLaunch,                        // launch app (or open document)
   actionURI,                   // URI
   actionNamed,                 // named action
+  actionMovie,                 // movie action
   actionUnknown                        // anything else
 };
 
@@ -43,6 +46,16 @@ public:
 
   // Check link action type.
   virtual LinkActionKind getKind() = 0;
+
+  // Parse a destination (old-style action) name, string, or array.
+  static LinkAction *parseDest(Object *obj);
+
+  // Parse an action dictionary.
+  static LinkAction *parseAction(Object *obj, GString *baseURI = NULL);
+
+  // Extract a file name from a file specification (string or
+  // dictionary).
+  static GString *getFileSpecName(Object *fileSpecObj);
 };
 
 //------------------------------------------------------------------------
@@ -240,6 +253,30 @@ private:
 };
 
 //------------------------------------------------------------------------
+// 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
 //------------------------------------------------------------------------
 
@@ -265,6 +302,42 @@ private:
 };
 
 //------------------------------------------------------------------------
+// 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
 //------------------------------------------------------------------------
 
@@ -287,16 +360,18 @@ public:
   // Get action.
   LinkAction *getAction() { return action; }
 
-  // Get border corners and width.
-  void getBorder(double *xa1, double *ya1, double *xa2, double *ya2,
-                double *wa)
-    { *xa1 = x1; *ya1 = y1; *xa2 = x2; *ya2 = y2; *wa = borderW; }
+  // 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
-  double borderW;              // border width
+  LinkBorderStyle *borderStyle;        // border style
   LinkAction *action;          // action
   GBool ok;                    // is link valid?
 };
index b9cde77..8f22a90 100644 (file)
@@ -2,15 +2,16 @@
 //
 // NameToCharCode.cc
 //
-// Copyright 2001-2002 Glyph & Cog, LLC
+// Copyright 2001-2003 Glyph & Cog, LLC
 //
 //========================================================================
 
-#ifdef __GNUC__
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
 #pragma implementation
 #endif
 
-#include <aconf.h>
 #include <string.h>
 #include "gmem.h"
 #include "NameToCharCode.h"
index 22e41b6..65453c3 100644 (file)
@@ -2,14 +2,16 @@
 //
 // NameToCharCode.h
 //
-// Copyright 2001-2002 Glyph & Cog, LLC
+// Copyright 2001-2003 Glyph & Cog, LLC
 //
 //========================================================================
 
 #ifndef NAMETOCHARCODE_H
 #define NAMETOCHARCODE_H
 
-#ifdef __GNUC__
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
 #pragma interface
 #endif
 
index 432fafb..4f28fff 100644 (file)
@@ -2,7 +2,7 @@
 //
 // NameToUnicodeTable.h
 //
-// Copyright 2001-2002 Glyph & Cog, LLC
+// Copyright 2001-2003 Glyph & Cog, LLC
 //
 //========================================================================
 
@@ -10,6 +10,37 @@ 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"},
@@ -304,6 +335,12 @@ static struct {
   {0x017b, "Zdotaccent"},
   {0x0396, "Zeta"},
   {0x005a, "Zsmall"},
+  {0x0022, "\""},
+  {0x005c, "\\"},
+  {0x005d, "]"},
+  {0x005e, "^"},
+  {0x005f, "_"},
+  {0x0060, "`"},
   {0x0061, "a"},
   {0x00e1, "aacute"},
   {0x0103, "abreve"},
@@ -647,8 +684,8 @@ static struct {
   {0xf6e2, "commasuperior"},
   {0x2245, "congruent"},
   {0x00a9, "copyright"},
-  {0xf8e9, "copyrightsans"},
-  {0xf6d9, "copyrightserif"},
+  {0x00a9, "copyrightsans"},
+  {0x00a9, "copyrightserif"},
   {0x00a4, "currency"},
   {0xf6d1, "cyrBreve"},
   {0xf6d2, "cyrFlex"},
@@ -715,6 +752,7 @@ static struct {
   {0x203c, "exclamdbl"},
   {0x00a1, "exclamdown"},
   {0x00a1, "exclamdownsmall"},
+  {0x0021, "exclamleft"},
   {0x0021, "exclamsmall"},
   {0x2203, "existential"},
   {0x0066, "f"},
@@ -934,8 +972,8 @@ static struct {
   {0x2286, "reflexsubset"},
   {0x2287, "reflexsuperset"},
   {0x00ae, "registered"},
-  {0xf8e8, "registersans"},
-  {0xf6da, "registerserif"},
+  {0x00ae, "registersans"},
+  {0x00ae, "registerserif"},
   {0x2310, "revlogicalnot"},
   {0x03c1, "rho"},
   {0x02da, "ring"},
@@ -993,8 +1031,8 @@ static struct {
   {0x0303, "tildecomb"},
   {0x0384, "tonos"},
   {0x2122, "trademark"},
-  {0xf8ea, "trademarksans"},
-  {0xf6db, "trademarkserif"},
+  {0x2122, "trademarksans"},
+  {0x2122, "trademarkserif"},
   {0x25bc, "triagdn"},
   {0x25c4, "triaglf"},
   {0x25ba, "triagrt"},
@@ -1051,5 +1089,9 @@ static struct {
   {0x0030, "zerooldstyle"},
   {0x2070, "zerosuperior"},
   {0x03b6, "zeta"},
+  {0x007b, "{"},
+  {0x007c, "|"},
+  {0x007d, "}"},
+  {0x007e, "~"},
   { 0, NULL }
 };
index 6d92c6a..ddd6da6 100644 (file)
@@ -2,15 +2,16 @@
 //
 // Object.cc
 //
-// Copyright 1996-2002 Glyph & Cog, LLC
+// Copyright 1996-2003 Glyph & Cog, LLC
 //
 //========================================================================
 
-#ifdef __GNUC__
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
 #pragma implementation
 #endif
 
-#include <aconf.h>
 #include <stddef.h>
 #include "Object.h"
 #include "Array.h"
@@ -57,6 +58,13 @@ Object *Object::initDict(XRef *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;
index 65d0be0..8b1807c 100644 (file)
@@ -2,14 +2,16 @@
 //
 // Object.h
 //
-// Copyright 1996-2002 Glyph & Cog, LLC
+// Copyright 1996-2003 Glyph & Cog, LLC
 //
 //========================================================================
 
 #ifndef OBJECT_H
 #define OBJECT_H
 
-#ifdef __GNUC__
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
 #pragma interface
 #endif
 
@@ -93,6 +95,7 @@ public:
     { 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; }
@@ -152,6 +155,7 @@ public:
   Ref getRef() { return ref; }
   int getRefNum() { return ref.num; }
   int getRefGen() { return ref.gen; }
+  char *getCmd() { return cmd; }
 
   // Array accessors.
   int arrayGetLength();
diff --git a/pdf2swf/xpdf/Outline.cc b/pdf2swf/xpdf/Outline.cc
new file mode 100644 (file)
index 0000000..04891f3
--- /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 *)gmalloc(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 *)gmalloc(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)) {
+      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/pdf2swf/xpdf/Outline.h b/pdf2swf/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
index 6d46542..e83882d 100644 (file)
@@ -2,15 +2,16 @@
 //
 // OutputDev.cc
 //
-// Copyright 1996-2002 Glyph & Cog, LLC
+// Copyright 1996-2003 Glyph & Cog, LLC
 //
 //========================================================================
 
-#ifdef __GNUC__
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
 #pragma implementation
 #endif
 
-#include <aconf.h>
 #include <stddef.h>
 #include "Object.h"
 #include "Stream.h"
@@ -37,7 +38,7 @@ void OutputDev::setDefaultCTM(double *ctm) {
   defICTM[5] = (defCTM[1] * defCTM[4] - defCTM[0] * defCTM[5]) * det;
 }
 
-void OutputDev::cvtDevToUser(int dx, int dy, double *ux, double *uy) {
+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];
 }
@@ -59,7 +60,8 @@ void OutputDev::updateAll(GfxState *state) {
   updateFont(state);
 }
 
-GBool OutputDev::beginType3Char(GfxState *state,
+GBool OutputDev::beginType3Char(GfxState *state, double x, double y,
+                               double dx, double dy,
                                CharCode code, Unicode *u, int uLen) {
   return gFalse;
 }
index 4a05f1a..fb7d8ab 100644 (file)
@@ -2,14 +2,16 @@
 //
 // OutputDev.h
 //
-// Copyright 1996-2002 Glyph & Cog, LLC
+// Copyright 1996-2003 Glyph & Cog, LLC
 //
 //========================================================================
 
 #ifndef OUTPUTDEV_H
 #define OUTPUTDEV_H
 
-#ifdef __GNUC__
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
 #pragma interface
 #endif
 
@@ -59,7 +61,7 @@ public:
   virtual void setDefaultCTM(double *ctm);
 
   // Start a page.
-  virtual void startPage(int pageNum, GfxState *state, double x1,double y1,double x2,double y2) {}
+  virtual void startPage(int pageNum, GfxState *state) {}
 
   // End a page.
   virtual void endPage() {}
@@ -70,7 +72,7 @@ public:
   //----- coordinate conversion
 
   // Convert between device and user coordinates.
-  virtual void cvtDevToUser(int dx, int dy, double *ux, double *uy);
+  virtual void cvtDevToUser(double dx, double dy, double *ux, double *uy);
   virtual void cvtUserToDev(double ux, double uy, int *dx, int *dy);
 
   //----- link borders
@@ -123,9 +125,11 @@ public:
                        double originX, double originY,
                        CharCode code, Unicode *u, int uLen) {}
   virtual void drawString(GfxState *state, GString *s) {}
-  virtual GBool beginType3Char(GfxState *state,
+  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,
index 29abba0..b5981d9 100644 (file)
@@ -2,21 +2,23 @@
 //
 // PDFDoc.cc
 //
-// Copyright 1996-2002 Glyph & Cog, LLC
+// Copyright 1996-2003 Glyph & Cog, LLC
 //
 //========================================================================
 
-#ifdef __GNUC__
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
 #pragma implementation
 #endif
 
-#include <aconf.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <stddef.h>
 #include <string.h>
 #include "GString.h"
 #include "config.h"
+#include "GlobalParams.h"
 #include "Page.h"
 #include "Catalog.h"
 #include "Stream.h"
@@ -27,6 +29,9 @@
 #include "ErrorCodes.h"
 #include "Lexer.h"
 #include "Parser.h"
+#ifndef DISABLE_OUTLINE
+#include "Outline.h"
+#endif
 #include "PDFDoc.h"
 
 //------------------------------------------------------------------------
@@ -39,9 +44,9 @@
 //------------------------------------------------------------------------
 
 PDFDoc::PDFDoc(GString *fileNameA, GString *ownerPassword,
-              GString *userPassword, GBool printCommandsA) {
+              GString *userPassword) {
   Object obj;
-  GString *fileName2;
+  GString *fileName1, *fileName2;
 
   ok = gFalse;
   errCode = errNone;
@@ -51,19 +56,24 @@ PDFDoc::PDFDoc(GString *fileNameA, GString *ownerPassword,
   xref = NULL;
   catalog = NULL;
   links = NULL;
-  printCommands = printCommandsA;
+#ifndef DISABLE_OUTLINE
+  outline = NULL;
+#endif
 
-  // try to open file
   fileName = fileNameA;
+  fileName1 = fileName;
+
+
+  // try to open file
   fileName2 = NULL;
 #ifdef VMS
-  if (!(file = fopen(fileName->getCString(), "rb", "ctx=stm"))) {
-    error(-1, "Couldn't open file '%s'", fileName->getCString());
+  if (!(file = fopen(fileName1->getCString(), "rb", "ctx=stm"))) {
+    error(-1, "Couldn't open file '%s'", fileName1->getCString());
     errCode = errOpenFile;
     return;
   }
 #else
-  if (!(file = fopen(fileName->getCString(), "rb"))) {
+  if (!(file = fopen(fileName1->getCString(), "rb"))) {
     fileName2 = fileName->copy();
     fileName2->lowerCase();
     if (!(file = fopen(fileName2->getCString(), "rb"))) {
@@ -87,7 +97,7 @@ PDFDoc::PDFDoc(GString *fileNameA, GString *ownerPassword,
 }
 
 PDFDoc::PDFDoc(BaseStream *strA, GString *ownerPassword,
-              GString *userPassword, GBool printCommandsA) {
+              GString *userPassword) {
   ok = gFalse;
   errCode = errNone;
   fileName = NULL;
@@ -96,11 +106,15 @@ PDFDoc::PDFDoc(BaseStream *strA, GString *ownerPassword,
   xref = NULL;
   catalog = NULL;
   links = NULL;
-  printCommands = printCommandsA;
+#ifndef DISABLE_OUTLINE
+  outline = NULL;
+#endif
   ok = setup(ownerPassword, userPassword);
 }
 
 GBool PDFDoc::setup(GString *ownerPassword, GString *userPassword) {
+  str->reset();
+
   // check header
   checkHeader();
 
@@ -113,18 +127,28 @@ GBool PDFDoc::setup(GString *ownerPassword, GString *userPassword) {
   }
 
   // read catalog
-  catalog = new Catalog(xref, printCommands);
+  catalog = new Catalog(xref);
   if (!catalog->isOk()) {
     error(-1, "Couldn't read page catalog");
     errCode = errBadCatalog;
     return gFalse;
   }
 
+#ifndef DISABLE_OUTLINE
+  // read outline
+  outline = new Outline(catalog->getOutline(), xref);
+#endif
+
   // done
   return gTrue;
 }
 
 PDFDoc::~PDFDoc() {
+#ifndef DISABLE_OUTLINE
+  if (outline) {
+    delete outline;
+  }
+#endif
   if (catalog) {
     delete catalog;
   }
@@ -176,11 +200,13 @@ void PDFDoc::checkHeader() {
   }
 }
 
-void PDFDoc::displayPage(OutputDev *out, int page, double zoom,
-                        int rotate, GBool doLinks) {
+void PDFDoc::displayPage(OutputDev *out, int page, double hDPI, double vDPI,
+                        int rotate, GBool crop, GBool doLinks,
+                        GBool (*abortCheckCbk)(void *data),
+                        void *abortCheckCbkData) {
   Page *p;
 
-  if (printCommands) {
+  if (globalParams->getPrintCommands()) {
     printf("***** page %d *****\n", page);
   }
   p = catalog->getPage(page);
@@ -189,21 +215,41 @@ void PDFDoc::displayPage(OutputDev *out, int page, double zoom,
       delete links;
     }
     getLinks(p);
-    p->display(out, zoom, rotate, links, catalog);
+    p->display(out, hDPI, vDPI, rotate, crop, links, catalog,
+              abortCheckCbk, abortCheckCbkData);
   } else {
-    p->display(out, zoom, rotate, NULL, catalog);
+    p->display(out, hDPI, vDPI, rotate, crop, NULL, catalog,
+              abortCheckCbk, abortCheckCbkData);
   }
 }
 
 void PDFDoc::displayPages(OutputDev *out, int firstPage, int lastPage,
-                         int zoom, int rotate, GBool doLinks) {
+                         double hDPI, double vDPI, int rotate,
+                         GBool crop, GBool doLinks,
+                         GBool (*abortCheckCbk)(void *data),
+                         void *abortCheckCbkData) {
   int page;
 
   for (page = firstPage; page <= lastPage; ++page) {
-    displayPage(out, page, zoom, rotate, doLinks);
+    displayPage(out, page, hDPI, vDPI, rotate, crop, doLinks,
+               abortCheckCbk, abortCheckCbkData);
   }
 }
 
+void PDFDoc::displayPageSlice(OutputDev *out, int page,
+                             double hDPI, double vDPI,
+                             int rotate, GBool crop,
+                             int sliceX, int sliceY, int sliceW, int sliceH,
+                             GBool (*abortCheckCbk)(void *data),
+                             void *abortCheckCbkData) {
+  Page *p;
+
+  p = catalog->getPage(page);
+  p->displaySlice(out, hDPI, vDPI, rotate, crop,
+                 sliceX, sliceY, sliceW, sliceH,
+                 NULL, catalog, abortCheckCbk, abortCheckCbkData);
+}
+
 GBool PDFDoc::isLinearized() {
   Parser *parser;
   Object obj1, obj2, obj3, obj4, obj5;
index c12531e..bdcbd65 100644 (file)
@@ -2,14 +2,16 @@
 //
 // PDFDoc.h
 //
-// Copyright 1996-2002 Glyph & Cog, LLC
+// Copyright 1996-2003 Glyph & Cog, LLC
 //
 //========================================================================
 
 #ifndef PDFDOC_H
 #define PDFDOC_H
 
-#ifdef __GNUC__
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
 #pragma interface
 #endif
 
@@ -25,6 +27,7 @@ class OutputDev;
 class Links;
 class LinkAction;
 class LinkDest;
+class Outline;
 
 //------------------------------------------------------------------------
 // PDFDoc
@@ -34,9 +37,9 @@ class PDFDoc {
 public:
 
   PDFDoc(GString *fileNameA, GString *ownerPassword = NULL,
-        GString *userPassword = NULL, GBool printCommandsA = gFalse);
+        GString *userPassword = NULL);
   PDFDoc(BaseStream *strA, GString *ownerPassword = NULL,
-        GString *userPassword = NULL, GBool printCommandsA = gFalse);
+        GString *userPassword = NULL);
   ~PDFDoc();
 
   // Was PDF document successfully opened?
@@ -76,12 +79,25 @@ public:
   Object *getStructTreeRoot() { return catalog->getStructTreeRoot(); }
 
   // Display a page.
-  void displayPage(OutputDev *out, int page, double zoom,
-                  int rotate, GBool doLinks);
+  void displayPage(OutputDev *out, int page, double hDPI, double vDPI,
+                  int rotate, 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,
-                   int zoom, int rotate, GBool doLinks);
+                   double hDPI, double vDPI, int rotate,
+                   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 crop,
+                       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.
@@ -89,7 +105,8 @@ public:
 
   // If point <x>,<y> is in a link, return the associated action;
   // else return NULL.
-  LinkAction *findLink(double x, double y) { return links->find(x, y); }
+  LinkAction *findLink(double x, double y)
+    { return links ? links->find(x, y) : (LinkAction *)NULL; }
 
   // Return true if <x>,<y> is in a link.
   GBool onLink(double x, double y) { return links->onLink(x, y); }
@@ -99,6 +116,11 @@ public:
   LinkDest *findDest(GString *name)
     { return catalog->findDest(name); }
 
+#ifndef DISABLE_OUTLINE
+  // Return the outline object.
+  Outline *getOutline() { return outline; }
+#endif
+
   // Is the file encrypted?
   GBool isEncrypted() { return xref->isEncrypted(); }
 
@@ -117,6 +139,7 @@ public:
 
   // Return the document's Info dictionary (if any).
   Object *getDocInfo(Object *obj) { return xref->getDocInfo(obj); }
+  Object *getDocInfoNF(Object *obj) { return xref->getDocInfoNF(obj); }
 
   // Return the PDF version specified by the file.
   double getPDFVersion() { return pdfVersion; }
@@ -124,6 +147,7 @@ public:
   // Save this file with another name.
   GBool saveAs(GString *name);
 
+
 private:
 
   GBool setup(GString *ownerPassword, GString *userPassword);
@@ -137,7 +161,10 @@ private:
   XRef *xref;
   Catalog *catalog;
   Links *links;
-  GBool printCommands;
+#ifndef DISABLE_OUTLINE
+  Outline *outline;
+#endif
+
 
   GBool ok;
   int errCode;
diff --git a/pdf2swf/xpdf/PDFDocEncoding.cc b/pdf2swf/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/pdf2swf/xpdf/PDFDocEncoding.h b/pdf2swf/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
index 8d654bd..a65c324 100644 (file)
@@ -2,11 +2,13 @@
 //
 // PSTokenizer.cc
 //
-// Copyright 2002 Glyph & Cog, LLC
+// Copyright 2002-2003 Glyph & Cog, LLC
 //
 //========================================================================
 
-#ifdef __GNUC__
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
 #pragma implementation
 #endif
 
index 1053c67..4d5ee97 100644 (file)
@@ -2,14 +2,16 @@
 //
 // PSTokenizer.h
 //
-// Copyright 2002 Glyph & Cog, LLC
+// Copyright 2002-2003 Glyph & Cog, LLC
 //
 //========================================================================
 
 #ifndef PSTOKENIZER_H
 #define PSTOKENIZER_H
 
-#ifdef __GNUC__
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
 #pragma interface
 #endif
 
index c601857..ab8504d 100644 (file)
@@ -2,16 +2,18 @@
 //
 // Page.cc
 //
-// Copyright 1996-2002 Glyph & Cog, LLC
+// Copyright 1996-2003 Glyph & Cog, LLC
 //
 //========================================================================
 
-#ifdef __GNUC__
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
 #pragma implementation
 #endif
 
-#include <aconf.h>
 #include <stddef.h>
+#include "GlobalParams.h"
 #include "Object.h"
 #include "Array.h"
 #include "Dict.h"
@@ -57,8 +59,12 @@ PageAttrs::PageAttrs(PageAttrs *attrs, Dict *dict) {
   readBox(dict, "MediaBox", &mediaBox);
 
   // crop box
-  cropBox = mediaBox;
-  haveCropBox = readBox(dict, "CropBox", &cropBox);
+  if (readBox(dict, "CropBox", &cropBox)) {
+    haveCropBox = gTrue;
+  }
+  if (!haveCropBox) {
+    cropBox = mediaBox;
+  }
 
   // if the MediaBox is excessively larger than the CropBox,
   // just use the CropBox
@@ -170,13 +176,10 @@ GBool PageAttrs::readBox(Dict *dict, char *key, PDFRectangle *box) {
 // Page
 //------------------------------------------------------------------------
 
-Page::Page(XRef *xrefA, int numA, Dict *pageDict, PageAttrs *attrsA,
-          GBool printCommandsA) {
-
+Page::Page(XRef *xrefA, int numA, Dict *pageDict, PageAttrs *attrsA) {
   ok = gTrue;
   xref = xrefA;
   num = numA;
-  printCommands = printCommandsA;
 
   // get attributes
   attrs = attrsA;
@@ -215,22 +218,91 @@ Page::~Page() {
   contents.free();
 }
 
-void Page::display(OutputDev *out, double dpi, int rotate,
-                  Links *links, Catalog *catalog) {
+void Page::display(OutputDev *out, double hDPI, double vDPI,
+                  int rotate, GBool crop,
+                  Links *links, Catalog *catalog,
+                  GBool (*abortCheckCbk)(void *data),
+                  void *abortCheckCbkData) {
+  displaySlice(out, hDPI, vDPI, rotate, crop, -1, -1, -1, -1, links, catalog,
+              abortCheckCbk, abortCheckCbkData);
+}
+
+void Page::displaySlice(OutputDev *out, double hDPI, double vDPI,
+                       int rotate, 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 *box, *cropBox;
+  PDFRectangle *mediaBox, *cropBox;
+  PDFRectangle box;
   Gfx *gfx;
   Object obj;
   Link *link;
-  int i;
   Annots *annotList;
+  double kx, ky;
+  int i;
 
-  box = getBox();
+  rotate += getRotate();
+  if (rotate >= 360) {
+    rotate -= 360;
+  } else if (rotate < 0) {
+    rotate += 360;
+  }
+
+  mediaBox = getBox();
+  if (sliceW >= 0 && sliceH >= 0) {
+    kx = 72.0 / hDPI;
+    ky = 72.0 / vDPI;
+    if (rotate == 90) {
+      if (out->upsideDown()) {
+       box.x1 = mediaBox->x1 + ky * sliceY;
+       box.x2 = mediaBox->x1 + ky * (sliceY + sliceH);
+      } else {
+       box.x1 = mediaBox->x2 - ky * (sliceY + sliceH);
+       box.x2 = mediaBox->x2 - ky * sliceY;
+      }
+      box.y1 = mediaBox->y1 + kx * sliceX;
+      box.y2 = mediaBox->y1 + kx * (sliceX + sliceW);
+    } else if (rotate == 180) {
+      box.x1 = mediaBox->x2 - kx * (sliceX + sliceW);
+      box.x2 = mediaBox->x2 - kx * sliceX;
+      if (out->upsideDown()) {
+       box.y1 = mediaBox->y1 + ky * sliceY;
+       box.y2 = mediaBox->y1 + ky * (sliceY + sliceH);
+      } else {
+       box.y1 = mediaBox->y2 - ky * (sliceY + sliceH);
+       box.y2 = mediaBox->y2 - ky * sliceY;
+      }
+    } else if (rotate == 270) {
+      if (out->upsideDown()) {
+       box.x1 = mediaBox->x2 - ky * (sliceY + sliceH);
+       box.x2 = mediaBox->x2 - ky * sliceY;
+      } else {
+       box.x1 = mediaBox->x1 + ky * sliceY;
+       box.x2 = mediaBox->x1 + ky * (sliceY + sliceH);
+      }
+      box.y1 = mediaBox->y2 - kx * (sliceX + sliceW);
+      box.y2 = mediaBox->y2 - kx * sliceX;
+    } else {
+      box.x1 = mediaBox->x1 + kx * sliceX;
+      box.x2 = mediaBox->x1 + kx * (sliceX + sliceW);
+      if (out->upsideDown()) {
+       box.y1 = mediaBox->y2 - ky * (sliceY + sliceH);
+       box.y2 = mediaBox->y2 - ky * sliceY;
+      } else {
+       box.y1 = mediaBox->y1 + ky * sliceY;
+       box.y2 = mediaBox->y1 + ky * (sliceY + sliceH);
+      }
+    }
+  } else {
+    box = *mediaBox;
+  }
   cropBox = getCropBox();
 
-  if (printCommands) {
+  if (globalParams->getPrintCommands()) {
     printf("***** MediaBox = ll:%g,%g ur:%g,%g\n",
-          box->x1, box->y1, box->x2, box->y2);
+          box.x1, box.y1, box.x2, box.y2);
     if (isCropped()) {
       printf("***** CropBox = ll:%g,%g ur:%g,%g\n",
             cropBox->x1, cropBox->y1, cropBox->x2, cropBox->y2);
@@ -238,35 +310,33 @@ void Page::display(OutputDev *out, double dpi, int rotate,
     printf("***** Rotate = %d\n", attrs->getRotate());
   }
 
-  rotate += getRotate();
-  if (rotate >= 360) {
-    rotate -= 360;
-  } else if (rotate < 0) {
-    rotate += 360;
-  }
   gfx = new Gfx(xref, out, num, attrs->getResourceDict(),
-               dpi, box, isCropped(), cropBox, rotate, printCommands);
+               hDPI, vDPI, &box, crop && isCropped(), cropBox, 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
-  //~ need to reset CTM ???
   annotList = new Annots(xref, annots.fetch(xref, &obj));
   obj.free();
   if (annotList->getNumAnnots() > 0) {
-    if (printCommands) {
+    if (globalParams->getPrintCommands()) {
       printf("***** Annotations\n");
     }
     for (i = 0; i < annotList->getNumAnnots(); ++i) {
index 7207b20..2376cb4 100644 (file)
@@ -2,14 +2,16 @@
 //
 // Page.h
 //
-// Copyright 1996-2002 Glyph & Cog, LLC
+// Copyright 1996-2003 Glyph & Cog, LLC
 //
 //========================================================================
 
 #ifndef PAGE_H
 #define PAGE_H
 
-#ifdef __GNUC__
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
 #pragma interface
 #endif
 
@@ -23,8 +25,14 @@ class Catalog;
 
 //------------------------------------------------------------------------
 
-struct PDFRectangle {
+class PDFRectangle {
+public:
   double x1, y1, x2, y2;
+
+  PDFRectangle() { x1 = y1 = x2 = y2 = 0; }
+  PDFRectangle(double x1A, double y1A, double x2A, double y2A)
+    { x1 = x1A; y1 = y1A; x2 = x2A; y2 = y2A; }
+  GBool isValid() { return x1 != 0 || y1 != 0 || x2 != 0 || y2 != 0; }
 };
 
 //------------------------------------------------------------------------
@@ -97,8 +105,7 @@ class Page {
 public:
 
   // Constructor.
-  Page(XRef *xrefA, int numA, Dict *pageDict, PageAttrs *attrsA,
-       GBool printCommandsA);
+  Page(XRef *xrefA, int numA, Dict *pageDict, PageAttrs *attrsA);
 
   // Destructor.
   ~Page();
@@ -134,8 +141,19 @@ public:
   Object *getContents(Object *obj) { return contents.fetch(xref, obj); }
 
   // Display a page.
-  void display(OutputDev *out, double dpi, int rotate,
-              Links *links, Catalog *catalog);
+  void display(OutputDev *out, double hDPI, double vDPI,
+              int rotate, 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 crop,
+                   int sliceX, int sliceY, int sliceW, int sliceH,
+                   Links *links, Catalog *catalog,
+                   GBool (*abortCheckCbk)(void *data) = NULL,
+                   void *abortCheckCbkData = NULL);
 
 private:
 
@@ -144,7 +162,6 @@ private:
   PageAttrs *attrs;            // page attributes
   Object annots;               // annotations array
   Object contents;             // page contents
-  GBool printCommands;         // print the drawing commands (for debugging)
   GBool ok;                    // true if page is valid
 };
 
index 4df53c9..0aa66d3 100644 (file)
@@ -2,15 +2,16 @@
 //
 // Parser.cc
 //
-// Copyright 1996-2002 Glyph & Cog, LLC
+// Copyright 1996-2003 Glyph & Cog, LLC
 //
 //========================================================================
 
-#ifdef __GNUC__
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
 #pragma implementation
 #endif
 
-#include <aconf.h>
 #include <stddef.h>
 #include "Object.h"
 #include "Array.h"
@@ -88,8 +89,10 @@ Object *Parser::getObj(Object *obj) {
       } else {
        key = copyString(buf1.getName());
        shift();
-       if (buf1.isEOF() || buf1.isError())
+       if (buf1.isEOF() || buf1.isError()) {
+         gfree(key);
          break;
+       }
 #ifndef NO_DECRYPTION
        obj->dictAdd(key, getObj(&obj2, fileKey, keyLength, objNum, objGen));
 #else
@@ -173,10 +176,16 @@ Stream *Parser::makeStream(Object *dict) {
   }
 
   // check for length in damaged file
-  if (xref->getStreamEnd(pos, &endPos)) {
+  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;
+  }
+
   // make base stream
   str = lexer->getStream()->getBaseStream()->makeSubStream(pos, gTrue,
                                                           length, dict);
@@ -190,17 +199,25 @@ Stream *Parser::makeStream(Object *dict) {
   // refill token buffers and check for 'endstream'
   shift();  // kill '>>'
   shift();  // kill 'stream'
-  if (buf1.isCmd("endstream"))
+  if (buf1.isCmd("endstream")) {
     shift();
-  else
+  } else {
     error(getPos(), "Missing 'endstream'");
+    str->ignoreLength();
+  }
 
   return str;
 }
 
 void Parser::shift() {
   if (inlineImg > 0) {
-    ++inlineImg;
+    if (inlineImg < 2) {
+      ++inlineImg;
+    } else {
+      // in a damaged content stream, if 'ID' shows up in the middle
+      // of a dictionary, we need to reset
+      inlineImg = 0;
+    }
   } else if (buf2.isCmd("ID")) {
     lexer->skipChar();         // skip char after 'ID' command
     inlineImg = 1;
index c11475b..3bc3ab2 100644 (file)
@@ -2,14 +2,16 @@
 //
 // Parser.h
 //
-// Copyright 1996-2002 Glyph & Cog, LLC
+// Copyright 1996-2003 Glyph & Cog, LLC
 //
 //========================================================================
 
 #ifndef PARSER_H
 #define PARSER_H
 
-#ifdef __GNUC__
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
 #pragma interface
 #endif
 
index f5a77b0..c4458fe 100644 (file)
@@ -4,7 +4,7 @@
 //
 // Tables for CCITT Fax decoding.
 //
-// Copyright 1996-2002 Glyph & Cog, LLC
+// Copyright 1996-2003 Glyph & Cog, LLC
 //
 //========================================================================
 
index c47c5af..49bbb46 100644 (file)
@@ -2,22 +2,21 @@
 //
 // Stream.cc
 //
-// Copyright 1996-2002 Glyph & Cog, LLC
+// Copyright 1996-2003 Glyph & Cog, LLC
 //
 //========================================================================
 
-#ifdef __GNUC__
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
 #pragma implementation
 #endif
 
-#include <aconf.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <stddef.h>
 #ifndef WIN32
 #include <unistd.h>
-#else
-extern "C" int unlink(char *filename);
 #endif
 #include <string.h>
 #include <ctype.h>
@@ -30,6 +29,8 @@ extern "C" int unlink(char *filename);
 #include "Decrypt.h"
 #endif
 #include "Stream.h"
+#include "JBIG2Stream.h"
+#include "JPXStream.h"
 #include "Stream-CCITT.h"
 
 #ifdef __DJGPP__
@@ -37,9 +38,6 @@ static GBool setDJSYSFLAGS = gFalse;
 #endif
 
 #ifdef VMS
-#if (__VMS_VER < 70000000)
-extern "C" int unlink(char *filename);
-#endif
 #ifdef __GNUC__
 #define SEEK_SET 0
 #define SEEK_CUR 1
@@ -47,10 +45,6 @@ extern "C" int unlink(char *filename);
 #endif
 #endif
 
-#ifdef MACOS
-#include "StuffItEngineLib.h"
-#endif
-
 //------------------------------------------------------------------------
 // Stream (base class)
 //------------------------------------------------------------------------
@@ -91,7 +85,7 @@ char *Stream::getLine(char *buf, int size) {
   return buf;
 }
 
-GString *Stream::getPSFilter(char *indent) {
+GString *Stream::getPSFilter(int psLevel, char *indent) {
   return new GString();
 }
 
@@ -147,7 +141,7 @@ Stream *Stream::makeFilter(char *name, Stream *str, Object *params) {
   int encoding;
   GBool endOfLine, byteAlign, endOfBlock, black;
   int columns, rows;
-  Object obj;
+  Object globals, obj;
 
   if (!strcmp(name, "ASCIIHexDecode") || !strcmp(name, "AHx")) {
     str = new ASCIIHexStream(str);
@@ -257,6 +251,14 @@ Stream *Stream::makeFilter(char *name, Stream *str, Object *params) {
       obj.free();
     }
     str = new FlateStream(str, pred, columns, colors, bits);
+  } else if (!strcmp(name, "JBIG2Decode")) {
+    if (params->isDict()) {
+      params->dictLookup("JBIG2Globals", &globals);
+    }
+    str = new JBIG2Stream(str, &globals);
+    globals.free();
+  } else if (!strcmp(name, "JPXDecode")) {
+    str = new JPXStream(str);
   } else {
     error(getPos(), "Unknown filter '%s'", name);
     str = new EOFStream(str);
@@ -340,51 +342,54 @@ void ImageStream::reset() {
 }
 
 GBool ImageStream::getPixel(Guchar *pix) {
+  int i;
+
+  if (imgIdx >= nVals) {
+    getLine();
+    imgIdx = 0;
+  }
+  for (i = 0; i < nComps; ++i) {
+    pix[i] = imgLine[imgIdx++];
+  }
+  return gTrue;
+}
+
+Guchar *ImageStream::getLine() {
   Gulong buf, bitMask;
   int bits;
   int c;
   int i;
 
-  if (imgIdx >= nVals) {
-
-    // read one line of image pixels
-    if (nBits == 1) {
-      for (i = 0; i < nVals; i += 8) {
-       c = str->getChar();
-       imgLine[i+0] = (Guchar)((c >> 7) & 1);
-       imgLine[i+1] = (Guchar)((c >> 6) & 1);
-       imgLine[i+2] = (Guchar)((c >> 5) & 1);
-       imgLine[i+3] = (Guchar)((c >> 4) & 1);
-       imgLine[i+4] = (Guchar)((c >> 3) & 1);
-       imgLine[i+5] = (Guchar)((c >> 2) & 1);
-       imgLine[i+6] = (Guchar)((c >> 1) & 1);
-       imgLine[i+7] = (Guchar)(c & 1);
-      }
-    } else if (nBits == 8) {
-      for (i = 0; i < nVals; ++i) {
-       imgLine[i] = str->getChar();
-      }
-    } else {
-      bitMask = (1 << nBits) - 1;
-      buf = 0;
-      bits = 0;
-      for (i = 0; i < nVals; ++i) {
-       if (bits < nBits) {
-         buf = (buf << 8) | (str->getChar() & 0xff);
-         bits += 8;
-       }
-       imgLine[i] = (Guchar)((buf >> (bits - nBits)) & bitMask);
-       bits -= nBits;
+  if (nBits == 1) {
+    for (i = 0; i < nVals; i += 8) {
+      c = str->getChar();
+      imgLine[i+0] = (Guchar)((c >> 7) & 1);
+      imgLine[i+1] = (Guchar)((c >> 6) & 1);
+      imgLine[i+2] = (Guchar)((c >> 5) & 1);
+      imgLine[i+3] = (Guchar)((c >> 4) & 1);
+      imgLine[i+4] = (Guchar)((c >> 3) & 1);
+      imgLine[i+5] = (Guchar)((c >> 2) & 1);
+      imgLine[i+6] = (Guchar)((c >> 1) & 1);
+      imgLine[i+7] = (Guchar)(c & 1);
+    }
+  } else if (nBits == 8) {
+    for (i = 0; i < nVals; ++i) {
+      imgLine[i] = str->getChar();
+    }
+  } else {
+    bitMask = (1 << nBits) - 1;
+    buf = 0;
+    bits = 0;
+    for (i = 0; i < nVals; ++i) {
+      if (bits < nBits) {
+       buf = (buf << 8) | (str->getChar() & 0xff);
+       bits += 8;
       }
+      imgLine[i] = (Guchar)((buf >> (bits - nBits)) & bitMask);
+      bits -= nBits;
     }
-
-    // reset to start of line
-    imgIdx = 0;
   }
-
-  for (i = 0; i < nComps; ++i)
-    pix[i] = imgLine[imgIdx++];
-  return gTrue;
+  return imgLine;
 }
 
 void ImageStream::skipLine() {
@@ -448,7 +453,7 @@ GBool StreamPredictor::getNextLine() {
   int i, j, k;
 
   // get PNG optimum predictor number
-  if (predictor == 15) {
+  if (predictor >= 10) {
     if ((curPred = str->getRawChar()) == EOF) {
       return gFalse;
     }
@@ -465,7 +470,7 @@ GBool StreamPredictor::getNextLine() {
     upLeftBuf[1] = upLeftBuf[0];
     upLeftBuf[0] = predLine[i];
     if ((c = str->getRawChar()) == EOF) {
-      break;
+      return gFalse;
     }
     switch (curPred) {
     case 11:                   // PNG sub
@@ -504,7 +509,6 @@ GBool StreamPredictor::getNextLine() {
   }
 
   // apply TIFF (component) predictor
-  //~ this is completely untested
   if (predictor == 2) {
     if (nBits == 1) {
       inBuf = predLine[pixBytes - 1];
@@ -579,7 +583,10 @@ Stream *FileStream::makeSubStream(Guint startA, GBool limitedA,
 }
 
 void FileStream::reset() {
-#if HAVE_FSEEK64
+#if HAVE_FSEEKO
+  savePos = (Guint)ftello(f);
+  fseeko(f, start, SEEK_SET);
+#elif HAVE_FSEEK64
   savePos = (Guint)ftell64(f);
   fseek64(f, start, SEEK_SET);
 #else
@@ -597,7 +604,9 @@ void FileStream::reset() {
 
 void FileStream::close() {
   if (saved) {
-#if HAVE_FSEEK64
+#if HAVE_FSEEKO
+    fseeko(f, savePos, SEEK_SET);
+#elif HAVE_FSEEK64
     fseek64(f, savePos, SEEK_SET);
 #else
     fseek(f, savePos, SEEK_SET);
@@ -641,14 +650,19 @@ void FileStream::setPos(Guint pos, int dir) {
   Guint size;
 
   if (dir >= 0) {
-#if HAVE_FSEEK64
+#if HAVE_FSEEKO
+    fseeko(f, pos, SEEK_SET);
+#elif HAVE_FSEEK64
     fseek64(f, pos, SEEK_SET);
 #else
     fseek(f, pos, SEEK_SET);
 #endif
     bufPos = pos;
   } else {
-#if HAVE_FSEEK64
+#if HAVE_FSEEKO
+    fseeko(f, 0, SEEK_END);
+    size = (Guint)ftello(f);
+#elif HAVE_FSEEK64
     fseek64(f, 0, SEEK_END);
     size = (Guint)ftell64(f);
 #else
@@ -661,7 +675,10 @@ void FileStream::setPos(Guint pos, int dir) {
     //~ work around a bug in cygwin's implementation of fseek
     rewind(f);
 #endif
-#if HAVE_FSEEK64
+#if HAVE_FSEEKO
+    fseeko(f, -(int)pos, SEEK_END);
+    bufPos = (Guint)ftello(f);
+#elif HAVE_FSEEK64
     fseek64(f, -(int)pos, SEEK_END);
     bufPos = (Guint)ftell64(f);
 #else
@@ -682,13 +699,14 @@ void FileStream::moveStart(int delta) {
 // MemStream
 //------------------------------------------------------------------------
 
-MemStream::MemStream(char *bufA, Guint lengthA, Object *dictA):
+MemStream::MemStream(char *bufA, Guint startA, Guint lengthA, Object *dictA):
     BaseStream(dictA) {
   buf = bufA;
-  needFree = gFalse;
+  start = startA;
   length = lengthA;
-  bufEnd = buf + length;
-  bufPtr = buf;
+  bufEnd = buf + start + length;
+  bufPtr = buf + start;
+  needFree = gFalse;
 }
 
 MemStream::~MemStream() {
@@ -697,20 +715,22 @@ MemStream::~MemStream() {
   }
 }
 
-Stream *MemStream::makeSubStream(Guint start, GBool limited,
+Stream *MemStream::makeSubStream(Guint startA, GBool limited,
                                 Guint lengthA, Object *dictA) {
+  MemStream *subStr;
   Guint newLength;
 
-  if (!limited || start + lengthA > length) {
-    newLength = length - start;
+  if (!limited || startA + lengthA > start + length) {
+    newLength = start + length - startA;
   } else {
     newLength = lengthA;
   }
-  return new MemStream(buf + start, newLength, dictA);
+  subStr = new MemStream(buf, startA, newLength, dictA);
+  return subStr;
 }
 
 void MemStream::reset() {
-  bufPtr = buf;
+  bufPtr = buf + start;
 #ifndef NO_DECRYPTION
   if (decrypt) {
     decrypt->reset();
@@ -722,24 +742,24 @@ void MemStream::close() {
 }
 
 void MemStream::setPos(Guint pos, int dir) {
+  Guint i;
+
   if (dir >= 0) {
-    if (pos > length) {
-      bufPtr = bufEnd;
-    } else {
-      bufPtr = buf + pos;
-    }
+    i = pos;
   } else {
-    if (pos > length) {
-      bufPtr = buf;
-    } else {
-      bufPtr = bufEnd - pos;
-    }
+    i = start + length - pos;
+  }
+  if (i < start) {
+    i = start;
+  } else if (i > start + length) {
+    i = start + length;
   }
+  bufPtr = buf + i;
 }
 
 void MemStream::moveStart(int delta) {
-  buf += delta;
-  bufPtr = buf;
+  start += delta;
+  bufPtr = buf + start;
 }
 
 #ifndef NO_DECRYPTION
@@ -750,12 +770,13 @@ void MemStream::doDecryption(Guchar *fileKey, int keyLength,
 
   this->BaseStream::doDecryption(fileKey, keyLength, objNum, objGen);
   if (decrypt) {
-    newBuf = (char *)gmalloc(bufEnd - buf);
-    for (p = buf, q = newBuf; p < bufEnd; ++p, ++q) {
+    newBuf = (char *)gmalloc(length);
+    for (p = buf + start, q = newBuf; p < bufEnd; ++p, ++q) {
       *q = (char)decrypt->decryptByte((Guchar)*p);
     }
-    bufEnd = newBuf + (bufEnd - buf);
-    bufPtr = newBuf + (bufPtr - buf);
+    bufEnd = newBuf + length;
+    bufPtr = newBuf + (bufPtr - (buf + start));
+    start = 0;
     buf = newBuf;
     needFree = gTrue;
   }
@@ -766,20 +787,38 @@ void MemStream::doDecryption(Guchar *fileKey, int keyLength,
 // EmbedStream
 //------------------------------------------------------------------------
 
-EmbedStream::EmbedStream(Stream *strA, Object *dictA):
+EmbedStream::EmbedStream(Stream *strA, Object *dictA,
+                        GBool limitedA, Guint lengthA):
     BaseStream(dictA) {
   str = strA;
+  limited = limitedA;
+  length = lengthA;
 }
 
 EmbedStream::~EmbedStream() {
 }
 
-Stream *EmbedStream::makeSubStream(Guint start, GBool limited,
-                                  Guint length, Object *dictA) {
+Stream *EmbedStream::makeSubStream(Guint start, GBool limitedA,
+                                  Guint lengthA, Object *dictA) {
   error(-1, "Internal: called makeSubStream() on EmbedStream");
   return NULL;
 }
 
+int EmbedStream::getChar() {
+  if (limited && !length) {
+    return EOF;
+  }
+  --length;
+  return str->getChar();
+}
+
+int EmbedStream::lookChar() {
+  if (limited && !length) {
+    return EOF;
+  }
+  return str->lookChar();
+}
+
 void EmbedStream::setPos(Guint pos, int dir) {
   error(-1, "Internal: called setPos() on EmbedStream");
 }
@@ -866,10 +905,13 @@ int ASCIIHexStream::lookChar() {
   return buf;
 }
 
-GString *ASCIIHexStream::getPSFilter(char *indent) {
+GString *ASCIIHexStream::getPSFilter(int psLevel, char *indent) {
   GString *s;
 
-  if (!(s = str->getPSFilter(indent))) {
+  if (psLevel < 2) {
+    return NULL;
+  }
+  if (!(s = str->getPSFilter(psLevel, indent))) {
     return NULL;
   }
   s->append(indent)->append("/ASCIIHexDecode filter\n");
@@ -944,10 +986,13 @@ int ASCII85Stream::lookChar() {
   return b[index];
 }
 
-GString *ASCII85Stream::getPSFilter(char *indent) {
+GString *ASCII85Stream::getPSFilter(int psLevel, char *indent) {
   GString *s;
 
-  if (!(s = str->getPSFilter(indent))) {
+  if (psLevel < 2) {
+    return NULL;
+  }
+  if (!(s = str->getPSFilter(psLevel, indent))) {
     return NULL;
   }
   s->append(indent)->append("/ASCII85Decode filter\n");
@@ -971,21 +1016,12 @@ LZWStream::LZWStream(Stream *strA, int predictor, int columns, int colors,
     pred = NULL;
   }
   early = earlyA;
-  zPipe = NULL;
-  bufPtr = bufEnd = buf;
+  eof = gFalse;
+  inputBits = 0;
+  clearTable();
 }
 
 LZWStream::~LZWStream() {
-  if (zPipe) {
-#ifdef HAVE_POPEN
-    pclose(zPipe);
-#else
-    fclose(zPipe);
-#endif
-    zPipe = NULL;
-    unlink(zName->getCString());
-    delete zName;
-  }
   if (pred) {
     delete pred;
   }
@@ -996,275 +1032,149 @@ int LZWStream::getChar() {
   if (pred) {
     return pred->getChar();
   }
-  return (bufPtr >= bufEnd && !fillBuf()) ? EOF : (*bufPtr++ & 0xff);
+  if (eof) {
+    return EOF;
+  }
+  if (seqIndex >= seqLength) {
+    if (!processNextCode()) {
+      return EOF;
+    }
+  }
+  return seqBuf[seqIndex++];
 }
 
 int LZWStream::lookChar() {
   if (pred) {
     return pred->lookChar();
   }
-  return (bufPtr >= bufEnd && !fillBuf()) ? EOF : (*bufPtr & 0xff);
+  if (eof) {
+    return EOF;
+  }
+  if (seqIndex >= seqLength) {
+    if (!processNextCode()) {
+      return EOF;
+    }
+  }
+  return seqBuf[seqIndex];
 }
 
 int LZWStream::getRawChar() {
-  return (bufPtr >= bufEnd && !fillBuf()) ? EOF : (*bufPtr++ & 0xff);
+  if (eof) {
+    return EOF;
+  }
+  if (seqIndex >= seqLength) {
+    if (!processNextCode()) {
+      return EOF;
+    }
+  }
+  return seqBuf[seqIndex++];
 }
 
 void LZWStream::reset() {
-  FILE *f;
-  GString *zCmd;
-
-  //----- close old LZW stream
-  if (zPipe) {
-#ifdef HAVE_POPEN
-    pclose(zPipe);
-#else
-    fclose(zPipe);
-#endif
-    zPipe = NULL;
-    unlink(zName->getCString());
-    delete zName;
-  }
+  str->reset();
+  eof = gFalse;
+  inputBits = 0;
+  clearTable();
+}
 
-  //----- tell Delorie runtime to spawn a new instance of COMMAND.COM
-  //      to run gzip
-#if __DJGPP__
-  if (!setDJSYSFLAGS) {
-    setenv("DJSYSFLAGS", "0x0002", 0);
-    setDJSYSFLAGS = gTrue;
-  }
-#endif
+GBool LZWStream::processNextCode() {
+  int code;
+  int nextLength;
+  int i, j;
 
-  //----- create the .Z file
-  if (!openTempFile(&zName, &f, "wb", ".Z")) {
-    error(getPos(), "Couldn't create temporary file for LZW stream");
-    return;
+  // check for EOF
+  if (eof) {
+    return gFalse;
   }
-  dumpFile(f);
-  fclose(f);
 
-  //----- execute uncompress / gzip
-#ifdef WIN32
-  zCmd = new GString("c:\\swftools\\gzip.exe");
-#else
-  zCmd = new GString(uncompressCmd);
-#endif
-  zCmd->append(' ');
-  zCmd->append(zName);
-#if defined(MACOS)
-  long magicCookie;
-  // first we open the engine up
-  OSErr err = OpenSITEngine(kUseExternalEngine, &magicCookie);
-  // if we found it - let's use it!
-  if (!err && magicCookie) {
-    // make sure we have the correct version of the Engine
-    if (GetSITEngineVersion(magicCookie) >= kFirstSupportedEngine) {
-      FSSpec myFSS;
-      Str255 pName;
-      strcpy((char *)pName, zName->getCString());
-      c2pstr((char *)pName);
-      FSMakeFSSpec(0, 0, pName, &myFSS);
-      short ftype = DetermineFileType(magicCookie, &myFSS);
-      OSErr expandErr = ExpandFSSpec(magicCookie, ftype, &myFSS,
-                                    NULL, NULL, kCreateFolderNever,
-                                    kDeleteOriginal, kTextConvertSmart);
-    }
-  }
-#elif defined(HAVE_POPEN)
-  if (!(zPipe = popen(zCmd->getCString(), POPEN_READ_MODE))) {
-    error(getPos(), "Couldn't popen '%s'", zCmd->getCString());
-    unlink(zName->getCString());
-    delete zName;
-    return;
-  }
-#else // HAVE_POPEN
-  if (!executeCommand(zCmd->getCString())) {
-    error(getPos(), "Couldn't execute '%s'", zCmd->getCString());
-    unlink(zName->getCString());
-    delete zName;
-    return;
+  // check for eod and clear-table codes
+ start:
+  code = getCode();
+  if (code == EOF || code == 257) {
+    eof = gTrue;
+    return gFalse;
   }
-  zName->del(zName->getLength() - 2, 2);
-  if (!(zPipe = fopen(zName->getCString(), "rb"))) {
-    error(getPos(), "Couldn't open uncompress file '%s'", zName->getCString());
-    unlink(zName->getCString());
-    delete zName;
-    return;
+  if (code == 256) {
+    clearTable();
+    goto start;
+  }
+  if (nextCode >= 4097) {
+    error(getPos(), "Bad LZW stream - expected clear-table code");
+    clearTable();
+  }
+
+  // process the next code
+  nextLength = seqLength + 1;
+  if (code < 256) {
+    seqBuf[0] = code;
+    seqLength = 1;
+  } else if (code < nextCode) {
+    seqLength = table[code].length;
+    for (i = seqLength - 1, j = code; i > 0; --i) {
+      seqBuf[i] = table[j].tail;
+      j = table[j].head;
+    }
+    seqBuf[0] = j;
+  } else if (code == nextCode) {
+    seqBuf[seqLength] = newChar;
+    ++seqLength;
+  } else {
+    error(getPos(), "Bad LZW stream - unexpected code");
+    eof = gTrue;
+    return gFalse;
   }
-#endif // HAVE_POPEN
-
-  //----- clean up
-  delete zCmd;
+  newChar = seqBuf[0];
+  if (first) {
+    first = gFalse;
+  } else {
+    table[nextCode].length = nextLength;
+    table[nextCode].head = prevCode;
+    table[nextCode].tail = newChar;
+    ++nextCode;
+    if (nextCode + early == 512)
+      nextBits = 10;
+    else if (nextCode + early == 1024)
+      nextBits = 11;
+    else if (nextCode + early == 2048)
+      nextBits = 12;
+  }
+  prevCode = code;
+
+  // reset buffer
+  seqIndex = 0;
 
-  //----- initialize buffer
-  bufPtr = bufEnd = buf;
+  return gTrue;
 }
 
-void LZWStream::dumpFile(FILE *f) {
-  int outCodeBits;             // size of output code
-  int outBits;                 // max output code
-  int outBuf[8];               // output buffer
-  int outData;                 // temporary output buffer
-  int inCode, outCode;         // input and output codes
-  int nextCode;                        // next code index
-  GBool eof;                   // set when EOF is reached
-  GBool clear;                 // set if table needs to be cleared
-  GBool first;                 // indicates first code word after clear
-  int i, j;
-
-  str->reset();
-
-  // magic number
-  fputc(0x1f, f);
-  fputc(0x9d, f);
-
-  // max code length, block mode flag
-  fputc(0x8c, f);
-
-  // init input side
-  inCodeBits = 9;
-  inputBuf = 0;
-  inputBits = 0;
-  eof = gFalse;
-
-  // init output side
-  outCodeBits = 9;
-
-  // clear table
-  first = gTrue;
+void LZWStream::clearTable() {
   nextCode = 258;
-
-  clear = gFalse;
-  do {
-    for (i = 0; i < 8; ++i) {
-      // check for table overflow
-      if (nextCode + early > 0x1001) {
-       inCode = 256;
-
-      // read input code
-      } else {
-       do {
-         inCode = getCode();
-         if (inCode == EOF) {
-           eof = gTrue;
-           inCode = 0;
-         }
-       } while (first && inCode == 256);
-      }
-
-      // compute output code
-      if (inCode < 256) {
-       outCode = inCode;
-      } else if (inCode == 256) {
-       outCode = 256;
-       clear = gTrue;
-      } else if (inCode == 257) {
-       outCode = 0;
-       eof = gTrue;
-      } else {
-       outCode = inCode - 1;
-      }
-      outBuf[i] = outCode;
-
-      // next code index
-      if (first)
-       first = gFalse;
-      else
-       ++nextCode;
-
-      // check input code size
-      if (nextCode + early == 0x200)
-       inCodeBits = 10;
-      else if (nextCode + early == 0x400) {
-       inCodeBits = 11;
-      } else if (nextCode + early == 0x800) {
-       inCodeBits = 12;
-      }
-
-      // check for eof/clear
-      if (eof)
-       break;
-      if (clear) {
-       i = 8;
-       break;
-      }
-    }
-
-    // write output block
-    outData = 0;
-    outBits = 0;
-    j = 0;
-    while (j < i || outBits > 0) {
-      if (outBits < 8 && j < i) {
-       outData = outData | (outBuf[j++] << outBits);
-       outBits += outCodeBits;
-      }
-      fputc(outData & 0xff, f);
-      outData >>= 8;
-      outBits -= 8;
-    }
-
-    // check output code size
-    if (nextCode - 1 == 512 ||
-       nextCode - 1 == 1024 ||
-       nextCode - 1 == 2048 ||
-       nextCode - 1 == 4096) {
-      outCodeBits = inCodeBits;
-    }
-
-    // clear table if necessary
-    if (clear) {
-      inCodeBits = 9;
-      outCodeBits = 9;
-      first = gTrue;
-      nextCode = 258;
-      clear = gFalse;
-    }
-  } while (!eof);
+  nextBits = 9;
+  seqIndex = seqLength = 0;
+  first = gTrue;
 }
 
 int LZWStream::getCode() {
   int c;
   int code;
 
-  while (inputBits < inCodeBits) {
+  while (inputBits < nextBits) {
     if ((c = str->getChar()) == EOF)
       return EOF;
     inputBuf = (inputBuf << 8) | (c & 0xff);
     inputBits += 8;
   }
-  code = (inputBuf >> (inputBits - inCodeBits)) & ((1 << inCodeBits) - 1);
-  inputBits -= inCodeBits;
+  code = (inputBuf >> (inputBits - nextBits)) & ((1 << nextBits) - 1);
+  inputBits -= nextBits;
   return code;
 }
 
-GBool LZWStream::fillBuf() {
-  int n;
-
-  if (!zPipe)
-    return gFalse;
-  if ((n = fread(buf, 1, 256, zPipe)) < 256) {
-#ifdef HAVE_POPEN
-    pclose(zPipe);
-#else
-    fclose(zPipe);
-#endif
-    zPipe = NULL;
-    unlink(zName->getCString());
-    delete zName;
-  }
-  bufPtr = buf;
-  bufEnd = buf + n;
-  return n > 0;
-}
-
-GString *LZWStream::getPSFilter(char *indent) {
+GString *LZWStream::getPSFilter(int psLevel, char *indent) {
   GString *s;
 
-  if (pred) {
+  if (psLevel < 2 || pred) {
     return NULL;
   }
-  if (!(s = str->getPSFilter(indent))) {
+  if (!(s = str->getPSFilter(psLevel, indent))) {
     return NULL;
   }
   s->append(indent)->append("/LZWDecode filter\n");
@@ -1295,10 +1205,13 @@ void RunLengthStream::reset() {
   eof = gFalse;
 }
 
-GString *RunLengthStream::getPSFilter(char *indent) {
+GString *RunLengthStream::getPSFilter(int psLevel, char *indent) {
   GString *s;
 
-  if (!(s = str->getPSFilter(indent))) {
+  if (psLevel < 2) {
+    return NULL;
+  }
+  if (!(s = str->getPSFilter(psLevel, indent))) {
     return NULL;
   }
   s->append(indent)->append("/RunLengthDecode filter\n");
@@ -1371,7 +1284,7 @@ CCITTFaxStream::~CCITTFaxStream() {
 }
 
 void CCITTFaxStream::reset() {
-  int n;
+  short code1;
 
   str->reset();
   eof = gFalse;
@@ -1383,16 +1296,13 @@ void CCITTFaxStream::reset() {
   a0 = 1;
   buf = EOF;
 
-  // get initial end-of-line marker and 2D encoding tag
-  if (endOfBlock) {
-    if (lookBits(12) == 0x001) {
-      eatBits(12);
-    }
-  } else {
-    for (n = 0; n < 11 && lookBits(n) == 0; ++n) ;
-    if (n == 11 && lookBits(12) == 0x001) {
-      eatBits(12);
-    }
+  // skip any initial zero bits and end-of-line marker, and get the 2D
+  // encoding tag
+  while ((code1 = lookBits(12)) == 0) {
+    eatBits(1);
+  }
+  if (code1 == 0x001) {
+    eatBits(12);
   }
   if (encoding > 0) {
     nextLine2D = !lookBits(1);
@@ -1403,10 +1313,7 @@ void CCITTFaxStream::reset() {
 int CCITTFaxStream::lookChar() {
   short code1, code2, code3;
   int a0New;
-#if 0
-  GBool err;
-#endif
-  GBool gotEOL;
+  GBool err, gotEOL;
   int ret;
   int bits, i;
 
@@ -1416,9 +1323,7 @@ int CCITTFaxStream::lookChar() {
   }
 
   // read the next row
-#if 0
   err = gFalse;
-#endif
   if (codingLine[a0] >= columns) {
 
     // 2-D encoding
@@ -1455,12 +1360,14 @@ int CCITTFaxStream::lookChar() {
              code2 += code3 = getWhiteCode();
            } while (code3 >= 64);
          }
-         codingLine[a0 + 1] = a0New + code1;
-         ++a0;
-         a0New = codingLine[a0 + 1] = codingLine[a0] + code2;
-         ++a0;
-         while (refLine[b1] <= codingLine[a0] && refLine[b1] < columns)
-           b1 += 2;
+         if (code1 > 0 || code2 > 0) {
+           codingLine[a0 + 1] = a0New + code1;
+           ++a0;
+           a0New = codingLine[a0 + 1] = codingLine[a0] + code2;
+           ++a0;
+           while (refLine[b1] <= codingLine[a0] && refLine[b1] < columns)
+             b1 += 2;
+         }
          break;
        case twoDimVert0:
          a0New = codingLine[++a0] = refLine[b1];
@@ -1518,13 +1425,8 @@ int CCITTFaxStream::lookChar() {
          return EOF;
        default:
          error(getPos(), "Bad 2D code %04x in CCITTFax stream", code1);
-#if 0
          err = gTrue;
          break;
-#else
-         eof = gTrue;
-         return EOF;
-#endif
        }
       } while (codingLine[a0] < columns);
 
@@ -1553,9 +1455,12 @@ int CCITTFaxStream::lookChar() {
 
     if (codingLine[a0] != columns) {
       error(getPos(), "CCITTFax row is wrong length (%d)", codingLine[a0]);
-#if 0
+      // force the row to be the correct length
+      while (codingLine[a0] > columns) {
+       --a0;
+      }
+      codingLine[++a0] = columns;
       err = gTrue;
-#endif
     }
 
     // byte-align the row
@@ -1611,30 +1516,25 @@ int CCITTFaxStream::lookChar() {
        }
        eof = gTrue;
       }
-    }
 
-#if 0
-    // This looks for an end-of-line marker after an error, however
-    // some (most?) CCITT streams in PDF files don't use end-of-line
-    // markers, and the just-plow-on technique works better in those
-    // cases.
-    else if (err) {
+    // look for an end-of-line marker after an error -- we only do
+    // this if we know the stream contains end-of-line markers because
+    // the "just plow on" technique tends to work better otherwise
+    } else if (err && endOfLine) {
       do {
        if (code1 == EOF) {
          eof = gTrue;
          return EOF;
        }
        eatBits(1);
-       code1 = look13Bits();
+       code1 = lookBits(13);
       } while ((code1 >> 1) != 0x001);
       eatBits(12); 
-      codingLine[++a0] = columns;
       if (encoding > 0) {
        eatBits(1);
        nextLine2D = !(code1 & 1);
       }
     }
-#endif
 
     a0 = 0;
     outputBits = codingLine[1] - codingLine[0];
@@ -1845,11 +1745,14 @@ short CCITTFaxStream::lookBits(int n) {
   return (inputBuf >> (inputBits - n)) & (0xffff >> (16 - n));
 }
 
-GString *CCITTFaxStream::getPSFilter(char *indent) {
+GString *CCITTFaxStream::getPSFilter(int psLevel, char *indent) {
   GString *s;
   char s1[50];
 
-  if (!(s = str->getPSFilter(indent))) {
+  if (psLevel < 2) {
+    return NULL;
+  }
+  if (!(s = str->getPSFilter(psLevel, indent))) {
     return NULL;
   }
   s->append(indent)->append("<< ");
@@ -1888,7 +1791,6 @@ GBool CCITTFaxStream::isBinary(GBool last) {
 //------------------------------------------------------------------------
 
 // IDCT constants (20.12 fixed point format)
-#ifndef FP_IDCT
 #define dctCos1    4017                // cos(pi/16)
 #define dctSin1     799                // sin(pi/16)
 #define dctCos3    3406                // cos(3*pi/16)
@@ -1897,19 +1799,6 @@ GBool CCITTFaxStream::isBinary(GBool last) {
 #define dctSin6    3784                // sin(6*pi/16)
 #define dctSqrt2   5793                // sqrt(2)
 #define dctSqrt1d2 2896                // sqrt(2) / 2
-#endif
-
-// IDCT constants
-#ifdef FP_IDCT
-#define dctCos1    0.98078528  // cos(pi/16)
-#define dctSin1    0.19509032  // sin(pi/16)
-#define dctCos3    0.83146961  // cos(3*pi/16)
-#define dctSin3    0.55557023  // sin(3*pi/16)
-#define dctCos6    0.38268343  // cos(6*pi/16)
-#define dctSin6    0.92387953  // sin(6*pi/16)
-#define dctSqrt2   1.41421356  // sqrt(2)
-#define dctSqrt1d2 0.70710678  // sqrt(2) / 2
-#endif
 
 // color conversion parameters (16.16 fixed point format)
 #define dctCrToR   91881       //  1.4020
@@ -1945,14 +1834,18 @@ DCTStream::DCTStream(Stream *strA):
     FilterStream(strA) {
   int i, j;
 
+  progressive = interleaved = gFalse;
   width = height = 0;
   mcuWidth = mcuHeight = 0;
   numComps = 0;
   comp = 0;
   x = y = dy = 0;
-  for (i = 0; i < 4; ++i)
-    for (j = 0; j < 32; ++j)
+  for (i = 0; i < 4; ++i) {
+    for (j = 0; j < 32; ++j) {
       rowBuf[i][j] = NULL;
+    }
+    frameBuf[i] = NULL;
+  }
 
   if (!dctClipInit) {
     for (i = -256; i < 0; ++i)
@@ -1969,53 +1862,178 @@ DCTStream::~DCTStream() {
   int i, j;
 
   delete str;
-  for (i = 0; i < numComps; ++i)
-    for (j = 0; j < mcuHeight; ++j)
-      gfree(rowBuf[i][j]);
+  if (progressive || !interleaved) {
+    for (i = 0; i < numComps; ++i) {
+      gfree(frameBuf[i]);
+    }
+  } else {
+    for (i = 0; i < numComps; ++i) {
+      for (j = 0; j < mcuHeight; ++j) {
+       gfree(rowBuf[i][j]);
+      }
+    }
+  }
 }
 
 void DCTStream::reset() {
+  int minHSample, minVSample;
+  int i, j;
+
   str->reset();
+
+  progressive = interleaved = gFalse;
+  width = height = 0;
+  numComps = 0;
+  numQuantTables = 0;
+  numDCHuffTables = 0;
+  numACHuffTables = 0;
+  colorXform = 0;
+  gotJFIFMarker = gFalse;
+  gotAdobeMarker = gFalse;
+  restartInterval = 0;
+
   if (!readHeader()) {
     y = height;
     return;
   }
-  restartMarker = 0xd0;
-  restart();
-}
 
-int DCTStream::getChar() {
-  int c;
-
-  c = lookChar();
-  if (c == EOF)
-    return EOF;
-  if (++comp == numComps) {
-    comp = 0;
-    if (++x == width) {
-      x = 0;
-      ++y;
-      ++dy;
-    }
+  // compute MCU size
+  mcuWidth = minHSample = compInfo[0].hSample;
+  mcuHeight = minVSample = compInfo[0].vSample;
+  for (i = 1; i < numComps; ++i) {
+    if (compInfo[i].hSample < minHSample)
+      minHSample = compInfo[i].hSample;
+    if (compInfo[i].vSample < minVSample)
+      minVSample = compInfo[i].vSample;
+    if (compInfo[i].hSample > mcuWidth)
+      mcuWidth = compInfo[i].hSample;
+    if (compInfo[i].vSample > mcuHeight)
+      mcuHeight = compInfo[i].vSample;
   }
-  if (y == height)
-    readTrailer();
-  return c;
-}
+  for (i = 0; i < numComps; ++i) {
+    compInfo[i].hSample /= minHSample;
+    compInfo[i].vSample /= minVSample;
+  }
+  mcuWidth = (mcuWidth / minHSample) * 8;
+  mcuHeight = (mcuHeight / minVSample) * 8;
 
-int DCTStream::lookChar() {
-  if (y >= height)
-    return EOF;
-  if (dy >= mcuHeight) {
-    if (!readMCURow()) {
-      y = height;
-      return EOF;
+  // figure out color transform
+  if (!gotAdobeMarker && numComps == 3) {
+    if (gotJFIFMarker) {
+      colorXform = 1;
+    } else if (compInfo[0].id == 82 && compInfo[1].id == 71 &&
+              compInfo[2].id == 66) { // ASCII "RGB"
+      colorXform = 0;
+    } else {
+      colorXform = 1;
+    }
+  }
+
+  if (progressive || !interleaved) {
+
+    // allocate a buffer for the whole image
+    bufWidth = ((width + mcuWidth - 1) / mcuWidth) * mcuWidth;
+    bufHeight = ((height + mcuHeight - 1) / mcuHeight) * mcuHeight;
+    for (i = 0; i < numComps; ++i) {
+      frameBuf[i] = (int *)gmalloc(bufWidth * bufHeight * sizeof(int));
+      memset(frameBuf[i], 0, bufWidth * bufHeight * sizeof(int));
+    }
+
+    // read the image data
+    do {
+      restartMarker = 0xd0;
+      restart();
+      readScan();
+    } while (readHeader());
+
+    // decode
+    decodeImage();
+
+    // initialize counters
+    comp = 0;
+    x = 0;
+    y = 0;
+
+  } else {
+
+    // allocate a buffer for one row of MCUs
+    bufWidth = ((width + mcuWidth - 1) / mcuWidth) * mcuWidth;
+    for (i = 0; i < numComps; ++i) {
+      for (j = 0; j < mcuHeight; ++j) {
+       rowBuf[i][j] = (Guchar *)gmalloc(bufWidth * sizeof(Guchar));
+      }
     }
+
+    // initialize counters
     comp = 0;
     x = 0;
-    dy = 0;
+    y = 0;
+    dy = mcuHeight;
+
+    restartMarker = 0xd0;
+    restart();
+  }
+}
+
+int DCTStream::getChar() {
+  int c;
+
+  if (y >= height) {
+    return EOF;
+  }
+  if (progressive || !interleaved) {
+    c = frameBuf[comp][y * bufWidth + x];
+    if (++comp == numComps) {
+      comp = 0;
+      if (++x == width) {
+       x = 0;
+       ++y;
+      }
+    }
+  } else {
+    if (dy >= mcuHeight) {
+      if (!readMCURow()) {
+       y = height;
+       return EOF;
+      }
+      comp = 0;
+      x = 0;
+      dy = 0;
+    }
+    c = rowBuf[comp][dy][x];
+    if (++comp == numComps) {
+      comp = 0;
+      if (++x == width) {
+       x = 0;
+       ++y;
+       ++dy;
+       if (y == height) {
+         readTrailer();
+       }
+      }
+    }
+  }
+  return c;
+}
+
+int DCTStream::lookChar() {
+  if (y >= height) {
+    return EOF;
+  }
+  if (progressive || !interleaved) {
+    return frameBuf[comp][y * bufWidth + x];
+  } else {
+    if (dy >= mcuHeight) {
+      if (!readMCURow()) {
+       y = height;
+       return EOF;
+      }
+      comp = 0;
+      x = 0;
+      dy = 0;
+    }
+    return rowBuf[comp][dy][x];
   }
-  return rowBuf[comp][dy][x];
 }
 
 void DCTStream::restart() {
@@ -2023,12 +2041,16 @@ void DCTStream::restart() {
 
   inputBits = 0;
   restartCtr = restartInterval;
-  for (i = 0; i < numComps; ++i)
+  for (i = 0; i < numComps; ++i) {
     compInfo[i].prevDC = 0;
+  }
+  eobRun = 0;
 }
 
+// Read one row of MCUs from a sequential JPEG stream.
 GBool DCTStream::readMCURow() {
-  Guchar data[64];
+  int data1[64];
+  Guchar data2[64];
   Guchar *p1, *p2;
   int pY, pCb, pCr, pR, pG, pB;
   int h, v, horiz, vert, hSub, vSub;
@@ -2059,36 +2081,38 @@ GBool DCTStream::readMCURow() {
       vSub = vert / 8;
       for (y2 = 0; y2 < mcuHeight; y2 += vert) {
        for (x2 = 0; x2 < mcuWidth; x2 += horiz) {
-         if (!readDataUnit(&dcHuffTables[compInfo[cc].dcHuffTable],
-                           &acHuffTables[compInfo[cc].acHuffTable],
-                           quantTables[compInfo[cc].quantTable],
+         if (!readDataUnit(&dcHuffTables[scanInfo.dcHuffTable[cc]],
+                           &acHuffTables[scanInfo.acHuffTable[cc]],
                            &compInfo[cc].prevDC,
-                           data))
+                           data1)) {
            return gFalse;
+         }
+         transformDataUnit(quantTables[compInfo[cc].quantTable],
+                           data1, data2);
          if (hSub == 1 && vSub == 1) {
            for (y3 = 0, i = 0; y3 < 8; ++y3, i += 8) {
              p1 = &rowBuf[cc][y2+y3][x1+x2];
-             p1[0] = data[i];
-             p1[1] = data[i+1];
-             p1[2] = data[i+2];
-             p1[3] = data[i+3];
-             p1[4] = data[i+4];
-             p1[5] = data[i+5];
-             p1[6] = data[i+6];
-             p1[7] = data[i+7];
+             p1[0] = data2[i];
+             p1[1] = data2[i+1];
+             p1[2] = data2[i+2];
+             p1[3] = data2[i+3];
+             p1[4] = data2[i+4];
+             p1[5] = data2[i+5];
+             p1[6] = data2[i+6];
+             p1[7] = data2[i+7];
            }
          } else if (hSub == 2 && vSub == 2) {
            for (y3 = 0, i = 0; y3 < 16; y3 += 2, i += 8) {
              p1 = &rowBuf[cc][y2+y3][x1+x2];
              p2 = &rowBuf[cc][y2+y3+1][x1+x2];
-             p1[0] = p1[1] = p2[0] = p2[1] = data[i];
-             p1[2] = p1[3] = p2[2] = p2[3] = data[i+1];
-             p1[4] = p1[5] = p2[4] = p2[5] = data[i+2];
-             p1[6] = p1[7] = p2[6] = p2[7] = data[i+3];
-             p1[8] = p1[9] = p2[8] = p2[9] = data[i+4];
-             p1[10] = p1[11] = p2[10] = p2[11] = data[i+5];
-             p1[12] = p1[13] = p2[12] = p2[13] = data[i+6];
-             p1[14] = p1[15] = p2[14] = p2[15] = data[i+7];
+             p1[0] = p1[1] = p2[0] = p2[1] = data2[i];
+             p1[2] = p1[3] = p2[2] = p2[3] = data2[i+1];
+             p1[4] = p1[5] = p2[4] = p2[5] = data2[i+2];
+             p1[6] = p1[7] = p2[6] = p2[7] = data2[i+3];
+             p1[8] = p1[9] = p2[8] = p2[9] = data2[i+4];
+             p1[10] = p1[11] = p2[10] = p2[11] = data2[i+5];
+             p1[12] = p1[13] = p2[12] = p2[13] = data2[i+6];
+             p1[14] = p1[15] = p2[14] = p2[15] = data2[i+7];
            }
          } else {
            i = 0;
@@ -2096,7 +2120,7 @@ GBool DCTStream::readMCURow() {
              for (x3 = 0, x4 = 0; x3 < 8; ++x3, x4 += hSub) {
                for (y5 = 0; y5 < vSub; ++y5)
                  for (x5 = 0; x5 < hSub; ++x5)
-                   rowBuf[cc][y2+y4+y5][x1+x2+x4+x5] = data[i];
+                   rowBuf[cc][y2+y4+y5][x1+x2+x4+x5] = data2[i];
                ++i;
              }
            }
@@ -2144,71 +2168,469 @@ GBool DCTStream::readMCURow() {
   return gTrue;
 }
 
-// This IDCT algorithm is taken from:
-//   Christoph Loeffler, Adriaan Ligtenberg, George S. Moschytz,
-//   "Practical Fast 1-D DCT Algorithms with 11 Multiplications",
-//   IEEE Intl. Conf. on Acoustics, Speech & Signal Processing, 1989,
-//   988-991.
-// The stage numbers mentioned in the comments refer to Figure 1 in this
-// paper.
-#ifndef FP_IDCT
+// Read one scan from a progressive or non-interleaved JPEG stream.
+void DCTStream::readScan() {
+  int data[64];
+  int x1, y1, dx1, dy1, x2, y2, y3, cc, i;
+  int h, v, horiz, vert, vSub;
+  int *p1;
+  int c;
+
+  if (scanInfo.numComps == 1) {
+    for (cc = 0; cc < numComps; ++cc) {
+      if (scanInfo.comp[cc]) {
+       break;
+      }
+    }
+    dx1 = mcuWidth / compInfo[cc].hSample;
+    dy1 = mcuHeight / compInfo[cc].vSample;
+  } else {
+    dx1 = mcuWidth;
+    dy1 = mcuHeight;
+  }
+
+  for (y1 = 0; y1 < height; y1 += dy1) {
+    for (x1 = 0; x1 < width; x1 += dx1) {
+
+      // deal with restart marker
+      if (restartInterval > 0 && restartCtr == 0) {
+       c = readMarker();
+       if (c != restartMarker) {
+         error(getPos(), "Bad DCT data: incorrect restart marker");
+         return;
+       }
+       if (++restartMarker == 0xd8) {
+         restartMarker = 0xd0;
+       }
+       restart();
+      }
+
+      // read one MCU
+      for (cc = 0; cc < numComps; ++cc) {
+       if (!scanInfo.comp[cc]) {
+         continue;
+       }
+
+       h = compInfo[cc].hSample;
+       v = compInfo[cc].vSample;
+       horiz = mcuWidth / h;
+       vert = mcuHeight / v;
+       vSub = vert / 8;
+       for (y2 = 0; y2 < dy1; y2 += vert) {
+         for (x2 = 0; x2 < dx1; x2 += horiz) {
+
+           // pull out the current values
+           p1 = &frameBuf[cc][(y1+y2) * bufWidth + (x1+x2)];
+           for (y3 = 0, i = 0; y3 < 8; ++y3, i += 8) {
+             data[i] = p1[0];
+             data[i+1] = p1[1];
+             data[i+2] = p1[2];
+             data[i+3] = p1[3];
+             data[i+4] = p1[4];
+             data[i+5] = p1[5];
+             data[i+6] = p1[6];
+             data[i+7] = p1[7];
+             p1 += bufWidth * vSub;
+           }
+
+           // read one data unit
+           if (progressive) {
+             if (!readProgressiveDataUnit(
+                      &dcHuffTables[scanInfo.dcHuffTable[cc]],
+                      &acHuffTables[scanInfo.acHuffTable[cc]],
+                      &compInfo[cc].prevDC,
+                      data)) {
+               return;
+             }
+           } else {
+             if (!readDataUnit(&dcHuffTables[scanInfo.dcHuffTable[cc]],
+                               &acHuffTables[scanInfo.acHuffTable[cc]],
+                               &compInfo[cc].prevDC,
+                               data)) {
+               return;
+             }
+           }
+
+           // add the data unit into frameBuf
+           p1 = &frameBuf[cc][(y1+y2) * bufWidth + (x1+x2)];
+           for (y3 = 0, i = 0; y3 < 8; ++y3, i += 8) {
+             p1[0] = data[i];
+             p1[1] = data[i+1];
+             p1[2] = data[i+2];
+             p1[3] = data[i+3];
+             p1[4] = data[i+4];
+             p1[5] = data[i+5];
+             p1[6] = data[i+6];
+             p1[7] = data[i+7];
+             p1 += bufWidth * vSub;
+           }
+         }
+       }
+      }
+      --restartCtr;
+    }
+  }
+}
+
+// Read one data unit from a sequential JPEG stream.
 GBool DCTStream::readDataUnit(DCTHuffTable *dcHuffTable,
                              DCTHuffTable *acHuffTable,
-                             Guchar quantTable[64], int *prevDC,
-                             Guchar data[64]) {
-  int tmp1[64];
-  int v0, v1, v2, v3, v4, v5, v6, v7, t;
+                             int *prevDC, int data[64]) {
   int run, size, amp;
   int c;
   int i, j;
 
-  // Huffman decode and dequantize
-  size = readHuffSym(dcHuffTable);
-  if (size == 9999)
+  if ((size = readHuffSym(dcHuffTable)) == 9999) {
     return gFalse;
+  }
   if (size > 0) {
-    amp = readAmp(size);
-    if (amp == 9999)
+    if ((amp = readAmp(size)) == 9999) {
       return gFalse;
+    }
   } else {
     amp = 0;
   }
-  tmp1[0] = (*prevDC += amp) * quantTable[0];
-  for (i = 1; i < 64; ++i)
-    tmp1[i] = 0;
+  data[0] = *prevDC += amp;
+  for (i = 1; i < 64; ++i) {
+    data[i] = 0;
+  }
   i = 1;
   while (i < 64) {
     run = 0;
-    while ((c = readHuffSym(acHuffTable)) == 0xf0 && run < 0x30)
+    while ((c = readHuffSym(acHuffTable)) == 0xf0 && run < 0x30) {
       run += 0x10;
-    if (c == 9999)
+    }
+    if (c == 9999) {
       return gFalse;
+    }
     if (c == 0x00) {
       break;
     } else {
       run += (c >> 4) & 0x0f;
       size = c & 0x0f;
       amp = readAmp(size);
-      if (amp == 9999)
+      if (amp == 9999) {
        return gFalse;
+      }
       i += run;
+      if (i < 64) {
+       j = dctZigZag[i++];
+       data[j] = amp;
+      }
+    }
+  }
+  return gTrue;
+}
+
+// Read one data unit from a sequential JPEG stream.
+GBool DCTStream::readProgressiveDataUnit(DCTHuffTable *dcHuffTable,
+                                        DCTHuffTable *acHuffTable,
+                                        int *prevDC, int data[64]) {
+  int run, size, amp, bit, c;
+  int i, j, k;
+
+  // get the DC coefficient
+  i = scanInfo.firstCoeff;
+  if (i == 0) {
+    if (scanInfo.ah == 0) {
+      if ((size = readHuffSym(dcHuffTable)) == 9999) {
+       return gFalse;
+      }
+      if (size > 0) {
+       if ((amp = readAmp(size)) == 9999) {
+         return gFalse;
+       }
+      } else {
+       amp = 0;
+      }
+      data[0] += (*prevDC += amp) << scanInfo.al;
+    } else {
+      if ((bit = readBit()) == 9999) {
+       return gFalse;
+      }
+      data[0] += bit << scanInfo.al;
+    }
+    ++i;
+  }
+  if (scanInfo.lastCoeff == 0) {
+    return gTrue;
+  }
+
+  // check for an EOB run
+  if (eobRun > 0) {
+    while (i <= scanInfo.lastCoeff) {
       j = dctZigZag[i++];
-      tmp1[j] = amp * quantTable[j];
+      if (data[j] != 0) {
+       if ((bit = readBit()) == EOF) {
+         return gFalse;
+       }
+       if (bit) {
+         data[j] += 1 << scanInfo.al;
+       }
+      }
+    }
+    --eobRun;
+    return gTrue;
+  }
+
+  // read the AC coefficients
+  while (i <= scanInfo.lastCoeff) {
+    if ((c = readHuffSym(acHuffTable)) == 9999) {
+      return gFalse;
+    }
+
+    // ZRL
+    if (c == 0xf0) {
+      k = 0;
+      while (k < 16) {
+       j = dctZigZag[i++];
+       if (data[j] == 0) {
+         ++k;
+       } else {
+         if ((bit = readBit()) == EOF) {
+           return gFalse;
+         }
+         if (bit) {
+           data[j] += 1 << scanInfo.al;
+         }
+       }
+      }
+
+    // EOB run
+    } else if ((c & 0x0f) == 0x00) {
+      j = c >> 4;
+      eobRun = 0;
+      for (k = 0; k < j; ++k) {
+       if ((bit = readBit()) == EOF) {
+         return gFalse;
+       }
+       eobRun = (eobRun << 1) | bit;
+      }
+      eobRun += 1 << j;
+      while (i <= scanInfo.lastCoeff) {
+       j = dctZigZag[i++];
+       if (data[j] != 0) {
+         if ((bit = readBit()) == EOF) {
+           return gFalse;
+         }
+         if (bit) {
+           data[j] += 1 << scanInfo.al;
+         }
+       }
+      }
+      --eobRun;
+      break;
+
+    // zero run and one AC coefficient
+    } else {
+      run = (c >> 4) & 0x0f;
+      size = c & 0x0f;
+      if ((amp = readAmp(size)) == 9999) {
+       return gFalse;
+      }
+      k = 0;
+      do {
+       j = dctZigZag[i++];
+       while (data[j] != 0) {
+         if ((bit = readBit()) == EOF) {
+           return gFalse;
+         }
+         if (bit) {
+           data[j] += 1 << scanInfo.al;
+         }
+         j = dctZigZag[i++];
+       }
+       ++k;
+      } while (k <= run);
+      data[j] = amp << scanInfo.al;
+    }
+  }
+
+  return gTrue;
+}
+
+// Decode a progressive JPEG image.
+void DCTStream::decodeImage() {
+  int dataIn[64];
+  Guchar dataOut[64];
+  Guchar *quantTable;
+  int pY, pCb, pCr, pR, pG, pB;
+  int x1, y1, x2, y2, x3, y3, x4, y4, x5, y5, cc, i;
+  int h, v, horiz, vert, hSub, vSub;
+  int *p0, *p1, *p2;
+
+  for (y1 = 0; y1 < bufHeight; y1 += mcuHeight) {
+    for (x1 = 0; x1 < bufWidth; x1 += mcuWidth) {
+      for (cc = 0; cc < numComps; ++cc) {
+       quantTable = quantTables[compInfo[cc].quantTable];
+       h = compInfo[cc].hSample;
+       v = compInfo[cc].vSample;
+       horiz = mcuWidth / h;
+       vert = mcuHeight / v;
+       hSub = horiz / 8;
+       vSub = vert / 8;
+       for (y2 = 0; y2 < mcuHeight; y2 += vert) {
+         for (x2 = 0; x2 < mcuWidth; x2 += horiz) {
+
+           // pull out the coded data unit
+           p1 = &frameBuf[cc][(y1+y2) * bufWidth + (x1+x2)];
+           for (y3 = 0, i = 0; y3 < 8; ++y3, i += 8) {
+             dataIn[i]   = p1[0];
+             dataIn[i+1] = p1[1];
+             dataIn[i+2] = p1[2];
+             dataIn[i+3] = p1[3];
+             dataIn[i+4] = p1[4];
+             dataIn[i+5] = p1[5];
+             dataIn[i+6] = p1[6];
+             dataIn[i+7] = p1[7];
+             p1 += bufWidth * vSub;
+           }
+
+           // transform
+           transformDataUnit(quantTable, dataIn, dataOut);
+
+           // store back into frameBuf, doing replication for
+           // subsampled components
+           p1 = &frameBuf[cc][(y1+y2) * bufWidth + (x1+x2)];
+           if (hSub == 1 && vSub == 1) {
+             for (y3 = 0, i = 0; y3 < 8; ++y3, i += 8) {
+               p1[0] = dataOut[i] & 0xff;
+               p1[1] = dataOut[i+1] & 0xff;
+               p1[2] = dataOut[i+2] & 0xff;
+               p1[3] = dataOut[i+3] & 0xff;
+               p1[4] = dataOut[i+4] & 0xff;
+               p1[5] = dataOut[i+5] & 0xff;
+               p1[6] = dataOut[i+6] & 0xff;
+               p1[7] = dataOut[i+7] & 0xff;
+               p1 += bufWidth;
+             }
+           } else if (hSub == 2 && vSub == 2) {
+             p2 = p1 + bufWidth;
+             for (y3 = 0, i = 0; y3 < 16; y3 += 2, i += 8) {
+               p1[0] = p1[1] = p2[0] = p2[1] = dataOut[i] & 0xff;
+               p1[2] = p1[3] = p2[2] = p2[3] = dataOut[i+1] & 0xff;
+               p1[4] = p1[5] = p2[4] = p2[5] = dataOut[i+2] & 0xff;
+               p1[6] = p1[7] = p2[6] = p2[7] = dataOut[i+3] & 0xff;
+               p1[8] = p1[9] = p2[8] = p2[9] = dataOut[i+4] & 0xff;
+               p1[10] = p1[11] = p2[10] = p2[11] = dataOut[i+5] & 0xff;
+               p1[12] = p1[13] = p2[12] = p2[13] = dataOut[i+6] & 0xff;
+               p1[14] = p1[15] = p2[14] = p2[15] = dataOut[i+7] & 0xff;
+               p1 += bufWidth * 2;
+               p2 += bufWidth * 2;
+             }
+           } else {
+             i = 0;
+             for (y3 = 0, y4 = 0; y3 < 8; ++y3, y4 += vSub) {
+               for (x3 = 0, x4 = 0; x3 < 8; ++x3, x4 += hSub) {
+                 p2 = p1 + x4;
+                 for (y5 = 0; y5 < vSub; ++y5) {
+                   for (x5 = 0; x5 < hSub; ++x5) {
+                     p2[x5] = dataOut[i] & 0xff;
+                   }
+                   p2 += bufWidth;
+                 }
+                 ++i;
+               }
+               p1 += bufWidth * vSub;
+             }
+           }
+         }
+       }
+      }
+
+      // color space conversion
+      if (colorXform) {
+       // convert YCbCr to RGB
+       if (numComps == 3) {
+         for (y2 = 0; y2 < mcuHeight; ++y2) {
+           p0 = &frameBuf[0][(y1+y2) * bufWidth + x1];
+           p1 = &frameBuf[1][(y1+y2) * bufWidth + x1];
+           p2 = &frameBuf[2][(y1+y2) * bufWidth + x1];
+           for (x2 = 0; x2 < mcuWidth; ++x2) {
+             pY = *p0;
+             pCb = *p1 - 128;
+             pCr = *p2 - 128;
+             pR = ((pY << 16) + dctCrToR * pCr + 32768) >> 16;
+             *p0++ = dctClip[dctClipOffset + pR];
+             pG = ((pY << 16) + dctCbToG * pCb + dctCrToG * pCr +
+                   32768) >> 16;
+             *p1++ = dctClip[dctClipOffset + pG];
+             pB = ((pY << 16) + dctCbToB * pCb + 32768) >> 16;
+             *p2++ = dctClip[dctClipOffset + pB];
+           }
+         }
+       // convert YCbCrK to CMYK (K is passed through unchanged)
+       } else if (numComps == 4) {
+         for (y2 = 0; y2 < mcuHeight; ++y2) {
+           p0 = &frameBuf[0][(y1+y2) * bufWidth + x1];
+           p1 = &frameBuf[1][(y1+y2) * bufWidth + x1];
+           p2 = &frameBuf[2][(y1+y2) * bufWidth + x1];
+           for (x2 = 0; x2 < mcuWidth; ++x2) {
+             pY = *p0;
+             pCb = *p1 - 128;
+             pCr = *p2 - 128;
+             pR = ((pY << 16) + dctCrToR * pCr + 32768) >> 16;
+             *p0++ = 255 - dctClip[dctClipOffset + pR];
+             pG = ((pY << 16) + dctCbToG * pCb + dctCrToG * pCr +
+                   32768) >> 16;
+             *p1++ = 255 - dctClip[dctClipOffset + pG];
+             pB = ((pY << 16) + dctCbToB * pCb + 32768) >> 16;
+             *p2++ = 255 - dctClip[dctClipOffset + pB];
+           }
+         }
+       }
+      }
     }
   }
+}
+
+// Transform one data unit -- this performs the dequantization and
+// IDCT steps.  This IDCT algorithm is taken from:
+//   Christoph Loeffler, Adriaan Ligtenberg, George S. Moschytz,
+//   "Practical Fast 1-D DCT Algorithms with 11 Multiplications",
+//   IEEE Intl. Conf. on Acoustics, Speech & Signal Processing, 1989,
+//   988-991.
+// The stage numbers mentioned in the comments refer to Figure 1 in this
+// paper.
+void DCTStream::transformDataUnit(Guchar *quantTable,
+                                 int dataIn[64], Guchar dataOut[64]) {
+  int v0, v1, v2, v3, v4, v5, v6, v7, t;
+  int *p;
+  int i;
+
+  // dequant
+  for (i = 0; i < 64; ++i) {
+    dataIn[i] *= quantTable[i];
+  }
 
   // inverse DCT on rows
   for (i = 0; i < 64; i += 8) {
+    p = dataIn + i;
+
+    // check for all-zero AC coefficients
+    if (p[1] == 0 && p[2] == 0 && p[3] == 0 &&
+       p[4] == 0 && p[5] == 0 && p[6] == 0 && p[7] == 0) {
+      t = (dctSqrt2 * p[0] + 512) >> 10;
+      p[0] = t;
+      p[1] = t;
+      p[2] = t;
+      p[3] = t;
+      p[4] = t;
+      p[5] = t;
+      p[6] = t;
+      p[7] = t;
+      continue;
+    }
 
     // stage 4
-    v0 = (dctSqrt2 * tmp1[i+0] + 128) >> 8;
-    v1 = (dctSqrt2 * tmp1[i+4] + 128) >> 8;
-    v2 = tmp1[i+2];
-    v3 = tmp1[i+6];
-    v4 = (dctSqrt1d2 * (tmp1[i+1] - tmp1[i+7]) + 128) >> 8;
-    v7 = (dctSqrt1d2 * (tmp1[i+1] + tmp1[i+7]) + 128) >> 8;
-    v5 = tmp1[i+3] << 4;
-    v6 = tmp1[i+5] << 4;
+    v0 = (dctSqrt2 * p[0] + 128) >> 8;
+    v1 = (dctSqrt2 * p[4] + 128) >> 8;
+    v2 = p[2];
+    v3 = p[6];
+    v4 = (dctSqrt1d2 * (p[1] - p[7]) + 128) >> 8;
+    v7 = (dctSqrt1d2 * (p[1] + p[7]) + 128) >> 8;
+    v5 = p[3] << 4;
+    v6 = p[5] << 4;
 
     // stage 3
     t = (v0 - v1+ 1) >> 1;
@@ -2239,28 +2661,44 @@ GBool DCTStream::readDataUnit(DCTHuffTable *dcHuffTable,
     v6 = t;
 
     // stage 1
-    tmp1[i+0] = v0 + v7;
-    tmp1[i+7] = v0 - v7;
-    tmp1[i+1] = v1 + v6;
-    tmp1[i+6] = v1 - v6;
-    tmp1[i+2] = v2 + v5;
-    tmp1[i+5] = v2 - v5;
-    tmp1[i+3] = v3 + v4;
-    tmp1[i+4] = v3 - v4;
+    p[0] = v0 + v7;
+    p[7] = v0 - v7;
+    p[1] = v1 + v6;
+    p[6] = v1 - v6;
+    p[2] = v2 + v5;
+    p[5] = v2 - v5;
+    p[3] = v3 + v4;
+    p[4] = v3 - v4;
   }
 
   // inverse DCT on columns
   for (i = 0; i < 8; ++i) {
+    p = dataIn + i;
+
+    // check for all-zero AC coefficients
+    if (p[1*8] == 0 && p[2*8] == 0 && p[3*8] == 0 &&
+       p[4*8] == 0 && p[5*8] == 0 && p[6*8] == 0 && p[7*8] == 0) {
+      t = (dctSqrt2 * dataIn[i+0] + 8192) >> 14;
+      p[0*8] = t;
+      p[1*8] = t;
+      p[2*8] = t;
+      p[3*8] = t;
+      p[4*8] = t;
+      p[5*8] = t;
+      p[6*8] = t;
+      p[7*8] = t;
+      continue;
+    }
 
     // stage 4
-    v0 = (dctSqrt2 * tmp1[0*8+i] + 2048) >> 12;
-    v1 = (dctSqrt2 * tmp1[4*8+i] + 2048) >> 12;
-    v2 = tmp1[2*8+i];
-    v3 = tmp1[6*8+i];
-    v4 = (dctSqrt1d2 * (tmp1[1*8+i] - tmp1[7*8+i]) + 2048) >> 12;
-    v7 = (dctSqrt1d2 * (tmp1[1*8+i] + tmp1[7*8+i]) + 2048) >> 12;
-    v5 = tmp1[3*8+i];
-    v6 = tmp1[5*8+i];
+    v0 = (dctSqrt2 * p[0*8] + 2048) >> 12;
+    v1 = (dctSqrt2 * p[4*8] + 2048) >> 12;
+    v2 = p[2*8];
+    v3 = p[6*8];
+    v4 = (dctSqrt1d2 * (p[1*8] - p[7*8]) + 2048) >> 12;
+    v7 = (dctSqrt1d2 * (p[1*8] + p[7*8]) + 2048) >> 12;
+    v5 = p[3*8];
+    v6 = p[5*8];
 
     // stage 3
     t = (v0 - v1 + 1) >> 1;
@@ -2291,181 +2729,21 @@ GBool DCTStream::readDataUnit(DCTHuffTable *dcHuffTable,
     v6 = t;
 
     // stage 1
-    tmp1[0*8+i] = v0 + v7;
-    tmp1[7*8+i] = v0 - v7;
-    tmp1[1*8+i] = v1 + v6;
-    tmp1[6*8+i] = v1 - v6;
-    tmp1[2*8+i] = v2 + v5;
-    tmp1[5*8+i] = v2 - v5;
-    tmp1[3*8+i] = v3 + v4;
-    tmp1[4*8+i] = v3 - v4;
+    p[0*8] = v0 + v7;
+    p[7*8] = v0 - v7;
+    p[1*8] = v1 + v6;
+    p[6*8] = v1 - v6;
+    p[2*8] = v2 + v5;
+    p[5*8] = v2 - v5;
+    p[3*8] = v3 + v4;
+    p[4*8] = v3 - v4;
   }
 
   // convert to 8-bit integers
-  for (i = 0; i < 64; ++i)
-    data[i] = dctClip[dctClipOffset + 128 + ((tmp1[i] + 8) >> 4)];
-
-  return gTrue;
-}
-#endif
-
-#ifdef FP_IDCT
-GBool DCTStream::readDataUnit(DCTHuffTable *dcHuffTable,
-                             DCTHuffTable *acHuffTable,
-                             Guchar quantTable[64], int *prevDC,
-                             Guchar data[64]) {
-  double tmp1[64];
-  double v0, v1, v2, v3, v4, v5, v6, v7, t;
-  int run, size, amp;
-  int c;
-  int i, j;
-
-  // Huffman decode and dequantize
-  size = readHuffSym(dcHuffTable);
-  if (size == 9999)
-    return gFalse;
-  if (size > 0) {
-    amp = readAmp(size);
-    if (amp == 9999)
-      return gFalse;
-  } else {
-    amp = 0;
+  for (i = 0; i < 64; ++i) {
+    dataOut[i] = dctClip[dctClipOffset + 128 + ((dataIn[i] + 8) >> 4)];
   }
-  tmp1[0] = (*prevDC += amp) * quantTable[0];
-  for (i = 1; i < 64; ++i)
-    tmp1[i] = 0;
-  i = 1;
-  while (i < 64) {
-    run = 0;
-    while ((c = readHuffSym(acHuffTable)) == 0xf0 && run < 0x30)
-      run += 0x10;
-    if (c == 9999)
-      return gFalse;
-    if (c == 0x00) {
-      break;
-    } else {
-      run += (c >> 4) & 0x0f;
-      size = c & 0x0f;
-      amp = readAmp(size);
-      if (amp == 9999)
-       return gFalse;
-      i += run;
-      j = dctZigZag[i++];
-      tmp1[j] = amp * quantTable[j];
-    }
-  }
-
-  // inverse DCT on rows
-  for (i = 0; i < 64; i += 8) {
-
-    // stage 4
-    v0 = dctSqrt2 * tmp1[i+0];
-    v1 = dctSqrt2 * tmp1[i+4];
-    v2 = tmp1[i+2];
-    v3 = tmp1[i+6];
-    v4 = dctSqrt1d2 * (tmp1[i+1] - tmp1[i+7]);
-    v7 = dctSqrt1d2 * (tmp1[i+1] + tmp1[i+7]);
-    v5 = tmp1[i+3];
-    v6 = tmp1[i+5];
-
-    // stage 3
-    t = 0.5 * (v0 - v1);
-    v0 = 0.5 * (v0 + v1);
-    v1 = t;
-    t = v2 * dctSin6 + v3 * dctCos6;
-    v2 = v2 * dctCos6 - v3 * dctSin6;
-    v3 = t;
-    t = 0.5 * (v4 - v6);
-    v4 = 0.5 * (v4 + v6);
-    v6 = t;
-    t = 0.5 * (v7 + v5);
-    v5 = 0.5 * (v7 - v5);
-    v7 = t;
-
-    // stage 2
-    t = 0.5 * (v0 - v3);
-    v0 = 0.5 * (v0 + v3);
-    v3 = t;
-    t = 0.5 * (v1 - v2);
-    v1 = 0.5 * (v1 + v2);
-    v2 = t;
-    t = v4 * dctSin3 + v7 * dctCos3;
-    v4 = v4 * dctCos3 - v7 * dctSin3;
-    v7 = t;
-    t = v5 * dctSin1 + v6 * dctCos1;
-    v5 = v5 * dctCos1 - v6 * dctSin1;
-    v6 = t;
-
-    // stage 1
-    tmp1[i+0] = v0 + v7;
-    tmp1[i+7] = v0 - v7;
-    tmp1[i+1] = v1 + v6;
-    tmp1[i+6] = v1 - v6;
-    tmp1[i+2] = v2 + v5;
-    tmp1[i+5] = v2 - v5;
-    tmp1[i+3] = v3 + v4;
-    tmp1[i+4] = v3 - v4;
-  }
-
-  // inverse DCT on columns
-  for (i = 0; i < 8; ++i) {
-
-    // stage 4
-    v0 = dctSqrt2 * tmp1[0*8+i];
-    v1 = dctSqrt2 * tmp1[4*8+i];
-    v2 = tmp1[2*8+i];
-    v3 = tmp1[6*8+i];
-    v4 = dctSqrt1d2 * (tmp1[1*8+i] - tmp1[7*8+i]);
-    v7 = dctSqrt1d2 * (tmp1[1*8+i] + tmp1[7*8+i]);
-    v5 = tmp1[3*8+i];
-    v6 = tmp1[5*8+i];
-
-    // stage 3
-    t = 0.5 * (v0 - v1);
-    v0 = 0.5 * (v0 + v1);
-    v1 = t;
-    t = v2 * dctSin6 + v3 * dctCos6;
-    v2 = v2 * dctCos6 - v3 * dctSin6;
-    v3 = t;
-    t = 0.5 * (v4 - v6);
-    v4 = 0.5 * (v4 + v6);
-    v6 = t;
-    t = 0.5 * (v7 + v5);
-    v5 = 0.5 * (v7 - v5);
-    v7 = t;
-
-    // stage 2
-    t = 0.5 * (v0 - v3);
-    v0 = 0.5 * (v0 + v3);
-    v3 = t;
-    t = 0.5 * (v1 - v2);
-    v1 = 0.5 * (v1 + v2);
-    v2 = t;
-    t = v4 * dctSin3 + v7 * dctCos3;
-    v4 = v4 * dctCos3 - v7 * dctSin3;
-    v7 = t;
-    t = v5 * dctSin1 + v6 * dctCos1;
-    v5 = v5 * dctCos1 - v6 * dctSin1;
-    v6 = t;
-
-    // stage 1
-    tmp1[0*8+i] = v0 + v7;
-    tmp1[7*8+i] = v0 - v7;
-    tmp1[1*8+i] = v1 + v6;
-    tmp1[6*8+i] = v1 - v6;
-    tmp1[2*8+i] = v2 + v5;
-    tmp1[5*8+i] = v2 - v5;
-    tmp1[3*8+i] = v3 + v4;
-    tmp1[4*8+i] = v3 - v4;
-  }
-
-  // convert to 8-bit integers
-  for (i = 0; i < 64; ++i)
-    data[i] = dctClip[dctClipOffset + (int)(tmp1[i] + 128.5)];
-
-  return gTrue;
 }
-#endif
 
 int DCTStream::readHuffSym(DCTHuffTable *table) {
   Gushort code;
@@ -2533,20 +2811,9 @@ int DCTStream::readBit() {
 
 GBool DCTStream::readHeader() {
   GBool doScan;
-  int minHSample, minVSample;
-  int bufWidth;
   int n;
   int c = 0;
-  int i, j;
-
-  width = height = 0;
-  numComps = 0;
-  numQuantTables = 0;
-  numDCHuffTables = 0;
-  numACHuffTables = 0;
-  colorXform = 0;
-  gotAdobeMarker = gFalse;
-  restartInterval = 0;
+  int i;
 
   // read headers
   doScan = gFalse;
@@ -2554,31 +2821,49 @@ GBool DCTStream::readHeader() {
     c = readMarker();
     switch (c) {
     case 0xc0:                 // SOF0
-      if (!readFrameInfo())
+      if (!readBaselineSOF()) {
        return gFalse;
+      }
+      break;
+    case 0xc2:                 // SOF2
+      if (!readProgressiveSOF()) {
+       return gFalse;
+      }
       break;
     case 0xc4:                 // DHT
-      if (!readHuffmanTables())
+      if (!readHuffmanTables()) {
        return gFalse;
+      }
       break;
     case 0xd8:                 // SOI
       break;
+    case 0xd9:                 // EOI
+      return gFalse;
     case 0xda:                 // SOS
-      if (!readScanInfo())
+      if (!readScanInfo()) {
        return gFalse;
+      }
       doScan = gTrue;
       break;
     case 0xdb:                 // DQT
-      if (!readQuantTables())
+      if (!readQuantTables()) {
        return gFalse;
+      }
       break;
     case 0xdd:                 // DRI
-      if (!readRestartInterval())
+      if (!readRestartInterval()) {
        return gFalse;
+      }
+      break;
+    case 0xe0:                 // APP0
+      if (!readJFIFMarker()) {
+       return gFalse;
+      }
       break;
     case 0xee:                 // APP14
-      if (!readAdobeMarker())
+      if (!readAdobeMarker()) {
        return gFalse;
+      }
       break;
     case EOF:
       error(getPos(), "Bad DCT header");
@@ -2587,8 +2872,9 @@ GBool DCTStream::readHeader() {
       // skip APPn / COM / etc.
       if (c >= 0xe0) {
        n = read16() - 2;
-       for (i = 0; i < n; ++i)
+       for (i = 0; i < n; ++i) {
          str->getChar();
+       }
       } else {
        error(getPos(), "Unknown DCT marker <%02x>", c);
        return gFalse;
@@ -2597,107 +2883,105 @@ GBool DCTStream::readHeader() {
     }
   }
 
-  // compute MCU size
-  mcuWidth = minHSample = compInfo[0].hSample;
-  mcuHeight = minVSample = compInfo[0].vSample;
-  for (i = 1; i < numComps; ++i) {
-    if (compInfo[i].hSample < minHSample)
-      minHSample = compInfo[i].hSample;
-    if (compInfo[i].vSample < minVSample)
-      minVSample = compInfo[i].vSample;
-    if (compInfo[i].hSample > mcuWidth)
-      mcuWidth = compInfo[i].hSample;
-    if (compInfo[i].vSample > mcuHeight)
-      mcuHeight = compInfo[i].vSample;
-  }
-  for (i = 0; i < numComps; ++i) {
-    compInfo[i].hSample /= minHSample;
-    compInfo[i].vSample /= minVSample;
-  }
-  mcuWidth = (mcuWidth / minHSample) * 8;
-  mcuHeight = (mcuHeight / minVSample) * 8;
+  return gTrue;
+}
 
-  // allocate buffers
-  bufWidth = ((width + mcuWidth - 1) / mcuWidth) * mcuWidth;
-  for (i = 0; i < numComps; ++i)
-    for (j = 0; j < mcuHeight; ++j)
-      rowBuf[i][j] = (Guchar *)gmalloc(bufWidth * sizeof(Guchar));
+GBool DCTStream::readBaselineSOF() {
+  int length;
+  int prec;
+  int i;
+  int c;
 
-  // figure out color transform
-  if (!gotAdobeMarker && numComps == 3) {
-    if (compInfo[0].id == 1 && compInfo[1].id == 2 && compInfo[2].id == 3) {
-      colorXform = 1;
-    }
+  length = read16();
+  prec = str->getChar();
+  height = read16();
+  width = read16();
+  numComps = str->getChar();
+  if (prec != 8) {
+    error(getPos(), "Bad DCT precision %d", prec);
+    return gFalse;
   }
-
-  // initialize counters
-  comp = 0;
-  x = 0;
-  y = 0;
-  dy = mcuHeight;
-
+  for (i = 0; i < numComps; ++i) {
+    compInfo[i].id = str->getChar();
+    c = str->getChar();
+    compInfo[i].hSample = (c >> 4) & 0x0f;
+    compInfo[i].vSample = c & 0x0f;
+    compInfo[i].quantTable = str->getChar();
+  }
+  progressive = gFalse;
   return gTrue;
 }
 
-GBool DCTStream::readFrameInfo() {
+GBool DCTStream::readProgressiveSOF() {
   int length;
   int prec;
   int i;
   int c;
 
-  length = read16() - 2;
+  length = read16();
   prec = str->getChar();
   height = read16();
   width = read16();
   numComps = str->getChar();
-  length -= 6;
   if (prec != 8) {
     error(getPos(), "Bad DCT precision %d", prec);
     return gFalse;
   }
   for (i = 0; i < numComps; ++i) {
     compInfo[i].id = str->getChar();
-    compInfo[i].inScan = gFalse;
     c = str->getChar();
     compInfo[i].hSample = (c >> 4) & 0x0f;
     compInfo[i].vSample = c & 0x0f;
     compInfo[i].quantTable = str->getChar();
-    compInfo[i].dcHuffTable = 0;
-    compInfo[i].acHuffTable = 0;
   }
+  progressive = gTrue;
   return gTrue;
 }
 
 GBool DCTStream::readScanInfo() {
   int length;
-  int scanComps, id, c;
+  int id, c;
   int i, j;
 
   length = read16() - 2;
-  scanComps = str->getChar();
+  scanInfo.numComps = str->getChar();
   --length;
-  if (length != 2 * scanComps + 3) {
+  if (length != 2 * scanInfo.numComps + 3) {
     error(getPos(), "Bad DCT scan info block");
     return gFalse;
   }
-  for (i = 0; i < scanComps; ++i) {
+  interleaved = scanInfo.numComps == numComps;
+  for (j = 0; j < numComps; ++j) {
+    scanInfo.comp[j] = gFalse;
+  }
+  for (i = 0; i < scanInfo.numComps; ++i) {
     id = str->getChar();
-    for (j = 0; j < numComps; ++j) {
-      if (id == compInfo[j].id)
-       break;
-    }
-    if (j == numComps) {
-      error(getPos(), "Bad DCT component ID in scan info block");
-      return gFalse;
+    // some (broken) DCT streams reuse ID numbers, but at least they
+    // keep the components in order, so we check compInfo[i] first to
+    // work around the problem
+    if (id == compInfo[i].id) {
+      j = i;
+    } else {
+      for (j = 0; j < numComps; ++j) {
+       if (id == compInfo[j].id) {
+         break;
+       }
+      }
+      if (j == numComps) {
+       error(getPos(), "Bad DCT component ID in scan info block");
+       return gFalse;
+      }
     }
-    compInfo[j].inScan = gTrue;
+    scanInfo.comp[j] = gTrue;
     c = str->getChar();
-    compInfo[j].dcHuffTable = (c >> 4) & 0x0f;
-    compInfo[j].acHuffTable = c & 0x0f;
+    scanInfo.dcHuffTable[j] = (c >> 4) & 0x0f;
+    scanInfo.acHuffTable[j] = c & 0x0f;
   }
-  str->getChar();
-  str->getChar();
-  str->getChar();
+  scanInfo.firstCoeff = str->getChar();
+  scanInfo.lastCoeff = str->getChar();
+  c = str->getChar();
+  scanInfo.ah = (c >> 4) & 0x0f;
+  scanInfo.al = c & 0x0f;
   return gTrue;
 }
 
@@ -2779,23 +3063,61 @@ GBool DCTStream::readRestartInterval() {
   return gTrue;
 }
 
+GBool DCTStream::readJFIFMarker() {
+  int length, i;
+  char buf[5];
+  int c;
+
+  length = read16();
+  length -= 2;
+  if (length >= 5) {
+    for (i = 0; i < 5; ++i) {
+      if ((c = str->getChar()) == EOF) {
+       error(getPos(), "Bad DCT APP0 marker");
+       return gFalse;
+      }
+      buf[i] = c;
+    }
+    length -= 5;
+    if (!memcmp(buf, "JFIF\0", 5)) {
+      gotJFIFMarker = gTrue;
+    }
+  }
+  while (length > 0) {
+    if (str->getChar() == EOF) {
+      error(getPos(), "Bad DCT APP0 marker");
+      return gFalse;
+    }
+    --length;
+  }
+  return gTrue;
+}
+
 GBool DCTStream::readAdobeMarker() {
   int length, i;
   char buf[12];
   int c;
 
   length = read16();
-  if (length != 14)
+  if (length < 14) {
     goto err;
+  }
   for (i = 0; i < 12; ++i) {
-    if ((c = str->getChar()) == EOF)
+    if ((c = str->getChar()) == EOF) {
       goto err;
+    }
     buf[i] = c;
   }
-  if (strncmp(buf, "Adobe", 5))
+  if (strncmp(buf, "Adobe", 5)) {
     goto err;
+  }
   colorXform = buf[11];
   gotAdobeMarker = gTrue;
+  for (i = 14; i < length; ++i) {
+    if (str->getChar() == EOF) {
+      goto err;
+    }
+  }
   return gTrue;
 
  err:
@@ -2838,10 +3160,13 @@ int DCTStream::read16() {
   return (c1 << 8) + c2;
 }
 
-GString *DCTStream::getPSFilter(char *indent) {
+GString *DCTStream::getPSFilter(int psLevel, char *indent) {
   GString *s;
 
-  if (!(s = str->getPSFilter(indent))) {
+  if (psLevel < 2) {
+    return NULL;
+  }
+  if (!(s = str->getPSFilter(psLevel, indent))) {
     return NULL;
   }
   s->append(indent)->append("<< >> /DCTDecode filter\n");
@@ -2933,9 +3258,13 @@ FlateStream::FlateStream(Stream *strA, int predictor, int columns,
   } else {
     pred = NULL;
   }
+  litCodeTab.codes = NULL;
+  distCodeTab.codes = NULL;
 }
 
 FlateStream::~FlateStream() {
+  gfree(litCodeTab.codes);
+  gfree(distCodeTab.codes);
   if (pred) {
     delete pred;
   }
@@ -3024,8 +3353,17 @@ int FlateStream::getRawChar() {
   return c;
 }
 
-GString *FlateStream::getPSFilter(char *indent) {
-  return NULL;
+GString *FlateStream::getPSFilter(int psLevel, char *indent) {
+  GString *s;
+
+  if (psLevel < 3 || pred) {
+    return NULL;
+  }
+  if (!(s = str->getPSFilter(psLevel, indent))) {
+    return NULL;
+  }
+  s->append(indent)->append("<< >> /FlateDecode filter\n");
+  return s;
 }
 
 GBool FlateStream::isBinary(GBool last) {
@@ -3102,6 +3440,12 @@ GBool FlateStream::startBlock() {
   int c;
   int check;
 
+  // free the code tables from the previous block
+  gfree(litCodeTab.codes);
+  litCodeTab.codes = NULL;
+  gfree(distCodeTab.codes);
+  distCodeTab.codes = NULL;
+
   // read block header
   blockHdr = getCodeWord(3);
   if (blockHdr & 1)
@@ -3136,8 +3480,9 @@ GBool FlateStream::startBlock() {
   // compressed block with dynamic codes
   } else if (blockHdr == 2) {
     compressedBlock = gTrue;
-    if (!readDynamicCodes())
+    if (!readDynamicCodes()) {
       goto err;
+    }
 
   // unknown block type
   } else {
@@ -3156,186 +3501,196 @@ err:
 void FlateStream::loadFixedCodes() {
   int i;
 
-  // set up code arrays
-  litCodeTab.codes = allCodes;
-  distCodeTab.codes = allCodes + flateMaxLitCodes;
-
-  // initialize literal code table
-  for (i = 0; i <= 143; ++i)
-    litCodeTab.codes[i].len = 8;
-  for (i = 144; i <= 255; ++i)
-    litCodeTab.codes[i].len = 9;
-  for (i = 256; i <= 279; ++i)
-    litCodeTab.codes[i].len = 7;
-  for (i = 280; i <= 287; ++i)
-    litCodeTab.codes[i].len = 8;
-  compHuffmanCodes(&litCodeTab, flateMaxLitCodes);
-
-  // initialize distance code table
-  for (i = 0; i <= 5; ++i) {
-    distCodeTab.start[i] = 0;
+  // build the literal code table
+  for (i = 0; i <= 143; ++i) {
+    codeLengths[i] = 8;
+  }
+  for (i = 144; i <= 255; ++i) {
+    codeLengths[i] = 9;
   }
-  for (i = 6; i <= flateMaxHuffman+1; ++i) {
-    distCodeTab.start[i] = flateMaxDistCodes;
+  for (i = 256; i <= 279; ++i) {
+    codeLengths[i] = 7;
   }
+  for (i = 280; i <= 287; ++i) {
+    codeLengths[i] = 8;
+  }
+  compHuffmanCodes(codeLengths, flateMaxLitCodes, &litCodeTab);
+
+  // build the distance code table
   for (i = 0; i < flateMaxDistCodes; ++i) {
-    distCodeTab.codes[i].len = 5;
-    distCodeTab.codes[i].code = i;
-    distCodeTab.codes[i].val = i;
+    codeLengths[i] = 5;
   }
+  compHuffmanCodes(codeLengths, flateMaxDistCodes, &distCodeTab);
 }
 
 GBool FlateStream::readDynamicCodes() {
   int numCodeLenCodes;
   int numLitCodes;
   int numDistCodes;
-  FlateCode codeLenCodes[flateMaxCodeLenCodes];
+  int codeLenCodeLengths[flateMaxCodeLenCodes];
   FlateHuffmanTab codeLenCodeTab;
   int len, repeat, code;
   int i;
 
+  codeLenCodeTab.codes = NULL;
+
   // read lengths
-  if ((numLitCodes = getCodeWord(5)) == EOF)
+  if ((numLitCodes = getCodeWord(5)) == EOF) {
     goto err;
+  }
   numLitCodes += 257;
-  if ((numDistCodes = getCodeWord(5)) == EOF)
+  if ((numDistCodes = getCodeWord(5)) == EOF) {
     goto err;
+  }
   numDistCodes += 1;
-  if ((numCodeLenCodes = getCodeWord(4)) == EOF)
+  if ((numCodeLenCodes = getCodeWord(4)) == EOF) {
     goto err;
+  }
   numCodeLenCodes += 4;
   if (numLitCodes > flateMaxLitCodes ||
       numDistCodes > flateMaxDistCodes ||
-      numCodeLenCodes > flateMaxCodeLenCodes)
+      numCodeLenCodes > flateMaxCodeLenCodes) {
     goto err;
+  }
 
-  // read code length code table
-  codeLenCodeTab.codes = codeLenCodes;
-  for (i = 0; i < flateMaxCodeLenCodes; ++i)
-    codeLenCodes[i].len = 0;
+  // build the code length code table
+  for (i = 0; i < flateMaxCodeLenCodes; ++i) {
+    codeLenCodeLengths[i] = 0;
+  }
   for (i = 0; i < numCodeLenCodes; ++i) {
-    if ((codeLenCodes[codeLenCodeMap[i]].len = getCodeWord(3)) == -1)
+    if ((codeLenCodeLengths[codeLenCodeMap[i]] = getCodeWord(3)) == -1) {
       goto err;
+    }
   }
-  compHuffmanCodes(&codeLenCodeTab, flateMaxCodeLenCodes);
+  compHuffmanCodes(codeLenCodeLengths, flateMaxCodeLenCodes, &codeLenCodeTab);
 
-  // set up code arrays
-  litCodeTab.codes = allCodes;
-  distCodeTab.codes = allCodes + numLitCodes;
-
-  // read literal and distance code tables
+  // build the literal and distance code tables
   len = 0;
   repeat = 0;
   i = 0;
   while (i < numLitCodes + numDistCodes) {
-    if ((code = getHuffmanCodeWord(&codeLenCodeTab)) == EOF)
+    if ((code = getHuffmanCodeWord(&codeLenCodeTab)) == EOF) {
       goto err;
+    }
     if (code == 16) {
-      if ((repeat = getCodeWord(2)) == EOF)
+      if ((repeat = getCodeWord(2)) == EOF) {
+       goto err;
+      }
+      repeat += 3;
+      if (i + repeat > numLitCodes + numDistCodes) {
        goto err;
-      for (repeat += 3; repeat > 0; --repeat)
-       allCodes[i++].len = len;
+      }
+      for (; repeat > 0; --repeat) {
+       codeLengths[i++] = len;
+      }
     } else if (code == 17) {
-      if ((repeat = getCodeWord(3)) == EOF)
+      if ((repeat = getCodeWord(3)) == EOF) {
+       goto err;
+      }
+      repeat += 3;
+      if (i + repeat > numLitCodes + numDistCodes) {
        goto err;
+      }
       len = 0;
-      for (repeat += 3; repeat > 0; --repeat)
-       allCodes[i++].len = 0;
+      for (; repeat > 0; --repeat) {
+       codeLengths[i++] = 0;
+      }
     } else if (code == 18) {
-      if ((repeat = getCodeWord(7)) == EOF)
+      if ((repeat = getCodeWord(7)) == EOF) {
+       goto err;
+      }
+      repeat += 11;
+      if (i + repeat > numLitCodes + numDistCodes) {
        goto err;
+      }
       len = 0;
-      for (repeat += 11; repeat > 0; --repeat)
-       allCodes[i++].len = 0;
+      for (; repeat > 0; --repeat) {
+       codeLengths[i++] = 0;
+      }
     } else {
-      allCodes[i++].len = len = code;
+      codeLengths[i++] = len = code;
     }
   }
-  compHuffmanCodes(&litCodeTab, numLitCodes);
-  compHuffmanCodes(&distCodeTab, numDistCodes);
+  compHuffmanCodes(codeLengths, numLitCodes, &litCodeTab);
+  compHuffmanCodes(codeLengths + numLitCodes, numDistCodes, &distCodeTab);
 
+  gfree(codeLenCodeTab.codes);
   return gTrue;
 
 err:
   error(getPos(), "Bad dynamic code table in flate stream");
+  gfree(codeLenCodeTab.codes);
   return gFalse;
 }
 
-// On entry, the <tab->codes> array contains the lengths of each code,
-// stored in code value order.  This function computes the code words.
-// The result is sorted in order of (1) code length and (2) code word.
-// The length values are no longer valid.  The <tab->start> array is
-// filled with the indexes of the first code of each length.
-void FlateStream::compHuffmanCodes(FlateHuffmanTab *tab, int n) {
-  int numLengths[flateMaxHuffman+1];
-  int nextCode[flateMaxHuffman+1];
-  int nextIndex[flateMaxHuffman+2];
-  int code;
-  int i, j;
+// Convert an array <lengths> of <n> lengths, in value order, into a
+// Huffman code lookup table.
+void FlateStream::compHuffmanCodes(int *lengths, int n, FlateHuffmanTab *tab) {
+  int tabSize, len, code, code2, skip, val, i, t;
 
-  // count number of codes for each code length
-  for (i = 0; i <= flateMaxHuffman; ++i)
-    numLengths[i] = 0;
-  for (i = 0; i < n; ++i)
-    ++numLengths[tab->codes[i].len];
+  // find max code length
+  tab->maxLen = 0;
+  for (val = 0; val < n; ++val) {
+    if (lengths[val] > tab->maxLen) {
+      tab->maxLen = lengths[val];
+    }
+  }
 
-  // compute first index for each length
-  tab->start[0] = nextIndex[0] = 0;
-  for (i = 1; i <= flateMaxHuffman + 1; ++i)
-    tab->start[i] = nextIndex[i] = tab->start[i-1] + numLengths[i-1];
+  // allocate the table
+  tabSize = 1 << tab->maxLen;
+  tab->codes = (FlateCode *)gmalloc(tabSize * sizeof(FlateCode));
 
-  // compute first code for each length
-  code = 0;
-  numLengths[0] = 0;
-  for (i = 1; i <= flateMaxHuffman; ++i) {
-    code = (code + numLengths[i-1]) << 1;
-    nextCode[i] = code;
+  // clear the table
+  for (i = 0; i < tabSize; ++i) {
+    tab->codes[i].len = 0;
+    tab->codes[i].val = 0;
   }
 
-  // compute the codes -- this permutes the codes array from value
-  // order to length/code order
-  for (i = 0; i < n; ++i) {
-    j = nextIndex[tab->codes[i].len]++;
-    if (tab->codes[i].len == 0)
-      tab->codes[j].code = 0;
-    else
-      tab->codes[j].code = nextCode[tab->codes[i].len]++;
-    tab->codes[j].val = i;
+  // build the table
+  for (len = 1, code = 0, skip = 2;
+       len <= tab->maxLen;
+       ++len, code <<= 1, skip <<= 1) {
+    for (val = 0; val < n; ++val) {
+      if (lengths[val] == len) {
+
+       // bit-reverse the code
+       code2 = 0;
+       t = code;
+       for (i = 0; i < len; ++i) {
+         code2 = (code2 << 1) | (t & 1);
+         t >>= 1;
+       }
+
+       // fill in the table entries
+       for (i = code2; i < tabSize; i += skip) {
+         tab->codes[i].len = (Gushort)len;
+         tab->codes[i].val = (Gushort)val;
+       }
+
+       ++code;
+      }
+    }
   }
 }
 
 int FlateStream::getHuffmanCodeWord(FlateHuffmanTab *tab) {
-  int len;
-  int code;
+  FlateCode *code;
   int c;
-  int i, j;
 
-  code = 0;
-  for (len = 1; len <= flateMaxHuffman; ++len) {
-
-    // add a bit to the code
-    if (codeSize == 0) {
-      if ((c = str->getChar()) == EOF)
-       return EOF;
-      codeBuf = c & 0xff;
-      codeSize = 8;
-    }
-    code = (code << 1) | (codeBuf & 1);
-    codeBuf >>= 1;
-    --codeSize;
-
-    // look for code
-    i = tab->start[len];
-    j = tab->start[len + 1];
-    if (i < j && code >= tab->codes[i].code && code <= tab->codes[j-1].code) {
-      i += code - tab->codes[i].code;
-      return tab->codes[i].val;
+  while (codeSize < tab->maxLen) {
+    if ((c = str->getChar()) == EOF) {
+      break;
     }
+    codeBuf |= (c & 0xff) << codeSize;
+    codeSize += 8;
   }
-
-  // not found
-  error(getPos(), "Bad code (%04x) in flate stream", code);
-  return EOF;
+  code = &tab->codes[codeBuf & ((1 << tab->maxLen) - 1)];
+  if (codeSize == 0 || codeSize < code->len || code->len == 0) {
+    return EOF;
+  }
+  codeBuf >>= code->len;
+  codeSize -= code->len;
+  return (int)code->val;
 }
 
 int FlateStream::getCodeWord(int bits) {
@@ -3385,9 +3740,6 @@ void FixedLengthEncoder::reset() {
   count = 0;
 }
 
-void FixedLengthEncoder::close() {
-}
-
 int FixedLengthEncoder::getChar() {
   if (length >= 0 && count >= length)
     return EOF;
@@ -3401,6 +3753,10 @@ int FixedLengthEncoder::lookChar() {
   return str->getChar();
 }
 
+GBool FixedLengthEncoder::isBinary(GBool last) {
+  return str->isBinary(gTrue);
+}
+
 //------------------------------------------------------------------------
 // ASCIIHexEncoder
 //------------------------------------------------------------------------
@@ -3425,9 +3781,6 @@ void ASCIIHexEncoder::reset() {
   eof = gFalse;
 }
 
-void ASCIIHexEncoder::close() {
-}
-
 GBool ASCIIHexEncoder::fillBuf() {
   static char *hex = "0123456789abcdef";
   int c;
@@ -3474,9 +3827,6 @@ void ASCII85Encoder::reset() {
   eof = gFalse;
 }
 
-void ASCII85Encoder::close() {
-}
-
 GBool ASCII85Encoder::fillBuf() {
   Gulong t;
   char buf1[5];
@@ -3544,9 +3894,6 @@ void RunLengthEncoder::reset() {
   eof = gFalse;
 }
 
-void RunLengthEncoder::close() {
-}
-
 //
 // When fillBuf finishes, buf[] looks like this:
 //   +-----+--------------+-----------------+--
index 3319dcc..0b70afa 100644 (file)
@@ -2,14 +2,16 @@
 //
 // Stream.h
 //
-// Copyright 1996-2002 Glyph & Cog, LLC
+// Copyright 1996-2003 Glyph & Cog, LLC
 //
 //========================================================================
 
 #ifndef STREAM_H
 #define STREAM_H
 
-#ifdef __GNUC__
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
 #pragma interface
 #endif
 
@@ -33,6 +35,8 @@ enum StreamKind {
   strCCITTFax,
   strDCT,
   strFlate,
+  strJBIG2,
+  strJPX,
   strWeird                     // internal-use stream types
 };
 
@@ -84,12 +88,12 @@ public:
   virtual void setPos(Guint pos, int dir = 0) = 0;
 
   // Get PostScript command for the filter(s).
-  virtual GString *getPSFilter(char *indent);
+  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 or EmbedStream of this stream.
+  // Get the BaseStream of this stream.
   virtual BaseStream *getBaseStream() = 0;
 
   // Get the dictionary associated with this stream.
@@ -102,6 +106,11 @@ public:
   // Returns the new stream.
   Stream *addFilters(Object *dict);
 
+  // Tell this stream to ignore any length limitation -- this only
+  // applies to BaseStream subclasses, and is used as a hack to work
+  // around broken PDF files with incorrect stream lengths.
+  virtual void ignoreLength() {}
+
 private:
 
   Stream *makeFilter(char *name, Stream *str, Object *params);
@@ -123,6 +132,7 @@ public:
   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(); }
 
@@ -163,6 +173,7 @@ public:
   virtual void setPos(Guint pos, int dir = 0);
   virtual BaseStream *getBaseStream() { return str->getBaseStream(); }
   virtual Dict *getDict() { return str->getDict(); }
+  virtual void ignoreLength() { str->ignoreLength(); }
 
 protected:
 
@@ -190,6 +201,10 @@ public:
   // at least nComps elements.  Returns false at end of file.
   GBool getPixel(Guchar *pix);
 
+  // Returns a pointer to the next line of pixels.  Returns NULL at
+  // end of file.
+  Guchar *getLine();
+
   // Skip an entire line from the image.
   void skipLine();
 
@@ -260,7 +275,7 @@ public:
     { return (bufPtr >= bufEnd && !fillBuf()) ? EOF : (*bufPtr & 0xff); }
   virtual int getPos() { return bufPos + (bufPtr - buf); }
   virtual void setPos(Guint pos, int dir = 0);
-  virtual GBool isBinary(GBool last = gTrue) { return last; }
+  virtual void ignoreLength() { limited = gFalse; }
   virtual Guint getStart() { return start; }
   virtual void moveStart(int delta);
 
@@ -287,7 +302,7 @@ private:
 class MemStream: public BaseStream {
 public:
 
-  MemStream(char *bufA, Guint lengthA, Object *dictA);
+  MemStream(char *bufA, Guint startA, Guint lengthA, Object *dictA);
   virtual ~MemStream();
   virtual Stream *makeSubStream(Guint start, GBool limited,
                                Guint lengthA, Object *dictA);
@@ -298,10 +313,9 @@ public:
     { return (bufPtr < bufEnd) ? (*bufPtr++ & 0xff) : EOF; }
   virtual int lookChar()
     { return (bufPtr < bufEnd) ? (*bufPtr & 0xff) : EOF; }
-  virtual int getPos() { return bufPtr - buf; }
+  virtual int getPos() { return (int)(bufPtr - buf); }
   virtual void setPos(Guint pos, int dir = 0);
-  virtual GBool isBinary(GBool last = gTrue) { return last; }
-  virtual Guint getStart() { return 0; }
+  virtual Guint getStart() { return start; }
   virtual void moveStart(int delta);
 #ifndef NO_DECRYPTION
   virtual void doDecryption(Guchar *fileKey, int keyLength,
@@ -311,10 +325,11 @@ public:
 private:
 
   char *buf;
+  Guint start;
   Guint length;
-  GBool needFree;
   char *bufEnd;
   char *bufPtr;
+  GBool needFree;
 };
 
 //------------------------------------------------------------------------
@@ -330,23 +345,24 @@ private:
 class EmbedStream: public BaseStream {
 public:
 
-  EmbedStream(Stream *strA, Object *dictA);
+  EmbedStream(Stream *strA, Object *dictA, GBool limitedA, Guint lengthA);
   virtual ~EmbedStream();
-  virtual Stream *makeSubStream(Guint start, GBool limited,
-                               Guint length, Object *dictA);
+  virtual Stream *makeSubStream(Guint start, GBool limitedA,
+                               Guint lengthA, Object *dictA);
   virtual StreamKind getKind() { return str->getKind(); }
   virtual void reset() {}
-  virtual int getChar() { return str->getChar(); }
-  virtual int lookChar() { return str->lookChar(); }
+  virtual int getChar();
+  virtual int lookChar();
   virtual int getPos() { return str->getPos(); }
   virtual void setPos(Guint pos, int dir = 0);
-  virtual GBool isBinary(GBool last = gTrue) { return last; }
   virtual Guint getStart();
   virtual void moveStart(int delta);
 
 private:
 
   Stream *str;
+  GBool limited;
+  Guint length;
 };
 
 //------------------------------------------------------------------------
@@ -363,7 +379,7 @@ public:
   virtual int getChar()
     { int c = lookChar(); buf = EOF; return c; }
   virtual int lookChar();
-  virtual GString *getPSFilter(char *indent);
+  virtual GString *getPSFilter(int psLevel, char *indent);
   virtual GBool isBinary(GBool last = gTrue);
 
 private:
@@ -386,7 +402,7 @@ public:
   virtual int getChar()
     { int ch = lookChar(); ++index; return ch; }
   virtual int lookChar();
-  virtual GString *getPSFilter(char *indent);
+  virtual GString *getPSFilter(int psLevel, char *indent);
   virtual GBool isBinary(GBool last = gTrue);
 
 private:
@@ -412,25 +428,33 @@ public:
   virtual int getChar();
   virtual int lookChar();
   virtual int getRawChar();
-  virtual GString *getPSFilter(char *indent);
+  virtual GString *getPSFilter(int psLevel, char *indent);
   virtual GBool isBinary(GBool last = gTrue);
 
 private:
 
   StreamPredictor *pred;       // predictor
   int early;                   // early parameter
-  FILE *zPipe;                 // uncompress pipe
-  GString *zName;              // .Z file name
+  GBool eof;                   // true if at eof
   int inputBuf;                        // input buffer
   int inputBits;               // number of bits in input buffer
-  int inCodeBits;              // size of input code
-  char buf[256];               // buffer
-  char *bufPtr;                        // next char to read
-  char *bufEnd;                        // end of buffer
-
-  void dumpFile(FILE *f);
+  struct {                     // decoding table
+    int length;
+    int head;
+    Guchar tail;
+  } table[4097];
+  int nextCode;                        // next code to be used
+  int nextBits;                        // number of bits in next code word
+  int prevCode;                        // previous code used in stream
+  int newChar;                 // next char to be added to table
+  Guchar seqBuf[4097];         // buffer for current sequence
+  int seqLength;               // length of current sequence
+  int seqIndex;                        // index into current sequence
+  GBool first;                 // first code after a table clear
+
+  GBool processNextCode();
+  void clearTable();
   int getCode();
-  GBool fillBuf();
 };
 
 //------------------------------------------------------------------------
@@ -448,7 +472,7 @@ public:
     { return (bufPtr >= bufEnd && !fillBuf()) ? EOF : (*bufPtr++ & 0xff); }
   virtual int lookChar()
     { return (bufPtr >= bufEnd && !fillBuf()) ? EOF : (*bufPtr & 0xff); }
-  virtual GString *getPSFilter(char *indent);
+  virtual GString *getPSFilter(int psLevel, char *indent);
   virtual GBool isBinary(GBool last = gTrue);
 
 private:
@@ -479,7 +503,7 @@ public:
   virtual int getChar()
     { int c = lookChar(); buf = EOF; return c; }
   virtual int lookChar();
-  virtual GString *getPSFilter(char *indent);
+  virtual GString *getPSFilter(int psLevel, char *indent);
   virtual GBool isBinary(GBool last = gTrue);
 
 private:
@@ -517,13 +541,21 @@ private:
 // DCT component info
 struct DCTCompInfo {
   int id;                      // component ID
-  GBool inScan;                        // is this component in the current scan?
   int hSample, vSample;                // horiz/vert sampling resolutions
   int quantTable;              // quantization table number
-  int dcHuffTable, acHuffTable;        // Huffman table numbers
   int prevDC;                  // DC coefficient accumulator
 };
 
+struct DCTScanInfo {
+  GBool comp[4];               // comp[i] is set if component i is
+                               //   included in this scan
+  int numComps;                        // number of components in the scan
+  int dcHuffTable[4];          // DC Huffman table numbers
+  int acHuffTable[4];          // AC Huffman table numbers
+  int firstCoeff, lastCoeff;   // first and last DCT coefficient
+  int ah, al;                  // successive approximation parameters
+};
+
 // DCT Huffman decoding table
 struct DCTHuffTable {
   Guchar firstSym[17];         // first symbol for this bit length
@@ -541,17 +573,22 @@ public:
   virtual void reset();
   virtual int getChar();
   virtual int lookChar();
-  virtual GString *getPSFilter(char *indent);
+  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
   Guchar quantTables[4][64];   // quantization tables
@@ -560,26 +597,38 @@ private:
   DCTHuffTable acHuffTables[4];        // AC Huffman tables
   int numDCHuffTables;         // number of DC Huffman tables
   int numACHuffTables;         // number of AC Huffman tables
-  Guchar *rowBuf[4][32];       // buffer for one MCU
+  Guchar *rowBuf[4][32];       // buffer for one MCU (non-progressive mode)
+  int *frameBuf[4];            // buffer for frame (progressive mode)
   int comp, x, y, dy;          // current position within image/MCU
   int restartCtr;              // MCUs left until restart
   int restartMarker;           // next restart marker
+  int eobRun;                  // number of EOBs left in the current run
   int inputBuf;                        // input buffer for variable length codes
   int inputBits;               // number of valid bits in input buffer
 
   void restart();
   GBool readMCURow();
-  GBool readDataUnit(DCTHuffTable *dcHuffTable, DCTHuffTable *acHuffTable,
-                    Guchar quantTable[64], int *prevDC, Guchar data[64]);
+  void readScan();
+  GBool readDataUnit(DCTHuffTable *dcHuffTable,
+                    DCTHuffTable *acHuffTable,
+                    int *prevDC, int data[64]);
+  GBool readProgressiveDataUnit(DCTHuffTable *dcHuffTable,
+                               DCTHuffTable *acHuffTable,
+                               int *prevDC, int data[64]);
+  void decodeImage();
+  void transformDataUnit(Guchar *quantTable,
+                        int dataIn[64], Guchar dataOut[64]);
   int readHuffSym(DCTHuffTable *table);
   int readAmp(int size);
   int readBit();
   GBool readHeader();
-  GBool readFrameInfo();
+  GBool readBaselineSOF();
+  GBool readProgressiveSOF();
   GBool readScanInfo();
   GBool readQuantTables();
   GBool readHuffmanTables();
   GBool readRestartInterval();
+  GBool readJFIFMarker();
   GBool readAdobeMarker();
   GBool readTrailer();
   int readMarker();
@@ -599,15 +648,13 @@ private:
 
 // Huffman code table entry
 struct FlateCode {
-  int len;                     // code length in bits
-  int code;                    // code word
-  int val;                     // value represented by this code
+  Gushort len;                 // code length, in bits
+  Gushort val;                 // value represented by this code
 };
 
-// Huffman code table
 struct FlateHuffmanTab {
-  int start[flateMaxHuffman+2];        // indexes of first code of each length
-  FlateCode *codes;            // codes, sorted by length and code word
+  FlateCode *codes;
+  int maxLen;
 };
 
 // Decoding info for length and distance code words
@@ -627,7 +674,7 @@ public:
   virtual int getChar();
   virtual int lookChar();
   virtual int getRawChar();
-  virtual GString *getPSFilter(char *indent);
+  virtual GString *getPSFilter(int psLevel, char *indent);
   virtual GBool isBinary(GBool last = gTrue);
 
 private:
@@ -638,8 +685,8 @@ private:
   int remain;                  // number valid bytes in output buffer
   int codeBuf;                 // input buffer
   int codeSize;                        // number of bits in input buffer
-  FlateCode                    // literal and distance codes
-    allCodes[flateMaxLitCodes + flateMaxDistCodes];
+  int                          // literal and distance code lengths
+    codeLengths[flateMaxLitCodes + flateMaxDistCodes];
   FlateHuffmanTab litCodeTab;  // literal code table
   FlateHuffmanTab distCodeTab; // distance code table
   GBool compressedBlock;       // set if reading a compressed block
@@ -658,7 +705,7 @@ private:
   GBool startBlock();
   void loadFixedCodes();
   GBool readDynamicCodes();
-  void compHuffmanCodes(FlateHuffmanTab *tab, int n);
+  void compHuffmanCodes(int *lengths, int n, FlateHuffmanTab *tab);
   int getHuffmanCodeWord(FlateHuffmanTab *tab);
   int getCodeWord(int bits);
 };
@@ -676,7 +723,7 @@ public:
   virtual void reset() {}
   virtual int getChar() { return EOF; }
   virtual int lookChar() { return EOF; }
-  virtual GString *getPSFilter(char *indent)  { return NULL; }
+  virtual GString *getPSFilter(int psLevel, char *indent)  { return NULL; }
   virtual GBool isBinary(GBool last = gTrue) { return gFalse; }
 };
 
@@ -691,11 +738,10 @@ public:
   ~FixedLengthEncoder();
   virtual StreamKind getKind() { return strWeird; }
   virtual void reset();
-  virtual void close();
   virtual int getChar();
   virtual int lookChar();
-  virtual GString *getPSFilter(char *indent) { return NULL; }
-  virtual GBool isBinary(GBool last = gTrue) { return gFalse; }
+  virtual GString *getPSFilter(int psLevel, char *indent) { return NULL; }
+  virtual GBool isBinary(GBool last = gTrue);
   virtual GBool isEncoder() { return gTrue; }
 
 private:
@@ -715,12 +761,11 @@ public:
   virtual ~ASCIIHexEncoder();
   virtual StreamKind getKind() { return strWeird; }
   virtual void reset();
-  virtual void close();
   virtual int getChar()
     { return (bufPtr >= bufEnd && !fillBuf()) ? EOF : (*bufPtr++ & 0xff); }
   virtual int lookChar()
     { return (bufPtr >= bufEnd && !fillBuf()) ? EOF : (*bufPtr & 0xff); }
-  virtual GString *getPSFilter(char *indent) { return NULL; }
+  virtual GString *getPSFilter(int psLevel, char *indent) { return NULL; }
   virtual GBool isBinary(GBool last = gTrue) { return gFalse; }
   virtual GBool isEncoder() { return gTrue; }
 
@@ -746,12 +791,11 @@ public:
   virtual ~ASCII85Encoder();
   virtual StreamKind getKind() { return strWeird; }
   virtual void reset();
-  virtual void close();
   virtual int getChar()
     { return (bufPtr >= bufEnd && !fillBuf()) ? EOF : (*bufPtr++ & 0xff); }
   virtual int lookChar()
     { return (bufPtr >= bufEnd && !fillBuf()) ? EOF : (*bufPtr & 0xff); }
-  virtual GString *getPSFilter(char *indent) { return NULL; }
+  virtual GString *getPSFilter(int psLevel, char *indent) { return NULL; }
   virtual GBool isBinary(GBool last = gTrue) { return gFalse; }
   virtual GBool isEncoder() { return gTrue; }
 
@@ -777,13 +821,12 @@ public:
   virtual ~RunLengthEncoder();
   virtual StreamKind getKind() { return strWeird; }
   virtual void reset();
-  virtual void close();
   virtual int getChar()
     { return (bufPtr >= bufEnd && !fillBuf()) ? EOF : (*bufPtr++ & 0xff); }
   virtual int lookChar()
     { return (bufPtr >= bufEnd && !fillBuf()) ? EOF : (*bufPtr & 0xff); }
-  virtual GString *getPSFilter(char *indent) { return NULL; }
-  virtual GBool isBinary(GBool last = gTrue) { return gFalse; }
+  virtual GString *getPSFilter(int psLevel, char *indent) { return NULL; }
+  virtual GBool isBinary(GBool last = gTrue) { return gTrue; }
   virtual GBool isEncoder() { return gTrue; }
 
 private:
index d707e2f..8536dbf 100644 (file)
@@ -2,7 +2,7 @@
 //
 // UTF8.h
 //
-// Copyright 2001-2002 Glyph & Cog, LLC
+// Copyright 2001-2003 Glyph & Cog, LLC
 //
 //========================================================================
 
index 75f23d2..300d802 100644 (file)
@@ -2,15 +2,16 @@
 //
 // UnicodeMap.cc
 //
-// Copyright 2001-2002 Glyph & Cog, LLC
+// Copyright 2001-2003 Glyph & Cog, LLC
 //
 //========================================================================
 
-#ifdef __GNUC__
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
 #pragma implementation
 #endif
 
-#include <aconf.h>
 #include <stdio.h>
 #include <string.h>
 #include "gmem.h"
@@ -101,37 +102,52 @@ UnicodeMap *UnicodeMap::parse(GString *encodingNameA) {
     ++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,
+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, UnicodeMapFunc funcA) {
+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() {
@@ -142,14 +158,32 @@ UnicodeMap::~UnicodeMap() {
   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() {
-  if (--refCnt == 0) {
+  GBool done;
+
+#if MULTITHREADED
+  gLockMutex(&mutex);
+#endif
+  done = --refCnt == 0;
+#if MULTITHREADED
+  gUnlockMutex(&mutex);
+#endif
+  if (done) {
     delete this;
   }
 }
@@ -168,29 +202,28 @@ int UnicodeMap::mapUnicode(Unicode u, char *buf, int bufSize) {
 
   a = 0;
   b = len;
-  if (u < ranges[a].start) {
-    return 0;
-  }
-  // 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;
+  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;
+      }
     }
-    code = ranges[a].code + (u - ranges[a].start);
-    for (i = n - 1; i >= 0; --i) {
-      buf[i] = (char)(code & 0xff);
-      code >>= 8;
+    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;
     }
-    return n;
   }
 
   for (i = 0; i < eMapsLen; ++i) {
index 274c447..6fd4ed2 100644 (file)
@@ -4,20 +4,26 @@
 //
 // Mapping from Unicode to an encoding.
 //
-// Copyright 2001-2002 Glyph & Cog, LLC
+// Copyright 2001-2003 Glyph & Cog, LLC
 //
 //========================================================================
 
 #ifndef UNICODEMAP_H
 #define UNICODEMAP_H
 
-#ifdef __GNUC__
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
 #pragma interface
 #endif
 
 #include "gtypes.h"
 #include "CharTypes.h"
 
+#if MULTITHREADED
+#include "GMutex.h"
+#endif
+
 class GString;
 
 //------------------------------------------------------------------------
@@ -47,12 +53,13 @@ public:
   static UnicodeMap *parse(GString *encodingNameA);
 
   // Create a resident UnicodeMap.
-  UnicodeMap(char *encodingNameA,
+  UnicodeMap(char *encodingNameA, GBool unicodeOutA,
             UnicodeMapRange *rangesA, int lenA);
 
   // Create a resident UnicodeMap that uses a function instead of a
   // list of ranges.
-  UnicodeMap(char *encodingNameA, UnicodeMapFunc funcA);
+  UnicodeMap(char *encodingNameA, GBool unicodeOutA,
+            UnicodeMapFunc funcA);
 
   ~UnicodeMap();
 
@@ -61,6 +68,8 @@ public:
 
   GString *getEncodingName() { return encodingName; }
 
+  GBool isUnicode() { return unicodeOut; }
+
   // Return true if this UnicodeMap matches the specified
   // <encodingNameA>.
   GBool match(GString *encodingNameA);
@@ -77,6 +86,7 @@ private:
 
   GString *encodingName;
   UnicodeMapKind kind;
+  GBool unicodeOut;
   union {
     UnicodeMapRange *ranges;   // (user, resident)
     UnicodeMapFunc func;       // (func)
@@ -85,6 +95,9 @@ private:
   UnicodeMapExt *eMaps;                // (user)
   int eMapsLen;                        // (user)
   int refCnt;
+#ifdef MULTITHREADED
+  GMutex mutex;
+#endif
 };
 
 //------------------------------------------------------------------------
index 51dee98..9c51034 100644 (file)
@@ -2,7 +2,7 @@
 //
 // UnicodeMapTables.h
 //
-// Copyright 2001-2002 Glyph & Cog, LLC
+// Copyright 2001-2003 Glyph & Cog, LLC
 //
 //========================================================================
 
index 0e1bbc9..e0d82d2 100644 (file)
@@ -2,15 +2,16 @@
 //
 // XRef.cc
 //
-// Copyright 1996-2002 Glyph & Cog, LLC
+// Copyright 1996-2003 Glyph & Cog, LLC
 //
 //========================================================================
 
-#ifdef __GNUC__
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
 #pragma implementation
 #endif
 
-#include <aconf.h>
 #include <stdlib.h>
 #include <stddef.h>
 #include <string.h>
 #endif
 
 //------------------------------------------------------------------------
+// ObjectStream
+//------------------------------------------------------------------------
+
+class ObjectStream {
+public:
+
+  // Create an object stream, using object number <objStrNum>,
+  // generation 0.
+  ObjectStream(XRef *xref, int objStrNumA);
+
+  ~ObjectStream();
+
+  // Return the object number of this object stream.
+  int getObjStrNum() { return objStrNum; }
+
+  // Get the <objIdx>th object from this stream, which should be
+  // object number <objNum>, generation 0.
+  Object *getObject(int objIdx, int objNum, Object *obj);
+
+private:
+
+  int objStrNum;               // object number of the object stream
+  int nObjects;                        // number of objects in the stream
+  Object *objs;                        // the objects (length = nObjects)
+  int *objNums;                        // the object numbers (length = nObjects)
+};
+
+ObjectStream::ObjectStream(XRef *xref, int objStrNumA) {
+  Stream *str;
+  Parser *parser;
+  int *offsets;
+  Object objStr, obj1, obj2;
+  int first, i;
+
+  objStrNum = objStrNumA;
+  nObjects = 0;
+  objs = NULL;
+  objNums = NULL;
+
+  if (!xref->fetch(objStrNum, 0, &objStr)->isStream()) {
+    goto err1;
+  }
+
+  if (!objStr.streamGetDict()->lookup("N", &obj1)->isInt()) {
+    obj1.free();
+    goto err1;
+  }
+  nObjects = obj1.getInt();
+  obj1.free();
+  if (nObjects == 0) {
+    goto err1;
+  }
+
+  if (!objStr.streamGetDict()->lookup("First", &obj1)->isInt()) {
+    obj1.free();
+    goto err1;
+  }
+  first = obj1.getInt();
+  obj1.free();
+
+  objs = new Object[nObjects];
+  objNums = (int *)gmalloc(nObjects * sizeof(int));
+  offsets = (int *)gmalloc(nObjects * sizeof(int));
+
+  // parse the header: object numbers and offsets
+  objStr.streamReset();
+  obj1.initNull();
+  str = new EmbedStream(objStr.getStream(), &obj1, gTrue, first);
+  parser = new Parser(xref, new Lexer(xref, str));
+  for (i = 0; i < nObjects; ++i) {
+    parser->getObj(&obj1);
+    parser->getObj(&obj2);
+    if (!obj1.isInt() || !obj2.isInt()) {
+      obj1.free();
+      obj2.free();
+      delete parser;
+      gfree(offsets);
+      goto err1;
+    }
+    objNums[i] = obj1.getInt();
+    offsets[i] = obj2.getInt();
+    obj1.free();
+    obj2.free();
+  }
+  while (str->getChar() != EOF) ;
+  delete parser;
+
+  // skip to the first object - this shouldn't be necessary because
+  // the First key is supposed to be equal to offsets[0], but just in
+  // case...
+  for (i = first; i < offsets[0]; ++i) {
+    objStr.getStream()->getChar();
+  }
+
+  // parse the objects
+  for (i = 0; i < nObjects; ++i) {
+    obj1.initNull();
+    if (i == nObjects - 1) {
+      str = new EmbedStream(objStr.getStream(), &obj1, gFalse, 0);
+    } else {
+      str = new EmbedStream(objStr.getStream(), &obj1, gTrue,
+                           offsets[i+1] - offsets[i]);
+    }
+    parser = new Parser(xref, new Lexer(xref, str));
+    parser->getObj(&objs[i]);
+    while (str->getChar() != EOF) ;
+    delete parser;
+  }
+
+  gfree(offsets);
+
+ err1:
+  objStr.free();
+  return;
+}
+
+ObjectStream::~ObjectStream() {
+  int i;
+
+  if (objs) {
+    for (i = 0; i < nObjects; ++i) {
+      objs[i].free();
+    }
+    delete[] objs;
+  }
+  gfree(objNums);
+}
+
+Object *ObjectStream::getObject(int objIdx, int objNum, Object *obj) {
+  if (objIdx < 0 || objIdx >= nObjects || objNum != objNums[objIdx]) {
+    return obj->initNull();
+  }
+  return objs[objIdx].copy(obj);
+}
+
+//------------------------------------------------------------------------
 // XRef
 //------------------------------------------------------------------------
 
 XRef::XRef(BaseStream *strA, GString *ownerPassword, GString *userPassword) {
   Guint pos;
-  int i;
+  Object obj;
 
   ok = gTrue;
   errCode = errNone;
@@ -59,35 +196,28 @@ XRef::XRef(BaseStream *strA, GString *ownerPassword, GString *userPassword) {
   entries = NULL;
   streamEnds = NULL;
   streamEndsLen = 0;
+  objStr = NULL;
 
   // read the trailer
   str = strA;
   start = str->getStart();
-  pos = readTrailer();
+  pos = getStartXref();
 
-  // if there was a problem with the trailer,
-  // try to reconstruct the xref table
+  // if there was a problem with the 'startxref' position, try to
+  // reconstruct the xref table
   if (pos == 0) {
     if (!(ok = constructXRef())) {
       errCode = errDamaged;
       return;
     }
 
-  // trailer is ok - read the xref table
+  // read the xref table
   } else {
-    entries = (XRefEntry *)gmalloc(size * sizeof(XRefEntry));
-    for (i = 0; i < size; ++i) {
-      entries[i].offset = 0xffffffff;
-      entries[i].used = gFalse;
-    }
     while (readXRef(&pos)) ;
 
     // if there was a problem with the xref table,
     // try to reconstruct it
     if (!ok) {
-      gfree(entries);
-      size = 0;
-      entries = NULL;
       if (!(ok = constructXRef())) {
        errCode = errDamaged;
        return;
@@ -95,6 +225,20 @@ XRef::XRef(BaseStream *strA, GString *ownerPassword, GString *userPassword) {
     }
   }
 
+  // get the root dictionary (catalog) object
+  trailerDict.dictLookupNF("Root", &obj);
+  if (obj.isRef()) {
+    rootNum = obj.getRefNum();
+    rootGen = obj.getRefGen();
+    obj.free();
+  } else {
+    obj.free();
+    if (!(ok = constructXRef())) {
+      errCode = errDamaged;
+      return;
+    }
+  }
+
   // now set the trailer dictionary's xref pointer so we can fetch
   // indirect objects from it
   trailerDict.getDict()->setXRef(this);
@@ -116,188 +260,154 @@ XRef::~XRef() {
   if (streamEnds) {
     gfree(streamEnds);
   }
+  if (objStr) {
+    delete objStr;
+  }
 }
 
-// Read startxref position, xref table size, and root.  Returns
-// first xref position.
-Guint XRef::readTrailer() {
-  Parser *parser;
-  Object obj;
+// Read the 'startxref' position.
+Guint XRef::getStartXref() {
   char buf[xrefSearchSize+1];
-  int n;
-  Guint pos, pos1;
   char *p;
-  int c;
-  int i;
+  int c, n, i;
 
   // read last xrefSearchSize bytes
   str->setPos(xrefSearchSize, -1);
   for (n = 0; n < xrefSearchSize; ++n) {
-    if ((c = str->getChar()) == EOF)
+    if ((c = str->getChar()) == EOF) {
       break;
+    }
     buf[n] = c;
   }
   buf[n] = '\0';
 
   // find startxref
   for (i = n - 9; i >= 0; --i) {
-    if (!strncmp(&buf[i], "startxref", 9))
+    if (!strncmp(&buf[i], "startxref", 9)) {
       break;
+    }
   }
-  if (i < 0)
+  if (i < 0) {
     return 0;
+  }
   for (p = &buf[i+9]; isspace(*p); ++p) ;
-  pos = lastXRefPos = strToUnsigned(p);
-
-  // find trailer dict by looking after first xref table
-  // (NB: we can't just use the trailer dict at the end of the file --
-  // this won't work for linearized files.)
-  str->setPos(start + pos);
-  for (i = 0; i < 4; ++i)
-    buf[i] = str->getChar();
-  if (strncmp(buf, "xref", 4))
-    return 0;
-  pos1 = pos + 4;
-  while (1) {
-    str->setPos(start + pos1);
-    for (i = 0; i < 35; ++i) {
-      if ((c = str->getChar()) == EOF)
-       return 0;
-      buf[i] = c;
-    }
-    if (!strncmp(buf, "trailer", 7))
-      break;
-    p = buf;
-    while (isspace(*p)) ++p;
-    while ('0' <= *p && *p <= '9') ++p;
-    while (isspace(*p)) ++p;
-    n = atoi(p);
-    while ('0' <= *p && *p <= '9') ++p;
-    while (isspace(*p)) ++p;
-    if (p == buf)
-      return 0;
-    pos1 += (p - buf) + n * 20;
-  }
-  pos1 += 7;
-
-  // read trailer dict
+  lastXRefPos = strToUnsigned(p);
+
+  return lastXRefPos;
+}
+
+// Read one xref table section.  Also reads the associated trailer
+// dictionary, and returns the prev pointer (if any).
+GBool XRef::readXRef(Guint *pos) {
+  Parser *parser;
+  Object obj;
+  GBool more;
+
+  // start up a parser, parse one token
   obj.initNull();
   parser = new Parser(NULL,
             new Lexer(NULL,
-              str->makeSubStream(start + pos1, gFalse, 0, &obj)));
-  parser->getObj(&trailerDict);
-  if (trailerDict.isDict()) {
-    trailerDict.dictLookupNF("Size", &obj);
-    if (obj.isInt())
-      size = obj.getInt();
-    else
-      pos = 0;
+              str->makeSubStream(start + *pos, gFalse, 0, &obj)));
+  parser->getObj(&obj);
+
+  // parse an old-style xref table
+  if (obj.isCmd("xref")) {
     obj.free();
-    trailerDict.dictLookupNF("Root", &obj);
-    if (obj.isRef()) {
-      rootNum = obj.getRefNum();
-      rootGen = obj.getRefGen();
-    } else {
-      pos = 0;
+    more = readXRefTable(parser, pos);
+
+  // parse an xref stream
+  } else if (obj.isInt()) {
+    obj.free();
+    if (!parser->getObj(&obj)->isInt()) {
+      goto err1;
+    }
+    obj.free();
+    if (!parser->getObj(&obj)->isCmd("obj")) {
+      goto err1;
     }
     obj.free();
+    if (!parser->getObj(&obj)->isStream()) {
+      goto err1;
+    }
+    more = readXRefStream(obj.getStream(), pos);
+    obj.free();
+
   } else {
-    pos = 0;
+    goto err1;
   }
+
   delete parser;
+  return more;
 
-  // return first xref position
-  return pos;
+ err1:
+  obj.free();
+  delete parser;
+  ok = gFalse;
+  return gFalse;
 }
 
-// Read an xref table and the prev pointer from the trailer.
-GBool XRef::readXRef(Guint *pos) {
-  Parser *parser;
-  Object obj, obj2;
-  char s[20];
+GBool XRef::readXRefTable(Parser *parser, Guint *pos) {
+  XRefEntry entry;
   GBool more;
-  int first, newSize, n, i, j;
-  int c;
-
-  // seek to xref in stream
-  str->setPos(start + *pos);
-
-  // make sure it's an xref table
-  while ((c = str->getChar()) != EOF && isspace(c)) ;
-  s[0] = (char)c;
-  s[1] = (char)str->getChar();
-  s[2] = (char)str->getChar();
-  s[3] = (char)str->getChar();
-  if (!(s[0] == 'x' && s[1] == 'r' && s[2] == 'e' && s[3] == 'f')) {
-    goto err2;
-  }
+  Object obj, obj2;
+  Guint pos2;
+  int first, n, newSize, i;
 
-  // read xref
   while (1) {
-    while ((c = str->lookChar()) != EOF && isspace(c)) {
-      str->getChar();
-    }
-    if (c == 't') {
+    parser->getObj(&obj);
+    if (obj.isCmd("trailer")) {
+      obj.free();
       break;
     }
-    for (i = 0; (c = str->getChar()) != EOF && isdigit(c) && i < 20; ++i) {
-      s[i] = (char)c;
-    }
-    if (i == 0) {
-      goto err2;
+    if (!obj.isInt()) {
+      goto err1;
     }
-    s[i] = '\0';
-    first = atoi(s);
-    while ((c = str->lookChar()) != EOF && isspace(c)) {
-      str->getChar();
-    }
-    for (i = 0; (c = str->getChar()) != EOF && isdigit(c) && i < 20; ++i) {
-      s[i] = (char)c;
-    }
-    if (i == 0) {
-      goto err2;
-    }
-    s[i] = '\0';
-    n = atoi(s);
-    while ((c = str->lookChar()) != EOF && isspace(c)) {
-      str->getChar();
+    first = obj.getInt();
+    obj.free();
+    if (!parser->getObj(&obj)->isInt()) {
+      goto err1;
     }
-    // check for buggy PDF files with an incorrect (too small) xref
-    // table size
+    n = obj.getInt();
+    obj.free();
     if (first + n > size) {
-      newSize = size + 256;
+      for (newSize = size ? 2 * size : 1024;
+          first + n > newSize;
+          newSize <<= 1) ;
       entries = (XRefEntry *)grealloc(entries, newSize * sizeof(XRefEntry));
       for (i = size; i < newSize; ++i) {
        entries[i].offset = 0xffffffff;
-       entries[i].used = gFalse;
+       entries[i].type = xrefEntryFree;
       }
       size = newSize;
     }
     for (i = first; i < first + n; ++i) {
-      for (j = 0; j < 20; ++j) {
-       if ((c = str->getChar()) == EOF) {
-         goto err2;
-       }
-       s[j] = (char)c;
+      if (!parser->getObj(&obj)->isInt()) {
+       goto err1;
+      }
+      entry.offset = (Guint)obj.getInt();
+      obj.free();
+      if (!parser->getObj(&obj)->isInt()) {
+       goto err1;
       }
+      entry.gen = obj.getInt();
+      obj.free();
+      parser->getObj(&obj);
+      if (obj.isCmd("n")) {
+       entry.type = xrefEntryUncompressed;
+      } else if (obj.isCmd("f")) {
+       entry.type = xrefEntryFree;
+      } else {
+       goto err1;
+      }
+      obj.free();
       if (entries[i].offset == 0xffffffff) {
-       s[10] = '\0';
-       entries[i].offset = strToUnsigned(s);
-       s[16] = '\0';
-       entries[i].gen = atoi(&s[11]);
-       if (s[17] == 'n') {
-         entries[i].used = gTrue;
-       } else if (s[17] == 'f') {
-         entries[i].used = gFalse;
-       } else {
-         goto err2;
-       }
+       entries[i] = entry;
        // PDF files of patents from the IBM Intellectual Property
        // Network have a bug: the xref table claims to start at 1
        // instead of 0.
        if (i == 1 && first == 1 &&
            entries[1].offset == 0 && entries[1].gen == 65535 &&
-           !entries[1].used) {
+           entries[1].type == xrefEntryFree) {
          i = first = 0;
          entries[0] = entries[1];
          entries[1].offset = 0xffffffff;
@@ -306,44 +416,203 @@ GBool XRef::readXRef(Guint *pos) {
     }
   }
 
-  // read prev pointer from trailer dictionary
-  obj.initNull();
-  parser = new Parser(NULL,
-            new Lexer(NULL,
-              str->makeSubStream(str->getPos(), gFalse, 0, &obj)));
-  parser->getObj(&obj);
-  if (!obj.isCmd("trailer")) {
-    goto err1;
-  }
-  obj.free();
-  parser->getObj(&obj);
-  if (!obj.isDict()) {
+  // read the trailer dictionary
+  if (!parser->getObj(&obj)->isDict()) {
     goto err1;
   }
+
+  // get the 'Prev' pointer
   obj.getDict()->lookupNF("Prev", &obj2);
   if (obj2.isInt()) {
     *pos = (Guint)obj2.getInt();
     more = gTrue;
+  } else if (obj2.isRef()) {
+    // certain buggy PDF generators generate "/Prev NNN 0 R" instead
+    // of "/Prev NNN"
+    *pos = (Guint)obj2.getRefNum();
+    more = gTrue;
   } else {
     more = gFalse;
   }
-  obj.free();
   obj2.free();
 
-  delete parser;
+  // save the first trailer dictionary
+  if (trailerDict.isNone()) {
+    obj.copy(&trailerDict);
+  }
+
+  // check for an 'XRefStm' key
+  if (obj.getDict()->lookup("XRefStm", &obj2)->isInt()) {
+    pos2 = obj2.getInt();
+    readXRef(&pos2);
+    if (!ok) {
+      goto err1;
+    }
+  }
+  obj2.free();
+
+  obj.free();
   return more;
 
  err1:
   obj.free();
- err2:
   ok = gFalse;
   return gFalse;
 }
 
+GBool XRef::readXRefStream(Stream *xrefStr, Guint *pos) {
+  Dict *dict;
+  int w[3];
+  GBool more;
+  Object obj, obj2, idx;
+  int newSize, first, n, i;
+
+  dict = xrefStr->getDict();
+
+  if (!dict->lookupNF("Size", &obj)->isInt()) {
+    goto err1;
+  }
+  newSize = obj.getInt();
+  obj.free();
+  if (newSize > size) {
+    entries = (XRefEntry *)grealloc(entries, newSize * sizeof(XRefEntry));
+    for (i = size; i < newSize; ++i) {
+      entries[i].offset = 0xffffffff;
+      entries[i].type = xrefEntryFree;
+    }
+    size = newSize;
+  }
+
+  if (!dict->lookupNF("W", &obj)->isArray() ||
+      obj.arrayGetLength() < 3) {
+    goto err1;
+  }
+  for (i = 0; i < 3; ++i) {
+    if (!obj.arrayGet(i, &obj2)->isInt()) {
+      obj2.free();
+      goto err1;
+    }
+    w[i] = obj2.getInt();
+    obj2.free();
+  }
+  obj.free();
+
+  xrefStr->reset();
+  dict->lookupNF("Index", &idx);
+  if (idx.isArray()) {
+    for (i = 0; i+1 < idx.arrayGetLength(); i += 2) {
+      if (!idx.arrayGet(i, &obj)->isInt()) {
+       idx.free();
+       goto err1;
+      }
+      first = obj.getInt();
+      obj.free();
+      if (!idx.arrayGet(i+1, &obj)->isInt()) {
+       idx.free();
+       goto err1;
+      }
+      n = obj.getInt();
+      obj.free();
+      if (!readXRefStreamSection(xrefStr, w, first, n)) {
+       idx.free();
+       goto err0;
+      }
+    }
+  } else {
+    if (!readXRefStreamSection(xrefStr, w, 0, size)) {
+      idx.free();
+      goto err0;
+    }
+  }
+  idx.free();
+
+  dict->lookupNF("Prev", &obj);
+  if (obj.isInt()) {
+    *pos = (Guint)obj.getInt();
+    more = gTrue;
+  } else {
+    more = gFalse;
+  }
+  obj.free();
+  if (trailerDict.isNone()) {
+    trailerDict.initDict(dict);
+  }
+
+  return more;
+
+ err1:
+  obj.free();
+ err0:
+  ok = gFalse;
+  return gFalse;
+}
+
+GBool XRef::readXRefStreamSection(Stream *xrefStr, int *w, int first, int n) {
+  Guint offset;
+  int type, gen, c, newSize, i, j;
+
+  if (first + n > size) {
+    for (newSize = size ? 2 * size : 1024;
+        first + n > newSize;
+        newSize <<= 1) ;
+    entries = (XRefEntry *)grealloc(entries, newSize * sizeof(XRefEntry));
+    for (i = size; i < newSize; ++i) {
+      entries[i].offset = 0xffffffff;
+      entries[i].type = xrefEntryFree;
+    }
+    size = newSize;
+  }
+  for (i = first; i < first + n; ++i) {
+    if (w[0] == 0) {
+      type = 1;
+    } else {
+      for (type = 0, j = 0; j < w[0]; ++j) {
+       if ((c = xrefStr->getChar()) == EOF) {
+         return gFalse;
+       }
+       type = (type << 8) + c;
+      }
+    }
+    for (offset = 0, j = 0; j < w[1]; ++j) {
+      if ((c = xrefStr->getChar()) == EOF) {
+       return gFalse;
+      }
+      offset = (offset << 8) + c;
+    }
+    for (gen = 0, j = 0; j < w[2]; ++j) {
+      if ((c = xrefStr->getChar()) == EOF) {
+       return gFalse;
+      }
+      gen = (gen << 8) + c;
+    }
+    switch (type) {
+    case 0:
+      entries[i].offset = offset;
+      entries[i].gen = gen;
+      entries[i].type = xrefEntryFree;
+      break;
+    case 1:
+      entries[i].offset = offset;
+      entries[i].gen = gen;
+      entries[i].type = xrefEntryUncompressed;
+      break;
+    case 2:
+      entries[i].offset = offset;
+      entries[i].gen = gen;
+      entries[i].type = xrefEntryCompressed;
+      break;
+    default:
+      return gFalse;
+    }
+  }
+
+  return gTrue;
+}
+
 // Attempt to construct an xref table for a damaged file.
 GBool XRef::constructXRef() {
   Parser *parser;
-  Object obj;
+  Object newTrailerDict, obj;
   char buf[256];
   Guint pos;
   int num, gen;
@@ -353,6 +622,10 @@ GBool XRef::constructXRef() {
   int i;
   GBool gotRoot;
 
+  gfree(entries);
+  size = 0;
+  entries = NULL;
+
   error(0, "PDF file is damaged - attempting to reconstruct xref table...");
   gotRoot = gFalse;
   streamEndsLen = streamEndsSize = 0;
@@ -371,20 +644,21 @@ GBool XRef::constructXRef() {
       parser = new Parser(NULL,
                 new Lexer(NULL,
                   str->makeSubStream(start + pos + 7, gFalse, 0, &obj)));
-      if (!trailerDict.isNone())
-       trailerDict.free();
-      parser->getObj(&trailerDict);
-      if (trailerDict.isDict()) {
-       trailerDict.dictLookupNF("Root", &obj);
+      parser->getObj(&newTrailerDict);
+      if (newTrailerDict.isDict()) {
+       newTrailerDict.dictLookupNF("Root", &obj);
        if (obj.isRef()) {
          rootNum = obj.getRefNum();
          rootGen = obj.getRefGen();
+         if (!trailerDict.isNone()) {
+           trailerDict.free();
+         }
+         newTrailerDict.copy(&trailerDict);
          gotRoot = gTrue;
        }
        obj.free();
-      } else {
-       pos = 0;
       }
+      newTrailerDict.free();
       delete parser;
 
     // look for object
@@ -413,14 +687,15 @@ GBool XRef::constructXRef() {
                            grealloc(entries, newSize * sizeof(XRefEntry));
                for (i = size; i < newSize; ++i) {
                  entries[i].offset = 0xffffffff;
-                 entries[i].used = gFalse;
+                 entries[i].type = xrefEntryFree;
                }
                size = newSize;
              }
-             if (!entries[num].used || gen >= entries[num].gen) {
+             if (entries[num].type == xrefEntryFree ||
+                 gen >= entries[num].gen) {
                entries[num].offset = pos - start;
                entries[num].gen = gen;
-               entries[num].used = gTrue;
+               entries[num].type = xrefEntryUncompressed;
              }
            }
          }
@@ -451,9 +726,12 @@ GBool XRef::checkEncrypted(GString *ownerPassword, GString *userPassword) {
   GBool encrypted1;
   GBool ret;
 
+  keyLength = 0;
+  encVersion = encRevision = 0;
   ret = gFalse;
 
   permFlags = defPermFlags;
+  ownerPasswordOk = gFalse;
   trailerDict.dictLookup("Encrypt", &encrypt);
   if ((encrypted1 = encrypt.isDict())) {
     ret = gTrue;
@@ -544,38 +822,34 @@ GBool XRef::checkEncrypted(GString *ownerPassword, GString *userPassword) {
 
 GBool XRef::okToPrint(GBool ignoreOwnerPW) {
 #ifndef NO_DECRYPTION
-  if ((ignoreOwnerPW || !ownerPasswordOk) && !(permFlags & permPrint)) {
-    return gFalse;
-  }
-#endif
+  return (!ignoreOwnerPW && ownerPasswordOk) || (permFlags & permPrint);
+#else
   return gTrue;
+#endif
 }
 
 GBool XRef::okToChange(GBool ignoreOwnerPW) {
 #ifndef NO_DECRYPTION
-  if ((ignoreOwnerPW || !ownerPasswordOk) && !(permFlags & permChange)) {
-    return gFalse;
-  }
-#endif
+  return (!ignoreOwnerPW && ownerPasswordOk) || (permFlags & permChange);
+#else
   return gTrue;
+#endif
 }
 
 GBool XRef::okToCopy(GBool ignoreOwnerPW) {
 #ifndef NO_DECRYPTION
-  if ((ignoreOwnerPW || !ownerPasswordOk) && !(permFlags & permCopy)) {
-    return gFalse;
-  }
-#endif
+  return (!ignoreOwnerPW && ownerPasswordOk) || (permFlags & permCopy);
+#else
   return gTrue;
+#endif
 }
 
 GBool XRef::okToAddNotes(GBool ignoreOwnerPW) {
 #ifndef NO_DECRYPTION
-  if ((ignoreOwnerPW || !ownerPasswordOk) && !(permFlags & permNotes)) {
-    return gFalse;
-  }
-#endif
+  return (!ignoreOwnerPW && ownerPasswordOk) || (permFlags & permNotes);
+#else
   return gTrue;
+#endif
 }
 
 Object *XRef::fetch(int num, int gen, Object *obj) {
@@ -585,12 +859,16 @@ Object *XRef::fetch(int num, int gen, Object *obj) {
 
   // check for bogus ref - this can happen in corrupted PDF files
   if (num < 0 || num >= size) {
-    obj->initNull();
-    return obj;
+    goto err;
   }
 
   e = &entries[num];
-  if (e->gen == gen && e->offset != 0xffffffff) {
+  switch (e->type) {
+
+  case xrefEntryUncompressed:
+    if (e->gen != gen) {
+      goto err;
+    }
     obj1.initNull();
     parser = new Parser(this,
               new Lexer(this,
@@ -598,26 +876,44 @@ Object *XRef::fetch(int num, int gen, Object *obj) {
     parser->getObj(&obj1);
     parser->getObj(&obj2);
     parser->getObj(&obj3);
-    if (obj1.isInt() && obj1.getInt() == num &&
-       obj2.isInt() && obj2.getInt() == gen &&
-       obj3.isCmd("obj")) {
+    if (!obj1.isInt() || obj1.getInt() != num ||
+       !obj2.isInt() || obj2.getInt() != gen ||
+       !obj3.isCmd("obj")) {
+      goto err;
+    }
 #ifndef NO_DECRYPTION
-      parser->getObj(obj, encrypted ? fileKey : (Guchar *)NULL, keyLength,
-                    num, gen);
+    parser->getObj(obj, encrypted ? fileKey : (Guchar *)NULL, keyLength,
+                  num, gen);
 #else
-      parser->getObj(obj);
+    parser->getObj(obj);
 #endif
-    } else {
-      obj->initNull();
-    }
     obj1.free();
     obj2.free();
     obj3.free();
     delete parser;
-  } else {
-    obj->initNull();
+    break;
+
+  case xrefEntryCompressed:
+    if (gen != 0) {
+      goto err;
+    }
+    if (!objStr || objStr->getObjStrNum() != (int)e->offset) {
+      if (objStr) {
+       delete objStr;
+      }
+      objStr = new ObjectStream(this, e->offset);
+    }
+    objStr->getObject(e->gen, num, obj);
+    break;
+
+  default:
+    goto err;
   }
+
   return obj;
+
+ err:
+  return obj->initNull();
 }
 
 Object *XRef::getDocInfo(Object *obj) {
index 7876fa6..bec487a 100644 (file)
@@ -2,14 +2,16 @@
 //
 // XRef.h
 //
-// Copyright 1996-2002 Glyph & Cog, LLC
+// Copyright 1996-2003 Glyph & Cog, LLC
 //
 //========================================================================
 
 #ifndef XREF_H
 #define XREF_H
 
-#ifdef __GNUC__
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
 #pragma interface
 #endif
 
 
 class Dict;
 class Stream;
+class Parser;
+class ObjectStream;
 
 //------------------------------------------------------------------------
 // XRef
 //------------------------------------------------------------------------
 
+enum XRefEntryType {
+  xrefEntryFree,
+  xrefEntryUncompressed,
+  xrefEntryCompressed
+};
+
 struct XRefEntry {
   Guint offset;
   int gen;
-  GBool used;
+  XRefEntryType type;
 };
 
 class XRef {
@@ -81,6 +91,11 @@ public:
   // Returns false if unknown or file is not damaged.
   GBool getStreamEnd(Guint streamStart, Guint *streamEnd);
 
+  // Direct access.
+  int getSize() { return size; }
+  XRefEntry *getEntry(int i) { return &entries[i]; }
+  Object *getTrailerDict() { return &trailerDict; }
+
 private:
 
   BaseStream *str;             // input stream
@@ -96,6 +111,7 @@ private:
   Guint *streamEnds;           // 'endstream' positions - only used in
                                //   damaged files
   int streamEndsLen;           // number of valid entries in streamEnds
+  ObjectStream *objStr;                // cached object stream
 #ifndef NO_DECRYPTION
   GBool encrypted;             // true if file is encrypted
   int encVersion;              // encryption algorithm
@@ -106,8 +122,11 @@ private:
   GBool ownerPasswordOk;       // true if owner password is correct
 #endif
 
-  Guint readTrailer();
+  Guint getStartXref();
   GBool readXRef(Guint *pos);
+  GBool readXRefTable(Parser *parser, Guint *pos);
+  GBool readXRefStreamSection(Stream *xrefStr, int *w, int first, int n);
+  GBool readXRefStream(Stream *xrefStr, Guint *pos);
   GBool constructXRef();
   GBool checkEncrypted(GString *ownerPassword, GString *userPassword);
   Guint strToUnsigned(char *s);
index d288e79..524ccee 100644 (file)
@@ -19,5 +19,6 @@
 // SELECT_TAKES_INT
 // HAVE_FSEEK64
 // HAVE_MKSTEMPS
+// HAVE_FSEEKO 1
 
 #endif
index 39cb291..bf6baf4 100644 (file)
@@ -2,7 +2,7 @@
 //
 // config.h
 //
-// Copyright 1996-2002 Glyph & Cog, LLC
+// Copyright 1996-2004 Glyph & Cog, LLC
 //
 //========================================================================
 
 //------------------------------------------------------------------------
 
 // xpdf version
-
-#define xpdfVersion "1.01"
+#define xpdfVersion         "3.00"
+#define xpdfVersionNum      3.00
+#define xpdfMajorVersion    3
+#define xpdfMinorVersion    0
+#define xpdfMajorVersionStr "3"
+#define xpdfMinorVersionStr "0"
 
 // supported PDF version
-#define supportedPDFVersionStr "1.4"
-#define supportedPDFVersionNum 1.4
+#define supportedPDFVersionStr "1.5"
+#define supportedPDFVersionNum 1.5
 
 // copyright notice
-#define xpdfCopyright "Copyright 1996-2002 Glyph & Cog, LLC"
+#define xpdfCopyright "Copyright 1996-2004 Glyph & Cog, LLC"
+
+// Windows resource file stuff
+#define winxpdfVersion "WinXpdf 3.00"
+#define xpdfCopyrightAmp "Copyright 1996-2004 Glyph && Cog, LLC"
 
 //------------------------------------------------------------------------
 // paper size
 // popen
 //------------------------------------------------------------------------
 
-#ifdef _MSC_VER
+#if defined(_MSC_VER) || defined(__BORLANDC__)
 #define popen _popen
 #define pclose _pclose
 #endif
 
-#if defined(VMS) || defined(VMCMS) || defined(DOS) || defined(OS2) || defined(__EMX__) || defined(WIN32) || defined(__DJGPP__) || defined(__CYGWIN32__) || defined(MACOS)
+#if defined(VMS) || defined(VMCMS) || defined(DOS) || defined(OS2) || defined(__EMX__) || defined(WIN32) || defined(__DJGPP__) || defined(MACOS)
 #define POPEN_READ_MODE "rb"
 #else
 #define POPEN_READ_MODE "r"
 #endif
 
 //------------------------------------------------------------------------
-// uncompress program
-//------------------------------------------------------------------------
-
-#ifdef HAVE_POPEN
-
-// command to uncompress to stdout
-#  ifdef USE_GZIP
-#    define uncompressCmd "gzip -d -c -q"
-#  else
-#    ifdef __EMX__
-#      define uncompressCmd "compress -d -c"
-#    else
-#      define uncompressCmd "uncompress -c"
-#    endif // __EMX__
-#  endif // USE_GZIP
-
-#else // HAVE_POPEN
-
-// command to uncompress a file
-#  ifdef USE_GZIP
-#    define uncompressCmd "gzip -d -q"
-#  else
-#    define uncompressCmd "uncompress"
-#  endif // USE_GZIP
-
-#endif // HAVE_POPEN
-
-//------------------------------------------------------------------------
 // Win32 stuff
 //------------------------------------------------------------------------
 
 #undef CDECL
 #endif
 
-#ifdef _MSC_VER
+#if defined(_MSC_VER) || defined(__BORLANDC__)
 #define CDECL __cdecl
 #else
 #define CDECL
index b4d88cd..11f5cf6 100644 (file)
@@ -4,20 +4,13 @@
 //
 // Miscellaneous file and directory name manipulation.
 //
-// Copyright 1996-2002 Glyph & Cog, LLC
+// Copyright 1996-2003 Glyph & Cog, LLC
 //
 //========================================================================
 
 #include <aconf.h>
-#include "../../config.h"
 
-#ifdef WIN32
-   extern "C" {
-//#  ifndef _MSC_VER
-//#    include <kpathsea/win32lib.h>
-//#  endif
-   }
-#else // !WIN32
+#ifndef WIN32
 #  if defined(MACOS)
 #    include <sys/stat.h>
 #  elif !defined(ACORN)
@@ -443,58 +436,11 @@ time_t getModTime(char *fileName) {
   return statBuf.st_mtime;
 #endif
 }
-static char* getTempDir()
-{
-#ifdef WIN32
-    char*dir = getenv("TMP");
-    if(!dir) dir = getenv("TEMP");
-    if(!dir) dir = getenv("tmp");
-    if(!dir) dir = getenv("temp");
-    if(!dir) dir = "C:\\";
-#else
-    char* dir = "/tmp/";
-#endif
-    return dir;
-}
-
-char* mktmpname(char*ptr) {
-    static char tmpbuf[128];
-    char*dir = getTempDir();
-    int l = strlen(dir);
-    char*sep = "";
-    if(!ptr)
-       ptr = tmpbuf;
-    if(l && dir[l-1]!='/' && dir[l-1]!='\\') {
-#ifdef WIN32
-       sep = "\\";
-#else
-       sep = "/";
-#endif
-    }
-
- //   used to be mktemp. This does remove the warnings, but
- //   It's not exactly an improvement.
-#ifdef HAVE_LRAND48
-    sprintf(ptr, "%s%s%08x%08x",dir,sep,lrand48(),lrand48());
-#else
-#   ifdef HAVE_RAND
-       sprintf(ptr, "%s%s%08x%08x",dir,sep,rand(),rand());
-#   else
-       static int count = 1;
-       sprintf(ptr, "%s%s%08x%04x%04x",dir,sep,time(0),(unsigned int)tmpbuf^((unsigned int)tmpbuf)>>16,count);
-       count ++;
-#   endif
-#endif
-     return ptr;
-}
 
 GBool openTempFile(GString **name, FILE **f, char *mode, char *ext) {
 #if defined(WIN32)
   //---------- Win32 ----------
   char *s;
-  char buf[_MAX_PATH];
-  char *fp;
 
   if (!(s = _tempnam(getenv("TEMP"), NULL))) {
     return gFalse;
@@ -517,7 +463,7 @@ GBool openTempFile(GString **name, FILE **f, char *mode, char *ext) {
   // with this file name after the tmpnam call and before the fopen
   // call.  I will happily accept fixes to this function for non-Unix
   // OSs.
-  if (!(s = mktmpname(NULL))) { //was: tmpnam
+  if (!(s = tmpnam(NULL))) {
     return gFalse;
   }
   *name = new GString(s);
@@ -544,7 +490,7 @@ GBool openTempFile(GString **name, FILE **f, char *mode, char *ext) {
     (*name)->append("/XXXXXX")->append(ext);
     fd = mkstemps((*name)->getCString(), strlen(ext));
 #else
-    if (!(s = mktmpname(NULL))) { //was: tmpnam
+    if (!(s = tmpnam(NULL))) {
       return gFalse;
     }
     *name = new GString(s);
@@ -561,7 +507,7 @@ GBool openTempFile(GString **name, FILE **f, char *mode, char *ext) {
     (*name)->append("/XXXXXX");
     fd = mkstemp((*name)->getCString());
 #else // HAVE_MKSTEMP
-    if (!(s = mktmpname(NULL))) { //was: tmpnam
+    if (!(s = tmpnam(NULL))) {
       return gFalse;
     }
     *name = new GString(s);
@@ -692,36 +638,48 @@ GDir::~GDir() {
 }
 
 GDirEntry *GDir::getNextEntry() {
-  struct dirent *ent;
   GDirEntry *e;
 
-  e = NULL;
 #if defined(WIN32)
-  e = new GDirEntry(path->getCString(), ffd.cFileName, doStat);
-  if (hnd  && !FindNextFile(hnd, &ffd)) {
-    FindClose(hnd);
-    hnd = NULL;
+  if (hnd) {
+    e = new GDirEntry(path->getCString(), ffd.cFileName, doStat);
+    if (hnd  && !FindNextFile(hnd, &ffd)) {
+      FindClose(hnd);
+      hnd = NULL;
+    }
+  } else {
+    e = NULL;
   }
 #elif defined(ACORN)
 #elif defined(MACOS)
-#else
+#elif defined(VMS)
+  struct dirent *ent;
+  e = NULL;
   if (dir) {
-#ifdef VMS
     if (needParent) {
       e = new GDirEntry(path->getCString(), "-", doStat);
       needParent = gFalse;
       return e;
     }
-#endif
     ent = readdir(dir);
-#ifndef VMS
-    if (ent && !strcmp(ent->d_name, "."))
+    if (ent) {
+      e = new GDirEntry(path->getCString(), ent->d_name, doStat);
+    }
+  }
+#else
+  struct dirent *ent;
+  e = NULL;
+  if (dir) {
+    ent = readdir(dir);
+    if (ent && !strcmp(ent->d_name, ".")) {
       ent = readdir(dir);
-#endif
-    if (ent)
+    }
+    if (ent) {
       e = new GDirEntry(path->getCString(), ent->d_name, doStat);
+    }
   }
 #endif
+
   return e;
 }
 
@@ -734,6 +692,7 @@ void GDir::rewind() {
   tmp = path->copy();
   tmp->append("/*.*");
   hnd = FindFirstFile(tmp->getCString(), &ffd);
+  delete tmp;
 #elif defined(ACORN)
 #elif defined(MACOS)
 #else
index aa5e9d8..82f1d7a 100644 (file)
@@ -4,7 +4,7 @@
 //
 // Miscellaneous file and directory name manipulation.
 //
-// Copyright 1996-2002 Glyph & Cog, LLC
+// Copyright 1996-2003 Glyph & Cog, LLC
 //
 //========================================================================
 
 #include <stdio.h>
 #include <stdlib.h>
 #include <stddef.h>
-#include "./aconf.h"
 #if defined(WIN32)
 #  include <sys/stat.h>
 #  ifdef FPTEX
 #    include <win32lib.h>
 #  else
 #    include <windows.h>
-#    include <winbase.h>
 #  endif
 #elif defined(ACORN)
 #elif defined(MACOS)
@@ -137,7 +135,4 @@ private:
 #endif
 };
 
-/* create a temporary filename */
-char* mktmpname(char*ptr);
-
 #endif
index 93ccb94..587e7fa 100644 (file)
@@ -3,7 +3,7 @@
  *
  * Memory routines with out-of-memory checking.
  *
- * Copyright 1996-2002 Glyph & Cog, LLC
+ * Copyright 1996-2003 Glyph & Cog, LLC
  */
 
 #ifndef GMEM_H
index 1879b88..9f64f57 100644 (file)
@@ -3,7 +3,7 @@
  *
  * Some useful simple types.
  *
- * Copyright 1996-2002 Glyph & Cog, LLC
+ * Copyright 1996-2003 Glyph & Cog, LLC
  */
 
 #ifndef GTYPES_H