upgrade to xpdf 1.01.
[swftools.git] / pdf2swf / xpdf / FontFile.cc
index 777b56a..ae58547 100644 (file)
@@ -2,7 +2,7 @@
 //
 // FontFile.cc
 //
-// Copyright 1999 Derek B. Noonburg
+// Copyright 1999-2002 Glyph & Cog, LLC
 //
 //========================================================================
 
@@ -10,6 +10,7 @@
 #pragma implementation
 #endif
 
+#include <aconf.h>
 #include <math.h>
 #include <stdlib.h>
 #include <stddef.h>
 #include <ctype.h>
 #include "gmem.h"
 #include "Error.h"
+#include "GlobalParams.h"
+#include "CharCodeToUnicode.h"
+#include "FontEncodingTables.h"
 #include "FontFile.h"
 
-#include "StdFontInfo.h"
-#include "CompactFontInfo.h"
-
-//------------------------------------------------------------------------
-
-static Guint getWord(Guchar *ptr, int size);
-static double getNum(Guchar **ptr, GBool *fp);
-static char *getString(int sid, Guchar *stringIdxPtr,
-                      Guchar *stringStartPtr, int stringOffSize,
-                      char *buf);
+#include "CompactFontTables.h"
 
 //------------------------------------------------------------------------
 
@@ -58,34 +53,46 @@ FontFile::~FontFile() {
 
 Type1FontFile::Type1FontFile(char *file, int len) {
   char *line, *line1, *p, *p2;
+  GBool haveEncoding;
   char buf[256];
   char c;
-  int n, code, i;
+  int n, code, i, j;
 
   name = NULL;
-  encoding = NULL;
-  freeEnc = gTrue;
+  encoding = (char **)gmalloc(256 * sizeof(char *));
+  for (i = 0; i < 256; ++i) {
+    encoding[i] = NULL;
+  }
+  haveEncoding = gFalse;
 
-  for (i = 1, line = file; i <= 100 && line < file + len && !encoding; ++i) {
+  for (i = 1, line = file;
+       i <= 100 && line < file + len && !haveEncoding;
+       ++i) {
 
     // get font name
     if (!strncmp(line, "/FontName", 9)) {
       strncpy(buf, line, 255);
       buf[255] = '\0';
       if ((p = strchr(buf+9, '/')) &&
-         (p = strtok(p+1, " \t\n\r")))
+         (p = strtok(p+1, " \t\n\r"))) {
        name = copyString(p);
+      }
       line = nextLine(line, file + len);
 
     // get encoding
     } else if (!strncmp(line, "/Encoding StandardEncoding def", 30)) {
-      encoding = type1StdEncoding.copy();
+      for (j = 0; j < 256; ++j) {
+       if (standardEncoding[j]) {
+         encoding[j] = copyString(standardEncoding[j]);
+       }
+      }
+      haveEncoding = gTrue;
     } else if (!strncmp(line, "/Encoding 256 array", 19)) {
-      encoding = new FontEncoding();
-      for (i = 0; i < 300; ++i) {
+      for (j = 0; j < 300; ++j) {
        line1 = nextLine(line, file + len);
-       if ((n = line1 - line) > 255)
+       if ((n = line1 - line) > 255) {
          n = 255;
+       }
        strncpy(buf, line, n);
        buf[n] = '\0';
        for (p = buf; *p == ' ' || *p == '\t'; ++p) ;
@@ -102,7 +109,7 @@ Type1FontFile::Type1FontFile(char *file, int len) {
                ++p;
                for (p2 = p; *p2 && *p2 != ' ' && *p2 != '\t'; ++p2) ;
                *p2 = '\0';
-               encoding->addChar(code, copyString(p));
+               encoding[code] = copyString(p);
              }
            }
          }
@@ -115,6 +122,7 @@ Type1FontFile::Type1FontFile(char *file, int len) {
        line = line1;
       }
       //~ check for getinterval/putinterval junk
+      haveEncoding = gTrue;
 
     } else {
       line = nextLine(line, file + len);
@@ -123,77 +131,143 @@ Type1FontFile::Type1FontFile(char *file, int len) {
 }
 
 Type1FontFile::~Type1FontFile() {
-  if (name)
-    gfree(name);
-  if (encoding && freeEnc)
-    delete encoding;
-}
+  int i;
 
-FontEncoding *Type1FontFile::getEncoding(GBool taken) {
-  if (taken)
-    freeEnc = gFalse;
-  return encoding;
+  if (name) {
+    gfree(name);
+  }
+  for (i = 0; i < 256; ++i) {
+    gfree(encoding[i]);
+  }
+  gfree(encoding);
 }
 
 //------------------------------------------------------------------------
 // Type1CFontFile
 //------------------------------------------------------------------------
 
-Type1CFontFile::Type1CFontFile(char *file, int len) {
+struct Type1CTopDict {
+  int version;
+  int notice;
+  int copyright;
+  int fullName;
+  int familyName;
+  int weight;
+  int isFixedPitch;
+  double italicAngle;
+  double underlinePosition;
+  double underlineThickness;
+  int paintType;
+  int charstringType;
+  double fontMatrix[6];
+  int uniqueID;
+  double fontBBox[4];
+  double strokeWidth;
+  int charset;
+  int encoding;
+  int charStrings;
+  int privateSize;
+  int privateOffset;
+
+  //----- CIDFont entries
+  int registry;
+  int ordering;
+  int supplement;
+  int fdArrayOffset;
+  int fdSelectOffset;
+};
+
+struct Type1CPrivateDict {
+  GString *dictData;
+  int subrsOffset;
+  double defaultWidthX;
+  GBool defaultWidthXFP;
+  double nominalWidthX;
+  GBool nominalWidthXFP;
+};
+
+Type1CFontFile::Type1CFontFile(char *fileA, int lenA) {
+  Guchar *nameIdxPtr, *idxPtr0, *idxPtr1;
+
+  file = fileA;
+  len = lenA;
+  name = NULL;
+  encoding = NULL;
+
+  // some tools embed Type 1C fonts with an extra whitespace char at
+  // the beginning
+  if (file[0] != '\x01') {
+    ++file;
+  }
+
+  // read header
+  topOffSize = file[3] & 0xff;
+
+  // read name index (first font only)
+  nameIdxPtr = (Guchar *)file + (file[2] & 0xff);
+  idxPtr0 = getIndexValPtr(nameIdxPtr, 0);
+  idxPtr1 = getIndexValPtr(nameIdxPtr, 1);
+  name = new GString((char *)idxPtr0, idxPtr1 - idxPtr0);
+
+  topDictIdxPtr = getIndexEnd(nameIdxPtr);
+  stringIdxPtr = getIndexEnd(topDictIdxPtr);
+  gsubrIdxPtr = getIndexEnd(stringIdxPtr);
+}
+
+Type1CFontFile::~Type1CFontFile() {
+  int i;
+
+  delete name;
+  if (encoding) {
+    for (i = 0; i < 256; ++i) {
+      gfree(encoding[i]);
+    }
+    gfree(encoding);
+  }
+}
+
+char *Type1CFontFile::getName() {
+  return name->getCString();
+}
+
+char **Type1CFontFile::getEncoding() {
+  if (!encoding) {
+    readNameAndEncoding();
+  }
+  return encoding;
+}
+
+void Type1CFontFile::readNameAndEncoding() {
   char buf[256];
-  Guchar *topPtr, *idxStartPtr, *idxPtr0, *idxPtr1;
-  Guchar *stringIdxPtr, *stringStartPtr;
-  int topOffSize, idxOffSize, stringOffSize;
-  int nFonts, nStrings, nGlyphs;
+  Guchar *idxPtr0, *idxPtr1, *ptr;
+  int nGlyphs;
   int nCodes, nRanges, nLeft, nSups;
   Gushort *glyphNames;
   int charset, enc, charstrings;
-  int charsetFormat, encFormat;
+  int encFormat;
   int c, sid;
-  double op[48];
   double x;
   GBool isFP;
   int key;
-  int i, j, n;
-
-  name = NULL;
-  encoding = NULL;
-  freeEnc = gTrue;
+  int i, j;
 
-  // read header
-  topPtr = (Guchar *)file + (file[2] & 0xff);
-  topOffSize = file[3] & 0xff;
+  encoding = (char **)gmalloc(256 * sizeof(char *));
+  for (i = 0; i < 256; ++i) {
+    encoding[i] = NULL;
+  }
 
-  // read name index (first font only)
-  nFonts = getWord(topPtr, 2);
-  idxOffSize = topPtr[2];
-  topPtr += 3;
-  idxStartPtr = topPtr + (nFonts + 1) * idxOffSize - 1;
-  idxPtr0 = idxStartPtr + getWord(topPtr, idxOffSize);
-  idxPtr1 = idxStartPtr + getWord(topPtr + idxOffSize, idxOffSize);
-  if ((n = idxPtr1 - idxPtr0) > 255)
-    n = 255;
-  strncpy(buf, (char *)idxPtr0, n);
-  buf[n] = '\0';
-  name = copyString(buf);
-  topPtr = idxStartPtr + getWord(topPtr + nFonts * idxOffSize, idxOffSize);
-
-  // read top dict index (first font only)
-  nFonts = getWord(topPtr, 2);
-  idxOffSize = topPtr[2];
-  topPtr += 3;
-  idxStartPtr = topPtr + (nFonts + 1) * idxOffSize - 1;
-  idxPtr0 = idxStartPtr + getWord(topPtr, idxOffSize);
-  idxPtr1 = idxStartPtr + getWord(topPtr + idxOffSize, idxOffSize);
-  charset = 0;
-  enc = 0;
-  charstrings = 0;
+  // read top dict (first font only)
+  idxPtr0 = getIndexValPtr(topDictIdxPtr, 0);
+  idxPtr1 = getIndexValPtr(topDictIdxPtr, 1);
+  charset = enc = charstrings = 0;
   i = 0;
-  while (idxPtr0 < idxPtr1) {
-    if (*idxPtr0 <= 27 || *idxPtr0 == 31) {
-      key = *idxPtr0++;
-      if (key == 0x0c)
-       key = (key << 8) | *idxPtr0++;
+  ptr = idxPtr0;
+  while (ptr < idxPtr1) {
+    if (*ptr <= 27 || *ptr == 31) {
+      key = *ptr++;
+      if (key == 0x0c) {
+       key = (key << 8) | *ptr++;
+      }
       if (key == 0x0f) { // charset
        charset = (int)op[0];
       } else if (key == 0x10) { // encoding
@@ -203,445 +277,130 @@ Type1CFontFile::Type1CFontFile(char *file, int len) {
       }
       i = 0;
     } else {
-      x = getNum(&idxPtr0, &isFP);
-      if (i < 48)
+      x = getNum(&ptr, &isFP);
+      if (i < 48) {
        op[i++] = x;
+      }
     }
   }
-  topPtr = idxStartPtr + getWord(topPtr + nFonts * idxOffSize, idxOffSize);
-
-  // read string index
-  nStrings = getWord(topPtr, 2);
-  stringOffSize = topPtr[2];
-  topPtr += 3;
-  stringIdxPtr = topPtr;
-  stringStartPtr = topPtr + (nStrings + 1) * stringOffSize - 1;
-  topPtr = stringStartPtr + getWord(topPtr + nStrings * stringOffSize,
-                                   stringOffSize);
 
   // get number of glyphs from charstrings index
-  topPtr = (Guchar *)file + charstrings;
-  nGlyphs = getWord(topPtr, 2);
+  nGlyphs = getIndexLen((Guchar *)file + charstrings);
 
-  // read charset
-  if (charset == 0) {
-    glyphNames = type1CISOAdobeCharset;
-  } else if (charset == 1) {
-    glyphNames = type1CExpertCharset;
-  } else if (charset == 2) {
-    glyphNames = type1CExpertSubsetCharset;
-  } else {
-    glyphNames = (Gushort *)gmalloc(nGlyphs * sizeof(Gushort));
-    glyphNames[0] = 0;
-    topPtr = (Guchar *)file + charset;
-    charsetFormat = *topPtr++;
-    if (charsetFormat == 0) {
-      for (i = 1; i < nGlyphs; ++i) {
-       glyphNames[i] = getWord(topPtr, 2);
-       topPtr += 2;
-      }
-    } else if (charsetFormat == 1) {
-      i = 1;
-      while (i < nGlyphs) {
-       c = getWord(topPtr, 2);
-       topPtr += 2;
-       nLeft = *topPtr++;
-       for (j = 0; j <= nLeft; ++j)
-         glyphNames[i++] = c++;
-      }
-    } else if (charsetFormat == 2) {
-      i = 1;
-      while (i < nGlyphs) {
-       c = getWord(topPtr, 2);
-       topPtr += 2;
-       nLeft = getWord(topPtr, 2);
-       topPtr += 2;
-       for (j = 0; j <= nLeft; ++j)
-         glyphNames[i++] = c++;
-      }
-    }
-  }
+  // read charset (GID -> name mapping)
+  glyphNames = readCharset(charset, nGlyphs);
 
-  // read encoding (glyph -> code mapping)
+  // read encoding (GID -> code mapping)
   if (enc == 0) {
-    encoding = type1StdEncoding.copy();
+    for (i = 0; i < 256; ++i) {
+      if (standardEncoding[i]) {
+       encoding[i] = copyString(standardEncoding[i]);
+      }
+    }
   } else if (enc == 1) {
-    encoding = type1ExpertEncoding.copy();
+    for (i = 0; i < 256; ++i) {
+      if (expertEncoding[i]) {
+       encoding[i] = copyString(expertEncoding[i]);
+      }
+    }
   } else {
-    encoding = new FontEncoding();
-    topPtr = (Guchar *)file + enc;
-    encFormat = *topPtr++;
+    ptr = (Guchar *)file + enc;
+    encFormat = *ptr++;
     if ((encFormat & 0x7f) == 0) {
-      nCodes = 1 + *topPtr++;
+      nCodes = 1 + *ptr++;
       if (nCodes > nGlyphs) {
        nCodes = nGlyphs;
       }
       for (i = 1; i < nCodes; ++i) {
-       c = *topPtr++;
-       getString(glyphNames[i], stringIdxPtr, stringStartPtr,
-                 stringOffSize, buf);
-       encoding->addChar(c, copyString(buf));
+       c = *ptr++;
+       encoding[c] = copyString(getString(glyphNames[i], buf));
       }
     } else if ((encFormat & 0x7f) == 1) {
-      nRanges = *topPtr++;
+      nRanges = *ptr++;
       nCodes = 1;
       for (i = 0; i < nRanges; ++i) {
-       c = *topPtr++;
-       nLeft = *topPtr++;
+       c = *ptr++;
+       nLeft = *ptr++;
        for (j = 0; j <= nLeft && nCodes < nGlyphs; ++j) {
-         getString(glyphNames[nCodes], stringIdxPtr, stringStartPtr,
-                   stringOffSize, buf);
-         encoding->addChar(c, copyString(buf));
+         encoding[c] = copyString(getString(glyphNames[nCodes], buf));
          ++nCodes;
          ++c;
        }
       }
     }
     if (encFormat & 0x80) {
-      nSups = *topPtr++;
+      nSups = *ptr++;
       for (i = 0; i < nSups; ++i) {
-       c = *topPtr++;
-       sid = getWord(topPtr, 2);
-       topPtr += 2;
-       getString(sid, stringIdxPtr, stringStartPtr,
-                 stringOffSize, buf);
-       encoding->addChar(c, copyString(buf));
+       c = *ptr++;
+       sid = getWord(ptr, 2);
+       ptr += 2;
+       encoding[c] = copyString(getString(sid, buf));
       }
     }
   }
 
-  if (charset > 2)
+  if (charset > 2) {
     gfree(glyphNames);
-}
-
-Type1CFontFile::~Type1CFontFile() {
-  if (name)
-    gfree(name);
-  if (encoding && freeEnc)
-    delete encoding;
-}
-
-FontEncoding *Type1CFontFile::getEncoding(GBool taken) {
-  if (taken)
-    freeEnc = gFalse;
-  return encoding;
-}
-
-static Guint getWord(Guchar *ptr, int size) {
-  Guint x;
-  int i;
-
-  x = 0;
-  for (i = 0; i < size; ++i)
-    x = (x << 8) + *ptr++;
-  return x;
-}
-
-static double getNum(Guchar **ptr, GBool *fp) {
-  static char nybChars[16] = "0123456789.ee -";
-  int b0, b, nyb0, nyb1;
-  double x;
-  char buf[65];
-  int i;
-
-  x = 0;
-  *fp = gFalse;
-  b0 = (*ptr)[0];
-  if (b0 < 28) {
-    x = 0;
-  } else if (b0 == 28) {
-    x = ((*ptr)[1] << 8) + (*ptr)[2];
-    *ptr += 3;
-  } else if (b0 == 29) {
-    x = ((*ptr)[1] << 24) + ((*ptr)[2] << 16) + ((*ptr)[3] << 8) + (*ptr)[4];
-    *ptr += 5;
-  } else if (b0 == 30) {
-    *ptr += 1;
-    i = 0;
-    do {
-      b = *(*ptr)++;
-      nyb0 = b >> 4;
-      nyb1 = b & 0x0f;
-      if (nyb0 == 0xf)
-       break;
-      buf[i++] = nybChars[nyb0];
-      if (i == 64)
-       break;
-      if (nyb0 == 0xc)
-       buf[i++] = '-';
-      if (i == 64)
-       break;
-      if (nyb1 == 0xf)
-       break;
-      buf[i++] = nybChars[nyb1];
-      if (i == 64)
-       break;
-      if (nyb1 == 0xc)
-       buf[i++] = '-';
-    } while (i < 64);
-    buf[i] = '\0';
-    x = atof(buf);
-    *fp = gTrue;
-  } else if (b0 == 31) {
-    x = 0;
-  } else if (b0 < 247) {
-    x = b0 - 139;
-    *ptr += 1;
-  } else if (b0 < 251) {
-    x = ((b0 - 247) << 8) + (*ptr)[1] + 108;
-    *ptr += 2;
-  } else {
-    x = -((b0 - 251) << 8) - (*ptr)[1] - 108;
-    *ptr += 2;
-  }
-  return x;
-}
-
-static char *getString(int sid, Guchar *stringIdxPtr,
-                      Guchar *stringStartPtr, int stringOffSize,
-                      char *buf) {
-  Guchar *idxPtr0, *idxPtr1;
-  int len;
-
-  if (sid < 391) {
-    strcpy(buf, type1CStdStrings[sid]);
-  } else {
-    sid -= 391;
-    idxPtr0 = stringStartPtr + getWord(stringIdxPtr + sid * stringOffSize,
-                                      stringOffSize);
-    idxPtr1 = stringStartPtr + getWord(stringIdxPtr + (sid+1) * stringOffSize,
-                                      stringOffSize);
-    if ((len = idxPtr1 - idxPtr0) > 255)
-      len = 255;
-    strncpy(buf, (char *)idxPtr0, len);
-    buf[len] = '\0';
   }
-  return buf;
-}
-
-//------------------------------------------------------------------------
-// Type1CFontConverter
-//------------------------------------------------------------------------
-
-Type1CFontConverter::Type1CFontConverter(char *file, int len, FILE *out) {
-  this->file = file;
-  this->len = len;
-  this->out = out;
-  r1 = 55665;
-  line = 0;
-}
-
-Type1CFontConverter::~Type1CFontConverter() {
 }
 
-void Type1CFontConverter::convert() {
-  char *fontName;
-  struct {
-    int version;
-    int notice;
-    int copyright;
-    int fullName;
-    int familyName;
-    int weight;
-    int isFixedPitch;
-    double italicAngle;
-    double underlinePosition;
-    double underlineThickness;
-    int paintType;
-    int charstringType;                //~ ???
-    double fontMatrix[6];
-    int uniqueID;
-    double fontBBox[4];
-    double strokeWidth;                //~ ???
-    int charset;
-    int encoding;
-    int charStrings;
-    int privateSize;
-    int privateOffset;
-  } dict;
+void Type1CFontFile::convertToType1(FILE *outA) {
+  Type1CTopDict dict;
+  Type1CPrivateDict privateDict;
   char buf[256], eBuf[256];
-  Guchar *topPtr, *idxStartPtr, *idxPtr0, *idxPtr1;
-  Guchar *stringIdxPtr, *stringStartPtr;
-  int topOffSize, idxOffSize, stringOffSize;
-  int nFonts, nStrings, nGlyphs;
-  int nCodes, nRanges, nLeft, nSups;
+  Guchar *idxPtr0, *idxPtr1, *subrsIdxPtr, *charStringsIdxPtr, *ptr;
+  int nGlyphs, nCodes, nRanges, nLeft, nSups;
   Gushort *glyphNames;
-  int charsetFormat, encFormat;
-  int subrsOffset, nSubrs;
-  int nCharStrings;
+  int encFormat, nSubrs, nCharStrings;
   int c, sid;
-  double x;
-  GBool isFP;
-  int key;
   int i, j, n;
 
-  // read header
-  topPtr = (Guchar *)file + (file[2] & 0xff);
-  topOffSize = file[3] & 0xff;
-
-  // read name (first font only)
-  nFonts = getWord(topPtr, 2);
-  idxOffSize = topPtr[2];
-  topPtr += 3;
-  idxStartPtr = topPtr + (nFonts + 1) * idxOffSize - 1;
-  idxPtr0 = idxStartPtr + getWord(topPtr, idxOffSize);
-  idxPtr1 = idxStartPtr + getWord(topPtr + idxOffSize, idxOffSize);
-  if ((n = idxPtr1 - idxPtr0) > 255)
-    n = 255;
-  strncpy(buf, (char *)idxPtr0, n);
-  buf[n] = '\0';
-  fontName = copyString(buf);
-  topPtr = idxStartPtr + getWord(topPtr + nFonts * idxOffSize, idxOffSize);
+  out = outA;
 
   // read top dict (first font only)
-  nFonts = getWord(topPtr, 2);
-  idxOffSize = topPtr[2];
-  topPtr += 3;
-  idxStartPtr = topPtr + (nFonts + 1) * idxOffSize - 1;
-  idxPtr0 = idxStartPtr + getWord(topPtr, idxOffSize);
-  idxPtr1 = idxStartPtr + getWord(topPtr + idxOffSize, idxOffSize);
-  dict.version = 0;
-  dict.notice = 0;
-  dict.copyright = 0;
-  dict.fullName = 0;
-  dict.familyName = 0;
-  dict.weight = 0;
-  dict.isFixedPitch = 0;
-  dict.italicAngle = 0;
-  dict.underlinePosition = -100;
-  dict.underlineThickness = 50;
-  dict.paintType = 0;
-  dict.charstringType = 2;
-  dict.fontMatrix[0] = 0.001;
-  dict.fontMatrix[1] = 0;
-  dict.fontMatrix[2] = 0;
-  dict.fontMatrix[3] = 0.001;
-  dict.fontMatrix[4] = 0;
-  dict.fontMatrix[5] = 0;
-  dict.uniqueID = 0;
-  dict.fontBBox[0] = 0;
-  dict.fontBBox[1] = 0;
-  dict.fontBBox[2] = 0;
-  dict.fontBBox[3] = 0;
-  dict.strokeWidth = 0;
-  dict.charset = 0;
-  dict.encoding = 0;
-  dict.charStrings = 0;
-  dict.privateSize = 0;
-  dict.privateOffset = 0;
-  i = 0;
-  while (idxPtr0 < idxPtr1) {
-    if (*idxPtr0 <= 27 || *idxPtr0 == 31) {
-      key = *idxPtr0++;
-      if (key == 0x0c)
-       key = (key << 8) | *idxPtr0++;
-      switch (key) {
-      case 0x0000: dict.version = (int)op[0]; break;
-      case 0x0001: dict.notice = (int)op[0]; break;
-      case 0x0c00: dict.copyright = (int)op[0]; break;
-      case 0x0002: dict.fullName = (int)op[0]; break;
-      case 0x0003: dict.familyName = (int)op[0]; break;
-      case 0x0004: dict.weight = (int)op[0]; break;
-      case 0x0c01: dict.isFixedPitch = (int)op[0]; break;
-      case 0x0c02: dict.italicAngle = op[0]; break;
-      case 0x0c03: dict.underlinePosition = op[0]; break;
-      case 0x0c04: dict.underlineThickness = op[0]; break;
-      case 0x0c05: dict.paintType = (int)op[0]; break;
-      case 0x0c06: dict.charstringType = (int)op[0]; break;
-      case 0x0c07: dict.fontMatrix[0] = op[0];
-                  dict.fontMatrix[1] = op[1];
-                  dict.fontMatrix[2] = op[2];
-                  dict.fontMatrix[3] = op[3];
-                  dict.fontMatrix[4] = op[4];
-                  dict.fontMatrix[5] = op[5]; break;
-      case 0x000d: dict.uniqueID = (int)op[0]; break;
-      case 0x0005: dict.fontBBox[0] = op[0];
-                  dict.fontBBox[1] = op[1];
-                  dict.fontBBox[2] = op[2];
-                  dict.fontBBox[3] = op[3]; break;
-      case 0x0c08: dict.strokeWidth = op[0]; break;
-      case 0x000f: dict.charset = (int)op[0]; break;
-      case 0x0010: dict.encoding = (int)op[0]; break;
-      case 0x0011: dict.charStrings = (int)op[0]; break;
-      case 0x0012: dict.privateSize = (int)op[0];
-                  dict.privateOffset = (int)op[1]; break;
-      }
-      i = 0;
-    } else {
-      x = getNum(&idxPtr0, &isFP);
-      if (i < 48) {
-       op[i] = x;
-       fp[i++] = isFP;
-      }
-    }
-  }
-  topPtr = idxStartPtr + getWord(topPtr + nFonts * idxOffSize, idxOffSize);
-
-  // read string index
-  nStrings = getWord(topPtr, 2);
-  stringOffSize = topPtr[2];
-  topPtr += 3;
-  stringIdxPtr = topPtr;
-  stringStartPtr = topPtr + (nStrings + 1) * stringOffSize - 1;
-  topPtr = stringStartPtr + getWord(topPtr + nStrings * stringOffSize,
-                                   stringOffSize);
+  readTopDict(&dict);
 
-#if 1 //~
   // get global subrs
-  int nGSubrs;
-  int gSubrOffSize;
-
-  nGSubrs = getWord(topPtr, 2);
-  gSubrOffSize = topPtr[2];
-  topPtr += 3;
-#endif
+  //~ ... global subrs are unimplemented
 
   // write header and font dictionary, up to encoding
-  fprintf(out, "%%!FontType1-1.0: %s", fontName);
+  fprintf(out, "%%!FontType1-1.0: %s", name->getCString());
   if (dict.version != 0) {
-    fprintf(out, "%s",
-           getString(dict.version, stringIdxPtr, stringStartPtr,
-                     stringOffSize, buf));
+    fprintf(out, "%s", getString(dict.version, buf));
   }
   fprintf(out, "\n");
   fprintf(out, "11 dict begin\n");
   fprintf(out, "/FontInfo 10 dict dup begin\n");
   if (dict.version != 0) {
     fprintf(out, "/version (%s) readonly def\n",
-           getString(dict.version, stringIdxPtr, stringStartPtr,
-                     stringOffSize, buf));
+           getString(dict.version, buf));
   }
   if (dict.notice != 0) {
     fprintf(out, "/Notice (%s) readonly def\n",
-           getString(dict.notice, stringIdxPtr, stringStartPtr,
-                     stringOffSize, buf));
+           getString(dict.notice, buf));
   }
   if (dict.copyright != 0) {
     fprintf(out, "/Copyright (%s) readonly def\n",
-           getString(dict.copyright, stringIdxPtr, stringStartPtr,
-                     stringOffSize, buf));
+           getString(dict.copyright, buf));
   }
   if (dict.fullName != 0) {
     fprintf(out, "/FullName (%s) readonly def\n",
-           getString(dict.fullName, stringIdxPtr, stringStartPtr,
-                     stringOffSize, buf));
+           getString(dict.fullName, buf));
   }
   if (dict.familyName != 0) {
     fprintf(out, "/FamilyName (%s) readonly def\n",
-           getString(dict.familyName, stringIdxPtr, stringStartPtr,
-                     stringOffSize, buf));
+           getString(dict.familyName, buf));
   }
   if (dict.weight != 0) {
     fprintf(out, "/Weight (%s) readonly def\n",
-           getString(dict.weight, stringIdxPtr, stringStartPtr,
-                     stringOffSize, buf));
+           getString(dict.weight, buf));
   }
   fprintf(out, "/isFixedPitch %s def\n", dict.isFixedPitch ? "true" : "false");
   fprintf(out, "/ItalicAngle %g def\n", dict.italicAngle);
   fprintf(out, "/UnderlinePosition %g def\n", dict.underlinePosition);
   fprintf(out, "/UnderlineThickness %g def\n", dict.underlineThickness);
   fprintf(out, "end readonly def\n");
-  fprintf(out, "/FontName /%s def\n", fontName);
+  fprintf(out, "/FontName /%s def\n", name->getCString());
   fprintf(out, "/PaintType %d def\n", dict.paintType);
   fprintf(out, "/FontType 1 def\n");
   fprintf(out, "/FontMatrix [%g %g %g %g %g %g] readonly def\n",
@@ -650,52 +409,16 @@ void Type1CFontConverter::convert() {
   fprintf(out, "/FontBBox [%g %g %g %g] readonly def\n",
          dict.fontBBox[0], dict.fontBBox[1],
          dict.fontBBox[2], dict.fontBBox[3]);
+  fprintf(out, "/StrokeWidth %g def\n", dict.strokeWidth);
   if (dict.uniqueID != 0) {
     fprintf(out, "/UniqueID %d def\n", dict.uniqueID);
   }
 
   // get number of glyphs from charstrings index
-  topPtr = (Guchar *)file + dict.charStrings;
-  nGlyphs = getWord(topPtr, 2);
+  nGlyphs = getIndexLen((Guchar *)file + dict.charStrings);
 
   // read charset
-  if (dict.charset == 0) {
-    glyphNames = type1CISOAdobeCharset;
-  } else if (dict.charset == 1) {
-    glyphNames = type1CExpertCharset;
-  } else if (dict.charset == 2) {
-    glyphNames = type1CExpertSubsetCharset;
-  } else {
-    glyphNames = (Gushort *)gmalloc(nGlyphs * sizeof(Gushort));
-    glyphNames[0] = 0;
-    topPtr = (Guchar *)file + dict.charset;
-    charsetFormat = *topPtr++;
-    if (charsetFormat == 0) {
-      for (i = 1; i < nGlyphs; ++i) {
-       glyphNames[i] = getWord(topPtr, 2);
-       topPtr += 2;
-      }
-    } else if (charsetFormat == 1) {
-      i = 1;
-      while (i < nGlyphs) {
-       c = getWord(topPtr, 2);
-       topPtr += 2;
-       nLeft = *topPtr++;
-       for (j = 0; j <= nLeft; ++j)
-         glyphNames[i++] = c++;
-      }
-    } else if (charsetFormat == 2) {
-      i = 1;
-      while (i < nGlyphs) {
-       c = getWord(topPtr, 2);
-       topPtr += 2;
-       nLeft = getWord(topPtr, 2);
-       topPtr += 2;
-       for (j = 0; j <= nLeft; ++j)
-         glyphNames[i++] = c++;
-      }
-    }
-  }
+  glyphNames = readCharset(dict.charset, nGlyphs);
 
   // read encoding (glyph -> code mapping), write Type 1 encoding
   fprintf(out, "/Encoding ");
@@ -706,54 +429,55 @@ void Type1CFontConverter::convert() {
     fprintf(out, "0 1 255 {1 index exch /.notdef put} for\n");
     if (dict.encoding == 1) {
       for (i = 0; i < 256; ++i) {
-       if (type1ExpertEncodingNames[i])
-         fprintf(out, "dup %d /%s put\n", i, type1ExpertEncodingNames[i]);
+       if (expertEncoding[i]) {
+         fprintf(out, "dup %d /%s put\n", i, expertEncoding[i]);
+       }
       }
     } else {
-      topPtr = (Guchar *)file + dict.encoding;
-      encFormat = *topPtr++;
+      ptr = (Guchar *)file + dict.encoding;
+      encFormat = *ptr++;
       if ((encFormat & 0x7f) == 0) {
-       nCodes = 1 + *topPtr++;
+       nCodes = 1 + *ptr++;
        if (nCodes > nGlyphs) {
          nCodes = nGlyphs;
        }
        for (i = 1; i < nCodes; ++i) {
-         c = *topPtr++;
-         fprintf(out, "dup %d /%s put\n", c,
-                 getString(glyphNames[i], stringIdxPtr, stringStartPtr,
-                           stringOffSize, buf));
+         c = *ptr++;
+         fprintf(out, "dup %d /%s put\n",
+                 c, getString(glyphNames[i], buf));
        }
       } else if ((encFormat & 0x7f) == 1) {
-       nRanges = *topPtr++;
+       nRanges = *ptr++;
        nCodes = 1;
        for (i = 0; i < nRanges; ++i) {
-         c = *topPtr++;
-         nLeft = *topPtr++;
+         c = *ptr++;
+         nLeft = *ptr++;
          for (j = 0; j <= nLeft && nCodes < nGlyphs; ++j) {
-           fprintf(out, "dup %d /%s put\n", c,
-                   getString(glyphNames[nCodes], stringIdxPtr, stringStartPtr,
-                             stringOffSize, buf));
+           fprintf(out, "dup %d /%s put\n",
+                   c, getString(glyphNames[nCodes], buf));
            ++nCodes;
            ++c;
          }
        }
       }
       if (encFormat & 0x80) {
-       nSups = *topPtr++;
+       nSups = *ptr++;
        for (i = 0; i < nSups; ++i) {
-         c = *topPtr++;
-         sid = getWord(topPtr, 2);
-         topPtr += 2;
-         fprintf(out, "dup %d /%s put\n", c,
-                 getString(sid, stringIdxPtr, stringStartPtr,
-                           stringOffSize, buf));
+         c = *ptr++;
+         sid = getWord(ptr, 2);
+         ptr += 2;
+         fprintf(out, "dup %d /%s put\n", c, getString(sid, buf));
        }
       }
     }
     fprintf(out, "readonly def\n");
   }
   fprintf(out, "currentdict end\n");
+
+  // start the binary section
   fprintf(out, "currentfile eexec\n");
+  r1 = 55665;
+  line = 0;
 
   // get private dictionary
   eexecWrite("\x83\xca\x73\xd5");
@@ -762,128 +486,31 @@ void Type1CFontConverter::convert() {
   eexecWrite("/ND {noaccess def} executeonly def\n");
   eexecWrite("/NP {noaccess put} executeonly def\n");
   eexecWrite("/MinFeature {16 16} ND\n");
-  eexecWrite("/password 5839 def\n");
-  subrsOffset = 0;
-  defaultWidthX = 0;
-  nominalWidthX = 0;
-  topPtr = (Guchar *)file + dict.privateOffset;
-  idxPtr0 = topPtr;
-  idxPtr1 = idxPtr0 + dict.privateSize;
-  i = 0;
-  while (idxPtr0 < idxPtr1) {
-    if (*idxPtr0 <= 27 || *idxPtr0 == 31) {
-      key = *idxPtr0++;
-      if (key == 0x0c)
-       key = (key << 8) | *idxPtr0++;
-      switch (key) {
-      case 0x0006:
-       getDeltaInt(eBuf, "BlueValues", op, i);
-       eexecWrite(eBuf);
-       break;
-      case 0x0007:
-       getDeltaInt(eBuf, "OtherBlues", op, i);
-       eexecWrite(eBuf);
-       break;
-      case 0x0008:
-       getDeltaInt(eBuf, "FamilyBlues", op, i);
-       eexecWrite(eBuf);
-       break;
-      case 0x0009:
-       getDeltaInt(eBuf, "FamilyOtherBlues", op, i);
-       eexecWrite(eBuf);
-       break;
-      case 0x0c09:
-       sprintf(eBuf, "/BlueScale %g def\n", op[0]);
-       eexecWrite(eBuf);
-       break;
-      case 0x0c0a:
-       sprintf(eBuf, "/BlueShift %d def\n", (int)op[0]);
-       eexecWrite(eBuf);
-       break;
-      case 0x0c0b:
-       sprintf(eBuf, "/BlueFuzz %d def\n", (int)op[0]);
-       eexecWrite(eBuf);
-       break;
-      case 0x000a:
-       sprintf(eBuf, "/StdHW [%g] def\n", op[0]);
-       eexecWrite(eBuf);
-       break;
-      case 0x000b:
-       sprintf(eBuf, "/StdVW [%g] def\n", op[0]);
-       eexecWrite(eBuf);
-       break;
-      case 0x0c0c:
-       getDeltaReal(eBuf, "StemSnapH", op, i);
-       eexecWrite(eBuf);
-       break;
-      case 0x0c0d:
-       getDeltaReal(eBuf, "StemSnapV", op, i);
-       eexecWrite(eBuf);
-       break;
-      case 0x0c0e:
-       sprintf(eBuf, "/ForceBold %s def\n", op[0] ? "true" : "false");
-       eexecWrite(eBuf);
-       break;
-      case 0x0c0f:
-       sprintf(eBuf, "/ForceBoldThreshold %g def\n", op[0]);
-       eexecWrite(eBuf);
-       break;
-      case 0x0c11:
-       sprintf(eBuf, "/LanguageGroup %d def\n", (int)op[0]);
-       eexecWrite(eBuf);
-       break;
-      case 0x0c12:
-       sprintf(eBuf, "/ExpansionFactor %g def\n", op[0]);
-       eexecWrite(eBuf);
-       break;
-      case 0x0c13:
-       error(-1, "Got Type 1C InitialRandomSeed");
-       break;
-      case 0x0013:
-       subrsOffset = (int)op[0];
-       break;
-      case 0x0014:
-       defaultWidthX = op[0];
-       defaultWidthXFP = fp[0];
-       break;
-      case 0x0015:
-       nominalWidthX = op[0];
-       nominalWidthXFP = fp[0];
-       break;
-      default:
-       error(-1, "Uknown Type 1C private dict entry %04x", key);
-       break;
-      }
-      i = 0;
-    } else {
-      x = getNum(&idxPtr0, &isFP);
-      if (i < 48) {
-       op[i] = x;
-       fp[i++] = isFP;
-      }
-    }
-  }
+  readPrivateDict(&privateDict, dict.privateOffset, dict.privateSize);
+  eexecWrite(privateDict.dictData->getCString());
+  defaultWidthX = privateDict.defaultWidthX;
+  defaultWidthXFP = privateDict.defaultWidthXFP;
+  nominalWidthX = privateDict.nominalWidthX;
+  nominalWidthXFP = privateDict.nominalWidthXFP;
 
   // get subrs
-  if (subrsOffset != 0) {
-    topPtr += subrsOffset;
-    nSubrs = getWord(topPtr, 2);
-    idxOffSize = topPtr[2];
-    topPtr += 3;
+  if (privateDict.subrsOffset != 0) {
+    subrsIdxPtr = (Guchar *)file + dict.privateOffset +
+                  privateDict.subrsOffset;
+    nSubrs = getIndexLen(subrsIdxPtr);
     sprintf(eBuf, "/Subrs %d array\n", nSubrs);
     eexecWrite(eBuf);
-    idxStartPtr = topPtr + (nSubrs + 1) * idxOffSize - 1;
-    idxPtr1 = idxStartPtr + getWord(topPtr, idxOffSize);
+    idxPtr1 = getIndexValPtr(subrsIdxPtr, 0);
     for (i = 0; i < nSubrs; ++i) {
       idxPtr0 = idxPtr1;
-      idxPtr1 = idxStartPtr + getWord(topPtr + (i+1)*idxOffSize, idxOffSize);
+      idxPtr1 = getIndexValPtr(subrsIdxPtr, i+1);
       n = idxPtr1 - idxPtr0;
-#if 1 //~
+#if 1 //~ Type 2 subrs are unimplemented
       error(-1, "Unimplemented Type 2 subrs");
 #else
       sprintf(eBuf, "dup %d %d RD ", i, n);
       eexecWrite(eBuf);
-      cvtGlyph(idxPtr0, n);
+      eexecCvtGlyph(idxPtr0, n);
       eexecWrite(" NP\n");
 #endif
     }
@@ -891,21 +518,16 @@ void Type1CFontConverter::convert() {
   }
 
   // get CharStrings
-  topPtr = (Guchar *)file + dict.charStrings;
-  nCharStrings = getWord(topPtr, 2);
-  idxOffSize = topPtr[2];
-  topPtr += 3;
+  charStringsIdxPtr = (Guchar *)file + dict.charStrings;
+  nCharStrings = getIndexLen(charStringsIdxPtr);
   sprintf(eBuf, "2 index /CharStrings %d dict dup begin\n", nCharStrings);
   eexecWrite(eBuf);
-  idxStartPtr = topPtr + (nCharStrings + 1) * idxOffSize - 1;
-  idxPtr1 = idxStartPtr + getWord(topPtr, idxOffSize);
+  idxPtr1 = getIndexValPtr(charStringsIdxPtr, 0);
   for (i = 0; i < nCharStrings; ++i) {
     idxPtr0 = idxPtr1;
-    idxPtr1 = idxStartPtr + getWord(topPtr + (i+1)*idxOffSize, idxOffSize);
+    idxPtr1 = getIndexValPtr(charStringsIdxPtr, i+1);
     n = idxPtr1 - idxPtr0;
-    cvtGlyph(getString(glyphNames[i], stringIdxPtr, stringStartPtr,
-                      stringOffSize, buf),
-            idxPtr0, n);
+    eexecCvtGlyph(getString(glyphNames[i], buf), idxPtr0, n);
   }
   eexecWrite("end\n");
   eexecWrite("end\n");
@@ -915,719 +537,3145 @@ void Type1CFontConverter::convert() {
   eexecWrite("mark currentfile closefile\n");
 
   // trailer
-  if (line > 0)
+  if (line > 0) {
     fputc('\n', out);
+  }
   for (i = 0; i < 8; ++i) {
     fprintf(out, "0000000000000000000000000000000000000000000000000000000000000000\n");
   }
   fprintf(out, "cleartomark\n");
 
   // clean up
-  if (dict.charset > 2)
+  delete privateDict.dictData;
+  if (dict.charset > 2) {
     gfree(glyphNames);
-  gfree(fontName);
+  }
 }
 
-void Type1CFontConverter::eexecWrite(char *s) {
-  Guchar *p;
-  Guchar x;
+void Type1CFontFile::convertToCIDType0(char *psName, FILE *outA) {
+  Type1CTopDict dict;
+  Type1CPrivateDict *privateDicts;
+  GString *charStrings;
+  int *charStringOffsets;
+  Gushort *charset;
+  int *cidMap;
+  Guchar *fdSelect;
+  Guchar *charStringsIdxPtr, *fdArrayIdx, *idxPtr0, *idxPtr1, *ptr;
+  char buf[256];
+  int nGlyphs, nCIDs, gdBytes, nFDs;
+  int fdSelectFmt, nRanges, gid0, gid1, fd, offset;
+  int key;
+  double x;
+  GBool isFP;
+  int i, j, k, n;
 
-  for (p = (Guchar *)s; *p; ++p) {
-    x = *p ^ (r1 >> 8);
-    r1 = (x + r1) * 52845 + 22719;
-    fputc(hexChars[x >> 4], out);
-    fputc(hexChars[x & 0x0f], out);
-    line += 2;
-    if (line == 64) {
-      fputc('\n', out);
-      line = 0;
+  out = outA;
+
+  fprintf(out, "/CIDInit /ProcSet findresource begin\n");
+
+  // read top dict (first font only)
+  readTopDict(&dict);
+
+  // read the FDArray dictionaries and Private dictionaries
+  if (dict.fdArrayOffset == 0) {
+    nFDs = 1;
+    privateDicts = (Type1CPrivateDict *)
+                     gmalloc(nFDs * sizeof(Type1CPrivateDict));
+    privateDicts[0].dictData = new GString();
+    privateDicts[0].subrsOffset = 0;
+    privateDicts[0].defaultWidthX = 0;
+    privateDicts[0].defaultWidthXFP = gFalse;
+    privateDicts[0].nominalWidthX = 0;
+    privateDicts[0].nominalWidthXFP = gFalse;
+  } else {
+    fdArrayIdx = (Guchar *)file + dict.fdArrayOffset;
+    nFDs = getIndexLen(fdArrayIdx);
+    privateDicts = (Type1CPrivateDict *)
+                     gmalloc(nFDs * sizeof(Type1CPrivateDict));
+    idxPtr1 = getIndexValPtr(fdArrayIdx, 0);
+    for (i = 0; i < nFDs; ++i) {
+      privateDicts[i].dictData = NULL;
+      idxPtr0 = idxPtr1;
+      idxPtr1 = getIndexValPtr(fdArrayIdx, i + 1);
+      ptr = idxPtr0;
+      j = 0;
+      while (ptr < idxPtr1) {
+       if (*ptr <= 27 || *ptr == 31) {
+         key = *ptr++;
+         if (key == 0x0c) {
+           key = (key << 8) | *ptr++;
+         }
+         if (key == 0x0012) {
+           readPrivateDict(&privateDicts[i], (int)op[1], (int)op[0]);
+         }
+         j = 0;
+       } else {
+         x = getNum(&ptr, &isFP);
+         if (j < 48) {
+           op[j] = x;
+           fp[j++] = isFP;
+         }
+       }
+      }
+      if (!privateDicts[i].dictData) {
+       privateDicts[i].dictData = new GString();
+       privateDicts[i].subrsOffset = 0;
+       privateDicts[i].defaultWidthX = 0;
+       privateDicts[i].defaultWidthXFP = gFalse;
+       privateDicts[i].nominalWidthX = 0;
+       privateDicts[i].nominalWidthXFP = gFalse;
+      }
+    }
+  }
+
+  // get the glyph count
+  charStringsIdxPtr = (Guchar *)file + dict.charStrings;
+  nGlyphs = getIndexLen(charStringsIdxPtr);
+
+  // read the FDSelect table
+  fdSelect = (Guchar *)gmalloc(nGlyphs);
+  if (dict.fdSelectOffset == 0) {
+    for (i = 0; i < nGlyphs; ++i) {
+      fdSelect[i] = 0;
+    }
+  } else {
+    ptr = (Guchar *)file + dict.fdSelectOffset;
+    fdSelectFmt = *ptr++;
+    if (fdSelectFmt == 0) {
+      memcpy(fdSelect, ptr, nGlyphs);
+    } else if (fdSelectFmt == 3) {
+      nRanges = getWord(ptr, 2);
+      ptr += 2;
+      gid0 = getWord(ptr, 2);
+      ptr += 2;
+      for (i = 1; i <= nRanges; ++i) {
+       fd = *ptr++;
+       gid1 = getWord(ptr, 2);
+       ptr += 2;
+       for (j = gid0; j < gid1; ++j) {
+         fdSelect[j] = fd;
+       }
+       gid0 = gid1;
+      }
+    } else {
+      error(-1, "Unknown FDSelect table format in CID font");
+      for (i = 0; i < nGlyphs; ++i) {
+       fdSelect[i] = 0;
+      }
+    }
+  }
+
+  // read the charset, compute the CID-to-GID mapping
+  charset = readCharset(dict.charset, nGlyphs);
+  nCIDs = 0;
+  for (i = 0; i < nGlyphs; ++i) {
+    if (charset[i] >= nCIDs) {
+      nCIDs = charset[i] + 1;
+    }
+  }
+  cidMap = (int *)gmalloc(nCIDs * sizeof(int));
+  for (i = 0; i < nCIDs; ++i) {
+    cidMap[i] = -1;
+  }
+  for (i = 0; i < nGlyphs; ++i) {
+    cidMap[charset[i]] = i;
+  }
+
+  // build the charstrings
+  charStrings = new GString();
+  charStringOffsets = (int *)gmalloc((nCIDs + 1) * sizeof(int));
+  for (i = 0; i < nCIDs; ++i) {
+    charStringOffsets[i] = charStrings->getLength();
+    if (cidMap[i] >= 0) {
+      idxPtr0 = getIndexValPtr(charStringsIdxPtr, cidMap[i]);
+      idxPtr1 = getIndexValPtr(charStringsIdxPtr, cidMap[i]+1);
+      n = idxPtr1 - idxPtr0;
+      j = fdSelect[cidMap[i]];
+      defaultWidthX = privateDicts[j].defaultWidthX;
+      defaultWidthXFP = privateDicts[j].defaultWidthXFP;
+      nominalWidthX = privateDicts[j].nominalWidthX;
+      nominalWidthXFP = privateDicts[j].nominalWidthXFP;
+      cvtGlyph(idxPtr0, n);
+      charStrings->append(charBuf);
+      delete charBuf;
+    }
+  }
+  charStringOffsets[nCIDs] = charStrings->getLength();
+
+  // compute gdBytes = number of bytes needed for charstring offsets
+  // (offset size needs to account for the charstring offset table,
+  // with a worst case of five bytes per entry, plus the charstrings
+  // themselves)
+  i = (nCIDs + 1) * 5 + charStrings->getLength();
+  if (i < 0x100) {
+    gdBytes = 1;
+  } else if (i < 0x10000) {
+    gdBytes = 2;
+  } else if (i < 0x1000000) {
+    gdBytes = 3;
+  } else {
+    gdBytes = 4;
+  }
+
+  // begin the font dictionary
+  fprintf(out, "20 dict begin\n");
+  fprintf(out, "/CIDFontName /%s def\n", psName);
+  fprintf(out, "/CIDFontType 0 def\n");
+  fprintf(out, "/CIDSystemInfo 3 dict dup begin\n");
+  if (dict.registry > 0 && dict.ordering > 0) {
+    fprintf(out, "  /Registry (%s) def\n", getString(dict.registry, buf));
+    fprintf(out, "  /Ordering (%s) def\n", getString(dict.ordering, buf));
+  } else {
+    fprintf(out, "  /Registry (Adobe) def\n");
+    fprintf(out, "  /Ordering (Identity) def\n");
+  }
+  fprintf(out, "  /Supplement %d def\n", dict.supplement);
+  fprintf(out, "end def\n");
+  fprintf(out, "/FontMatrix [%g %g %g %g %g %g] def\n",
+         dict.fontMatrix[0], dict.fontMatrix[1], dict.fontMatrix[2],
+         dict.fontMatrix[3], dict.fontMatrix[4], dict.fontMatrix[5]);
+  fprintf(out, "/FontBBox [%g %g %g %g] def\n",
+         dict.fontBBox[0], dict.fontBBox[1],
+         dict.fontBBox[2], dict.fontBBox[3]);
+  fprintf(out, "/FontInfo 1 dict dup begin\n");
+  fprintf(out, "  /FSType 8 def\n");
+  fprintf(out, "end def\n");
+
+  // CIDFont-specific entries
+  fprintf(out, "/CIDCount %d def\n", nCIDs);
+  fprintf(out, "/FDBytes 1 def\n");
+  fprintf(out, "/GDBytes %d def\n", gdBytes);
+  fprintf(out, "/CIDMapOffset 0 def\n");
+  if (dict.paintType != 0) {
+    fprintf(out, "/PaintType %d def\n", dict.paintType);
+    fprintf(out, "/StrokeWidth %g def\n", dict.strokeWidth);
+  }
+
+  // FDArray entry
+  fprintf(out, "/FDArray %d array\n", nFDs);
+  for (i = 0; i < nFDs; ++i) {
+    fprintf(out, "dup %d 10 dict begin\n", i);
+    fprintf(out, "/FontType 1 def\n");
+    fprintf(out, "/FontMatrix [1 0 0 1 0 0] def\n");
+    fprintf(out, "/PaintType %d def\n", dict.paintType);
+    fprintf(out, "/Private 32 dict begin\n");
+    fwrite(privateDicts[i].dictData->getCString(), 1,
+          privateDicts[i].dictData->getLength(), out);
+    fprintf(out, "currentdict end def\n");
+    fprintf(out, "currentdict end put\n");
+  }
+  fprintf(out, "def\n");
+
+  //~ need to deal with subrs
+  
+  // start the binary section
+  offset = (nCIDs + 1) * (1 + gdBytes);
+  fprintf(out, "(Hex) %d StartData\n",
+         offset + charStrings->getLength());
+
+  // write the charstring offset (CIDMap) table
+  for (i = 0; i <= nCIDs; i += 6) {
+    for (j = 0; j < 6 && i+j <= nCIDs; ++j) {
+      if (cidMap[i+j] >= 0) {
+       buf[0] = (char)fdSelect[cidMap[i+j]];
+      } else {
+       buf[0] = (char)0;
+      }
+      n = offset + charStringOffsets[i+j];
+      for (k = gdBytes; k >= 1; --k) {
+       buf[k] = (char)(n & 0xff);
+       n >>= 8;
+      }
+      for (k = 0; k <= gdBytes; ++k) {
+       fprintf(out, "%02x", buf[k] & 0xff);
+      }
+    }
+    fputc('\n', out);
+  }
+
+  // write the charstring data
+  n = charStrings->getLength();
+  for (i = 0; i < n; i += 32) {
+    for (j = 0; j < 32 && i+j < n; ++j) {
+      fprintf(out, "%02x", charStrings->getChar(i+j) & 0xff);
+    }
+    if (i + 32 >= n) {
+      fputc('>', out);
     }
+    fputc('\n', out);
+  }
+
+  for (i = 0; i < nFDs; ++i) {
+    delete privateDicts[i].dictData;
   }
+  gfree(privateDicts);
+  gfree(cidMap);
+  gfree(charset);
+  gfree(charStringOffsets);
+  delete charStrings;
+  gfree(fdSelect);
 }
 
-void Type1CFontConverter::cvtGlyph(char *name, Guchar *s, int n) {
-  int nHints;
-  int x;
-  GBool first = gTrue;
+void Type1CFontFile::convertToType0(char *psName, FILE *outA) {
+  Type1CTopDict dict;
+  Type1CPrivateDict *privateDicts;
+  Gushort *charset;
+  int *cidMap;
+  Guchar *fdSelect;
+  Guchar *charStringsIdxPtr, *fdArrayIdx, *idxPtr0, *idxPtr1, *ptr;
+  char buf[256];
   char eBuf[256];
-  double d, dx, dy;
-  GBool dFP;
-  int i, k;
+  int nGlyphs, nCIDs, nFDs;
+  int fdSelectFmt, nRanges, gid0, gid1, fd;
+  int key;
+  double x;
+  GBool isFP;
+  int i, j, n;
 
-  charBuf = new GString();
-  charBuf->append((char)73);
-  charBuf->append((char)58);
-  charBuf->append((char)147);
-  charBuf->append((char)134);
+  out = outA;
 
-  i = 0;
-  nOps = 0;
-  nHints = 0;
-  while (i < n) {
-    if (s[i] == 12) {
-      switch (s[i+1]) {
-      case 0:                  // dotsection (should be Type 1 only?)
-       //~ ignored
-       break;
-      case 34:                 // hflex
-       if (nOps != 7) {
-         error(-1, "Wrong number of args (%d) to Type 2 hflex", nOps);
-       }
-       eexecDumpNum(op[0], fp[0]);
-       eexecDumpNum(0, gFalse);
-       eexecDumpNum(op[1], fp[1]);
-       eexecDumpNum(op[2], fp[2]);
-       eexecDumpNum(op[3], fp[3]);
-       eexecDumpNum(0, gFalse);
-       eexecDumpOp1(8);
-       eexecDumpNum(op[4], fp[4]);
-       eexecDumpNum(0, gFalse);
-       eexecDumpNum(op[5], fp[5]);
-       eexecDumpNum(-op[2], fp[2]);
-       eexecDumpNum(op[6], fp[6]);
-       eexecDumpNum(0, gFalse);
-       eexecDumpOp1(8);
-       break;
-      case 35:                 // flex
-       if (nOps != 13) {
-         error(-1, "Wrong number of args (%d) to Type 2 flex", nOps);
-       }
-       eexecDumpNum(op[0], fp[0]);
-       eexecDumpNum(op[1], fp[1]);
-       eexecDumpNum(op[2], fp[2]);
-       eexecDumpNum(op[3], fp[3]);
-       eexecDumpNum(op[4], fp[4]);
-       eexecDumpNum(op[5], fp[5]);
-       eexecDumpOp1(8);
-       eexecDumpNum(op[6], fp[6]);
-       eexecDumpNum(op[7], fp[7]);
-       eexecDumpNum(op[8], fp[8]);
-       eexecDumpNum(op[9], fp[9]);
-       eexecDumpNum(op[10], fp[10]);
-       eexecDumpNum(op[11], fp[11]);
-       eexecDumpOp1(8);
-       break;
-      case 36:                 // hflex1
-       if (nOps != 9) {
-         error(-1, "Wrong number of args (%d) to Type 2 hflex1", nOps);
-       }
-       eexecDumpNum(op[0], fp[0]);
-       eexecDumpNum(op[1], fp[1]);
-       eexecDumpNum(op[2], fp[2]);
-       eexecDumpNum(op[3], fp[3]);
-       eexecDumpNum(op[4], fp[4]);
-       eexecDumpNum(0, gFalse);
-       eexecDumpOp1(8);
-       eexecDumpNum(op[5], fp[5]);
-       eexecDumpNum(0, gFalse);
-       eexecDumpNum(op[6], fp[6]);
-       eexecDumpNum(op[7], fp[7]);
-       eexecDumpNum(op[8], fp[8]);
-       eexecDumpNum(-(op[1] + op[3] + op[7]), fp[1] | fp[3] | fp[7]);
-       eexecDumpOp1(8);
-       break;
-      case 37:                 // flex1
-       if (nOps != 11) {
-         error(-1, "Wrong number of args (%d) to Type 2 flex1", nOps);
-       }
-       eexecDumpNum(op[0], fp[0]);
-       eexecDumpNum(op[1], fp[1]);
-       eexecDumpNum(op[2], fp[2]);
-       eexecDumpNum(op[3], fp[3]);
-       eexecDumpNum(op[4], fp[4]);
-       eexecDumpNum(op[5], fp[5]);
-       eexecDumpOp1(8);
-       eexecDumpNum(op[6], fp[6]);
-       eexecDumpNum(op[7], fp[7]);
-       eexecDumpNum(op[8], fp[8]);
-       eexecDumpNum(op[9], fp[9]);
-       dx = op[0] + op[2] + op[4] + op[6] + op[8];
-       dy = op[1] + op[3] + op[5] + op[7] + op[9];
-       if (fabs(dx) > fabs(dy)) {
-         eexecDumpNum(op[10], fp[10]);
-         eexecDumpNum(-dy, fp[1] | fp[3] | fp[5] | fp[7] | fp[9]);
+  // read top dict (first font only)
+  readTopDict(&dict);
+
+  // read the FDArray dictionaries and Private dictionaries
+  if (dict.fdArrayOffset == 0) {
+    nFDs = 1;
+    privateDicts = (Type1CPrivateDict *)
+                     gmalloc(nFDs * sizeof(Type1CPrivateDict));
+    privateDicts[0].dictData = new GString();
+    privateDicts[0].subrsOffset = 0;
+    privateDicts[0].defaultWidthX = 0;
+    privateDicts[0].defaultWidthXFP = gFalse;
+    privateDicts[0].nominalWidthX = 0;
+    privateDicts[0].nominalWidthXFP = gFalse;
+  } else {
+    fdArrayIdx = (Guchar *)file + dict.fdArrayOffset;
+    nFDs = getIndexLen(fdArrayIdx);
+    privateDicts = (Type1CPrivateDict *)
+                     gmalloc(nFDs * sizeof(Type1CPrivateDict));
+    idxPtr1 = getIndexValPtr(fdArrayIdx, 0);
+    for (i = 0; i < nFDs; ++i) {
+      privateDicts[i].dictData = NULL;
+      idxPtr0 = idxPtr1;
+      idxPtr1 = getIndexValPtr(fdArrayIdx, i + 1);
+      ptr = idxPtr0;
+      j = 0;
+      while (ptr < idxPtr1) {
+       if (*ptr <= 27 || *ptr == 31) {
+         key = *ptr++;
+         if (key == 0x0c) {
+           key = (key << 8) | *ptr++;
+         }
+         if (key == 0x0012) {
+           readPrivateDict(&privateDicts[i], (int)op[1], (int)op[0]);
+         }
+         j = 0;
        } else {
-         eexecDumpNum(-dx, fp[0] | fp[2] | fp[4] | fp[6] | fp[8]);
-         eexecDumpNum(op[10], fp[10]);
+         x = getNum(&ptr, &isFP);
+         if (j < 48) {
+           op[j] = x;
+           fp[j++] = isFP;
+         }
        }
-       eexecDumpOp1(8);
-       break;
-      case 3:                  // and
-      case 4:                  // or
-      case 5:                  // not
-      case 8:                  // store
-      case 9:                  // abs
-      case 10:                 // add
-      case 11:                 // sub
-      case 12:                 // div
-      case 13:                 // load
-      case 14:                 // neg
-      case 15:                 // eq
-      case 18:                 // drop
-      case 20:                 // put
-      case 21:                 // get
-      case 22:                 // ifelse
-      case 23:                 // random
-      case 24:                 // mul
-      case 26:                 // sqrt
-      case 27:                 // dup
-      case 28:                 // exch
-      case 29:                 // index
-      case 30:                 // roll
-       error(-1, "Unimplemented Type 2 charstring op: 12.%d", s[i+1]);
-       break;
-      default:
-       error(-1, "Illegal Type 2 charstring op: 12.%d", s[i+1]);
-       break;
       }
-      i += 2;
-      nOps = 0;
-    } else if (s[i] == 19) {   // hintmask
-      //~ ignored
-      if (first) {
-       cvtGlyphWidth(nOps == 1);
-       first = gFalse;
+      if (!privateDicts[i].dictData) {
+       privateDicts[i].dictData = new GString();
+       privateDicts[i].subrsOffset = 0;
+       privateDicts[i].defaultWidthX = 0;
+       privateDicts[i].defaultWidthXFP = gFalse;
+       privateDicts[i].nominalWidthX = 0;
+       privateDicts[i].nominalWidthXFP = gFalse;
       }
-      if (nOps > 0) {
-       if (nOps & 1) {
-         error(-1, "Wrong number of args (%d) to Type 2 hintmask/vstemhm",
-               nOps);
+    }
+  }
+
+  // get the glyph count
+  charStringsIdxPtr = (Guchar *)file + dict.charStrings;
+  nGlyphs = getIndexLen(charStringsIdxPtr);
+
+  // read the FDSelect table
+  fdSelect = (Guchar *)gmalloc(nGlyphs);
+  if (dict.fdSelectOffset == 0) {
+    for (i = 0; i < nGlyphs; ++i) {
+      fdSelect[i] = 0;
+    }
+  } else {
+    ptr = (Guchar *)file + dict.fdSelectOffset;
+    fdSelectFmt = *ptr++;
+    if (fdSelectFmt == 0) {
+      memcpy(fdSelect, ptr, nGlyphs);
+    } else if (fdSelectFmt == 3) {
+      nRanges = getWord(ptr, 2);
+      ptr += 2;
+      gid0 = getWord(ptr, 2);
+      ptr += 2;
+      for (i = 1; i <= nRanges; ++i) {
+       fd = *ptr++;
+       gid1 = getWord(ptr, 2);
+       ptr += 2;
+       for (j = gid0; j < gid1; ++j) {
+         fdSelect[j] = fd;
        }
-       nHints += nOps / 2;
+       gid0 = gid1;
       }
-      i += 1 + ((nHints + 7) >> 3);
-      nOps = 0;
-    } else if (s[i] == 20) {   // cntrmask
-      //~ ignored
-      if (first) {
-       cvtGlyphWidth(nOps == 1);
-       first = gFalse;
+    } else {
+      error(-1, "Unknown FDSelect table format in CID font");
+      for (i = 0; i < nGlyphs; ++i) {
+       fdSelect[i] = 0;
       }
-      if (nOps > 0) {
-       if (nOps & 1) {
-         error(-1, "Wrong number of args (%d) to Type 2 cntrmask/vstemhm",
-               nOps);
-       }
-       nHints += nOps / 2;
+    }
+  }
+
+  // read the charset, compute the CID-to-GID mapping
+  charset = readCharset(dict.charset, nGlyphs);
+  nCIDs = 0;
+  for (i = 0; i < nGlyphs; ++i) {
+    if (charset[i] >= nCIDs) {
+      nCIDs = charset[i] + 1;
+    }
+  }
+  cidMap = (int *)gmalloc(nCIDs * sizeof(int));
+  for (i = 0; i < nCIDs; ++i) {
+    cidMap[i] = -1;
+  }
+  for (i = 0; i < nGlyphs; ++i) {
+    cidMap[charset[i]] = i;
+  }
+
+  // write the descendant Type 1 fonts
+  for (i = 0; i < nCIDs; i += 256) {
+
+    //~ this assumes that all CIDs in this block have the same FD --
+    //~ to handle multiple FDs correctly, need to somehow divide the
+    //~ font up by FD
+    fd = 0;
+    for (j = 0; j < 256 && i+j < nCIDs; ++j) {
+      if (cidMap[i+j] >= 0) {
+       fd = fdSelect[cidMap[i+j]];
+       break;
       }
-      i += 1 + ((nHints + 7) >> 3);
-      nOps = 0;
-    } else if (s[i] == 28) {
-      x = (s[i+1] << 8) + s[i+2];
-      if (x & 0x8000)
-       x |= -1 << 15;
-      if (nOps < 48) {
-       fp[nOps] = gFalse;
-       op[nOps++] = x;
+    }
+
+    // font dictionary (unencrypted section)
+    fprintf(out, "16 dict begin\n");
+    fprintf(out, "/FontName /%s_%02x def\n", psName, i >> 8);
+    fprintf(out, "/FontType 1 def\n");
+    fprintf(out, "/FontMatrix [%g %g %g %g %g %g] def\n",
+           dict.fontMatrix[0], dict.fontMatrix[1], dict.fontMatrix[2],
+           dict.fontMatrix[3], dict.fontMatrix[4], dict.fontMatrix[5]);
+    fprintf(out, "/FontBBox [%g %g %g %g] def\n",
+           dict.fontBBox[0], dict.fontBBox[1],
+           dict.fontBBox[2], dict.fontBBox[3]);
+    fprintf(out, "/PaintType %d def\n", dict.paintType);
+    if (dict.paintType != 0) {
+      fprintf(out, "/StrokeWidth %g def\n", dict.strokeWidth);
+    }
+    fprintf(out, "/Encoding 256 array\n");
+    for (j = 0; j < 256 && i+j < nCIDs; ++j) {
+      fprintf(out, "dup %d /c%02x put\n", j, j);
+    }
+    fprintf(out, "readonly def\n");
+    fprintf(out, "currentdict end\n");
+
+    // start the binary section
+    fprintf(out, "currentfile eexec\n");
+    r1 = 55665;
+    line = 0;
+
+    // start the private dictionary
+    eexecWrite("\x83\xca\x73\xd5");
+    eexecWrite("dup /Private 32 dict dup begin\n");
+    eexecWrite("/RD {string currentfile exch readstring pop} executeonly def\n");
+    eexecWrite("/ND {noaccess def} executeonly def\n");
+    eexecWrite("/NP {noaccess put} executeonly def\n");
+    eexecWrite("/MinFeature {16 16} ND\n");
+    eexecWrite(privateDicts[fd].dictData->getCString());
+    defaultWidthX = privateDicts[fd].defaultWidthX;
+    defaultWidthXFP = privateDicts[fd].defaultWidthXFP;
+    nominalWidthX = privateDicts[fd].nominalWidthX;
+    nominalWidthXFP = privateDicts[fd].nominalWidthXFP;
+
+    // start the CharStrings
+    sprintf(eBuf, "2 index /CharStrings 256 dict dup begin\n");
+    eexecWrite(eBuf);
+
+    // write the .notdef CharString
+    idxPtr0 = getIndexValPtr(charStringsIdxPtr, 0);
+    idxPtr1 = getIndexValPtr(charStringsIdxPtr, 1);
+    n = idxPtr1 - idxPtr0;
+    eexecCvtGlyph(".notdef", idxPtr0, n);
+
+    // write the CharStrings
+    for (j = 0; j < 256 && i+j < nCIDs; ++j) {
+      if (cidMap[i+j] >= 0) {
+       idxPtr0 = getIndexValPtr(charStringsIdxPtr, cidMap[i+j]);
+       idxPtr1 = getIndexValPtr(charStringsIdxPtr, cidMap[i+j]+1);
+       n = idxPtr1 - idxPtr0;
+       sprintf(buf, "c%02x", j);
+       eexecCvtGlyph(buf, idxPtr0, n);
       }
-      i += 3;
-    } else if (s[i] <= 31) {
-      switch (s[i]) {
-      case 4:                  // vmoveto
-       if (first) {
-         cvtGlyphWidth(nOps == 2);
-         first = gFalse;
-       }
-       if (nOps != 1)
-         error(-1, "Wrong number of args (%d) to Type 2 vmoveto", nOps);
-       eexecDumpNum(op[0], fp[0]);
-       eexecDumpOp1(4);
+    }
+    eexecWrite("end\n");
+    eexecWrite("end\n");
+    eexecWrite("readonly put\n");
+    eexecWrite("noaccess put\n");
+    eexecWrite("dup /FontName get exch definefont pop\n");
+    eexecWrite("mark currentfile closefile\n");
+
+    // trailer
+    if (line > 0) {
+      fputc('\n', out);
+    }
+    for (j = 0; j < 8; ++j) {
+      fprintf(out, "0000000000000000000000000000000000000000000000000000000000000000\n");
+    }
+    fprintf(out, "cleartomark\n");
+  }
+
+  // write the Type 0 parent font
+  fprintf(out, "16 dict begin\n");
+  fprintf(out, "/FontName /%s def\n", psName);
+  fprintf(out, "/FontType 0 def\n");
+  fprintf(out, "/FontMatrix [1 0 0 1 0 0] def\n");
+  fprintf(out, "/FMapType 2 def\n");
+  fprintf(out, "/Encoding [\n");
+  for (i = 0; i < nCIDs; i += 256) {
+    fprintf(out, "%d\n", i >> 8);
+  }
+  fprintf(out, "] def\n");
+  fprintf(out, "/FDepVector [\n");
+  for (i = 0; i < nCIDs; i += 256) {
+    fprintf(out, "/%s_%02x findfont\n", psName, i >> 8);
+  }
+  fprintf(out, "] def\n");
+  fprintf(out, "FontName currentdict end definefont pop\n");
+
+  // clean up
+  for (i = 0; i < nFDs; ++i) {
+    delete privateDicts[i].dictData;
+  }
+  gfree(privateDicts);
+  gfree(cidMap);
+  gfree(charset);
+  gfree(fdSelect);
+}
+
+void Type1CFontFile::readTopDict(Type1CTopDict *dict) {
+  Guchar *idxPtr0, *idxPtr1, *ptr;
+  double x;
+  GBool isFP;
+  int key;
+  int i;
+
+  idxPtr0 = getIndexValPtr(topDictIdxPtr, 0);
+  idxPtr1 = getIndexValPtr(topDictIdxPtr, 1);
+  dict->version = 0;
+  dict->notice = 0;
+  dict->copyright = 0;
+  dict->fullName = 0;
+  dict->familyName = 0;
+  dict->weight = 0;
+  dict->isFixedPitch = 0;
+  dict->italicAngle = 0;
+  dict->underlinePosition = -100;
+  dict->underlineThickness = 50;
+  dict->paintType = 0;
+  dict->charstringType = 2;
+  dict->fontMatrix[0] = 0.001;
+  dict->fontMatrix[1] = 0;
+  dict->fontMatrix[2] = 0;
+  dict->fontMatrix[3] = 0.001;
+  dict->fontMatrix[4] = 0;
+  dict->fontMatrix[5] = 0;
+  dict->uniqueID = 0;
+  dict->fontBBox[0] = 0;
+  dict->fontBBox[1] = 0;
+  dict->fontBBox[2] = 0;
+  dict->fontBBox[3] = 0;
+  dict->strokeWidth = 0;
+  dict->charset = 0;
+  dict->encoding = 0;
+  dict->charStrings = 0;
+  dict->privateSize = 0;
+  dict->privateOffset = 0;
+  dict->registry = 0;
+  dict->ordering = 0;
+  dict->supplement = 0;
+  dict->fdArrayOffset = 0;
+  dict->fdSelectOffset = 0;
+  i = 0;
+  ptr = idxPtr0;
+  while (ptr < idxPtr1) {
+    if (*ptr <= 27 || *ptr == 31) {
+      key = *ptr++;
+      if (key == 0x0c) {
+       key = (key << 8) | *ptr++;
+      }
+      switch (key) {
+      case 0x0000: dict->version = (int)op[0]; break;
+      case 0x0001: dict->notice = (int)op[0]; break;
+      case 0x0c00: dict->copyright = (int)op[0]; break;
+      case 0x0002: dict->fullName = (int)op[0]; break;
+      case 0x0003: dict->familyName = (int)op[0]; break;
+      case 0x0004: dict->weight = (int)op[0]; break;
+      case 0x0c01: dict->isFixedPitch = (int)op[0]; break;
+      case 0x0c02: dict->italicAngle = op[0]; break;
+      case 0x0c03: dict->underlinePosition = op[0]; break;
+      case 0x0c04: dict->underlineThickness = op[0]; break;
+      case 0x0c05: dict->paintType = (int)op[0]; break;
+      case 0x0c06: dict->charstringType = (int)op[0]; break;
+      case 0x0c07: dict->fontMatrix[0] = op[0];
+                  dict->fontMatrix[1] = op[1];
+                  dict->fontMatrix[2] = op[2];
+                  dict->fontMatrix[3] = op[3];
+                  dict->fontMatrix[4] = op[4];
+                  dict->fontMatrix[5] = op[5]; break;
+      case 0x000d: dict->uniqueID = (int)op[0]; break;
+      case 0x0005: dict->fontBBox[0] = op[0];
+                  dict->fontBBox[1] = op[1];
+                  dict->fontBBox[2] = op[2];
+                  dict->fontBBox[3] = op[3]; break;
+      case 0x0c08: dict->strokeWidth = op[0]; break;
+      case 0x000f: dict->charset = (int)op[0]; break;
+      case 0x0010: dict->encoding = (int)op[0]; break;
+      case 0x0011: dict->charStrings = (int)op[0]; break;
+      case 0x0012: dict->privateSize = (int)op[0];
+                  dict->privateOffset = (int)op[1]; break;
+      case 0x0c1e: dict->registry = (int)op[0];
+                  dict->ordering = (int)op[1];
+                  dict->supplement = (int)op[2]; break;
+      case 0x0c24: dict->fdArrayOffset = (int)op[0]; break;
+      case 0x0c25: dict->fdSelectOffset = (int)op[0]; break;
+      }
+      i = 0;
+    } else {
+      x = getNum(&ptr, &isFP);
+      if (i < 48) {
+       op[i] = x;
+       fp[i++] = isFP;
+      }
+    }
+  }
+}
+
+void Type1CFontFile::readPrivateDict(Type1CPrivateDict *privateDict,
+                                    int offset, int size) {
+  Guchar *idxPtr0, *idxPtr1, *ptr;
+  char eBuf[256];
+  int key;
+  double x;
+  GBool isFP;
+  int i;
+
+  privateDict->dictData = new GString();
+  privateDict->subrsOffset = 0;
+  privateDict->defaultWidthX = 0;
+  privateDict->defaultWidthXFP = gFalse;
+  privateDict->nominalWidthX = 0;
+  privateDict->nominalWidthXFP = gFalse;
+  idxPtr0 = (Guchar *)file + offset;
+  idxPtr1 = idxPtr0 + size;
+  ptr = idxPtr0;
+  i = 0;
+  while (ptr < idxPtr1) {
+    if (*ptr <= 27 || *ptr == 31) {
+      key = *ptr++;
+      if (key == 0x0c) {
+       key = (key << 8) | *ptr++;
+      }
+      switch (key) {
+      case 0x0006:
+       getDeltaInt(eBuf, "BlueValues", op, i);
+       privateDict->dictData->append(eBuf);
        break;
-      case 5:                  // rlineto
-       if (nOps < 2 || nOps % 2 != 0)
-         error(-1, "Wrong number of args (%d) to Type 2 rlineto", nOps);
-       for (k = 0; k < nOps; k += 2) {
-         eexecDumpNum(op[k], fp[k]);
-         eexecDumpNum(op[k+1], fp[k+1]);
-         eexecDumpOp1(5);
-       }
+      case 0x0007:
+       getDeltaInt(eBuf, "OtherBlues", op, i);
+       privateDict->dictData->append(eBuf);
        break;
-      case 6:                  // hlineto
-       if (nOps < 1)
-         error(-1, "Wrong number of args (%d) to Type 2 hlineto", nOps);
-       for (k = 0; k < nOps; ++k) {
-         eexecDumpNum(op[k], fp[k]);
-         eexecDumpOp1((k & 1) ? 7 : 6);
-       }
+      case 0x0008:
+       getDeltaInt(eBuf, "FamilyBlues", op, i);
+       privateDict->dictData->append(eBuf);
        break;
-      case 7:                  // vlineto
-       if (nOps < 1)
-         error(-1, "Wrong number of args (%d) to Type 2 vlineto", nOps);
-       for (k = 0; k < nOps; ++k) {
-         eexecDumpNum(op[k], fp[k]);
-         eexecDumpOp1((k & 1) ? 6 : 7);
-       }
+      case 0x0009:
+       getDeltaInt(eBuf, "FamilyOtherBlues", op, i);
+       privateDict->dictData->append(eBuf);
        break;
-      case 8:                  // rrcurveto
-       if (nOps < 6 || nOps % 6 != 0)
-         error(-1, "Wrong number of args (%d) to Type 2 rrcurveto", nOps);
-       for (k = 0; k < nOps; k += 6) {
-         eexecDumpNum(op[k], fp[k]);
-         eexecDumpNum(op[k+1], fp[k+1]);
-         eexecDumpNum(op[k+2], fp[k+2]);
-         eexecDumpNum(op[k+3], fp[k+3]);
-         eexecDumpNum(op[k+4], fp[k+4]);
-         eexecDumpNum(op[k+5], fp[k+5]);
-         eexecDumpOp1(8);
-       }
+      case 0x0c09:
+       sprintf(eBuf, "/BlueScale %g def\n", op[0]);
+       privateDict->dictData->append(eBuf);
        break;
-      case 14:                 // endchar / seac
-       if (first) {
-         cvtGlyphWidth(nOps == 1 || nOps == 5);
-         first = gFalse;
-       }
-       if (nOps == 4) {
-         eexecDumpNum(0, 0);
-         eexecDumpNum(op[0], fp[0]);
-         eexecDumpNum(op[1], fp[1]);
-         eexecDumpNum(op[2], fp[2]);
-         eexecDumpNum(op[3], fp[3]);
-         eexecDumpOp2(6);
-       } else if (nOps == 0) {
-         eexecDumpOp1(14);
-       } else {
-         error(-1, "Wrong number of args (%d) to Type 2 endchar", nOps);
-       }
+      case 0x0c0a:
+       sprintf(eBuf, "/BlueShift %d def\n", (int)op[0]);
+       privateDict->dictData->append(eBuf);
        break;
-      case 21:                 // rmoveto
-       if (first) {
-         cvtGlyphWidth(nOps == 3);
-         first = gFalse;
-       }
-       if (nOps != 2)
-         error(-1, "Wrong number of args (%d) to Type 2 rmoveto", nOps);
-       eexecDumpNum(op[0], fp[0]);
-       eexecDumpNum(op[1], fp[1]);
-       eexecDumpOp1(21);
+      case 0x0c0b:
+       sprintf(eBuf, "/BlueFuzz %d def\n", (int)op[0]);
+       privateDict->dictData->append(eBuf);
        break;
-      case 22:                 // hmoveto
-       if (first) {
-         cvtGlyphWidth(nOps == 2);
-         first = gFalse;
-       }
-       if (nOps != 1)
-         error(-1, "Wrong number of args (%d) to Type 2 hmoveto", nOps);
-       eexecDumpNum(op[0], fp[0]);
-       eexecDumpOp1(22);
+      case 0x000a:
+       sprintf(eBuf, "/StdHW [%g] def\n", op[0]);
+       privateDict->dictData->append(eBuf);
        break;
-      case 24:                 // rcurveline
-       if (nOps < 8 || (nOps - 2) % 6 != 0)
-         error(-1, "Wrong number of args (%d) to Type 2 rcurveline", nOps);
-       for (k = 0; k < nOps - 2; k += 6) {
-         eexecDumpNum(op[k], fp[k]);
-         eexecDumpNum(op[k+1], fp[k+1]);
-         eexecDumpNum(op[k+2], fp[k+2]);
-         eexecDumpNum(op[k+3], fp[k+3]);
-         eexecDumpNum(op[k+4], fp[k+4]);
-         eexecDumpNum(op[k+5], fp[k+5]);
-         eexecDumpOp1(8);
-       }
-       eexecDumpNum(op[k], fp[k]);
-       eexecDumpNum(op[k+1], fp[k]);
-       eexecDumpOp1(5);
+      case 0x000b:
+       sprintf(eBuf, "/StdVW [%g] def\n", op[0]);
+       privateDict->dictData->append(eBuf);
        break;
-      case 25:                 // rlinecurve
-       if (nOps < 8 || (nOps - 6) % 2 != 0)
-         error(-1, "Wrong number of args (%d) to Type 2 rlinecurve", nOps);
-       for (k = 0; k < nOps - 6; k += 2) {
-         eexecDumpNum(op[k], fp[k]);
-         eexecDumpNum(op[k+1], fp[k]);
-         eexecDumpOp1(5);
-       }
-       eexecDumpNum(op[k], fp[k]);
-       eexecDumpNum(op[k+1], fp[k+1]);
-       eexecDumpNum(op[k+2], fp[k+2]);
-       eexecDumpNum(op[k+3], fp[k+3]);
-       eexecDumpNum(op[k+4], fp[k+4]);
-       eexecDumpNum(op[k+5], fp[k+5]);
-       eexecDumpOp1(8);
+      case 0x0c0c:
+       getDeltaReal(eBuf, "StemSnapH", op, i);
+       privateDict->dictData->append(eBuf);
        break;
-      case 26:                 // vvcurveto
-       if (nOps < 4 || !(nOps % 4 == 0 || (nOps-1) % 4 == 0))
-         error(-1, "Wrong number of args (%d) to Type 2 vvcurveto", nOps);
-       if (nOps % 2 == 1) {
-         eexecDumpNum(op[0], fp[0]);
-         eexecDumpNum(op[1], fp[1]);
-         eexecDumpNum(op[2], fp[2]);
-         eexecDumpNum(op[3], fp[3]);
-         eexecDumpNum(0, gFalse);
-         eexecDumpNum(op[4], fp[4]);
-         eexecDumpOp1(8);
-         k = 5;
-       } else {
-         k = 0;
-       }
-       for (; k < nOps; k += 4) {
-         eexecDumpNum(0, gFalse);
-         eexecDumpNum(op[k], fp[k]);
-         eexecDumpNum(op[k+1], fp[k+1]);
-         eexecDumpNum(op[k+2], fp[k+2]);
-         eexecDumpNum(0, gFalse);
-         eexecDumpNum(op[k+3], fp[k+3]);
-         eexecDumpOp1(8);
-       }
+      case 0x0c0d:
+       getDeltaReal(eBuf, "StemSnapV", op, i);
+       privateDict->dictData->append(eBuf);
        break;
-      case 27:                 // hhcurveto
-       if (nOps < 4 || !(nOps % 4 == 0 || (nOps-1) % 4 == 0))
-         error(-1, "Wrong number of args (%d) to Type 2 hhcurveto", nOps);
-       if (nOps % 2 == 1) {
-         eexecDumpNum(op[1], fp[1]);
-         eexecDumpNum(op[0], fp[0]);
-         eexecDumpNum(op[2], fp[2]);
-         eexecDumpNum(op[3], fp[3]);
-         eexecDumpNum(op[4], fp[4]);
-         eexecDumpNum(0, gFalse);
-         eexecDumpOp1(8);
-         k = 5;
-       } else {
-         k = 0;
-       }
-       for (; k < nOps; k += 4) {
-         eexecDumpNum(op[k], fp[k]);
-         eexecDumpNum(0, gFalse);
-         eexecDumpNum(op[k+1], fp[k+1]);
-         eexecDumpNum(op[k+2], fp[k+2]);
-         eexecDumpNum(op[k+3], fp[k+3]);
-         eexecDumpNum(0, gFalse);
-         eexecDumpOp1(8);
-       }
+      case 0x0c0e:
+       sprintf(eBuf, "/ForceBold %s def\n", op[0] ? "true" : "false");
+       privateDict->dictData->append(eBuf);
        break;
-      case 30:                 // vhcurveto
-       if (nOps < 4 || !(nOps % 4 == 0 || (nOps-1) % 4 == 0))
-         error(-1, "Wrong number of args (%d) to Type 2 vhcurveto", nOps);
-       for (k = 0; k < nOps && k != nOps-5; k += 4) {
-         if (k % 8 == 0) {
-           eexecDumpNum(op[k], fp[k]);
-           eexecDumpNum(op[k+1], fp[k+1]);
-           eexecDumpNum(op[k+2], fp[k+2]);
+      case 0x0c0f:
+       sprintf(eBuf, "/ForceBoldThreshold %g def\n", op[0]);
+       privateDict->dictData->append(eBuf);
+       break;
+      case 0x0c11:
+       sprintf(eBuf, "/LanguageGroup %d def\n", (int)op[0]);
+       privateDict->dictData->append(eBuf);
+       break;
+      case 0x0c12:
+       sprintf(eBuf, "/ExpansionFactor %g def\n", op[0]);
+       privateDict->dictData->append(eBuf);
+       break;
+      case 0x0c13:
+       error(-1, "Got Type 1C InitialRandomSeed");
+       break;
+      case 0x0013:
+       privateDict->subrsOffset = (int)op[0];
+       break;
+      case 0x0014:
+       privateDict->defaultWidthX = op[0];
+       privateDict->defaultWidthXFP = fp[0];
+       break;
+      case 0x0015:
+       privateDict->nominalWidthX = op[0];
+       privateDict->nominalWidthXFP = fp[0];
+       break;
+      default:
+       error(-1, "Unknown Type 1C private dict entry %04x", key);
+       break;
+      }
+      i = 0;
+    } else {
+      x = getNum(&ptr, &isFP);
+      if (i < 48) {
+       op[i] = x;
+       fp[i++] = isFP;
+      }
+    }
+  }
+}
+
+Gushort *Type1CFontFile::readCharset(int charset, int nGlyphs) {
+  Gushort *glyphNames;
+  Guchar *ptr;
+  int charsetFormat, c;
+  int nLeft, i, j;
+
+  if (charset == 0) {
+    glyphNames = type1CISOAdobeCharset;
+  } else if (charset == 1) {
+    glyphNames = type1CExpertCharset;
+  } else if (charset == 2) {
+    glyphNames = type1CExpertSubsetCharset;
+  } else {
+    glyphNames = (Gushort *)gmalloc(nGlyphs * sizeof(Gushort));
+    glyphNames[0] = 0;
+    ptr = (Guchar *)file + charset;
+    charsetFormat = *ptr++;
+    if (charsetFormat == 0) {
+      for (i = 1; i < nGlyphs; ++i) {
+       glyphNames[i] = getWord(ptr, 2);
+       ptr += 2;
+      }
+    } else if (charsetFormat == 1) {
+      i = 1;
+      while (i < nGlyphs) {
+       c = getWord(ptr, 2);
+       ptr += 2;
+       nLeft = *ptr++;
+       for (j = 0; j <= nLeft && i < nGlyphs; ++j) {
+         glyphNames[i++] = c++;
+       }
+      }
+    } else if (charsetFormat == 2) {
+      i = 1;
+      while (i < nGlyphs) {
+       c = getWord(ptr, 2);
+       ptr += 2;
+       nLeft = getWord(ptr, 2);
+       ptr += 2;
+       for (j = 0; j <= nLeft && i < nGlyphs; ++j) {
+         glyphNames[i++] = c++;
+       }
+      }
+    }
+  }
+  return glyphNames;
+}
+
+void Type1CFontFile::eexecWrite(char *s) {
+  Guchar *p;
+  Guchar x;
+
+  for (p = (Guchar *)s; *p; ++p) {
+    x = *p ^ (r1 >> 8);
+    r1 = (x + r1) * 52845 + 22719;
+    fputc(hexChars[x >> 4], out);
+    fputc(hexChars[x & 0x0f], out);
+    line += 2;
+    if (line == 64) {
+      fputc('\n', out);
+      line = 0;
+    }
+  }
+}
+
+void Type1CFontFile::eexecCvtGlyph(char *glyphName, Guchar *s, int n) {
+  char eBuf[256];
+
+  cvtGlyph(s, n);
+  sprintf(eBuf, "/%s %d RD ", glyphName, charBuf->getLength());
+  eexecWrite(eBuf);
+  eexecWriteCharstring((Guchar *)charBuf->getCString(), charBuf->getLength());
+  eexecWrite(" ND\n");
+  delete charBuf;
+}
+
+void Type1CFontFile::cvtGlyph(Guchar *s, int n) {
+  int nHints;
+  int x;
+  GBool first = gTrue;
+  double d, dx, dy;
+  GBool dFP;
+  Gushort r2;
+  Guchar byte;
+  int i, k;
+
+  charBuf = new GString();
+  charBuf->append((char)73);
+  charBuf->append((char)58);
+  charBuf->append((char)147);
+  charBuf->append((char)134);
+
+  i = 0;
+  nOps = 0;
+  nHints = 0;
+  while (i < n) {
+    if (s[i] == 12) {
+      switch (s[i+1]) {
+      case 0:                  // dotsection (should be Type 1 only?)
+       // ignored
+       break;
+      case 34:                 // hflex
+       if (nOps != 7) {
+         error(-1, "Wrong number of args (%d) to Type 2 hflex", nOps);
+       }
+       eexecDumpNum(op[0], fp[0]);
+       eexecDumpNum(0, gFalse);
+       eexecDumpNum(op[1], fp[1]);
+       eexecDumpNum(op[2], fp[2]);
+       eexecDumpNum(op[3], fp[3]);
+       eexecDumpNum(0, gFalse);
+       eexecDumpOp1(8);
+       eexecDumpNum(op[4], fp[4]);
+       eexecDumpNum(0, gFalse);
+       eexecDumpNum(op[5], fp[5]);
+       eexecDumpNum(-op[2], fp[2]);
+       eexecDumpNum(op[6], fp[6]);
+       eexecDumpNum(0, gFalse);
+       eexecDumpOp1(8);
+       break;
+      case 35:                 // flex
+       if (nOps != 13) {
+         error(-1, "Wrong number of args (%d) to Type 2 flex", nOps);
+       }
+       eexecDumpNum(op[0], fp[0]);
+       eexecDumpNum(op[1], fp[1]);
+       eexecDumpNum(op[2], fp[2]);
+       eexecDumpNum(op[3], fp[3]);
+       eexecDumpNum(op[4], fp[4]);
+       eexecDumpNum(op[5], fp[5]);
+       eexecDumpOp1(8);
+       eexecDumpNum(op[6], fp[6]);
+       eexecDumpNum(op[7], fp[7]);
+       eexecDumpNum(op[8], fp[8]);
+       eexecDumpNum(op[9], fp[9]);
+       eexecDumpNum(op[10], fp[10]);
+       eexecDumpNum(op[11], fp[11]);
+       eexecDumpOp1(8);
+       break;
+      case 36:                 // hflex1
+       if (nOps != 9) {
+         error(-1, "Wrong number of args (%d) to Type 2 hflex1", nOps);
+       }
+       eexecDumpNum(op[0], fp[0]);
+       eexecDumpNum(op[1], fp[1]);
+       eexecDumpNum(op[2], fp[2]);
+       eexecDumpNum(op[3], fp[3]);
+       eexecDumpNum(op[4], fp[4]);
+       eexecDumpNum(0, gFalse);
+       eexecDumpOp1(8);
+       eexecDumpNum(op[5], fp[5]);
+       eexecDumpNum(0, gFalse);
+       eexecDumpNum(op[6], fp[6]);
+       eexecDumpNum(op[7], fp[7]);
+       eexecDumpNum(op[8], fp[8]);
+       eexecDumpNum(-(op[1] + op[3] + op[7]), fp[1] | fp[3] | fp[7]);
+       eexecDumpOp1(8);
+       break;
+      case 37:                 // flex1
+       if (nOps != 11) {
+         error(-1, "Wrong number of args (%d) to Type 2 flex1", nOps);
+       }
+       eexecDumpNum(op[0], fp[0]);
+       eexecDumpNum(op[1], fp[1]);
+       eexecDumpNum(op[2], fp[2]);
+       eexecDumpNum(op[3], fp[3]);
+       eexecDumpNum(op[4], fp[4]);
+       eexecDumpNum(op[5], fp[5]);
+       eexecDumpOp1(8);
+       eexecDumpNum(op[6], fp[6]);
+       eexecDumpNum(op[7], fp[7]);
+       eexecDumpNum(op[8], fp[8]);
+       eexecDumpNum(op[9], fp[9]);
+       dx = op[0] + op[2] + op[4] + op[6] + op[8];
+       dy = op[1] + op[3] + op[5] + op[7] + op[9];
+       if (fabs(dx) > fabs(dy)) {
+         eexecDumpNum(op[10], fp[10]);
+         eexecDumpNum(-dy, fp[1] | fp[3] | fp[5] | fp[7] | fp[9]);
+       } else {
+         eexecDumpNum(-dx, fp[0] | fp[2] | fp[4] | fp[6] | fp[8]);
+         eexecDumpNum(op[10], fp[10]);
+       }
+       eexecDumpOp1(8);
+       break;
+      case 3:                  // and
+      case 4:                  // or
+      case 5:                  // not
+      case 8:                  // store
+      case 9:                  // abs
+      case 10:                 // add
+      case 11:                 // sub
+      case 12:                 // div
+      case 13:                 // load
+      case 14:                 // neg
+      case 15:                 // eq
+      case 18:                 // drop
+      case 20:                 // put
+      case 21:                 // get
+      case 22:                 // ifelse
+      case 23:                 // random
+      case 24:                 // mul
+      case 26:                 // sqrt
+      case 27:                 // dup
+      case 28:                 // exch
+      case 29:                 // index
+      case 30:                 // roll
+       error(-1, "Unimplemented Type 2 charstring op: 12.%d", s[i+1]);
+       break;
+      default:
+       error(-1, "Illegal Type 2 charstring op: 12.%d", s[i+1]);
+       break;
+      }
+      i += 2;
+      nOps = 0;
+    } else if (s[i] == 19) {   // hintmask
+      // ignored
+      if (first) {
+       cvtGlyphWidth(nOps == 1);
+       first = gFalse;
+      }
+      if (nOps > 0) {
+       if (nOps & 1) {
+         error(-1, "Wrong number of args (%d) to Type 2 hintmask/vstemhm",
+               nOps);
+       }
+       nHints += nOps / 2;
+      }
+      i += 1 + ((nHints + 7) >> 3);
+      nOps = 0;
+    } else if (s[i] == 20) {   // cntrmask
+      // ignored
+      if (first) {
+       cvtGlyphWidth(nOps == 1);
+       first = gFalse;
+      }
+      if (nOps > 0) {
+       if (nOps & 1) {
+         error(-1, "Wrong number of args (%d) to Type 2 cntrmask/vstemhm",
+               nOps);
+       }
+       nHints += nOps / 2;
+      }
+      i += 1 + ((nHints + 7) >> 3);
+      nOps = 0;
+    } else if (s[i] == 28) {
+      x = (s[i+1] << 8) + s[i+2];
+      if (x & 0x8000) {
+       x |= -1 << 15;
+      }
+      if (nOps < 48) {
+       fp[nOps] = gFalse;
+       op[nOps++] = x;
+      }
+      i += 3;
+    } else if (s[i] <= 31) {
+      switch (s[i]) {
+      case 4:                  // vmoveto
+       if (first) {
+         cvtGlyphWidth(nOps == 2);
+         first = gFalse;
+       }
+       if (nOps != 1) {
+         error(-1, "Wrong number of args (%d) to Type 2 vmoveto", nOps);
+       }
+       eexecDumpNum(op[0], fp[0]);
+       eexecDumpOp1(4);
+       break;
+      case 5:                  // rlineto
+       if (nOps < 2 || nOps % 2 != 0) {
+         error(-1, "Wrong number of args (%d) to Type 2 rlineto", nOps);
+       }
+       for (k = 0; k < nOps; k += 2) {
+         eexecDumpNum(op[k], fp[k]);
+         eexecDumpNum(op[k+1], fp[k+1]);
+         eexecDumpOp1(5);
+       }
+       break;
+      case 6:                  // hlineto
+       if (nOps < 1) {
+         error(-1, "Wrong number of args (%d) to Type 2 hlineto", nOps);
+       }
+       for (k = 0; k < nOps; ++k) {
+         eexecDumpNum(op[k], fp[k]);
+         eexecDumpOp1((k & 1) ? 7 : 6);
+       }
+       break;
+      case 7:                  // vlineto
+       if (nOps < 1) {
+         error(-1, "Wrong number of args (%d) to Type 2 vlineto", nOps);
+       }
+       for (k = 0; k < nOps; ++k) {
+         eexecDumpNum(op[k], fp[k]);
+         eexecDumpOp1((k & 1) ? 6 : 7);
+       }
+       break;
+      case 8:                  // rrcurveto
+       if (nOps < 6 || nOps % 6 != 0) {
+         error(-1, "Wrong number of args (%d) to Type 2 rrcurveto", nOps);
+       }
+       for (k = 0; k < nOps; k += 6) {
+         eexecDumpNum(op[k], fp[k]);
+         eexecDumpNum(op[k+1], fp[k+1]);
+         eexecDumpNum(op[k+2], fp[k+2]);
+         eexecDumpNum(op[k+3], fp[k+3]);
+         eexecDumpNum(op[k+4], fp[k+4]);
+         eexecDumpNum(op[k+5], fp[k+5]);
+         eexecDumpOp1(8);
+       }
+       break;
+      case 14:                 // endchar / seac
+       if (first) {
+         cvtGlyphWidth(nOps == 1 || nOps == 5);
+         first = gFalse;
+       }
+       if (nOps == 4) {
+         eexecDumpNum(0, 0);
+         eexecDumpNum(op[0], fp[0]);
+         eexecDumpNum(op[1], fp[1]);
+         eexecDumpNum(op[2], fp[2]);
+         eexecDumpNum(op[3], fp[3]);
+         eexecDumpOp2(6);
+       } else if (nOps == 0) {
+         eexecDumpOp1(14);
+       } else {
+         error(-1, "Wrong number of args (%d) to Type 2 endchar", nOps);
+       }
+       break;
+      case 21:                 // rmoveto
+       if (first) {
+         cvtGlyphWidth(nOps == 3);
+         first = gFalse;
+       }
+       if (nOps != 2) {
+         error(-1, "Wrong number of args (%d) to Type 2 rmoveto", nOps);
+       }
+       eexecDumpNum(op[0], fp[0]);
+       eexecDumpNum(op[1], fp[1]);
+       eexecDumpOp1(21);
+       break;
+      case 22:                 // hmoveto
+       if (first) {
+         cvtGlyphWidth(nOps == 2);
+         first = gFalse;
+       }
+       if (nOps != 1) {
+         error(-1, "Wrong number of args (%d) to Type 2 hmoveto", nOps);
+       }
+       eexecDumpNum(op[0], fp[0]);
+       eexecDumpOp1(22);
+       break;
+      case 24:                 // rcurveline
+       if (nOps < 8 || (nOps - 2) % 6 != 0) {
+         error(-1, "Wrong number of args (%d) to Type 2 rcurveline", nOps);
+       }
+       for (k = 0; k < nOps - 2; k += 6) {
+         eexecDumpNum(op[k], fp[k]);
+         eexecDumpNum(op[k+1], fp[k+1]);
+         eexecDumpNum(op[k+2], fp[k+2]);
+         eexecDumpNum(op[k+3], fp[k+3]);
+         eexecDumpNum(op[k+4], fp[k+4]);
+         eexecDumpNum(op[k+5], fp[k+5]);
+         eexecDumpOp1(8);
+       }
+       eexecDumpNum(op[k], fp[k]);
+       eexecDumpNum(op[k+1], fp[k]);
+       eexecDumpOp1(5);
+       break;
+      case 25:                 // rlinecurve
+       if (nOps < 8 || (nOps - 6) % 2 != 0) {
+         error(-1, "Wrong number of args (%d) to Type 2 rlinecurve", nOps);
+       }
+       for (k = 0; k < nOps - 6; k += 2) {
+         eexecDumpNum(op[k], fp[k]);
+         eexecDumpNum(op[k+1], fp[k]);
+         eexecDumpOp1(5);
+       }
+       eexecDumpNum(op[k], fp[k]);
+       eexecDumpNum(op[k+1], fp[k+1]);
+       eexecDumpNum(op[k+2], fp[k+2]);
+       eexecDumpNum(op[k+3], fp[k+3]);
+       eexecDumpNum(op[k+4], fp[k+4]);
+       eexecDumpNum(op[k+5], fp[k+5]);
+       eexecDumpOp1(8);
+       break;
+      case 26:                 // vvcurveto
+       if (nOps < 4 || !(nOps % 4 == 0 || (nOps-1) % 4 == 0)) {
+         error(-1, "Wrong number of args (%d) to Type 2 vvcurveto", nOps);
+       }
+       if (nOps % 2 == 1) {
+         eexecDumpNum(op[0], fp[0]);
+         eexecDumpNum(op[1], fp[1]);
+         eexecDumpNum(op[2], fp[2]);
+         eexecDumpNum(op[3], fp[3]);
+         eexecDumpNum(0, gFalse);
+         eexecDumpNum(op[4], fp[4]);
+         eexecDumpOp1(8);
+         k = 5;
+       } else {
+         k = 0;
+       }
+       for (; k < nOps; k += 4) {
+         eexecDumpNum(0, gFalse);
+         eexecDumpNum(op[k], fp[k]);
+         eexecDumpNum(op[k+1], fp[k+1]);
+         eexecDumpNum(op[k+2], fp[k+2]);
+         eexecDumpNum(0, gFalse);
+         eexecDumpNum(op[k+3], fp[k+3]);
+         eexecDumpOp1(8);
+       }
+       break;
+      case 27:                 // hhcurveto
+       if (nOps < 4 || !(nOps % 4 == 0 || (nOps-1) % 4 == 0)) {
+         error(-1, "Wrong number of args (%d) to Type 2 hhcurveto", nOps);
+       }
+       if (nOps % 2 == 1) {
+         eexecDumpNum(op[1], fp[1]);
+         eexecDumpNum(op[0], fp[0]);
+         eexecDumpNum(op[2], fp[2]);
+         eexecDumpNum(op[3], fp[3]);
+         eexecDumpNum(op[4], fp[4]);
+         eexecDumpNum(0, gFalse);
+         eexecDumpOp1(8);
+         k = 5;
+       } else {
+         k = 0;
+       }
+       for (; k < nOps; k += 4) {
+         eexecDumpNum(op[k], fp[k]);
+         eexecDumpNum(0, gFalse);
+         eexecDumpNum(op[k+1], fp[k+1]);
+         eexecDumpNum(op[k+2], fp[k+2]);
+         eexecDumpNum(op[k+3], fp[k+3]);
+         eexecDumpNum(0, gFalse);
+         eexecDumpOp1(8);
+       }
+       break;
+      case 30:                 // vhcurveto
+       if (nOps < 4 || !(nOps % 4 == 0 || (nOps-1) % 4 == 0)) {
+         error(-1, "Wrong number of args (%d) to Type 2 vhcurveto", nOps);
+       }
+       for (k = 0; k < nOps && k != nOps-5; k += 4) {
+         if (k % 8 == 0) {
+           eexecDumpNum(op[k], fp[k]);
+           eexecDumpNum(op[k+1], fp[k+1]);
+           eexecDumpNum(op[k+2], fp[k+2]);
+           eexecDumpNum(op[k+3], fp[k+3]);
+           eexecDumpOp1(30);
+         } else {
+           eexecDumpNum(op[k], fp[k]);
+           eexecDumpNum(op[k+1], fp[k+1]);
+           eexecDumpNum(op[k+2], fp[k+2]);
+           eexecDumpNum(op[k+3], fp[k+3]);
+           eexecDumpOp1(31);
+         }
+       }
+       if (k == nOps-5) {
+         if (k % 8 == 0) {
+           eexecDumpNum(0, gFalse);
+           eexecDumpNum(op[k], fp[k]);
+           eexecDumpNum(op[k+1], fp[k+1]);
+           eexecDumpNum(op[k+2], fp[k+2]);
+           eexecDumpNum(op[k+3], fp[k+3]);
+           eexecDumpNum(op[k+4], fp[k+4]);
+         } else {
+           eexecDumpNum(op[k], fp[k]);
+           eexecDumpNum(0, gFalse);
+           eexecDumpNum(op[k+1], fp[k+1]);
+           eexecDumpNum(op[k+2], fp[k+2]);
+           eexecDumpNum(op[k+4], fp[k+4]);
+           eexecDumpNum(op[k+3], fp[k+3]);
+         }
+         eexecDumpOp1(8);
+       }
+       break;
+      case 31:                 // hvcurveto
+       if (nOps < 4 || !(nOps % 4 == 0 || (nOps-1) % 4 == 0)) {
+         error(-1, "Wrong number of args (%d) to Type 2 hvcurveto", nOps);
+       }
+       for (k = 0; k < nOps && k != nOps-5; k += 4) {
+         if (k % 8 == 0) {
+           eexecDumpNum(op[k], fp[k]);
+           eexecDumpNum(op[k+1], fp[k+1]);
+           eexecDumpNum(op[k+2], fp[k+2]);
+           eexecDumpNum(op[k+3], fp[k+3]);
+           eexecDumpOp1(31);
+         } else {
+           eexecDumpNum(op[k], fp[k]);
+           eexecDumpNum(op[k+1], fp[k+1]);
+           eexecDumpNum(op[k+2], fp[k+2]);
            eexecDumpNum(op[k+3], fp[k+3]);
            eexecDumpOp1(30);
+         }
+       }
+       if (k == nOps-5) {
+         if (k % 8 == 0) {
+           eexecDumpNum(op[k], fp[k]);
+           eexecDumpNum(0, gFalse);
+           eexecDumpNum(op[k+1], fp[k+1]);
+           eexecDumpNum(op[k+2], fp[k+2]);
+           eexecDumpNum(op[k+4], fp[k+4]);
+           eexecDumpNum(op[k+3], fp[k+3]);
+         } else {
+           eexecDumpNum(0, gFalse);
+           eexecDumpNum(op[k], fp[k]);
+           eexecDumpNum(op[k+1], fp[k+1]);
+           eexecDumpNum(op[k+2], fp[k+2]);
+           eexecDumpNum(op[k+3], fp[k+3]);
+           eexecDumpNum(op[k+4], fp[k+4]);
+         }
+         eexecDumpOp1(8);
+       }
+       break;
+      case 1:                  // hstem
+       if (first) {
+         cvtGlyphWidth(nOps & 1);
+         first = gFalse;
+       }
+       if (nOps & 1) {
+         error(-1, "Wrong number of args (%d) to Type 2 hstem", nOps);
+       }
+       d = 0;
+       dFP = gFalse;
+       for (k = 0; k < nOps; k += 2) {
+         if (op[k+1] < 0) {
+           d += op[k] + op[k+1];
+           dFP |= fp[k] | fp[k+1];
+           eexecDumpNum(d, dFP);
+           eexecDumpNum(-op[k+1], fp[k+1]);
          } else {
-           eexecDumpNum(op[k], fp[k]);
+           d += op[k];
+           dFP |= fp[k];
+           eexecDumpNum(d, dFP);
            eexecDumpNum(op[k+1], fp[k+1]);
-           eexecDumpNum(op[k+2], fp[k+2]);
-           eexecDumpNum(op[k+3], fp[k+3]);
-           eexecDumpOp1(31);
+           d += op[k+1];
+           dFP |= fp[k+1];
          }
+         eexecDumpOp1(1);
        }
-       if (k == nOps-5) {
-         if (k % 8 == 0) {
-           eexecDumpNum(0, gFalse);
-           eexecDumpNum(op[k], fp[k]);
-           eexecDumpNum(op[k+1], fp[k+1]);
-           eexecDumpNum(op[k+2], fp[k+2]);
-           eexecDumpNum(op[k+3], fp[k+3]);
-           eexecDumpNum(op[k+4], fp[k+4]);
+       nHints += nOps / 2;
+       break;
+      case 3:                  // vstem
+       if (first) {
+         cvtGlyphWidth(nOps & 1);
+         first = gFalse;
+       }
+       if (nOps & 1) {
+         error(-1, "Wrong number of args (%d) to Type 2 vstem", nOps);
+       }
+       d = 0;
+       dFP = gFalse;
+       for (k = 0; k < nOps; k += 2) {
+         if (op[k+1] < 0) {
+           d += op[k] + op[k+1];
+           dFP |= fp[k] | fp[k+1];
+           eexecDumpNum(d, dFP);
+           eexecDumpNum(-op[k+1], fp[k+1]);
          } else {
-           eexecDumpNum(op[k], fp[k]);
-           eexecDumpNum(0, gFalse);
+           d += op[k];
+           dFP |= fp[k];
+           eexecDumpNum(d, dFP);
            eexecDumpNum(op[k+1], fp[k+1]);
-           eexecDumpNum(op[k+2], fp[k+2]);
-           eexecDumpNum(op[k+4], fp[k+4]);
-           eexecDumpNum(op[k+3], fp[k+3]);
+           d += op[k+1];
+           dFP |= fp[k+1];
          }
-         eexecDumpOp1(8);
+         eexecDumpOp1(3);
+       }
+       nHints += nOps / 2;
+       break;
+      case 18:                 // hstemhm
+       // ignored
+       if (first) {
+         cvtGlyphWidth(nOps & 1);
+         first = gFalse;
+       }
+       if (nOps & 1) {
+         error(-1, "Wrong number of args (%d) to Type 2 hstemhm", nOps);
+       }
+       nHints += nOps / 2;
+       break;
+      case 23:                 // vstemhm
+       // ignored
+       if (first) {
+         cvtGlyphWidth(nOps & 1);
+         first = gFalse;
+       }
+       if (nOps & 1) {
+         error(-1, "Wrong number of args (%d) to Type 2 vstemhm", nOps);
        }
+       nHints += nOps / 2;
+       break;
+      case 10:                 // callsubr
+      case 11:                 // return
+      case 16:                 // blend
+      case 29:                 // callgsubr
+       error(-1, "Unimplemented Type 2 charstring op: %d", s[i]);
+       break;
+      default:
+       error(-1, "Illegal Type 2 charstring op: %d", s[i]);
+       break;
+      }
+      ++i;
+      nOps = 0;
+    } else if (s[i] <= 246) {
+      if (nOps < 48) {
+       fp[nOps] = gFalse;
+       op[nOps++] = (int)s[i] - 139;
+      }
+      ++i;
+    } else if (s[i] <= 250) {
+      if (nOps < 48) {
+       fp[nOps] = gFalse;
+       op[nOps++] = (((int)s[i] - 247) << 8) + (int)s[i+1] + 108;
+      }
+      i += 2;
+    } else if (s[i] <= 254) {
+      if (nOps < 48) {
+       fp[nOps] = gFalse;
+       op[nOps++] = -(((int)s[i] - 251) << 8) - (int)s[i+1] - 108;
+      }
+      i += 2;
+    } else {
+      x = (s[i+1] << 24) | (s[i+2] << 16) | (s[i+3] << 8) | s[i+4];
+      if (x & 0x80000000)
+       x |= -1 << 31;
+      if (nOps < 48) {
+       fp[nOps] = gTrue;
+       op[nOps++] = (double)x / 65536.0;
+      }
+      i += 5;
+    }
+  }
+
+  // charstring encryption
+  r2 = 4330;
+  for (i = 0; i < charBuf->getLength(); ++i) {
+    byte = charBuf->getChar(i) ^ (r2 >> 8);
+    charBuf->setChar(i, byte);
+    r2 = (byte + r2) * 52845 + 22719;
+  }
+}
+
+void Type1CFontFile::cvtGlyphWidth(GBool useOp) {
+  double w;
+  GBool wFP;
+  int i;
+
+  if (useOp) {
+    w = nominalWidthX + op[0];
+    wFP = nominalWidthXFP | fp[0];
+    for (i = 1; i < nOps; ++i) {
+      op[i-1] = op[i];
+      fp[i-1] = fp[i];
+    }
+    --nOps;
+  } else {
+    w = defaultWidthX;
+    wFP = defaultWidthXFP;
+  }
+  eexecDumpNum(0, gFalse);
+  eexecDumpNum(w, wFP);
+  eexecDumpOp1(13);
+}
+
+void Type1CFontFile::eexecDumpNum(double x, GBool fpA) {
+  Guchar buf[12];
+  int y, n;
+
+  n = 0;
+  if (fpA) {
+    if (x >= -32768 && x < 32768) {
+      y = (int)(x * 256.0);
+      buf[0] = 255;
+      buf[1] = (Guchar)(y >> 24);
+      buf[2] = (Guchar)(y >> 16);
+      buf[3] = (Guchar)(y >> 8);
+      buf[4] = (Guchar)y;
+      buf[5] = 255;
+      buf[6] = 0;
+      buf[7] = 0;
+      buf[8] = 1;
+      buf[9] = 0;
+      buf[10] = 12;
+      buf[11] = 12;
+      n = 12;
+    } else {
+      error(-1, "Type 2 fixed point constant out of range");
+    }
+  } else {
+    y = (int)x;
+    if (y >= -107 && y <= 107) {
+      buf[0] = (Guchar)(y + 139);
+      n = 1;
+    } else if (y > 107 && y <= 1131) {
+      y -= 108;
+      buf[0] = (Guchar)((y >> 8) + 247);
+      buf[1] = (Guchar)(y & 0xff);
+      n = 2;
+    } else if (y < -107 && y >= -1131) {
+      y = -y - 108;
+      buf[0] = (Guchar)((y >> 8) + 251);
+      buf[1] = (Guchar)(y & 0xff);
+      n = 2;
+    } else {
+      buf[0] = 255;
+      buf[1] = (Guchar)(y >> 24);
+      buf[2] = (Guchar)(y >> 16);
+      buf[3] = (Guchar)(y >> 8);
+      buf[4] = (Guchar)y;
+      n = 5;
+    }
+  }
+  charBuf->append((char *)buf, n);
+}
+
+void Type1CFontFile::eexecDumpOp1(int opA) {
+  charBuf->append((char)opA);
+}
+
+void Type1CFontFile::eexecDumpOp2(int opA) {
+  charBuf->append((char)12);
+  charBuf->append((char)opA);
+}
+
+void Type1CFontFile::eexecWriteCharstring(Guchar *s, int n) {
+  Guchar x;
+  int i;
+
+  // eexec encryption
+  for (i = 0; i < n; ++i) {
+    x = s[i] ^ (r1 >> 8);
+    r1 = (x + r1) * 52845 + 22719;
+    fputc(hexChars[x >> 4], out);
+    fputc(hexChars[x & 0x0f], out);
+    line += 2;
+    if (line == 64) {
+      fputc('\n', out);
+      line = 0;
+    }
+  }
+}
+
+void Type1CFontFile::getDeltaInt(char *buf, char *key, double *opA,
+                                int n) {
+  int x, i;
+
+  sprintf(buf, "/%s [", key);
+  buf += strlen(buf);
+  x = 0;
+  for (i = 0; i < n; ++i) {
+    x += (int)opA[i];
+    sprintf(buf, "%s%d", i > 0 ? " " : "", x);
+    buf += strlen(buf);
+  }
+  sprintf(buf, "] def\n");
+}
+
+void Type1CFontFile::getDeltaReal(char *buf, char *key, double *opA,
+                                 int n) {
+  double x;
+  int i;
+
+  sprintf(buf, "/%s [", key);
+  buf += strlen(buf);
+  x = 0;
+  for (i = 0; i < n; ++i) {
+    x += opA[i];
+    sprintf(buf, "%s%g", i > 0 ? " " : "", x);
+    buf += strlen(buf);
+  }
+  sprintf(buf, "] def\n");
+}
+
+int Type1CFontFile::getIndexLen(Guchar *indexPtr) {
+  return (int)getWord(indexPtr, 2);
+}
+
+Guchar *Type1CFontFile::getIndexValPtr(Guchar *indexPtr, int i) {
+  int n, offSize;
+  Guchar *idxStartPtr;
+
+  n = (int)getWord(indexPtr, 2);
+  offSize = indexPtr[2];
+  idxStartPtr = indexPtr + 3 + (n + 1) * offSize - 1;
+  return idxStartPtr + getWord(indexPtr + 3 + i * offSize, offSize);
+}
+
+Guchar *Type1CFontFile::getIndexEnd(Guchar *indexPtr) {
+  int n, offSize;
+  Guchar *idxStartPtr;
+
+  n = (int)getWord(indexPtr, 2);
+  offSize = indexPtr[2];
+  idxStartPtr = indexPtr + 3 + (n + 1) * offSize - 1;
+  return idxStartPtr + getWord(indexPtr + 3 + n * offSize, offSize);
+}
+
+Guint Type1CFontFile::getWord(Guchar *ptr, int size) {
+  Guint x;
+  int i;
+
+  x = 0;
+  for (i = 0; i < size; ++i) {
+    x = (x << 8) + *ptr++;
+  }
+  return x;
+}
+
+double Type1CFontFile::getNum(Guchar **ptr, GBool *isFP) {
+  static char nybChars[16] = "0123456789.ee -";
+  int b0, b, nyb0, nyb1;
+  double x;
+  char buf[65];
+  int i;
+
+  x = 0;
+  *isFP = gFalse;
+  b0 = (*ptr)[0];
+  if (b0 < 28) {
+    x = 0;
+  } else if (b0 == 28) {
+    x = ((*ptr)[1] << 8) + (*ptr)[2];
+    *ptr += 3;
+  } else if (b0 == 29) {
+    x = ((*ptr)[1] << 24) + ((*ptr)[2] << 16) + ((*ptr)[3] << 8) + (*ptr)[4];
+    *ptr += 5;
+  } else if (b0 == 30) {
+    *ptr += 1;
+    i = 0;
+    do {
+      b = *(*ptr)++;
+      nyb0 = b >> 4;
+      nyb1 = b & 0x0f;
+      if (nyb0 == 0xf) {
        break;
-      case 31:                 // hvcurveto
-       if (nOps < 4 || !(nOps % 4 == 0 || (nOps-1) % 4 == 0))
-         error(-1, "Wrong number of args (%d) to Type 2 hvcurveto", nOps);
-       for (k = 0; k < nOps && k != nOps-5; k += 4) {
-         if (k % 8 == 0) {
-           eexecDumpNum(op[k], fp[k]);
-           eexecDumpNum(op[k+1], fp[k+1]);
-           eexecDumpNum(op[k+2], fp[k+2]);
-           eexecDumpNum(op[k+3], fp[k+3]);
-           eexecDumpOp1(31);
-         } else {
-           eexecDumpNum(op[k], fp[k]);
-           eexecDumpNum(op[k+1], fp[k+1]);
-           eexecDumpNum(op[k+2], fp[k+2]);
-           eexecDumpNum(op[k+3], fp[k+3]);
-           eexecDumpOp1(30);
-         }
-       }
-       if (k == nOps-5) {
-         if (k % 8 == 0) {
-           eexecDumpNum(op[k], fp[k]);
-           eexecDumpNum(0, gFalse);
-           eexecDumpNum(op[k+1], fp[k+1]);
-           eexecDumpNum(op[k+2], fp[k+2]);
-           eexecDumpNum(op[k+4], fp[k+4]);
-           eexecDumpNum(op[k+3], fp[k+3]);
-         } else {
-           eexecDumpNum(0, gFalse);
-           eexecDumpNum(op[k], fp[k]);
-           eexecDumpNum(op[k+1], fp[k+1]);
-           eexecDumpNum(op[k+2], fp[k+2]);
-           eexecDumpNum(op[k+3], fp[k+3]);
-           eexecDumpNum(op[k+4], fp[k+4]);
-         }
-         eexecDumpOp1(8);
-       }
+      }
+      buf[i++] = nybChars[nyb0];
+      if (i == 64) {
        break;
-      case 1:                  // hstem
-       if (first) {
-         cvtGlyphWidth(nOps & 1);
-         first = gFalse;
-       }
-       if (nOps & 1) {
-         error(-1, "Wrong number of args (%d) to Type 2 hstem", nOps);
+      }
+      if (nyb0 == 0xc) {
+       buf[i++] = '-';
+      }
+      if (i == 64) {
+       break;
+      }
+      if (nyb1 == 0xf) {
+       break;
+      }
+      buf[i++] = nybChars[nyb1];
+      if (i == 64) {
+       break;
+      }
+      if (nyb1 == 0xc) {
+       buf[i++] = '-';
+      }
+    } while (i < 64);
+    buf[i] = '\0';
+    x = atof(buf);
+    *isFP = gTrue;
+  } else if (b0 == 31) {
+    x = 0;
+  } else if (b0 < 247) {
+    x = b0 - 139;
+    *ptr += 1;
+  } else if (b0 < 251) {
+    x = ((b0 - 247) << 8) + (*ptr)[1] + 108;
+    *ptr += 2;
+  } else {
+    x = -((b0 - 251) << 8) - (*ptr)[1] - 108;
+    *ptr += 2;
+  }
+  return x;
+}
+
+char *Type1CFontFile::getString(int sid, char *buf) {
+  Guchar *idxPtr0, *idxPtr1;
+  int n;
+
+  if (sid < 391) {
+    strcpy(buf, type1CStdStrings[sid]);
+  } else {
+    sid -= 391;
+    idxPtr0 = getIndexValPtr(stringIdxPtr, sid);
+    idxPtr1 = getIndexValPtr(stringIdxPtr, sid + 1);
+    if ((n = idxPtr1 - idxPtr0) > 255) {
+      n = 255;
+    }
+    strncpy(buf, (char *)idxPtr0, n);
+    buf[n] = '\0';
+  }
+  return buf;
+}
+
+//------------------------------------------------------------------------
+// TrueTypeFontFile
+//------------------------------------------------------------------------
+
+//
+// Terminology
+// -----------
+//
+// character code = number used as an element of a text string
+//
+// character name = glyph name = name for a particular glyph within a
+//                  font
+//
+// glyph index = position (within some internal table in the font)
+//               where the instructions to draw a particular glyph are
+//               stored
+//
+// Type 1 fonts
+// ------------
+//
+// Type 1 fonts contain:
+//
+// Encoding: array of glyph names, maps char codes to glyph names
+//
+//           Encoding[charCode] = charName
+//
+// CharStrings: dictionary of instructions, keyed by character names,
+//              maps character name to glyph data
+//
+//              CharStrings[charName] = glyphData
+//
+// TrueType fonts
+// --------------
+//
+// TrueType fonts contain:
+//
+// 'cmap' table: mapping from character code to glyph index; there may
+//               be multiple cmaps in a TrueType font
+//
+//               cmap[charCode] = glyphIdx
+//
+// 'post' table: mapping from glyph index to glyph name
+//
+//               post[glyphIdx] = glyphName
+//
+// Type 42 fonts
+// -------------
+//
+// Type 42 fonts contain:
+//
+// Encoding: array of glyph names, maps char codes to glyph names
+//
+//           Encoding[charCode] = charName
+//
+// CharStrings: dictionary of glyph indexes, keyed by character names,
+//              maps character name to glyph index
+//
+//              CharStrings[charName] = glyphIdx
+//
+
+struct TTFontTableHdr {
+  char tag[4];
+  Guint checksum;
+  Guint offset;
+  Guint length;
+};
+
+struct T42Table {
+  char *tag;                   // 4-byte tag
+  GBool required;              // required by the TrueType spec?
+};
+
+// TrueType tables to be embedded in Type 42 fonts.
+// NB: the table names must be in alphabetical order here.
+#define nT42Tables 11
+static T42Table t42Tables[nT42Tables] = {
+  { "cvt ", gTrue  },
+  { "fpgm", gTrue  },
+  { "glyf", gTrue  },
+  { "head", gTrue  },
+  { "hhea", gTrue  },
+  { "hmtx", gTrue  },
+  { "loca", gTrue  },
+  { "maxp", gTrue  },
+  { "prep", gTrue  },
+  { "vhea", gFalse },
+  { "vmtx", gFalse }
+};
+#define t42HeadTable 3
+#define t42LocaTable 6
+#define t42GlyfTable 2
+
+// Glyph names in some arbitrary standard that Apple uses for their
+// TrueType fonts.
+static char *macGlyphNames[258] = {
+  ".notdef",
+  "null",
+  "CR",
+  "space",
+  "exclam",
+  "quotedbl",
+  "numbersign",
+  "dollar",
+  "percent",
+  "ampersand",
+  "quotesingle",
+  "parenleft",
+  "parenright",
+  "asterisk",
+  "plus",
+  "comma",
+  "hyphen",
+  "period",
+  "slash",
+  "zero",
+  "one",
+  "two",
+  "three",
+  "four",
+  "five",
+  "six",
+  "seven",
+  "eight",
+  "nine",
+  "colon",
+  "semicolon",
+  "less",
+  "equal",
+  "greater",
+  "question",
+  "at",
+  "A",
+  "B",
+  "C",
+  "D",
+  "E",
+  "F",
+  "G",
+  "H",
+  "I",
+  "J",
+  "K",
+  "L",
+  "M",
+  "N",
+  "O",
+  "P",
+  "Q",
+  "R",
+  "S",
+  "T",
+  "U",
+  "V",
+  "W",
+  "X",
+  "Y",
+  "Z",
+  "bracketleft",
+  "backslash",
+  "bracketright",
+  "asciicircum",
+  "underscore",
+  "grave",
+  "a",
+  "b",
+  "c",
+  "d",
+  "e",
+  "f",
+  "g",
+  "h",
+  "i",
+  "j",
+  "k",
+  "l",
+  "m",
+  "n",
+  "o",
+  "p",
+  "q",
+  "r",
+  "s",
+  "t",
+  "u",
+  "v",
+  "w",
+  "x",
+  "y",
+  "z",
+  "braceleft",
+  "bar",
+  "braceright",
+  "asciitilde",
+  "Adieresis",
+  "Aring",
+  "Ccedilla",
+  "Eacute",
+  "Ntilde",
+  "Odieresis",
+  "Udieresis",
+  "aacute",
+  "agrave",
+  "acircumflex",
+  "adieresis",
+  "atilde",
+  "aring",
+  "ccedilla",
+  "eacute",
+  "egrave",
+  "ecircumflex",
+  "edieresis",
+  "iacute",
+  "igrave",
+  "icircumflex",
+  "idieresis",
+  "ntilde",
+  "oacute",
+  "ograve",
+  "ocircumflex",
+  "odieresis",
+  "otilde",
+  "uacute",
+  "ugrave",
+  "ucircumflex",
+  "udieresis",
+  "dagger",
+  "degree",
+  "cent",
+  "sterling",
+  "section",
+  "bullet",
+  "paragraph",
+  "germandbls",
+  "registered",
+  "copyright",
+  "trademark",
+  "acute",
+  "dieresis",
+  "notequal",
+  "AE",
+  "Oslash",
+  "infinity",
+  "plusminus",
+  "lessequal",
+  "greaterequal",
+  "yen",
+  "mu1",
+  "partialdiff",
+  "summation",
+  "product",
+  "pi",
+  "integral",
+  "ordfeminine",
+  "ordmasculine",
+  "Ohm",
+  "ae",
+  "oslash",
+  "questiondown",
+  "exclamdown",
+  "logicalnot",
+  "radical",
+  "florin",
+  "approxequal",
+  "increment",
+  "guillemotleft",
+  "guillemotright",
+  "ellipsis",
+  "nbspace",
+  "Agrave",
+  "Atilde",
+  "Otilde",
+  "OE",
+  "oe",
+  "endash",
+  "emdash",
+  "quotedblleft",
+  "quotedblright",
+  "quoteleft",
+  "quoteright",
+  "divide",
+  "lozenge",
+  "ydieresis",
+  "Ydieresis",
+  "fraction",
+  "currency",
+  "guilsinglleft",
+  "guilsinglright",
+  "fi",
+  "fl",
+  "daggerdbl",
+  "periodcentered",
+  "quotesinglbase",
+  "quotedblbase",
+  "perthousand",
+  "Acircumflex",
+  "Ecircumflex",
+  "Aacute",
+  "Edieresis",
+  "Egrave",
+  "Iacute",
+  "Icircumflex",
+  "Idieresis",
+  "Igrave",
+  "Oacute",
+  "Ocircumflex",
+  "applelogo",
+  "Ograve",
+  "Uacute",
+  "Ucircumflex",
+  "Ugrave",
+  "dotlessi",
+  "circumflex",
+  "tilde",
+  "overscore",
+  "breve",
+  "dotaccent",
+  "ring",
+  "cedilla",
+  "hungarumlaut",
+  "ogonek",
+  "caron",
+  "Lslash",
+  "lslash",
+  "Scaron",
+  "scaron",
+  "Zcaron",
+  "zcaron",
+  "brokenbar",
+  "Eth",
+  "eth",
+  "Yacute",
+  "yacute",
+  "Thorn",
+  "thorn",
+  "minus",
+  "multiply",
+  "onesuperior",
+  "twosuperior",
+  "threesuperior",
+  "onehalf",
+  "onequarter",
+  "threequarters",
+  "franc",
+  "Gbreve",
+  "gbreve",
+  "Idot",
+  "Scedilla",
+  "scedilla",
+  "Cacute",
+  "cacute",
+  "Ccaron",
+  "ccaron",
+  "dmacron"
+};
+
+enum T42FontIndexMode {
+  t42FontModeUnicode,
+  t42FontModeCharCode,
+  t42FontModeCharCodeOffset,
+  t42FontModeMacRoman
+};
+
+TrueTypeFontFile::TrueTypeFontFile(char *fileA, int lenA) {
+  int pos, i;
+
+  file = fileA;
+  len = lenA;
+
+  encoding = NULL;
+
+  // read table directory
+  nTables = getUShort(4);
+  tableHdrs = (TTFontTableHdr *)gmalloc(nTables * sizeof(TTFontTableHdr));
+  pos = 12;
+  for (i = 0; i < nTables; ++i) {
+    tableHdrs[i].tag[0] = getByte(pos+0);
+    tableHdrs[i].tag[1] = getByte(pos+1);
+    tableHdrs[i].tag[2] = getByte(pos+2);
+    tableHdrs[i].tag[3] = getByte(pos+3);
+    tableHdrs[i].checksum = getULong(pos+4);
+    tableHdrs[i].offset = getULong(pos+8);
+    tableHdrs[i].length = getULong(pos+12);
+    pos += 16;
+  }
+
+  // check for tables that are required by both the TrueType spec
+  // and the Type 42 spec
+  if (seekTable("head") < 0 ||
+      seekTable("hhea") < 0 ||
+      seekTable("loca") < 0 ||
+      seekTable("maxp") < 0 ||
+      seekTable("glyf") < 0 ||
+      seekTable("hmtx") < 0) {
+    error(-1, "TrueType font file is missing a required table");
+    return;
+  }
+
+  // read the 'head' table
+  pos = seekTable("head");
+  bbox[0] = getShort(pos + 36);
+  bbox[1] = getShort(pos + 38);
+  bbox[2] = getShort(pos + 40);
+  bbox[3] = getShort(pos + 42);
+  locaFmt = getShort(pos + 50);
+
+  // read the 'maxp' table
+  pos = seekTable("maxp");
+  nGlyphs = getUShort(pos + 4);
+}
+
+TrueTypeFontFile::~TrueTypeFontFile() {
+  int i;
+
+  if (encoding) {
+    for (i = 0; i < 256; ++i) {
+      gfree(encoding[i]);
+    }
+    gfree(encoding);
+  }
+  gfree(tableHdrs);
+}
+
+char *TrueTypeFontFile::getName() {
+  return NULL;
+}
+
+char **TrueTypeFontFile::getEncoding() {
+  int cmap[256];
+  int nCmaps, cmapPlatform, cmapEncoding, cmapFmt;
+  int cmapLen, cmapOffset, cmapFirst;
+  int segCnt, segStart, segEnd, segDelta, segOffset;
+  int pos, i, j, k;
+  Guint fmt;
+  GString *s;
+  int stringIdx, stringPos, n;
+
+  if (encoding) {
+    return encoding;
+  }
+
+  //----- construct the (char code) -> (glyph idx) mapping
+
+  // map everything to the missing glyph
+  for (i = 0; i < 256; ++i) {
+    cmap[i] = 0;
+  }
+
+  // look for the 'cmap' table
+  if ((pos = seekTable("cmap")) >= 0) {
+    nCmaps = getUShort(pos+2);
+
+    // if the font has a Windows-symbol cmap, use it;
+    // otherwise, use the first cmap in the table
+    for (i = 0; i < nCmaps; ++i) {
+      cmapPlatform = getUShort(pos + 4 + 8*i);
+      cmapEncoding = getUShort(pos + 4 + 8*i + 2);
+      if (cmapPlatform == 3 && cmapEncoding == 0) {
+       break;
+      }
+    }
+    if (i >= nCmaps) {
+      i = 0;
+      cmapPlatform = getUShort(pos + 4);
+      cmapEncoding = getUShort(pos + 4 + 2);
+    }
+    pos += getULong(pos + 4 + 8*i + 4);
+
+    // read the cmap
+    cmapFmt = getUShort(pos);
+    switch (cmapFmt) {
+    case 0: // byte encoding table (Apple standard)
+      cmapLen = getUShort(pos + 2);
+      for (i = 0; i < cmapLen && i < 256; ++i) {
+       cmap[i] = getByte(pos + 6 + i);
+      }
+      break;
+    case 4: // segment mapping to delta values (Microsoft standard)
+      if (cmapPlatform == 3 && cmapEncoding == 0) {
+       // Windows-symbol uses char codes 0xf000 - 0xf0ff
+       cmapOffset = 0xf000;
+      } else {
+       cmapOffset = 0;
+      }
+      segCnt = getUShort(pos + 6) / 2;
+      for (i = 0; i < segCnt; ++i) {
+       segEnd = getUShort(pos + 14 + 2*i);
+       segStart = getUShort(pos + 16 + 2*segCnt + 2*i);
+       segDelta = getUShort(pos + 16 + 4*segCnt + 2*i);
+       segOffset = getUShort(pos + 16 + 6*segCnt + 2*i);
+       if (segStart - cmapOffset <= 0xff &&
+           segEnd - cmapOffset >= 0) {
+         for (j = (segStart - cmapOffset >= 0) ? segStart : cmapOffset;
+              j <= segEnd && j - cmapOffset <= 0xff;
+              ++j) {
+           if (segOffset == 0) {
+             k = (j + segDelta) & 0xffff;
+           } else {
+             k = getUShort(pos + 16 + 6*segCnt + 2*i +
+                           segOffset + 2 * (j - segStart));
+             if (k != 0) {
+               k = (k + segDelta) & 0xffff;
+             }
+           }
+           cmap[j - cmapOffset] = k;
+         }
        }
-       d = 0;
-       dFP = gFalse;
-       for (k = 0; k < nOps; k += 2) {
-         if (op[k+1] < 0) {
-           d += op[k] + op[k+1];
-           dFP |= fp[k] | fp[k+1];
-           eexecDumpNum(d, dFP);
-           eexecDumpNum(-op[k+1], fp[k+1]);
+      }
+      break;
+    case 6: // trimmed table mapping
+      cmapFirst = getUShort(pos + 6);
+      cmapLen = getUShort(pos + 8);
+      for (i = cmapFirst; i < 256 && i < cmapFirst + cmapLen; ++i) {
+       cmap[i] = getUShort(pos + 10 + 2*i);
+      }
+      break;
+    default:
+      error(-1, "Unimplemented cmap format (%d) in TrueType font file",
+           cmapFmt);
+      break;
+    }
+  }
+
+  //----- construct the (glyph idx) -> (glyph name) mapping
+  //----- and compute the (char code) -> (glyph name) mapping
+
+  encoding = (char **)gmalloc(256 * sizeof(char *));
+  for (i = 0; i < 256; ++i) {
+    encoding[i] = NULL;
+  }
+
+  if ((pos = seekTable("post")) >= 0) {
+    fmt = getULong(pos);
+
+    // Apple font
+    if (fmt == 0x00010000) {
+      for (i = 0; i < 256; ++i) {
+       j = (cmap[i] < 258) ? cmap[i] : 0;
+       encoding[i] = copyString(macGlyphNames[j]);
+      }
+
+    // Microsoft font
+    } else if (fmt == 0x00020000) {
+      stringIdx = 0;
+      stringPos = pos + 34 + 2*nGlyphs;
+      for (i = 0; i < 256; ++i) {
+       if (cmap[i] < nGlyphs) {
+         j = getUShort(pos + 34 + 2 * cmap[i]);
+         if (j < 258) {
+           encoding[i] = copyString(macGlyphNames[j]);
          } else {
-           d += op[k];
-           dFP |= fp[k];
-           eexecDumpNum(d, dFP);
-           eexecDumpNum(op[k+1], fp[k+1]);
-           d += op[k+1];
-           dFP |= fp[k+1];
+           j -= 258;
+           if (j != stringIdx) {
+             for (stringIdx = 0, stringPos = pos + 34 + 2*nGlyphs;
+                  stringIdx < j;
+                  ++stringIdx, stringPos += 1 + getByte(stringPos)) ;
+           }
+           n = getByte(stringPos);
+           s = new GString(file + stringPos + 1, n);
+           encoding[i] = copyString(s->getCString());
+           delete s;
+           ++stringIdx;
+           stringPos += 1 + n;
          }
-         eexecDumpOp1(1);
-       }
-       nHints += nOps / 2;
-       break;
-      case 3:                  // vstem
-       if (first) {
-         cvtGlyphWidth(nOps & 1);
-         first = gFalse;
+       } else {
+         encoding[i] = copyString(macGlyphNames[0]);
        }
-       if (nOps & 1) {
-         error(-1, "Wrong number of args (%d) to Type 2 vstem", nOps);
+      }
+
+    // Apple subset
+    } else if (fmt == 0x000280000) {
+      for (i = 0; i < 256; ++i) {
+       if (cmap[i] < nGlyphs) {
+         j = i + getChar(pos + 32 + cmap[i]);
+       } else {
+         j = 0;
        }
-       d = 0;
-       dFP = gFalse;
-       for (k = 0; k < nOps; k += 2) {
-         if (op[k+1] < 0) {
-           d += op[k] + op[k+1];
-           dFP |= fp[k] | fp[k+1];
-           eexecDumpNum(d, dFP);
-           eexecDumpNum(-op[k+1], fp[k+1]);
-         } else {
-           d += op[k];
-           dFP |= fp[k];
-           eexecDumpNum(d, dFP);
-           eexecDumpNum(op[k+1], fp[k+1]);
-           d += op[k+1];
-           dFP |= fp[k+1];
+       encoding[i] = copyString(macGlyphNames[j]);
+      }
+
+    // Ugh, just assume the Apple glyph set
+    } else {
+      for (i = 0; i < 256; ++i) {
+       j = (cmap[i] < 258) ? cmap[i] : 0;
+       encoding[i] = copyString(macGlyphNames[j]);
+      }
+    }
+
+  // no "post" table: assume the Apple glyph set
+  } else {
+    for (i = 0; i < 256; ++i) {
+      j = (cmap[i] < 258) ? cmap[i] : 0;
+      encoding[i] = copyString(macGlyphNames[j]);
+    }
+  }
+
+  return encoding;
+}
+
+void TrueTypeFontFile::convertToType42(char *name, char **encodingA,
+                                      CharCodeToUnicode *toUnicode,
+                                      GBool pdfFontHasEncoding, FILE *out) {
+  // write the header
+  fprintf(out, "%%!PS-TrueTypeFont-%g\n", getFixed(0));
+
+  // begin the font dictionary
+  fprintf(out, "10 dict begin\n");
+  fprintf(out, "/FontName /%s def\n", name);
+  fprintf(out, "/FontType 42 def\n");
+  fprintf(out, "/FontMatrix [1 0 0 1 0 0] def\n");
+  fprintf(out, "/FontBBox [%d %d %d %d] def\n",
+         bbox[0], bbox[1], bbox[2], bbox[3]);
+  fprintf(out, "/PaintType 0 def\n");
+
+  // write the guts of the dictionary
+  cvtEncoding(encodingA, out);
+  cvtCharStrings(encodingA, toUnicode, pdfFontHasEncoding, out);
+  cvtSfnts(out, NULL);
+
+  // end the dictionary and define the font
+  fprintf(out, "FontName currentdict end definefont pop\n");
+}
+
+void TrueTypeFontFile::convertToCIDType2(char *name, Gushort *cidMap,
+                                        int nCIDs, FILE *out) {
+  Gushort cid;
+  int i, j, k;
+
+  // write the header
+  fprintf(out, "%%!PS-TrueTypeFont-%g\n", getFixed(0));
+
+  // begin the font dictionary
+  fprintf(out, "20 dict begin\n");
+  fprintf(out, "/CIDFontName /%s def\n", name);
+  fprintf(out, "/CIDFontType 2 def\n");
+  fprintf(out, "/FontType 42 def\n");
+  fprintf(out, "/CIDSystemInfo 3 dict dup begin\n");
+  fprintf(out, "  /Registry (Adobe) def\n");
+  fprintf(out, "  /Ordering (Identity) def\n");
+  fprintf(out, "  /Supplement 0 def\n");
+  fprintf(out, "  end def\n");
+  fprintf(out, "/GDBytes 2 def\n");
+  if (cidMap) {
+    fprintf(out, "/CIDCount %d def\n", nCIDs);
+    if (nCIDs > 32767) {
+      fprintf(out, "/CIDMap [");
+      for (i = 0; i < nCIDs; i += 32768 - 16) {
+       fprintf(out, "<\n");
+       for (j = 0; j < 32768 - 16 && i+j < nCIDs; j += 16) {
+         fprintf(out, "  ");
+         for (k = 0; k < 16 && i+j+k < nCIDs; ++k) {
+           cid = cidMap[i+j+k];
+           fprintf(out, "%02x%02x", (cid >> 8) & 0xff, cid & 0xff);
          }
-         eexecDumpOp1(3);
-       }
-       nHints += nOps / 2;
-       break;
-      case 18:                 // hstemhm
-       //~ ignored
-       if (first) {
-         cvtGlyphWidth(nOps & 1);
-         first = gFalse;
+         fprintf(out, "\n");
        }
-       if (nOps & 1) {
-         error(-1, "Wrong number of args (%d) to Type 2 hstemhm", nOps);
+       fprintf(out, "  >");
+      }
+      fprintf(out, "\n");
+      fprintf(out, "] def\n");
+    } else {
+      fprintf(out, "/CIDMap <\n");
+      for (i = 0; i < nCIDs; i += 16) {
+       fprintf(out, "  ");
+       for (j = 0; j < 16 && i+j < nCIDs; ++j) {
+         cid = cidMap[i+j];
+         fprintf(out, "%02x%02x", (cid >> 8) & 0xff, cid & 0xff);
        }
-       nHints += nOps / 2;
+       fprintf(out, "\n");
+      }
+      fprintf(out, "> def\n");
+    }
+  } else {
+    // direct mapping - just fill the string(s) with s[i]=i
+    fprintf(out, "/CIDCount %d def\n", nGlyphs);
+    if (nGlyphs > 32767) {
+      fprintf(out, "/CIDMap [\n");
+      for (i = 0; i < nGlyphs; i += 32767) {
+       j = nGlyphs - i < 32767 ? nGlyphs - i : 32767;
+       fprintf(out, "  %d string 0 1 %d {\n", 2 * j, j - 1);
+       fprintf(out, "    2 copy dup 2 mul exch %d add -8 bitshift put\n", i);
+       fprintf(out, "    1 index exch dup 2 mul 1 add exch %d add"
+               " 255 and put\n", i);
+       fprintf(out, "  } for\n");
+      }
+      fprintf(out, "] def\n");
+    } else {
+      fprintf(out, "/CIDMap %d string\n", 2 * nGlyphs);
+      fprintf(out, "  0 1 %d {\n", nGlyphs - 1);
+      fprintf(out, "    2 copy dup 2 mul exch -8 bitshift put\n");
+      fprintf(out, "    1 index exch dup 2 mul 1 add exch 255 and put\n");
+      fprintf(out, "  } for\n");
+      fprintf(out, "def\n");
+    }
+  }
+  fprintf(out, "/FontMatrix [1 0 0 1 0 0] def\n");
+  fprintf(out, "/FontBBox [%d %d %d %d] def\n",
+         bbox[0], bbox[1], bbox[2], bbox[3]);
+  fprintf(out, "/PaintType 0 def\n");
+  fprintf(out, "/Encoding [] readonly def\n");
+  fprintf(out, "/CharStrings 1 dict dup begin\n");
+  fprintf(out, "  /.notdef 0 def\n");
+  fprintf(out, "  end readonly def\n");
+
+  // write the guts of the dictionary
+  cvtSfnts(out, NULL);
+
+  // end the dictionary and define the font
+  fprintf(out, "CIDFontName currentdict end /CIDFont defineresource pop\n");
+}
+
+void TrueTypeFontFile::convertToType0(char *name, Gushort *cidMap,
+                                     int nCIDs, FILE *out) {
+  GString *sfntsName;
+  int n, i, j;
+
+  // write the Type 42 sfnts array
+  sfntsName = (new GString(name))->append("_sfnts");
+  cvtSfnts(out, sfntsName);
+  delete sfntsName;
+
+  // write the descendant Type 42 fonts
+  n = cidMap ? nCIDs : nGlyphs;
+  for (i = 0; i < n; i += 256) {
+    fprintf(out, "10 dict begin\n");
+    fprintf(out, "/FontName /%s_%02x def\n", name, i >> 8);
+    fprintf(out, "/FontType 42 def\n");
+    fprintf(out, "/FontMatrix [1 0 0 1 0 0] def\n");
+    fprintf(out, "/FontBBox [%d %d %d %d] def\n",
+           bbox[0], bbox[1], bbox[2], bbox[3]);
+    fprintf(out, "/PaintType 0 def\n");
+    fprintf(out, "/sfnts %s_sfnts def\n", name);
+    fprintf(out, "/Encoding 256 array\n");
+    for (j = 0; j < 256 && i+j < n; ++j) {
+      fprintf(out, "dup %d /c%02x put\n", j, j);
+    }
+    fprintf(out, "readonly def\n");
+    fprintf(out, "/CharStrings 257 dict dup begin\n");
+    fprintf(out, "/.notdef 0 def\n");
+    for (j = 0; j < 256 && i+j < n; ++j) {
+      fprintf(out, "/c%02x %d def\n", j, cidMap ? cidMap[i+j] : i+j);
+    }
+    fprintf(out, "end readonly def\n");
+    fprintf(out, "FontName currentdict end definefont pop\n");
+  }
+
+  // write the Type 0 parent font
+  fprintf(out, "16 dict begin\n");
+  fprintf(out, "/FontName /%s def\n", name);
+  fprintf(out, "/FontType 0 def\n");
+  fprintf(out, "/FontMatrix [1 0 0 1 0 0] def\n");
+  fprintf(out, "/FMapType 2 def\n");
+  fprintf(out, "/Encoding [\n");
+  for (i = 0; i < n; i += 256) {
+    fprintf(out, "%d\n", i >> 8);
+  }
+  fprintf(out, "] def\n");
+  fprintf(out, "/FDepVector [\n");
+  for (i = 0; i < n; i += 256) {
+    fprintf(out, "/%s_%02x findfont\n", name, i >> 8);
+  }
+  fprintf(out, "] def\n");
+  fprintf(out, "FontName currentdict end definefont pop\n");
+}
+
+int TrueTypeFontFile::getByte(int pos) {
+  if (pos < 0 || pos >= len) {
+    return 0;
+  }
+  return file[pos] & 0xff;
+}
+
+int TrueTypeFontFile::getChar(int pos) {
+  int x;
+
+  if (pos < 0 || pos >= len) {
+    return 0;
+  }
+  x = file[pos] & 0xff;
+  if (x & 0x80)
+    x |= 0xffffff00;
+  return x;
+}
+
+int TrueTypeFontFile::getUShort(int pos) {
+  int x;
+
+  if (pos < 0 || pos+1 >= len) {
+    return 0;
+  }
+  x = file[pos] & 0xff;
+  x = (x << 8) + (file[pos+1] & 0xff);
+  return x;
+}
+
+int TrueTypeFontFile::getShort(int pos) {
+  int x;
+
+  if (pos < 0 || pos+1 >= len) {
+    return 0;
+  }
+  x = file[pos] & 0xff;
+  x = (x << 8) + (file[pos+1] & 0xff);
+  if (x & 0x8000)
+    x |= 0xffff0000;
+  return x;
+}
+
+Guint TrueTypeFontFile::getULong(int pos) {
+  int x;
+
+  if (pos < 0 || pos+3 >= len) {
+    return 0;
+  }
+  x = file[pos] & 0xff;
+  x = (x << 8) + (file[pos+1] & 0xff);
+  x = (x << 8) + (file[pos+2] & 0xff);
+  x = (x << 8) + (file[pos+3] & 0xff);
+  return x;
+}
+
+double TrueTypeFontFile::getFixed(int pos) {
+  int x, y;
+
+  x = getShort(pos);
+  y = getUShort(pos+2);
+  return (double)x + (double)y / 65536;
+}
+
+int TrueTypeFontFile::seekTable(char *tag) {
+  int i;
+
+  for (i = 0; i < nTables; ++i) {
+    if (!strncmp(tableHdrs[i].tag, tag, 4)) {
+      return tableHdrs[i].offset;
+    }
+  }
+  return -1;
+}
+
+int TrueTypeFontFile::seekTableIdx(char *tag) {
+  int i;
+
+  for (i = 0; i < nTables; ++i) {
+    if (!strncmp(tableHdrs[i].tag, tag, 4)) {
+      return i;
+    }
+  }
+  return -1;
+}
+
+void TrueTypeFontFile::cvtEncoding(char **encodingA, FILE *out) {
+  char *name;
+  int i;
+
+  fprintf(out, "/Encoding 256 array\n");
+  for (i = 0; i < 256; ++i) {
+    if (!(name = encodingA[i])) {
+      name = ".notdef";
+    }
+    fprintf(out, "dup %d /%s put\n", i, name);
+  }
+  fprintf(out, "readonly def\n");
+}
+
+void TrueTypeFontFile::cvtCharStrings(char **encodingA,
+                                     CharCodeToUnicode *toUnicode,
+                                     GBool pdfFontHasEncoding, FILE *out) {
+  int unicodeCmap, macRomanCmap, msSymbolCmap;
+  int nCmaps, cmapPlatform, cmapEncoding, cmapFmt, cmapOffset;
+  T42FontIndexMode mode;
+  char *name;
+  Unicode u;
+  int pos, i, j, k;
+
+  // always define '.notdef'
+  fprintf(out, "/CharStrings 256 dict dup begin\n");
+  fprintf(out, "/.notdef 0 def\n");
+
+  // if there's no 'cmap' table, punt
+  if ((pos = seekTable("cmap")) < 0) {
+    goto err;
+  }
+
+  // To match up with the Adobe-defined behaviour, we choose a cmap
+  // like this:
+  // 1. If the PDF font has an encoding:
+  //    1a. If the TrueType font has a Microsoft Unicode cmap, use it,
+  //        and use the Unicode indexes, not the char codes.
+  //    1b. If the TrueType font has a Macintosh Roman cmap, use it,
+  //        and reverse map the char names through MacRomanEncoding to
+  //        get char codes.
+  // 2. If the PDF font does not have an encoding:
+  //    2a. If the TrueType font has a Macintosh Roman cmap, use it,
+  //        and use char codes directly.
+  //    2b. If the TrueType font has a Microsoft Symbol cmap, use it,
+  //        and use (0xf000 + char code).
+  // 3. If none of these rules apply, use the first cmap and hope for
+  //    the best (this shouldn't happen).
+  nCmaps = getUShort(pos+2);
+  unicodeCmap = macRomanCmap = msSymbolCmap = -1;
+  cmapOffset = 0;
+  for (i = 0; i < nCmaps; ++i) {
+    cmapPlatform = getUShort(pos + 4 + 8*i);
+    cmapEncoding = getUShort(pos + 4 + 8*i + 2);
+    if (cmapPlatform == 3 && cmapEncoding == 1) {
+      unicodeCmap = i;
+    } else if (cmapPlatform == 1 && cmapEncoding == 0) {
+      macRomanCmap = i;
+    } else if (cmapPlatform == 3 && cmapEncoding == 0) {
+      msSymbolCmap = i;
+    }
+  }
+  i = 0;
+  mode = t42FontModeCharCode;
+  if (pdfFontHasEncoding) {
+    if (unicodeCmap >= 0) {
+      i = unicodeCmap;
+      mode = t42FontModeUnicode;
+    } else if (macRomanCmap >= 0) {
+      i = macRomanCmap;
+      mode = t42FontModeMacRoman;
+    }
+  } else {
+    if (macRomanCmap >= 0) {
+      i = macRomanCmap;
+      mode = t42FontModeCharCode;
+    } else if (msSymbolCmap >= 0) {
+      i = msSymbolCmap;
+      mode = t42FontModeCharCodeOffset;
+      cmapOffset = 0xf000;
+    }
+  }
+  cmapPlatform = getUShort(pos + 4 + 8*i);
+  cmapEncoding = getUShort(pos + 4 + 8*i + 2);
+  pos += getULong(pos + 4 + 8*i + 4);
+  cmapFmt = getUShort(pos);
+  if (cmapFmt != 0 && cmapFmt != 4 && cmapFmt != 6) {
+    error(-1, "Unimplemented cmap format (%d) in TrueType font file",
+         cmapFmt);
+    goto err;
+  }
+
+  // map char name to glyph index:
+  // 1. use encoding to map name to char code
+  // 2. use cmap to map char code to glyph index
+  j = 0; // make gcc happy
+  for (i = 0; i < 256; ++i) {
+    name = encodingA[i];
+    if (name && strcmp(name, ".notdef")) {
+      switch (mode) {
+      case t42FontModeUnicode:
+       toUnicode->mapToUnicode((CharCode)i, &u, 1);
+       j = (int)u;
        break;
-      case 23:                 // vstemhm
-       //~ ignored
-       if (first) {
-         cvtGlyphWidth(nOps & 1);
-         first = gFalse;
-       }
-       if (nOps & 1) {
-         error(-1, "Wrong number of args (%d) to Type 2 vstemhm", nOps);
-       }
-       nHints += nOps / 2;
+      case t42FontModeCharCode:
+       j = i;
        break;
-      case 10:                 // callsubr
-      case 11:                 // return
-      case 16:                 // blend
-      case 29:                 // callgsubr
-       error(-1, "Unimplemented Type 2 charstring op: %d", s[i]);
+      case t42FontModeCharCodeOffset:
+       j = cmapOffset + i;
        break;
-      default:
-       error(-1, "Illegal Type 2 charstring op: %d", s[i]);
+      case t42FontModeMacRoman:
+       j = globalParams->getMacRomanCharCode(name);
        break;
       }
-      ++i;
-      nOps = 0;
-    } else if (s[i] <= 246) {
-      if (nOps < 48) {
-       fp[nOps] = gFalse;
-       op[nOps++] = (int)s[i] - 139;
-      }
-      ++i;
-    } else if (s[i] <= 250) {
-      if (nOps < 48) {
-       fp[nOps] = gFalse;
-       op[nOps++] = (((int)s[i] - 247) << 8) + (int)s[i+1] + 108;
+      // note: Distiller (maybe Adobe's PS interpreter in general)
+      // doesn't like TrueType fonts that have CharStrings entries
+      // which point to nonexistent glyphs, hence the (k < nGlyphs)
+      // test
+      if ((k = getCmapEntry(cmapFmt, pos, j)) > 0 &&
+         k < nGlyphs) {
+       fprintf(out, "/%s %d def\n", name, k);
       }
-      i += 2;
-    } else if (s[i] <= 254) {
-      if (nOps < 48) {
-       fp[nOps] = gFalse;
-       op[nOps++] = -(((int)s[i] - 251) << 8) - (int)s[i+1] - 108;
+    }
+  }
+
+ err:
+  fprintf(out, "end readonly def\n");
+}
+
+int TrueTypeFontFile::getCmapEntry(int cmapFmt, int pos, int code) {
+  int cmapLen, cmapFirst;
+  int segCnt, segEnd, segStart, segDelta, segOffset;
+  int a, b, m, i;
+
+  switch (cmapFmt) {
+  case 0: // byte encoding table (Apple standard)
+    cmapLen = getUShort(pos + 2);
+    if (code >= cmapLen) {
+      return 0;
+    }
+    return getByte(pos + 6 + code);
+
+  case 4: // segment mapping to delta values (Microsoft standard)
+    segCnt = getUShort(pos + 6) / 2;
+    a = -1;
+    b = segCnt - 1;
+    segEnd = getUShort(pos + 14 + 2*b);
+    if (code > segEnd) {
+      // malformed font -- the TrueType spec requires the last segEnd
+      // to be 0xffff
+      return 0;
+    }
+    // invariant: seg[a].end < code <= seg[b].end
+    while (b - a > 1) {
+      m = (a + b) / 2;
+      segEnd = getUShort(pos + 14 + 2*m);
+      if (segEnd < code) {
+       a = m;
+      } else {
+       b = m;
       }
-      i += 2;
+    }
+    segStart = getUShort(pos + 16 + 2*segCnt + 2*b);
+    segDelta = getUShort(pos + 16 + 4*segCnt + 2*b);
+    segOffset = getUShort(pos + 16 + 6*segCnt + 2*b);
+    if (segOffset == 0) {
+      i = (code + segDelta) & 0xffff;
     } else {
-      x = (s[i+1] << 24) | (s[i+2] << 16) | (s[i+3] << 8) | s[i+4];
-      if (x & 0x80000000)
-       x |= -1 << 31;
-      if (nOps < 48) {
-       fp[nOps] = gTrue;
-       op[nOps++] = (double)x / 65536.0;
+      i = getUShort(pos + 16 + 6*segCnt + 2*b +
+                   segOffset + 2 * (code - segStart));
+      if (i != 0) {
+       i = (i + segDelta) & 0xffff;
       }
-      i += 5;
     }
-  }
+    return i;
 
-  sprintf(eBuf, "/%s %d RD ", name, charBuf->getLength());
-  eexecWrite(eBuf);
-  eexecWriteCharstring((Guchar *)charBuf->getCString(), charBuf->getLength());
-  eexecWrite(" ND\n");
-  delete charBuf;
-}
+  case 6: // trimmed table mapping
+    cmapFirst = getUShort(pos + 6);
+    cmapLen = getUShort(pos + 8);
+    if (code < cmapFirst || code >= cmapFirst + cmapLen) {
+      return 0;
+    }
+    return getUShort(pos + 10 + 2*(code - cmapFirst));
 
-void Type1CFontConverter::cvtGlyphWidth(GBool useOp) {
-  double w;
-  GBool wFP;
-  int i;
+  default:
+    // shouldn't happen - this is checked earlier
+    break;
+  }
+  return 0;
+}
 
-  if (useOp) {
-    w = nominalWidthX + op[0];
-    wFP = nominalWidthXFP | fp[0];
-    for (i = 1; i < nOps; ++i) {
-      op[i-1] = op[i];
-      fp[i-1] = fp[i];
+void TrueTypeFontFile::cvtSfnts(FILE *out, GString *name) {
+  TTFontTableHdr newTableHdrs[nT42Tables];
+  char tableDir[12 + nT42Tables*16];
+  char headTable[54];
+  int *origLocaTable;
+  char *locaTable;
+  int nNewTables;
+  Guint checksum;
+  int pos, glyfPos, length, glyphLength, pad;
+  int i, j, k;
+
+  // construct the 'head' table, zero out the font checksum
+  memcpy(headTable, file + seekTable("head"), 54);
+  headTable[8] = headTable[9] = headTable[10] = headTable[11] = (char)0;
+
+  // read the original 'loca' table and construct the new one
+  // (pad each glyph out to a multiple of 4 bytes)
+  origLocaTable = (int *)gmalloc((nGlyphs + 1) * sizeof(int));
+  pos = seekTable("loca");
+  for (i = 0; i <= nGlyphs; ++i) {
+    if (locaFmt) {
+      origLocaTable[i] = getULong(pos + 4*i);
+    } else {
+      origLocaTable[i] = 2 * getUShort(pos + 2*i);
     }
-    --nOps;
+  }
+  locaTable = (char *)gmalloc((nGlyphs + 1) * (locaFmt ? 4 : 2));
+  if (locaFmt) {
+    locaTable[0] = locaTable[1] = locaTable[2] = locaTable[3] = 0;
   } else {
-    w = defaultWidthX;
-    wFP = defaultWidthXFP;
+    locaTable[0] = locaTable[1] = 0;
+  }
+  pos = 0;
+  for (i = 1; i <= nGlyphs; ++i) {
+    length = origLocaTable[i] - origLocaTable[i-1];
+    if (length & 3) {
+      length += 4 - (length & 3);
+    }
+    pos += length;
+    if (locaFmt) {
+      locaTable[4*i  ] = (char)(pos >> 24);
+      locaTable[4*i+1] = (char)(pos >> 16);
+      locaTable[4*i+2] = (char)(pos >>  8);
+      locaTable[4*i+3] = (char) pos;
+    } else {
+      locaTable[2*i  ] = (char)(pos >> 9);
+      locaTable[2*i+1] = (char)(pos >> 1);
+    }
   }
-  eexecDumpNum(0, gFalse);
-  eexecDumpNum(w, wFP);
-  eexecDumpOp1(13);
-}
 
-void Type1CFontConverter::eexecDumpNum(double x, GBool fp) {
-  Guchar buf[12];
-  int y, n;
+  // count the number of tables
+  nNewTables = 0;
+  for (i = 0; i < nT42Tables; ++i) {
+    if (t42Tables[i].required ||
+       seekTable(t42Tables[i].tag) >= 0) {
+      ++nNewTables;
+    }
+  }
 
-  n = 0;
-  if (fp) {
-    if (x >= -32768 && x < 32768) {
-      y = (int)(x * 256.0);
-      buf[0] = 255;
-      buf[1] = (Guchar)(y >> 24);
-      buf[2] = (Guchar)(y >> 16);
-      buf[3] = (Guchar)(y >> 8);
-      buf[4] = (Guchar)y;
-      buf[5] = 255;
-      buf[6] = 0;
-      buf[7] = 0;
-      buf[8] = 1;
-      buf[9] = 0;
-      buf[10] = 12;
-      buf[11] = 12;
-      n = 12;
+  // construct the new table headers, including table checksums
+  // (pad each table out to a multiple of 4 bytes)
+  pos = 12 + nNewTables*16;
+  k = 0;
+  for (i = 0; i < nT42Tables; ++i) {
+    length = -1;
+    checksum = 0; // make gcc happy
+    if (i == t42HeadTable) {
+      length = 54;
+      checksum = computeTableChecksum(headTable, 54);
+    } else if (i == t42LocaTable) {
+      length = (nGlyphs + 1) * (locaFmt ? 4 : 2);
+      checksum = computeTableChecksum(locaTable, length);
+    } else if (i == t42GlyfTable) {
+      length = 0;
+      checksum = 0;
+      glyfPos = seekTable("glyf");
+      for (j = 0; j < nGlyphs; ++j) {
+       glyphLength = origLocaTable[j+1] - origLocaTable[j];
+       pad = (glyphLength & 3) ? 4 - (glyphLength & 3) : 0;
+       length += glyphLength + pad;
+       checksum += computeTableChecksum(file + glyfPos + origLocaTable[j],
+                                        glyphLength);
+      }
     } else {
-      error(-1, "Type 2 fixed point constant out of range");
+      if ((j = seekTableIdx(t42Tables[i].tag)) >= 0) {
+       length = tableHdrs[j].length;
+       checksum = computeTableChecksum(file + tableHdrs[j].offset, length);
+      } else if (t42Tables[i].required) {
+       error(-1, "Embedded TrueType font is missing a required table ('%s')",
+             t42Tables[i].tag);
+       length = 0;
+       checksum = 0;
+      }
+    }
+    if (length >= 0) {
+      strncpy(newTableHdrs[k].tag, t42Tables[i].tag, 4);
+      newTableHdrs[k].checksum = checksum;
+      newTableHdrs[k].offset = pos;
+      newTableHdrs[k].length = length;
+      pad = (length & 3) ? 4 - (length & 3) : 0;
+      pos += length + pad;
+      ++k;
     }
+  }
+
+  // construct the table directory
+  tableDir[0] = 0x00;          // sfnt version
+  tableDir[1] = 0x01;
+  tableDir[2] = 0x00;
+  tableDir[3] = 0x00;
+  tableDir[4] = 0;             // numTables
+  tableDir[5] = nNewTables;
+  tableDir[6] = 0;             // searchRange
+  tableDir[7] = (char)128;
+  tableDir[8] = 0;             // entrySelector
+  tableDir[9] = 3;
+  tableDir[10] = 0;            // rangeShift
+  tableDir[11] = (char)(16 * nNewTables - 128);
+  pos = 12;
+  for (i = 0; i < nNewTables; ++i) {
+    tableDir[pos   ] = newTableHdrs[i].tag[0];
+    tableDir[pos+ 1] = newTableHdrs[i].tag[1];
+    tableDir[pos+ 2] = newTableHdrs[i].tag[2];
+    tableDir[pos+ 3] = newTableHdrs[i].tag[3];
+    tableDir[pos+ 4] = (char)(newTableHdrs[i].checksum >> 24);
+    tableDir[pos+ 5] = (char)(newTableHdrs[i].checksum >> 16);
+    tableDir[pos+ 6] = (char)(newTableHdrs[i].checksum >>  8);
+    tableDir[pos+ 7] = (char) newTableHdrs[i].checksum;
+    tableDir[pos+ 8] = (char)(newTableHdrs[i].offset >> 24);
+    tableDir[pos+ 9] = (char)(newTableHdrs[i].offset >> 16);
+    tableDir[pos+10] = (char)(newTableHdrs[i].offset >>  8);
+    tableDir[pos+11] = (char) newTableHdrs[i].offset;
+    tableDir[pos+12] = (char)(newTableHdrs[i].length >> 24);
+    tableDir[pos+13] = (char)(newTableHdrs[i].length >> 16);
+    tableDir[pos+14] = (char)(newTableHdrs[i].length >>  8);
+    tableDir[pos+15] = (char) newTableHdrs[i].length;
+    pos += 16;
+  }
+
+  // compute the font checksum and store it in the head table
+  checksum = computeTableChecksum(tableDir, 12 + nNewTables*16);
+  for (i = 0; i < nNewTables; ++i) {
+    checksum += newTableHdrs[i].checksum;
+  }
+  checksum = 0xb1b0afba - checksum; // because the TrueType spec says so
+  headTable[ 8] = (char)(checksum >> 24);
+  headTable[ 9] = (char)(checksum >> 16);
+  headTable[10] = (char)(checksum >>  8);
+  headTable[11] = (char) checksum;
+
+  // start the sfnts array
+  if (name) {
+    fprintf(out, "/%s [\n", name->getCString());
   } else {
-    y = (int)x;
-    if (y >= -107 && y <= 107) {
-      buf[0] = (Guchar)(y + 139);
-      n = 1;
-    } else if (y > 107 && y <= 1131) {
-      y -= 108;
-      buf[0] = (Guchar)((y >> 8) + 247);
-      buf[1] = (Guchar)(y & 0xff);
-      n = 2;
-    } else if (y < -107 && y >= -1131) {
-      y = -y - 108;
-      buf[0] = (Guchar)((y >> 8) + 251);
-      buf[1] = (Guchar)(y & 0xff);
-      n = 2;
+    fprintf(out, "/sfnts [\n");
+  }
+
+  // write the table directory
+  dumpString(tableDir, 12 + nNewTables*16, out);
+
+  // write the tables
+  for (i = 0; i < nNewTables; ++i) {
+    if (i == t42HeadTable) {
+      dumpString(headTable, 54, out);
+    } else if (i == t42LocaTable) {
+      length = (nGlyphs + 1) * (locaFmt ? 4 : 2);
+      dumpString(locaTable, length, out);
+    } else if (i == t42GlyfTable) {
+      glyfPos = seekTable("glyf");
+      for (j = 0; j < nGlyphs; ++j) {
+       length = origLocaTable[j+1] - origLocaTable[j];
+       if (length > 0) {
+         dumpString(file + glyfPos + origLocaTable[j], length, out);
+       }
+      }
     } else {
-      buf[0] = 255;
-      buf[1] = (Guchar)(y >> 24);
-      buf[2] = (Guchar)(y >> 16);
-      buf[3] = (Guchar)(y >> 8);
-      buf[4] = (Guchar)y;
-      n = 5;
+      // length == 0 means the table is missing and the error was
+      // already reported during the construction of the table
+      // headers
+      if ((length = newTableHdrs[i].length) > 0) {
+       dumpString(file + seekTable(t42Tables[i].tag), length, out);
+      }
     }
   }
-  charBuf->append((char *)buf, n);
-}
 
-void Type1CFontConverter::eexecDumpOp1(int op) {
-  charBuf->append((char)op);
-}
+  // end the sfnts array
+  fprintf(out, "] def\n");
 
-void Type1CFontConverter::eexecDumpOp2(int op) {
-  charBuf->append((char)12);
-  charBuf->append((char)op);
+  gfree(origLocaTable);
+  gfree(locaTable);
 }
 
-void Type1CFontConverter::eexecWriteCharstring(Guchar *s, int n) {
-  Gushort r2;
-  Guchar x;
-  int i;
+void TrueTypeFontFile::dumpString(char *s, int length, FILE *out) {
+  int pad, i, j;
 
-  r2 = 4330;
+  fprintf(out, "<");
+  for (i = 0; i < length; i += 32) {
+    for (j = 0; j < 32 && i+j < length; ++j) {
+      fprintf(out, "%02X", s[i+j] & 0xff);
+    }
+    if (i % (65536 - 32) == 65536 - 64) {
+      fprintf(out, ">\n<");
+    } else if (i+32 < length) {
+      fprintf(out, "\n");
+    }
+  }
+  if (length & 3) {
+    pad = 4 - (length & 3);
+    for (i = 0; i < pad; ++i) {
+      fprintf(out, "00");
+    }
+  }
+  // add an extra zero byte because the Adobe Type 42 spec says so
+  fprintf(out, "00>\n");
+}
 
-  for (i = 0; i < n; ++i) {
-    // charstring encryption
-    x = s[i];
-    x ^= (r2 >> 8);
-    r2 = (x + r2) * 52845 + 22719;
+Guint TrueTypeFontFile::computeTableChecksum(char *data, int length) {
+  Guint checksum, word;
+  int i;
 
-    // eexec encryption
-    x ^= (r1 >> 8);
-    r1 = (x + r1) * 52845 + 22719;
-    fputc(hexChars[x >> 4], out);
-    fputc(hexChars[x & 0x0f], out);
-    line += 2;
-    if (line == 64) {
-      fputc('\n', out);
-      line = 0;
+  checksum = 0;
+  for (i = 0; i+3 < length; i += 4) {
+    word = ((data[i  ] & 0xff) << 24) +
+           ((data[i+1] & 0xff) << 16) +
+           ((data[i+2] & 0xff) <<  8) +
+            (data[i+3] & 0xff);
+    checksum += word;
+  }
+  if (length & 3) {
+    word = 0;
+    i = length & ~3;
+    switch (length & 3) {
+    case 3:
+      word |= (data[i+2] & 0xff) <<  8;
+    case 2:
+      word |= (data[i+1] & 0xff) << 16;
+    case 1:
+      word |= (data[i  ] & 0xff) << 24;
+      break;
     }
+    checksum += word;
   }
+  return checksum;
 }
 
-void Type1CFontConverter::getDeltaInt(char *buf, char *name, double *op,
-                                     int n) {
-  int x, i;
+void TrueTypeFontFile::writeTTF(FILE *out) {
+  static char cmapTab[20] = {
+    0, 0,                      // table version number
+    0, 1,                      // number of encoding tables
+    0, 1,                      // platform ID
+    0, 0,                      // encoding ID
+    0, 0, 0, 12,               // offset of subtable
+    0, 0,                      // subtable format
+    0, 1,                      // subtable length
+    0, 1,                      // subtable version
+    0,                         // map char 0 -> glyph 0
+    0                          // pad to multiple of four bytes
+  };
+  static char nameTab[8] = {
+    0, 0,                      // format
+    0, 0,                      // number of name records
+    0, 6,                      // offset to start of string storage
+    0, 0                       // pad to multiple of four bytes
+  };
+  static char postTab[32] = {
+    0, 1, 0, 0,                        // format
+    0, 0, 0, 0,                        // italic angle
+    0, 0,                      // underline position
+    0, 0,                      // underline thickness
+    0, 0, 0, 0,                        // fixed pitch
+    0, 0, 0, 0,                        // min Type 42 memory
+    0, 0, 0, 0,                        // max Type 42 memory
+    0, 0, 0, 0,                        // min Type 1 memory
+    0, 0, 0, 0                 // max Type 1 memory
+  };
+  GBool haveCmap, haveName, havePost;
+  GBool dirCmap, dirName, dirPost;
+  int nNewTables, nAllTables, pad;
+  char *tableDir;
+  Guint t, pos;
+  int i, j;
+
+  // check for missing tables
+  haveCmap = seekTable("cmap") >= 0;
+  haveName = seekTable("name") >= 0;
+  havePost = seekTable("post") >= 0;
+  nNewTables = (haveCmap ? 0 : 1) + (haveName ? 0 : 1) + (havePost ? 0 : 1);
+  if (!nNewTables) {
+    // none are missing - write the TTF file as is
+    fwrite(file, 1, len, out);
+    return;
+  }
 
-  sprintf(buf, "/%s [", name);
-  buf += strlen(buf);
-  x = 0;
-  for (i = 0; i < n; ++i) {
-    x += (int)op[i];
-    sprintf(buf, "%s%d", i > 0 ? " " : "", x);
-    buf += strlen(buf);
+  // construct the new table directory
+  nAllTables = nTables + nNewTables;
+  tableDir = (char *)gmalloc(12 + nAllTables * 16);
+  memcpy(tableDir, file, 12 + nTables * 16);
+  tableDir[4] = (char)((nAllTables >> 8) & 0xff);
+  tableDir[5] = (char)(nAllTables & 0xff);
+  for (i = -1, t = (Guint)nAllTables; t; ++i, t >>= 1) ;
+  t = 1 << (4 + i);
+  tableDir[6] = (char)((t >> 8) & 0xff);
+  tableDir[7] = (char)(t & 0xff);
+  tableDir[8] = (char)((i >> 8) & 0xff);
+  tableDir[9] = (char)(i & 0xff);
+  t = nAllTables * 16 - t;
+  tableDir[10] = (char)((t >> 8) & 0xff);
+  tableDir[11] = (char)(t & 0xff);
+  dirCmap = haveCmap;
+  dirName = haveName;
+  dirPost = havePost;
+  j = 0;
+  pad = (len & 3) ? 4 - (len & 3) : 0;
+  pos = len + pad + 16 * nNewTables;
+  for (i = 0; i < nTables; ++i) {
+    if (!dirCmap && strncmp(tableHdrs[i].tag, "cmap", 4) > 0) {
+      tableDir[12 + 16*j     ] = 'c';
+      tableDir[12 + 16*j +  1] = 'm';
+      tableDir[12 + 16*j +  2] = 'a';
+      tableDir[12 + 16*j +  3] = 'p';
+      tableDir[12 + 16*j +  4] = (char)0; //~ should compute the checksum
+      tableDir[12 + 16*j +  5] = (char)0;
+      tableDir[12 + 16*j +  6] = (char)0;
+      tableDir[12 + 16*j +  7] = (char)0;
+      tableDir[12 + 16*j +  8] = (char)((pos >> 24) & 0xff);
+      tableDir[12 + 16*j +  9] = (char)((pos >> 16) & 0xff);
+      tableDir[12 + 16*j + 10] = (char)((pos >>  8) & 0xff);
+      tableDir[12 + 16*j + 11] = (char)( pos        & 0xff);
+      tableDir[12 + 16*j + 12] = (char)((sizeof(cmapTab) >> 24) & 0xff);
+      tableDir[12 + 16*j + 13] = (char)((sizeof(cmapTab) >> 16) & 0xff);
+      tableDir[12 + 16*j + 14] = (char)((sizeof(cmapTab) >>  8) & 0xff);
+      tableDir[12 + 16*j + 15] = (char)( sizeof(cmapTab)        & 0xff);
+      pos += sizeof(cmapTab);
+      ++j;
+      dirCmap = gTrue;
+    }
+    if (!dirName && strncmp(tableHdrs[i].tag, "name", 4) > 0) {
+      tableDir[12 + 16*j     ] = 'n';
+      tableDir[12 + 16*j +  1] = 'a';
+      tableDir[12 + 16*j +  2] = 'm';
+      tableDir[12 + 16*j +  3] = 'e';
+      tableDir[12 + 16*j +  4] = (char)0; //~ should compute the checksum
+      tableDir[12 + 16*j +  5] = (char)0;
+      tableDir[12 + 16*j +  6] = (char)0;
+      tableDir[12 + 16*j +  7] = (char)0;
+      tableDir[12 + 16*j +  8] = (char)((pos >> 24) & 0xff);
+      tableDir[12 + 16*j +  9] = (char)((pos >> 16) & 0xff);
+      tableDir[12 + 16*j + 10] = (char)((pos >>  8) & 0xff);
+      tableDir[12 + 16*j + 11] = (char)( pos        & 0xff);
+      tableDir[12 + 16*j + 12] = (char)((sizeof(nameTab) >> 24) & 0xff);
+      tableDir[12 + 16*j + 13] = (char)((sizeof(nameTab) >> 16) & 0xff);
+      tableDir[12 + 16*j + 14] = (char)((sizeof(nameTab) >>  8) & 0xff);
+      tableDir[12 + 16*j + 15] = (char)( sizeof(nameTab)        & 0xff);
+      pos += sizeof(nameTab);
+      ++j;
+      dirName = gTrue;
+    }
+    if (!dirName && strncmp(tableHdrs[i].tag, "post", 4) > 0) {
+      tableDir[12 + 16*j     ] = 'p';
+      tableDir[12 + 16*j +  1] = 'o';
+      tableDir[12 + 16*j +  2] = 's';
+      tableDir[12 + 16*j +  3] = 't';
+      tableDir[12 + 16*j +  4] = (char)0; //~ should compute the checksum
+      tableDir[12 + 16*j +  5] = (char)0;
+      tableDir[12 + 16*j +  6] = (char)0;
+      tableDir[12 + 16*j +  7] = (char)0;
+      tableDir[12 + 16*j +  8] = (char)((pos >> 24) & 0xff);
+      tableDir[12 + 16*j +  9] = (char)((pos >> 16) & 0xff);
+      tableDir[12 + 16*j + 10] = (char)((pos >>  8) & 0xff);
+      tableDir[12 + 16*j + 11] = (char)( pos        & 0xff);
+      tableDir[12 + 16*j + 12] = (char)((sizeof(postTab) >> 24) & 0xff);
+      tableDir[12 + 16*j + 13] = (char)((sizeof(postTab) >> 16) & 0xff);
+      tableDir[12 + 16*j + 14] = (char)((sizeof(postTab) >>  8) & 0xff);
+      tableDir[12 + 16*j + 15] = (char)( sizeof(postTab)        & 0xff);
+      pos += sizeof(postTab);
+      ++j;
+      dirPost = gTrue;
+    }
+    memcpy(&tableDir[12 + 16*j], file + 12 + 16*i, 16);
+    t = tableHdrs[i].offset + nNewTables * 16;
+    tableDir[12 + 16*j +  8] = (char)((t >> 24) & 0xff);
+    tableDir[12 + 16*j +  9] = (char)((t >> 16) & 0xff);
+    tableDir[12 + 16*j + 10] = (char)((t >>  8) & 0xff);
+    tableDir[12 + 16*j + 11] = (char)( t        & 0xff);
+    ++j;
+  }
+  if (!dirCmap) {
+    tableDir[12 + 16*j     ] = 'c';
+    tableDir[12 + 16*j +  1] = 'm';
+    tableDir[12 + 16*j +  2] = 'a';
+    tableDir[12 + 16*j +  3] = 'p';
+    tableDir[12 + 16*j +  4] = (char)0; //~ should compute the checksum
+    tableDir[12 + 16*j +  5] = (char)0;
+    tableDir[12 + 16*j +  6] = (char)0;
+    tableDir[12 + 16*j +  7] = (char)0;
+    tableDir[12 + 16*j +  8] = (char)((pos >> 24) & 0xff);
+    tableDir[12 + 16*j +  9] = (char)((pos >> 16) & 0xff);
+    tableDir[12 + 16*j + 10] = (char)((pos >>  8) & 0xff);
+    tableDir[12 + 16*j + 11] = (char)( pos        & 0xff);
+    tableDir[12 + 16*j + 12] = (char)((sizeof(cmapTab) >> 24) & 0xff);
+    tableDir[12 + 16*j + 13] = (char)((sizeof(cmapTab) >> 16) & 0xff);
+    tableDir[12 + 16*j + 14] = (char)((sizeof(cmapTab) >>  8) & 0xff);
+    tableDir[12 + 16*j + 15] = (char)( sizeof(cmapTab)        & 0xff);
+    pos += sizeof(cmapTab);
+    ++j;
+    dirCmap = gTrue;
+  }
+  if (!dirName) {
+    tableDir[12 + 16*j     ] = 'n';
+    tableDir[12 + 16*j +  1] = 'a';
+    tableDir[12 + 16*j +  2] = 'm';
+    tableDir[12 + 16*j +  3] = 'e';
+    tableDir[12 + 16*j +  4] = (char)0; //~ should compute the checksum
+    tableDir[12 + 16*j +  5] = (char)0;
+    tableDir[12 + 16*j +  6] = (char)0;
+    tableDir[12 + 16*j +  7] = (char)0;
+    tableDir[12 + 16*j +  8] = (char)((pos >> 24) & 0xff);
+    tableDir[12 + 16*j +  9] = (char)((pos >> 16) & 0xff);
+    tableDir[12 + 16*j + 10] = (char)((pos >>  8) & 0xff);
+    tableDir[12 + 16*j + 11] = (char)( pos        & 0xff);
+    tableDir[12 + 16*j + 12] = (char)((sizeof(nameTab) >> 24) & 0xff);
+    tableDir[12 + 16*j + 13] = (char)((sizeof(nameTab) >> 16) & 0xff);
+    tableDir[12 + 16*j + 14] = (char)((sizeof(nameTab) >>  8) & 0xff);
+    tableDir[12 + 16*j + 15] = (char)( sizeof(nameTab)        & 0xff);
+    pos += sizeof(nameTab);
+    ++j;
+    dirName = gTrue;
+  }
+  if (!dirPost) {
+    tableDir[12 + 16*j     ] = 'p';
+    tableDir[12 + 16*j +  1] = 'o';
+    tableDir[12 + 16*j +  2] = 's';
+    tableDir[12 + 16*j +  3] = 't';
+    tableDir[12 + 16*j +  4] = (char)0; //~ should compute the checksum
+    tableDir[12 + 16*j +  5] = (char)0;
+    tableDir[12 + 16*j +  6] = (char)0;
+    tableDir[12 + 16*j +  7] = (char)0;
+    tableDir[12 + 16*j +  8] = (char)((pos >> 24) & 0xff);
+    tableDir[12 + 16*j +  9] = (char)((pos >> 16) & 0xff);
+    tableDir[12 + 16*j + 10] = (char)((pos >>  8) & 0xff);
+    tableDir[12 + 16*j + 11] = (char)( pos        & 0xff);
+    tableDir[12 + 16*j + 12] = (char)((sizeof(postTab) >> 24) & 0xff);
+    tableDir[12 + 16*j + 13] = (char)((sizeof(postTab) >> 16) & 0xff);
+    tableDir[12 + 16*j + 14] = (char)((sizeof(postTab) >>  8) & 0xff);
+    tableDir[12 + 16*j + 15] = (char)( sizeof(postTab)        & 0xff);
+    pos += sizeof(postTab);
+    ++j;
+    dirPost = gTrue;
   }
-  sprintf(buf, "] def\n");
-}
 
-void Type1CFontConverter::getDeltaReal(char *buf, char *name, double *op,
-                                      int n) {
-  double x;
-  int i;
+  // write the table directory
+  fwrite(tableDir, 1, 12 + 16 * nAllTables, out);
 
-  sprintf(buf, "/%s [", name);
-  buf += strlen(buf);
-  x = 0;
-  for (i = 0; i < n; ++i) {
-    x += op[i];
-    sprintf(buf, "%s%g", i > 0 ? " " : "", x);
-    buf += strlen(buf);
+  // write the original tables
+  fwrite(file + 12 + 16*nTables, 1, len - (12 + 16*nTables), out);
+
+  // write the new tables
+  for (i = 0; i < pad; ++i) {
+    fputc((char)0, out);
   }
-  sprintf(buf, "] def\n");
+  if (!haveCmap) {
+    fwrite(cmapTab, 1, sizeof(cmapTab), out);
+  }
+  if (!haveName) {
+    fwrite(nameTab, 1, sizeof(nameTab), out);
+  }
+  if (!havePost) {
+    fwrite(postTab, 1, sizeof(postTab), out);
+  }
+
+  gfree(tableDir);
 }