]> www.fi.muni.cz Git - evince.git/commitdiff
Initial revision
authorMartin Kretzschmar <mkretzschmar@src.gnome.org>
Mon, 17 May 2004 18:12:38 +0000 (18:12 +0000)
committerMartin Kretzschmar <mkretzschmar@src.gnome.org>
Mon, 17 May 2004 18:12:38 +0000 (18:12 +0000)
67 files changed:
pdf/fofi/FoFiBase.cc [new file with mode: 0644]
pdf/fofi/FoFiBase.h [new file with mode: 0644]
pdf/fofi/FoFiEncodings.cc [new file with mode: 0644]
pdf/fofi/FoFiEncodings.h [new file with mode: 0644]
pdf/fofi/FoFiTrueType.cc [new file with mode: 0644]
pdf/fofi/FoFiTrueType.h [new file with mode: 0644]
pdf/fofi/FoFiType1.cc [new file with mode: 0644]
pdf/fofi/FoFiType1.h [new file with mode: 0644]
pdf/fofi/FoFiType1C.cc [new file with mode: 0644]
pdf/fofi/FoFiType1C.h [new file with mode: 0644]
pdf/fofi/Makefile.dep [new file with mode: 0644]
pdf/fofi/Makefile.in [new file with mode: 0644]
pdf/fofi/vms_make.com [new file with mode: 0644]
pdf/splash/Makefile.dep [new file with mode: 0644]
pdf/splash/Makefile.in [new file with mode: 0644]
pdf/splash/Splash.cc [new file with mode: 0644]
pdf/splash/Splash.h [new file with mode: 0644]
pdf/splash/SplashBitmap.cc [new file with mode: 0644]
pdf/splash/SplashBitmap.h [new file with mode: 0644]
pdf/splash/SplashClip.cc [new file with mode: 0644]
pdf/splash/SplashClip.h [new file with mode: 0644]
pdf/splash/SplashErrorCodes.h [new file with mode: 0644]
pdf/splash/SplashFTFont.cc [new file with mode: 0644]
pdf/splash/SplashFTFont.h [new file with mode: 0644]
pdf/splash/SplashFTFontEngine.cc [new file with mode: 0644]
pdf/splash/SplashFTFontEngine.h [new file with mode: 0644]
pdf/splash/SplashFTFontFile.cc [new file with mode: 0644]
pdf/splash/SplashFTFontFile.h [new file with mode: 0644]
pdf/splash/SplashFont.cc [new file with mode: 0644]
pdf/splash/SplashFont.h [new file with mode: 0644]
pdf/splash/SplashFontEngine.cc [new file with mode: 0644]
pdf/splash/SplashFontEngine.h [new file with mode: 0644]
pdf/splash/SplashFontFile.cc [new file with mode: 0644]
pdf/splash/SplashFontFile.h [new file with mode: 0644]
pdf/splash/SplashFontFileID.cc [new file with mode: 0644]
pdf/splash/SplashFontFileID.h [new file with mode: 0644]
pdf/splash/SplashGlyphBitmap.h [new file with mode: 0644]
pdf/splash/SplashMath.h [new file with mode: 0644]
pdf/splash/SplashPath.cc [new file with mode: 0644]
pdf/splash/SplashPath.h [new file with mode: 0644]
pdf/splash/SplashPattern.cc [new file with mode: 0644]
pdf/splash/SplashPattern.h [new file with mode: 0644]
pdf/splash/SplashScreen.cc [new file with mode: 0644]
pdf/splash/SplashScreen.h [new file with mode: 0644]
pdf/splash/SplashState.cc [new file with mode: 0644]
pdf/splash/SplashState.h [new file with mode: 0644]
pdf/splash/SplashT1Font.cc [new file with mode: 0644]
pdf/splash/SplashT1Font.h [new file with mode: 0644]
pdf/splash/SplashT1FontEngine.cc [new file with mode: 0644]
pdf/splash/SplashT1FontEngine.h [new file with mode: 0644]
pdf/splash/SplashT1FontFile.cc [new file with mode: 0644]
pdf/splash/SplashT1FontFile.h [new file with mode: 0644]
pdf/splash/SplashTypes.h [new file with mode: 0644]
pdf/splash/SplashXPath.cc [new file with mode: 0644]
pdf/splash/SplashXPath.h [new file with mode: 0644]
pdf/splash/SplashXPathScanner.cc [new file with mode: 0644]
pdf/splash/SplashXPathScanner.h [new file with mode: 0644]
pdf/splash/vms_make.com [new file with mode: 0644]
pdf/xpdf/JArithmeticDecoder.cc [new file with mode: 0644]
pdf/xpdf/JArithmeticDecoder.h [new file with mode: 0644]
pdf/xpdf/JPXStream.cc [new file with mode: 0644]
pdf/xpdf/JPXStream.h [new file with mode: 0644]
pdf/xpdf/SplashOutputDev.cc [new file with mode: 0644]
pdf/xpdf/SplashOutputDev.h [new file with mode: 0644]
pdf/xpdf/XSplashOutputDev.cc [new file with mode: 0644]
pdf/xpdf/XSplashOutputDev.h [new file with mode: 0644]
pdf/xpdf/pdftoppm.cc [new file with mode: 0644]

diff --git a/pdf/fofi/FoFiBase.cc b/pdf/fofi/FoFiBase.cc
new file mode 100644 (file)
index 0000000..28d0b8c
--- /dev/null
@@ -0,0 +1,156 @@
+//========================================================================
+//
+// FoFiBase.cc
+//
+// Copyright 1999-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <stdio.h>
+#include "gmem.h"
+#include "FoFiBase.h"
+
+//------------------------------------------------------------------------
+// FoFiBase
+//------------------------------------------------------------------------
+
+FoFiBase::FoFiBase(char *fileA, int lenA, GBool freeFileDataA) {
+  fileData = file = (Guchar *)fileA;
+  len = lenA;
+  freeFileData = freeFileDataA;
+}
+
+FoFiBase::~FoFiBase() {
+  if (freeFileData) {
+    gfree(fileData);
+  }
+}
+
+char *FoFiBase::readFile(char *fileName, int *fileLen) {
+  FILE *f;
+  char *buf;
+  int n;
+
+  if (!(f = fopen(fileName, "rb"))) {
+    return NULL;
+  }
+  fseek(f, 0, SEEK_END);
+  n = (int)ftell(f);
+  fseek(f, 0, SEEK_SET);
+  buf = (char *)gmalloc(n);
+  if ((int)fread(buf, 1, n, f) != n) {
+    gfree(buf);
+    fclose(f);
+    return NULL;
+  }
+  fclose(f);
+  *fileLen = n;
+  return buf;
+}
+
+int FoFiBase::getS8(int pos, GBool *ok) {
+  int x;
+
+  if (pos < 0 || pos >= len) {
+    *ok = gFalse;
+    return 0;
+  }
+  x = file[pos];
+  if (x & 0x80) {
+    x |= ~0xff;
+  }
+  return x;
+}
+
+int FoFiBase::getU8(int pos, GBool *ok) {
+  if (pos < 0 || pos >= len) {
+    *ok = gFalse;
+    return 0;
+  }
+  return file[pos];
+}
+
+int FoFiBase::getS16BE(int pos, GBool *ok) {
+  int x;
+
+  if (pos < 0 || pos+1 >= len) {
+    *ok = gFalse;
+    return 0;
+  }
+  x = file[pos];
+  x = (x << 8) + file[pos+1];
+  if (x & 0x8000) {
+    x |= ~0xffff;
+  }
+  return x;
+}
+
+int FoFiBase::getU16BE(int pos, GBool *ok) {
+  int x;
+
+  if (pos < 0 || pos+1 >= len) {
+    *ok = gFalse;
+    return 0;
+  }
+  x = file[pos];
+  x = (x << 8) + file[pos+1];
+  return x;
+}
+
+int FoFiBase::getS32BE(int pos, GBool *ok) {
+  int x;
+
+  if (pos < 0 || pos+3 >= len) {
+    *ok = gFalse;
+    return 0;
+  }
+  x = file[pos];
+  x = (x << 8) + file[pos+1];
+  x = (x << 8) + file[pos+2];
+  x = (x << 8) + file[pos+3];
+  if (x & 0x80000000) {
+    x |= ~0xffffffff;
+  }
+  return x;
+}
+
+Guint FoFiBase::getU32BE(int pos, GBool *ok) {
+  Guint x;
+
+  if (pos < 0 || pos+3 >= len) {
+    *ok = gFalse;
+    return 0;
+  }
+  x = file[pos];
+  x = (x << 8) + file[pos+1];
+  x = (x << 8) + file[pos+2];
+  x = (x << 8) + file[pos+3];
+  return x;
+}
+
+Guint FoFiBase::getUVarBE(int pos, int size, GBool *ok) {
+  Guint x;
+  int i;
+
+  if (pos < 0 || pos + size > len) {
+    *ok = gFalse;
+    return 0;
+  }
+  x = 0;
+  for (i = 0; i < size; ++i) {
+    x = (x << 8) + file[pos + i];
+  }
+  return x;
+}
+
+GBool FoFiBase::checkRegion(int pos, int size) {
+  return pos >= 0 &&
+         pos + size >= pos &&
+         pos + size <= len;
+}
diff --git a/pdf/fofi/FoFiBase.h b/pdf/fofi/FoFiBase.h
new file mode 100644 (file)
index 0000000..b78840b
--- /dev/null
@@ -0,0 +1,57 @@
+//========================================================================
+//
+// FoFiBase.h
+//
+// Copyright 1999-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef FOFIBASE_H
+#define FOFIBASE_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#include "gtypes.h"
+
+//------------------------------------------------------------------------
+
+typedef void (*FoFiOutputFunc)(void *stream, char *data, int len);
+
+//------------------------------------------------------------------------
+// FoFiBase
+//------------------------------------------------------------------------
+
+class FoFiBase {
+public:
+
+  virtual ~FoFiBase();
+
+protected:
+
+  FoFiBase(char *fileA, int lenA, GBool freeFileDataA);
+  static char *readFile(char *fileName, int *fileLen);
+
+  // S = signed / U = unsigned
+  // 8/16/32/Var = word length, in bytes
+  // BE = big endian
+  int getS8(int pos, GBool *ok);
+  int getU8(int pos, GBool *ok);
+  int getS16BE(int pos, GBool *ok);
+  int getU16BE(int pos, GBool *ok);
+  int getS32BE(int pos, GBool *ok);
+  Guint getU32BE(int pos, GBool *ok);
+  Guint getUVarBE(int pos, int size, GBool *ok);
+
+  GBool checkRegion(int pos, int size);
+
+  Guchar *fileData;
+  Guchar *file;
+  int len;
+  GBool freeFileData;
+};
+
+#endif
diff --git a/pdf/fofi/FoFiEncodings.cc b/pdf/fofi/FoFiEncodings.cc
new file mode 100644 (file)
index 0000000..37a17f5
--- /dev/null
@@ -0,0 +1,994 @@
+//========================================================================
+//
+// FoFiEncodings.cc
+//
+// Copyright 1999-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <stdlib.h>
+#include "FoFiEncodings.h"
+
+//------------------------------------------------------------------------
+// Type 1 and 1C font data
+//------------------------------------------------------------------------
+
+char *fofiType1StandardEncoding[256] = {
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  "space",
+  "exclam",
+  "quotedbl",
+  "numbersign",
+  "dollar",
+  "percent",
+  "ampersand",
+  "quoteright",
+  "parenleft",
+  "parenright",
+  "asterisk",
+  "plus",
+  "comma",
+  "hyphen",
+  "period",
+  "slash",
+  "zero",
+  "one",
+  "two",
+  "three",
+  "four",
+  "five",
+  "six",
+  "seven",
+  "eight",
+  "nine",
+  "colon",
+  "semicolon",
+  "less",
+  "equal",
+  "greater",
+  "question",
+  "at",
+  "A",
+  "B",
+  "C",
+  "D",
+  "E",
+  "F",
+  "G",
+  "H",
+  "I",
+  "J",
+  "K",
+  "L",
+  "M",
+  "N",
+  "O",
+  "P",
+  "Q",
+  "R",
+  "S",
+  "T",
+  "U",
+  "V",
+  "W",
+  "X",
+  "Y",
+  "Z",
+  "bracketleft",
+  "backslash",
+  "bracketright",
+  "asciicircum",
+  "underscore",
+  "quoteleft",
+  "a",
+  "b",
+  "c",
+  "d",
+  "e",
+  "f",
+  "g",
+  "h",
+  "i",
+  "j",
+  "k",
+  "l",
+  "m",
+  "n",
+  "o",
+  "p",
+  "q",
+  "r",
+  "s",
+  "t",
+  "u",
+  "v",
+  "w",
+  "x",
+  "y",
+  "z",
+  "braceleft",
+  "bar",
+  "braceright",
+  "asciitilde",
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  "exclamdown",
+  "cent",
+  "sterling",
+  "fraction",
+  "yen",
+  "florin",
+  "section",
+  "currency",
+  "quotesingle",
+  "quotedblleft",
+  "guillemotleft",
+  "guilsinglleft",
+  "guilsinglright",
+  "fi",
+  "fl",
+  NULL,
+  "endash",
+  "dagger",
+  "daggerdbl",
+  "periodcentered",
+  NULL,
+  "paragraph",
+  "bullet",
+  "quotesinglbase",
+  "quotedblbase",
+  "quotedblright",
+  "guillemotright",
+  "ellipsis",
+  "perthousand",
+  NULL,
+  "questiondown",
+  NULL,
+  "grave",
+  "acute",
+  "circumflex",
+  "tilde",
+  "macron",
+  "breve",
+  "dotaccent",
+  "dieresis",
+  NULL,
+  "ring",
+  "cedilla",
+  NULL,
+  "hungarumlaut",
+  "ogonek",
+  "caron",
+  "emdash",
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  "AE",
+  NULL,
+  "ordfeminine",
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  "Lslash",
+  "Oslash",
+  "OE",
+  "ordmasculine",
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  "ae",
+  NULL,
+  NULL,
+  NULL,
+  "dotlessi",
+  NULL,
+  NULL,
+  "lslash",
+  "oslash",
+  "oe",
+  "germandbls",
+  NULL,
+  NULL,
+  NULL,
+  NULL
+};
+
+char *fofiType1ExpertEncoding[256] = {
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  "space",
+  "exclamsmall",
+  "Hungarumlautsmall",
+  NULL,
+  "dollaroldstyle",
+  "dollarsuperior",
+  "ampersandsmall",
+  "Acutesmall",
+  "parenleftsuperior",
+  "parenrightsuperior",
+  "twodotenleader",
+  "onedotenleader",
+  "comma",
+  "hyphen",
+  "period",
+  "fraction",
+  "zerooldstyle",
+  "oneoldstyle",
+  "twooldstyle",
+  "threeoldstyle",
+  "fouroldstyle",
+  "fiveoldstyle",
+  "sixoldstyle",
+  "sevenoldstyle",
+  "eightoldstyle",
+  "nineoldstyle",
+  "colon",
+  "semicolon",
+  "commasuperior",
+  "threequartersemdash",
+  "periodsuperior",
+  "questionsmall",
+  NULL,
+  "asuperior",
+  "bsuperior",
+  "centsuperior",
+  "dsuperior",
+  "esuperior",
+  NULL,
+  NULL,
+  NULL,
+  "isuperior",
+  NULL,
+  NULL,
+  "lsuperior",
+  "msuperior",
+  "nsuperior",
+  "osuperior",
+  NULL,
+  NULL,
+  "rsuperior",
+  "ssuperior",
+  "tsuperior",
+  NULL,
+  "ff",
+  "fi",
+  "fl",
+  "ffi",
+  "ffl",
+  "parenleftinferior",
+  NULL,
+  "parenrightinferior",
+  "Circumflexsmall",
+  "hyphensuperior",
+  "Gravesmall",
+  "Asmall",
+  "Bsmall",
+  "Csmall",
+  "Dsmall",
+  "Esmall",
+  "Fsmall",
+  "Gsmall",
+  "Hsmall",
+  "Ismall",
+  "Jsmall",
+  "Ksmall",
+  "Lsmall",
+  "Msmall",
+  "Nsmall",
+  "Osmall",
+  "Psmall",
+  "Qsmall",
+  "Rsmall",
+  "Ssmall",
+  "Tsmall",
+  "Usmall",
+  "Vsmall",
+  "Wsmall",
+  "Xsmall",
+  "Ysmall",
+  "Zsmall",
+  "colonmonetary",
+  "onefitted",
+  "rupiah",
+  "Tildesmall",
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  "exclamdownsmall",
+  "centoldstyle",
+  "Lslashsmall",
+  NULL,
+  NULL,
+  "Scaronsmall",
+  "Zcaronsmall",
+  "Dieresissmall",
+  "Brevesmall",
+  "Caronsmall",
+  NULL,
+  "Dotaccentsmall",
+  NULL,
+  NULL,
+  "Macronsmall",
+  NULL,
+  NULL,
+  "figuredash",
+  "hypheninferior",
+  NULL,
+  NULL,
+  "Ogoneksmall",
+  "Ringsmall",
+  "Cedillasmall",
+  NULL,
+  NULL,
+  NULL,
+  "onequarter",
+  "onehalf",
+  "threequarters",
+  "questiondownsmall",
+  "oneeighth",
+  "threeeighths",
+  "fiveeighths",
+  "seveneighths",
+  "onethird",
+  "twothirds",
+  NULL,
+  NULL,
+  "zerosuperior",
+  "onesuperior",
+  "twosuperior",
+  "threesuperior",
+  "foursuperior",
+  "fivesuperior",
+  "sixsuperior",
+  "sevensuperior",
+  "eightsuperior",
+  "ninesuperior",
+  "zeroinferior",
+  "oneinferior",
+  "twoinferior",
+  "threeinferior",
+  "fourinferior",
+  "fiveinferior",
+  "sixinferior",
+  "seveninferior",
+  "eightinferior",
+  "nineinferior",
+  "centinferior",
+  "dollarinferior",
+  "periodinferior",
+  "commainferior",
+  "Agravesmall",
+  "Aacutesmall",
+  "Acircumflexsmall",
+  "Atildesmall",
+  "Adieresissmall",
+  "Aringsmall",
+  "AEsmall",
+  "Ccedillasmall",
+  "Egravesmall",
+  "Eacutesmall",
+  "Ecircumflexsmall",
+  "Edieresissmall",
+  "Igravesmall",
+  "Iacutesmall",
+  "Icircumflexsmall",
+  "Idieresissmall",
+  "Ethsmall",
+  "Ntildesmall",
+  "Ogravesmall",
+  "Oacutesmall",
+  "Ocircumflexsmall",
+  "Otildesmall",
+  "Odieresissmall",
+  "OEsmall",
+  "Oslashsmall",
+  "Ugravesmall",
+  "Uacutesmall",
+  "Ucircumflexsmall",
+  "Udieresissmall",
+  "Yacutesmall",
+  "Thornsmall",
+  "Ydieresissmall"
+};
+
+//------------------------------------------------------------------------
+// Type 1C font data
+//------------------------------------------------------------------------
+
+char *fofiType1CStdStrings[391] = {
+  ".notdef",
+  "space",
+  "exclam",
+  "quotedbl",
+  "numbersign",
+  "dollar",
+  "percent",
+  "ampersand",
+  "quoteright",
+  "parenleft",
+  "parenright",
+  "asterisk",
+  "plus",
+  "comma",
+  "hyphen",
+  "period",
+  "slash",
+  "zero",
+  "one",
+  "two",
+  "three",
+  "four",
+  "five",
+  "six",
+  "seven",
+  "eight",
+  "nine",
+  "colon",
+  "semicolon",
+  "less",
+  "equal",
+  "greater",
+  "question",
+  "at",
+  "A",
+  "B",
+  "C",
+  "D",
+  "E",
+  "F",
+  "G",
+  "H",
+  "I",
+  "J",
+  "K",
+  "L",
+  "M",
+  "N",
+  "O",
+  "P",
+  "Q",
+  "R",
+  "S",
+  "T",
+  "U",
+  "V",
+  "W",
+  "X",
+  "Y",
+  "Z",
+  "bracketleft",
+  "backslash",
+  "bracketright",
+  "asciicircum",
+  "underscore",
+  "quoteleft",
+  "a",
+  "b",
+  "c",
+  "d",
+  "e",
+  "f",
+  "g",
+  "h",
+  "i",
+  "j",
+  "k",
+  "l",
+  "m",
+  "n",
+  "o",
+  "p",
+  "q",
+  "r",
+  "s",
+  "t",
+  "u",
+  "v",
+  "w",
+  "x",
+  "y",
+  "z",
+  "braceleft",
+  "bar",
+  "braceright",
+  "asciitilde",
+  "exclamdown",
+  "cent",
+  "sterling",
+  "fraction",
+  "yen",
+  "florin",
+  "section",
+  "currency",
+  "quotesingle",
+  "quotedblleft",
+  "guillemotleft",
+  "guilsinglleft",
+  "guilsinglright",
+  "fi",
+  "fl",
+  "endash",
+  "dagger",
+  "daggerdbl",
+  "periodcentered",
+  "paragraph",
+  "bullet",
+  "quotesinglbase",
+  "quotedblbase",
+  "quotedblright",
+  "guillemotright",
+  "ellipsis",
+  "perthousand",
+  "questiondown",
+  "grave",
+  "acute",
+  "circumflex",
+  "tilde",
+  "macron",
+  "breve",
+  "dotaccent",
+  "dieresis",
+  "ring",
+  "cedilla",
+  "hungarumlaut",
+  "ogonek",
+  "caron",
+  "emdash",
+  "AE",
+  "ordfeminine",
+  "Lslash",
+  "Oslash",
+  "OE",
+  "ordmasculine",
+  "ae",
+  "dotlessi",
+  "lslash",
+  "oslash",
+  "oe",
+  "germandbls",
+  "onesuperior",
+  "logicalnot",
+  "mu",
+  "trademark",
+  "Eth",
+  "onehalf",
+  "plusminus",
+  "Thorn",
+  "onequarter",
+  "divide",
+  "brokenbar",
+  "degree",
+  "thorn",
+  "threequarters",
+  "twosuperior",
+  "registered",
+  "minus",
+  "eth",
+  "multiply",
+  "threesuperior",
+  "copyright",
+  "Aacute",
+  "Acircumflex",
+  "Adieresis",
+  "Agrave",
+  "Aring",
+  "Atilde",
+  "Ccedilla",
+  "Eacute",
+  "Ecircumflex",
+  "Edieresis",
+  "Egrave",
+  "Iacute",
+  "Icircumflex",
+  "Idieresis",
+  "Igrave",
+  "Ntilde",
+  "Oacute",
+  "Ocircumflex",
+  "Odieresis",
+  "Ograve",
+  "Otilde",
+  "Scaron",
+  "Uacute",
+  "Ucircumflex",
+  "Udieresis",
+  "Ugrave",
+  "Yacute",
+  "Ydieresis",
+  "Zcaron",
+  "aacute",
+  "acircumflex",
+  "adieresis",
+  "agrave",
+  "aring",
+  "atilde",
+  "ccedilla",
+  "eacute",
+  "ecircumflex",
+  "edieresis",
+  "egrave",
+  "iacute",
+  "icircumflex",
+  "idieresis",
+  "igrave",
+  "ntilde",
+  "oacute",
+  "ocircumflex",
+  "odieresis",
+  "ograve",
+  "otilde",
+  "scaron",
+  "uacute",
+  "ucircumflex",
+  "udieresis",
+  "ugrave",
+  "yacute",
+  "ydieresis",
+  "zcaron",
+  "exclamsmall",
+  "Hungarumlautsmall",
+  "dollaroldstyle",
+  "dollarsuperior",
+  "ampersandsmall",
+  "Acutesmall",
+  "parenleftsuperior",
+  "parenrightsuperior",
+  "twodotenleader",
+  "onedotenleader",
+  "zerooldstyle",
+  "oneoldstyle",
+  "twooldstyle",
+  "threeoldstyle",
+  "fouroldstyle",
+  "fiveoldstyle",
+  "sixoldstyle",
+  "sevenoldstyle",
+  "eightoldstyle",
+  "nineoldstyle",
+  "commasuperior",
+  "threequartersemdash",
+  "periodsuperior",
+  "questionsmall",
+  "asuperior",
+  "bsuperior",
+  "centsuperior",
+  "dsuperior",
+  "esuperior",
+  "isuperior",
+  "lsuperior",
+  "msuperior",
+  "nsuperior",
+  "osuperior",
+  "rsuperior",
+  "ssuperior",
+  "tsuperior",
+  "ff",
+  "ffi",
+  "ffl",
+  "parenleftinferior",
+  "parenrightinferior",
+  "Circumflexsmall",
+  "hyphensuperior",
+  "Gravesmall",
+  "Asmall",
+  "Bsmall",
+  "Csmall",
+  "Dsmall",
+  "Esmall",
+  "Fsmall",
+  "Gsmall",
+  "Hsmall",
+  "Ismall",
+  "Jsmall",
+  "Ksmall",
+  "Lsmall",
+  "Msmall",
+  "Nsmall",
+  "Osmall",
+  "Psmall",
+  "Qsmall",
+  "Rsmall",
+  "Ssmall",
+  "Tsmall",
+  "Usmall",
+  "Vsmall",
+  "Wsmall",
+  "Xsmall",
+  "Ysmall",
+  "Zsmall",
+  "colonmonetary",
+  "onefitted",
+  "rupiah",
+  "Tildesmall",
+  "exclamdownsmall",
+  "centoldstyle",
+  "Lslashsmall",
+  "Scaronsmall",
+  "Zcaronsmall",
+  "Dieresissmall",
+  "Brevesmall",
+  "Caronsmall",
+  "Dotaccentsmall",
+  "Macronsmall",
+  "figuredash",
+  "hypheninferior",
+  "Ogoneksmall",
+  "Ringsmall",
+  "Cedillasmall",
+  "questiondownsmall",
+  "oneeighth",
+  "threeeighths",
+  "fiveeighths",
+  "seveneighths",
+  "onethird",
+  "twothirds",
+  "zerosuperior",
+  "foursuperior",
+  "fivesuperior",
+  "sixsuperior",
+  "sevensuperior",
+  "eightsuperior",
+  "ninesuperior",
+  "zeroinferior",
+  "oneinferior",
+  "twoinferior",
+  "threeinferior",
+  "fourinferior",
+  "fiveinferior",
+  "sixinferior",
+  "seveninferior",
+  "eightinferior",
+  "nineinferior",
+  "centinferior",
+  "dollarinferior",
+  "periodinferior",
+  "commainferior",
+  "Agravesmall",
+  "Aacutesmall",
+  "Acircumflexsmall",
+  "Atildesmall",
+  "Adieresissmall",
+  "Aringsmall",
+  "AEsmall",
+  "Ccedillasmall",
+  "Egravesmall",
+  "Eacutesmall",
+  "Ecircumflexsmall",
+  "Edieresissmall",
+  "Igravesmall",
+  "Iacutesmall",
+  "Icircumflexsmall",
+  "Idieresissmall",
+  "Ethsmall",
+  "Ntildesmall",
+  "Ogravesmall",
+  "Oacutesmall",
+  "Ocircumflexsmall",
+  "Otildesmall",
+  "Odieresissmall",
+  "OEsmall",
+  "Oslashsmall",
+  "Ugravesmall",
+  "Uacutesmall",
+  "Ucircumflexsmall",
+  "Udieresissmall",
+  "Yacutesmall",
+  "Thornsmall",
+  "Ydieresissmall",
+  "001.000",
+  "001.001",
+  "001.002",
+  "001.003",
+  "Black",
+  "Bold",
+  "Book",
+  "Light",
+  "Medium",
+  "Regular",
+  "Roman",
+  "Semibold"
+};
+
+Gushort fofiType1CISOAdobeCharset[229] = {
+    0,   1,   2,   3,   4,   5,   6,   7,   8,   9,
+   10,  11,  12,  13,  14,  15,  16,  17,  18,  19,
+   20,  21,  22,  23,  24,  25,  26,  27,  28,  29,
+   30,  31,  32,  33,  34,  35,  36,  37,  38,  39,
+   40,  41,  42,  43,  44,  45,  46,  47,  48,  49,
+   50,  51,  52,  53,  54,  55,  56,  57,  58,  59,
+   60,  61,  62,  63,  64,  65,  66,  67,  68,  69,
+   70,  71,  72,  73,  74,  75,  76,  77,  78,  79,
+   80,  81,  82,  83,  84,  85,  86,  87,  88,  89,
+   90,  91,  92,  93,  94,  95,  96,  97,  98,  99,
+  100, 101, 102, 103, 104, 105, 106, 107, 108, 109,
+  110, 111, 112, 113, 114, 115, 116, 117, 118, 119,
+  120, 121, 122, 123, 124, 125, 126, 127, 128, 129,
+  130, 131, 132, 133, 134, 135, 136, 137, 138, 139,
+  140, 141, 142, 143, 144, 145, 146, 147, 148, 149,
+  150, 151, 152, 153, 154, 155, 156, 157, 158, 159,
+  160, 161, 162, 163, 164, 165, 166, 167, 168, 169,
+  170, 171, 172, 173, 174, 175, 176, 177, 178, 179,
+  180, 181, 182, 183, 184, 185, 186, 187, 188, 189,
+  190, 191, 192, 193, 194, 195, 196, 197, 198, 199,
+  200, 201, 202, 203, 204, 205, 206, 207, 208, 209,
+  210, 211, 212, 213, 214, 215, 216, 217, 218, 219,
+  220, 221, 222, 223, 224, 225, 226, 227, 228
+};
+
+Gushort fofiType1CExpertCharset[166] = {
+    0,   1, 229, 230, 231, 232, 233, 234, 235, 236,
+  237, 238,  13,  14,  15,  99, 239, 240, 241, 242,
+  243, 244, 245, 246, 247, 248,  27,  28, 249, 250,
+  251, 252, 253, 254, 255, 256, 257, 258, 259, 260,
+  261, 262, 263, 264, 265, 266, 109, 110, 267, 268,
+  269, 270, 271, 272, 273, 274, 275, 276, 277, 278,
+  279, 280, 281, 282, 283, 284, 285, 286, 287, 288,
+  289, 290, 291, 292, 293, 294, 295, 296, 297, 298,
+  299, 300, 301, 302, 303, 304, 305, 306, 307, 308,
+  309, 310, 311, 312, 313, 314, 315, 316, 317, 318,
+  158, 155, 163, 319, 320, 321, 322, 323, 324, 325,
+  326, 150, 164, 169, 327, 328, 329, 330, 331, 332,
+  333, 334, 335, 336, 337, 338, 339, 340, 341, 342,
+  343, 344, 345, 346, 347, 348, 349, 350, 351, 352,
+  353, 354, 355, 356, 357, 358, 359, 360, 361, 362,
+  363, 364, 365, 366, 367, 368, 369, 370, 371, 372,
+  373, 374, 375, 376, 377, 378
+};
+
+Gushort fofiType1CExpertSubsetCharset[87] = {
+    0,   1, 231, 232, 235, 236, 237, 238,  13,  14,
+   15,  99, 239, 240, 241, 242, 243, 244, 245, 246,
+  247, 248,  27,  28, 249, 250, 251, 253, 254, 255,
+  256, 257, 258, 259, 260, 261, 262, 263, 264, 265,
+  266, 109, 110, 267, 268, 269, 270, 272, 300, 301,
+  302, 305, 314, 315, 158, 155, 163, 320, 321, 322,
+  323, 324, 325, 326, 150, 164, 169, 327, 328, 329,
+  330, 331, 332, 333, 334, 335, 336, 337, 338, 339,
+  340, 341, 342, 343, 344, 345, 346
+};
diff --git a/pdf/fofi/FoFiEncodings.h b/pdf/fofi/FoFiEncodings.h
new file mode 100644 (file)
index 0000000..50e285d
--- /dev/null
@@ -0,0 +1,36 @@
+//========================================================================
+//
+// FoFiEncodings.h
+//
+// Copyright 1999-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef FOFIENCODINGS_H
+#define FOFIENCODINGS_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#include "gtypes.h"
+
+//------------------------------------------------------------------------
+// Type 1 and 1C font data
+//------------------------------------------------------------------------
+
+extern char *fofiType1StandardEncoding[256];
+extern char *fofiType1ExpertEncoding[256];
+
+//------------------------------------------------------------------------
+// Type 1C font data
+//------------------------------------------------------------------------
+
+extern char *fofiType1CStdStrings[391];
+extern Gushort fofiType1CISOAdobeCharset[229];
+extern Gushort fofiType1CExpertCharset[166];
+extern Gushort fofiType1CExpertSubsetCharset[87];
+
+#endif
diff --git a/pdf/fofi/FoFiTrueType.cc b/pdf/fofi/FoFiTrueType.cc
new file mode 100644 (file)
index 0000000..a4cf43c
--- /dev/null
@@ -0,0 +1,1438 @@
+//========================================================================
+//
+// FoFiTrueType.cc
+//
+// Copyright 1999-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <stdlib.h>
+#include "gtypes.h"
+#include "gmem.h"
+#include "GString.h"
+#include "GHash.h"
+#include "FoFiTrueType.h"
+
+//
+// Terminology
+// -----------
+//
+// character code = number used as an element of a text string
+//
+// character name = glyph name = name for a particular glyph within a
+//                  font
+//
+// glyph index = GID = position (within some internal table in the font)
+//               where the instructions to draw a particular glyph are
+//               stored
+//
+// Type 1 fonts
+// ------------
+//
+// Type 1 fonts contain:
+//
+// Encoding: array of glyph names, maps char codes to glyph names
+//
+//           Encoding[charCode] = charName
+//
+// CharStrings: dictionary of instructions, keyed by character names,
+//              maps character name to glyph data
+//
+//              CharStrings[charName] = glyphData
+//
+// TrueType fonts
+// --------------
+//
+// TrueType fonts contain:
+//
+// 'cmap' table: mapping from character code to glyph index; there may
+//               be multiple cmaps in a TrueType font
+//
+//               cmap[charCode] = gid
+//
+// 'post' table: mapping from glyph index to glyph name
+//
+//               post[gid] = glyphName
+//
+// Type 42 fonts
+// -------------
+//
+// Type 42 fonts contain:
+//
+// Encoding: array of glyph names, maps char codes to glyph names
+//
+//           Encoding[charCode] = charName
+//
+// CharStrings: dictionary of glyph indexes, keyed by character names,
+//              maps character name to glyph index
+//
+//              CharStrings[charName] = gid
+//
+
+//------------------------------------------------------------------------
+
+struct TrueTypeTable {
+  Guint tag;
+  Guint checksum;
+  int offset;
+  int origOffset;
+  int len;
+};
+
+struct TrueTypeCmap {
+  int platform;
+  int encoding;
+  int offset;
+  int len;
+  int fmt;
+};
+
+struct TrueTypeLoca {
+  int idx;
+  int origOffset;
+  int newOffset;
+  int len;
+};
+
+#define cmapTag 0x636d6170
+#define glyfTag 0x676c7966
+#define locaTag 0x6c6f6361
+#define nameTag 0x6e616d65
+#define postTag 0x706f7374
+
+static int cmpTrueTypeLocaOffset(const void *p1, const void *p2) {
+  TrueTypeLoca *loca1 = (TrueTypeLoca *)p1;
+  TrueTypeLoca *loca2 = (TrueTypeLoca *)p2;
+
+  if (loca1->origOffset == loca2->origOffset) {
+    return loca1->idx - loca2->idx;
+  }
+  return loca1->origOffset - loca2->origOffset;
+}
+
+static int cmpTrueTypeLocaIdx(const void *p1, const void *p2) {
+  TrueTypeLoca *loca1 = (TrueTypeLoca *)p1;
+  TrueTypeLoca *loca2 = (TrueTypeLoca *)p2;
+
+  return loca1->idx - loca2->idx;
+}
+
+static int cmpTrueTypeTableTag(const void *p1, const void *p2) {
+  TrueTypeTable *tab1 = (TrueTypeTable *)p1;
+  TrueTypeTable *tab2 = (TrueTypeTable *)p2;
+
+  return (int)tab1->tag - (int)tab2->tag;
+}
+
+//------------------------------------------------------------------------
+
+struct T42Table {
+  char *tag;                   // 4-byte tag
+  GBool required;              // required by the TrueType spec?
+};
+
+// TrueType tables to be embedded in Type 42 fonts.
+// NB: the table names must be in alphabetical order here.
+#define nT42Tables 11
+static T42Table t42Tables[nT42Tables] = {
+  { "cvt ", gTrue  },
+  { "fpgm", gTrue  },
+  { "glyf", gTrue  },
+  { "head", gTrue  },
+  { "hhea", gTrue  },
+  { "hmtx", gTrue  },
+  { "loca", gTrue  },
+  { "maxp", gTrue  },
+  { "prep", gTrue  },
+  { "vhea", gFalse },
+  { "vmtx", gFalse }
+};
+#define t42HeadTable 3
+#define t42LocaTable 6
+#define t42GlyfTable 2
+
+//------------------------------------------------------------------------
+
+// Glyph names in some arbitrary standard order that Apple uses for
+// their TrueType fonts.
+static char *macGlyphNames[258] = {
+  ".notdef",        "null",           "CR",             "space",
+  "exclam",         "quotedbl",       "numbersign",     "dollar",
+  "percent",        "ampersand",      "quotesingle",    "parenleft",
+  "parenright",     "asterisk",       "plus",           "comma",
+  "hyphen",         "period",         "slash",          "zero",
+  "one",            "two",            "three",          "four",
+  "five",           "six",            "seven",          "eight",
+  "nine",           "colon",          "semicolon",      "less",
+  "equal",          "greater",        "question",       "at",
+  "A",              "B",              "C",              "D",
+  "E",              "F",              "G",              "H",
+  "I",              "J",              "K",              "L",
+  "M",              "N",              "O",              "P",
+  "Q",              "R",              "S",              "T",
+  "U",              "V",              "W",              "X",
+  "Y",              "Z",              "bracketleft",    "backslash",
+  "bracketright",   "asciicircum",    "underscore",     "grave",
+  "a",              "b",              "c",              "d",
+  "e",              "f",              "g",              "h",
+  "i",              "j",              "k",              "l",
+  "m",              "n",              "o",              "p",
+  "q",              "r",              "s",              "t",
+  "u",              "v",              "w",              "x",
+  "y",              "z",              "braceleft",      "bar",
+  "braceright",     "asciitilde",     "Adieresis",      "Aring",
+  "Ccedilla",       "Eacute",         "Ntilde",         "Odieresis",
+  "Udieresis",      "aacute",         "agrave",         "acircumflex",
+  "adieresis",      "atilde",         "aring",          "ccedilla",
+  "eacute",         "egrave",         "ecircumflex",    "edieresis",
+  "iacute",         "igrave",         "icircumflex",    "idieresis",
+  "ntilde",         "oacute",         "ograve",         "ocircumflex",
+  "odieresis",      "otilde",         "uacute",         "ugrave",
+  "ucircumflex",    "udieresis",      "dagger",         "degree",
+  "cent",           "sterling",       "section",        "bullet",
+  "paragraph",      "germandbls",     "registered",     "copyright",
+  "trademark",      "acute",          "dieresis",       "notequal",
+  "AE",             "Oslash",         "infinity",       "plusminus",
+  "lessequal",      "greaterequal",   "yen",            "mu1",
+  "partialdiff",    "summation",      "product",        "pi",
+  "integral",       "ordfeminine",    "ordmasculine",   "Ohm",
+  "ae",             "oslash",         "questiondown",   "exclamdown",
+  "logicalnot",     "radical",        "florin",         "approxequal",
+  "increment",      "guillemotleft",  "guillemotright", "ellipsis",
+  "nbspace",        "Agrave",         "Atilde",         "Otilde",
+  "OE",             "oe",             "endash",         "emdash",
+  "quotedblleft",   "quotedblright",  "quoteleft",      "quoteright",
+  "divide",         "lozenge",        "ydieresis",      "Ydieresis",
+  "fraction",       "currency",       "guilsinglleft",  "guilsinglright",
+  "fi",             "fl",             "daggerdbl",      "periodcentered",
+  "quotesinglbase", "quotedblbase",   "perthousand",    "Acircumflex",
+  "Ecircumflex",    "Aacute",         "Edieresis",      "Egrave",
+  "Iacute",         "Icircumflex",    "Idieresis",      "Igrave",
+  "Oacute",         "Ocircumflex",    "applelogo",      "Ograve",
+  "Uacute",         "Ucircumflex",    "Ugrave",         "dotlessi",
+  "circumflex",     "tilde",          "overscore",      "breve",
+  "dotaccent",      "ring",           "cedilla",        "hungarumlaut",
+  "ogonek",         "caron",          "Lslash",         "lslash",
+  "Scaron",         "scaron",         "Zcaron",         "zcaron",
+  "brokenbar",      "Eth",            "eth",            "Yacute",
+  "yacute",         "Thorn",          "thorn",          "minus",
+  "multiply",       "onesuperior",    "twosuperior",    "threesuperior",
+  "onehalf",        "onequarter",     "threequarters",  "franc",
+  "Gbreve",         "gbreve",         "Idot",           "Scedilla",
+  "scedilla",       "Cacute",         "cacute",         "Ccaron",
+  "ccaron",         "dmacron"
+};
+
+//------------------------------------------------------------------------
+// FoFiTrueType
+//------------------------------------------------------------------------
+
+FoFiTrueType *FoFiTrueType::make(char *fileA, int lenA) {
+  FoFiTrueType *ff;
+
+  ff = new FoFiTrueType(fileA, lenA, gFalse);
+  if (!ff->parsedOk) {
+    delete ff;
+    return NULL;
+  }
+  return ff;
+}
+
+FoFiTrueType *FoFiTrueType::load(char *fileName) {
+  FoFiTrueType *ff;
+  char *fileA;
+  int lenA;
+
+  if (!(fileA = FoFiBase::readFile(fileName, &lenA))) {
+    return NULL;
+  }
+  ff = new FoFiTrueType(fileA, lenA, gTrue);
+  if (!ff->parsedOk) {
+    delete ff;
+    return NULL;
+  }
+  return ff;
+}
+
+FoFiTrueType::FoFiTrueType(char *fileA, int lenA, GBool freeFileDataA):
+  FoFiBase(fileA, lenA, freeFileDataA)
+{
+  tables = NULL;
+  nTables = 0;
+  cmaps = NULL;
+  nCmaps = 0;
+  nameToGID = NULL;
+  parsedOk = gFalse;
+
+  parse();
+}
+
+FoFiTrueType::~FoFiTrueType() {
+  gfree(tables);
+  gfree(cmaps);
+  delete nameToGID;
+}
+
+int FoFiTrueType::getNumCmaps() {
+  return nCmaps;
+}
+
+int FoFiTrueType::getCmapPlatform(int i) {
+  return cmaps[i].platform;
+}
+
+int FoFiTrueType::getCmapEncoding(int i) {
+  return cmaps[i].encoding;
+}
+
+int FoFiTrueType::findCmap(int platform, int encoding) {
+  int i;
+
+  for (i = 0; i < nCmaps; ++i) {
+    if (cmaps[i].platform == platform && cmaps[i].encoding == encoding) {
+      return i;
+    }
+  }
+  return -1;
+}
+
+Gushort FoFiTrueType::mapCodeToGID(int i, int c) {
+  Gushort gid;
+  int segCnt, segEnd, segStart, segDelta, segOffset;
+  int cmapFirst, cmapLen;
+  int pos, a, b, m;
+  GBool ok;
+
+  if (i < 0 || i >= nCmaps) {
+    return 0;
+  }
+  ok = gTrue;
+  pos = cmaps[i].offset;
+  switch (cmaps[i].fmt) {
+  case 0:
+    if (c < 0 || c >= cmaps[i].len - 6) {
+      return 0;
+    }
+    gid = getU8(cmaps[i].offset + 6 + c, &ok);
+    break;
+  case 4:
+    segCnt = getU16BE(pos + 6, &ok) / 2;
+    a = -1;
+    b = segCnt - 1;
+    segEnd = getU16BE(pos + 14 + 2*b, &ok);
+    if (c > segEnd) {
+      // malformed font -- the TrueType spec requires the last segEnd
+      // to be 0xffff
+      return 0;
+    }
+    // invariant: seg[a].end < code <= seg[b].end
+    while (b - a > 1 && ok) {
+      m = (a + b) / 2;
+      segEnd = getU16BE(pos + 14 + 2*m, &ok);
+      if (segEnd < c) {
+       a = m;
+      } else {
+       b = m;
+      }
+    }
+    segStart = getU16BE(pos + 16 + 2*segCnt + 2*b, &ok);
+    segDelta = getU16BE(pos + 16 + 4*segCnt + 2*b, &ok);
+    segOffset = getU16BE(pos + 16 + 6*segCnt + 2*b, &ok);
+    if (c < segStart) {
+      return 0;
+    }
+    if (segOffset == 0) {
+      gid = (c + segDelta) & 0xffff;
+    } else {
+      gid = getU16BE(pos + 16 + 6*segCnt + 2*b +
+                      segOffset + 2 * (c - segStart), &ok);
+      if (gid != 0) {
+       gid = (gid + segDelta) & 0xffff;
+      }
+    }
+    break;
+  case 6:
+    cmapFirst = getU16BE(pos + 6, &ok);
+    cmapLen = getU16BE(pos + 8, &ok);
+    if (c < cmapFirst || c >= cmapFirst + cmapLen) {
+      return 0;
+    }
+    gid = getU16BE(pos + 10 + 2 * (c - cmapFirst), &ok);
+    break;
+  default:
+    return 0;
+  }
+  if (!ok) {
+    return 0;
+  }
+  return gid;
+}
+
+int FoFiTrueType::mapNameToGID(char *name) {
+  if (!nameToGID) {
+    return 0;
+  }
+  return nameToGID->lookupInt(name);
+}
+
+int FoFiTrueType::getEmbeddingRights() {
+  int i, fsType;
+  GBool ok;
+
+  if ((i = seekTable("OS/2")) < 0) {
+    return 4;
+  }
+  ok = gTrue;
+  fsType = getU16BE(tables[i].offset + 8, &ok);
+  if (!ok) {
+    return 4;
+  }
+  if (fsType & 0x0008) {
+    return 2;
+  }
+  if (fsType & 0x0004) {
+    return 1;
+  }
+  if (fsType & 0x0002) {
+    return 0;
+  }
+  return 3;
+}
+
+void FoFiTrueType::convertToType42(char *psName, char **encoding,
+                                  Gushort *codeToGID,
+                                  FoFiOutputFunc outputFunc,
+                                  void *outputStream) {
+  char buf[512];
+  GBool ok;
+
+  // write the header
+  ok = gTrue;
+  sprintf(buf, "%%!PS-TrueTypeFont-%g\n", (double)getS32BE(0, &ok) / 65536.0);
+  (*outputFunc)(outputStream, buf, strlen(buf));
+
+  // begin the font dictionary
+  (*outputFunc)(outputStream, "10 dict begin\n", 14);
+  (*outputFunc)(outputStream, "/FontName /", 11);
+  (*outputFunc)(outputStream, psName, strlen(psName));
+  (*outputFunc)(outputStream, " def\n", 5);
+  (*outputFunc)(outputStream, "/FontType 42 def\n", 17);
+  (*outputFunc)(outputStream, "/FontMatrix [1 0 0 1 0 0] def\n", 30);
+  sprintf(buf, "/FontBBox [%d %d %d %d] def\n",
+         bbox[0], bbox[1], bbox[2], bbox[3]);
+  (*outputFunc)(outputStream, buf, strlen(buf));
+  (*outputFunc)(outputStream, "/PaintType 0 def\n", 17);
+
+  // write the guts of the dictionary
+  cvtEncoding(encoding, outputFunc, outputStream);
+  cvtCharStrings(encoding, codeToGID, outputFunc, outputStream);
+  cvtSfnts(outputFunc, outputStream, NULL);
+
+  // end the dictionary and define the font
+  (*outputFunc)(outputStream, "FontName currentdict end definefont pop\n", 40);
+}
+
+void FoFiTrueType::convertToCIDType2(char *psName,
+                                    Gushort *cidMap, int nCIDs,
+                                    FoFiOutputFunc outputFunc,
+                                    void *outputStream) {
+  char buf[512];
+  Gushort cid;
+  GBool ok;
+  int i, j, k;
+
+  // write the header
+  ok = gTrue;
+  sprintf(buf, "%%!PS-TrueTypeFont-%g\n", (double)getS32BE(0, &ok) / 65536.0);
+  (*outputFunc)(outputStream, buf, strlen(buf));
+
+  // begin the font dictionary
+  (*outputFunc)(outputStream, "20 dict begin\n", 14);
+  (*outputFunc)(outputStream, "/CIDFontName /", 14);
+  (*outputFunc)(outputStream, psName, strlen(psName));
+  (*outputFunc)(outputStream, " def\n", 5);
+  (*outputFunc)(outputStream, "/CIDFontType 2 def\n", 19);
+  (*outputFunc)(outputStream, "/FontType 42 def\n", 17);
+  (*outputFunc)(outputStream, "/CIDSystemInfo 3 dict dup begin\n", 32);
+  (*outputFunc)(outputStream, "  /Registry (Adobe) def\n", 24);
+  (*outputFunc)(outputStream, "  /Ordering (Identity) def\n", 27);
+  (*outputFunc)(outputStream, "  /Supplement 0 def\n", 20);
+  (*outputFunc)(outputStream, "  end def\n", 10);
+  (*outputFunc)(outputStream, "/GDBytes 2 def\n", 15);
+  if (cidMap) {
+    sprintf(buf, "/CIDCount %d def\n", nCIDs);
+    (*outputFunc)(outputStream, buf, strlen(buf));
+    if (nCIDs > 32767) {
+      (*outputFunc)(outputStream, "/CIDMap [", 9);
+      for (i = 0; i < nCIDs; i += 32768 - 16) {
+       (*outputFunc)(outputStream, "<\n", 2);
+       for (j = 0; j < 32768 - 16 && i+j < nCIDs; j += 16) {
+         (*outputFunc)(outputStream, "  ", 2);
+         for (k = 0; k < 16 && i+j+k < nCIDs; ++k) {
+           cid = cidMap[i+j+k];
+           sprintf(buf, "%02x%02x", (cid >> 8) & 0xff, cid & 0xff);
+           (*outputFunc)(outputStream, buf, strlen(buf));
+         }
+         (*outputFunc)(outputStream, "\n", 1);
+       }
+       (*outputFunc)(outputStream, "  >", 3);
+      }
+      (*outputFunc)(outputStream, "\n", 1);
+      (*outputFunc)(outputStream, "] def\n", 6);
+    } else {
+      (*outputFunc)(outputStream, "/CIDMap <\n", 10);
+      for (i = 0; i < nCIDs; i += 16) {
+       (*outputFunc)(outputStream, "  ", 2);
+       for (j = 0; j < 16 && i+j < nCIDs; ++j) {
+         cid = cidMap[i+j];
+         sprintf(buf, "%02x%02x", (cid >> 8) & 0xff, cid & 0xff);
+         (*outputFunc)(outputStream, buf, strlen(buf));
+       }
+       (*outputFunc)(outputStream, "\n", 1);
+      }
+      (*outputFunc)(outputStream, "> def\n", 6);
+    }
+  } else {
+    // direct mapping - just fill the string(s) with s[i]=i
+    sprintf(buf, "/CIDCount %d def\n", nGlyphs);
+    (*outputFunc)(outputStream, buf, strlen(buf));
+    if (nGlyphs > 32767) {
+      (*outputFunc)(outputStream, "/CIDMap [\n", 10);
+      for (i = 0; i < nGlyphs; i += 32767) {
+       j = nGlyphs - i < 32767 ? nGlyphs - i : 32767;
+       sprintf(buf, "  %d string 0 1 %d {\n", 2 * j, j - 1);
+       (*outputFunc)(outputStream, buf, strlen(buf));
+       sprintf(buf, "    2 copy dup 2 mul exch %d add -8 bitshift put\n", i);
+       (*outputFunc)(outputStream, buf, strlen(buf));
+       sprintf(buf, "    1 index exch dup 2 mul 1 add exch %d add"
+               " 255 and put\n", i);
+       (*outputFunc)(outputStream, buf, strlen(buf));
+       (*outputFunc)(outputStream, "  } for\n", 8);
+      }
+      (*outputFunc)(outputStream, "] def\n", 6);
+    } else {
+      sprintf(buf, "/CIDMap %d string\n", 2 * nGlyphs);
+      (*outputFunc)(outputStream, buf, strlen(buf));
+      sprintf(buf, "  0 1 %d {\n", nGlyphs - 1);
+      (*outputFunc)(outputStream, buf, strlen(buf));
+      (*outputFunc)(outputStream,
+                   "    2 copy dup 2 mul exch -8 bitshift put\n", 42);
+      (*outputFunc)(outputStream,
+                   "    1 index exch dup 2 mul 1 add exch 255 and put\n", 50);
+      (*outputFunc)(outputStream, "  } for\n", 8);
+      (*outputFunc)(outputStream, "def\n", 4);
+    }
+  }
+  (*outputFunc)(outputStream, "/FontMatrix [1 0 0 1 0 0] def\n", 30);
+  sprintf(buf, "/FontBBox [%d %d %d %d] def\n",
+         bbox[0], bbox[1], bbox[2], bbox[3]);
+  (*outputFunc)(outputStream, buf, strlen(buf));
+  (*outputFunc)(outputStream, "/PaintType 0 def\n", 17);
+  (*outputFunc)(outputStream, "/Encoding [] readonly def\n", 26);
+  (*outputFunc)(outputStream, "/CharStrings 1 dict dup begin\n", 30);
+  (*outputFunc)(outputStream, "  /.notdef 0 def\n", 17);
+  (*outputFunc)(outputStream, "  end readonly def\n", 19);
+
+  // write the guts of the dictionary
+  cvtSfnts(outputFunc, outputStream, NULL);
+
+  // end the dictionary and define the font
+  (*outputFunc)(outputStream,
+               "CIDFontName currentdict end /CIDFont defineresource pop\n",
+               56);
+}
+
+void FoFiTrueType::convertToType0(char *psName, Gushort *cidMap, int nCIDs,
+                                 FoFiOutputFunc outputFunc,
+                                 void *outputStream) {
+  char buf[512];
+  GString *sfntsName;
+  int n, i, j;
+
+  // write the Type 42 sfnts array
+  sfntsName = (new GString(psName))->append("_sfnts");
+  cvtSfnts(outputFunc, outputStream, sfntsName);
+  delete sfntsName;
+
+  // write the descendant Type 42 fonts
+  n = cidMap ? nCIDs : nGlyphs;
+  for (i = 0; i < n; i += 256) {
+    (*outputFunc)(outputStream, "10 dict begin\n", 14);
+    (*outputFunc)(outputStream, "/FontName /", 11);
+    (*outputFunc)(outputStream, psName, strlen(psName));
+    sprintf(buf, "_%02x def\n", i >> 8);
+    (*outputFunc)(outputStream, buf, strlen(buf));
+    (*outputFunc)(outputStream, "/FontType 42 def\n", 17);
+    (*outputFunc)(outputStream, "/FontMatrix [1 0 0 1 0 0] def\n", 30);
+    sprintf(buf, "/FontBBox [%d %d %d %d] def\n",
+           bbox[0], bbox[1], bbox[2], bbox[3]);
+    (*outputFunc)(outputStream, buf, strlen(buf));
+    (*outputFunc)(outputStream, "/PaintType 0 def\n", 17);
+    (*outputFunc)(outputStream, "/sfnts ", 7);
+    (*outputFunc)(outputStream, psName, strlen(psName));
+    (*outputFunc)(outputStream, "_sfnts def\n", 11);
+    (*outputFunc)(outputStream, "/Encoding 256 array\n", 20);
+    for (j = 0; j < 256 && i+j < n; ++j) {
+      sprintf(buf, "dup %d /c%02x put\n", j, j);
+      (*outputFunc)(outputStream, buf, strlen(buf));
+    }
+    (*outputFunc)(outputStream, "readonly def\n", 13);
+    (*outputFunc)(outputStream, "/CharStrings 257 dict dup begin\n", 32);
+    (*outputFunc)(outputStream, "/.notdef 0 def\n", 15);
+    for (j = 0; j < 256 && i+j < n; ++j) {
+      sprintf(buf, "/c%02x %d def\n", j, cidMap ? cidMap[i+j] : i+j);
+      (*outputFunc)(outputStream, buf, strlen(buf));
+    }
+    (*outputFunc)(outputStream, "end readonly def\n", 17);
+    (*outputFunc)(outputStream,
+                 "FontName currentdict end definefont pop\n", 40);
+  }
+
+  // write the Type 0 parent font
+  (*outputFunc)(outputStream, "16 dict begin\n", 14);
+  (*outputFunc)(outputStream, "/FontName /", 11);
+  (*outputFunc)(outputStream, psName, strlen(psName));
+  (*outputFunc)(outputStream, " def\n", 5);
+  (*outputFunc)(outputStream, "/FontType 0 def\n", 16);
+  (*outputFunc)(outputStream, "/FontMatrix [1 0 0 1 0 0] def\n", 30);
+  (*outputFunc)(outputStream, "/FMapType 2 def\n", 16);
+  (*outputFunc)(outputStream, "/Encoding [\n", 12);
+  for (i = 0; i < n; i += 256) {
+    sprintf(buf, "%d\n", i >> 8);
+    (*outputFunc)(outputStream, buf, strlen(buf));
+  }
+  (*outputFunc)(outputStream, "] def\n", 6);
+  (*outputFunc)(outputStream, "/FDepVector [\n", 14);
+  for (i = 0; i < n; i += 256) {
+    (*outputFunc)(outputStream, "/", 1);
+    (*outputFunc)(outputStream, psName, strlen(psName));
+    sprintf(buf, "_%02x findfont\n", i >> 8);
+    (*outputFunc)(outputStream, buf, strlen(buf));
+  }
+  (*outputFunc)(outputStream, "] def\n", 6);
+  (*outputFunc)(outputStream, "FontName currentdict end definefont pop\n", 40);
+}
+
+void FoFiTrueType::writeTTF(FoFiOutputFunc outputFunc,
+                           void *outputStream) {
+  static char cmapTab[20] = {
+    0, 0,                      // table version number
+    0, 1,                      // number of encoding tables
+    0, 1,                      // platform ID
+    0, 0,                      // encoding ID
+    0, 0, 0, 12,               // offset of subtable
+    0, 0,                      // subtable format
+    0, 1,                      // subtable length
+    0, 1,                      // subtable version
+    0,                         // map char 0 -> glyph 0
+    0                          // pad to multiple of four bytes
+  };
+  static char nameTab[8] = {
+    0, 0,                      // format
+    0, 0,                      // number of name records
+    0, 6,                      // offset to start of string storage
+    0, 0                       // pad to multiple of four bytes
+  };
+  static char postTab[32] = {
+    0, 1, 0, 0,                        // format
+    0, 0, 0, 0,                        // italic angle
+    0, 0,                      // underline position
+    0, 0,                      // underline thickness
+    0, 0, 0, 0,                        // fixed pitch
+    0, 0, 0, 0,                        // min Type 42 memory
+    0, 0, 0, 0,                        // max Type 42 memory
+    0, 0, 0, 0,                        // min Type 1 memory
+    0, 0, 0, 0                 // max Type 1 memory
+  };
+  GBool missingCmap, missingName, missingPost, unsortedLoca, badCmapLen;
+  int nZeroLengthTables;
+  TrueTypeLoca *locaTable;
+  TrueTypeTable *newTables;
+  int nNewTables, cmapIdx, cmapLen, glyfLen;
+  char *tableDir;
+  char locaBuf[4];
+  GBool ok;
+  Guint t;
+  int pos, i, j, k, n;
+
+  // check for missing tables
+  missingCmap = (cmapIdx = seekTable("cmap")) < 0;
+  missingName = seekTable("name") < 0;
+  missingPost = seekTable("post") < 0;
+
+  // read the loca table, check to see if it's sorted
+  locaTable = (TrueTypeLoca *)gmalloc((nGlyphs + 1) * sizeof(TrueTypeLoca));
+  unsortedLoca = gFalse;
+  i = seekTable("loca");
+  pos = tables[i].offset;
+  ok = gTrue;
+  for (i = 0; i <= nGlyphs; ++i) {
+    if (locaFmt) {
+      locaTable[i].origOffset = (int)getU32BE(pos + i*4, &ok);
+    } else {
+      locaTable[i].origOffset = 2 * getU16BE(pos + i*2, &ok);
+    }
+    if (i > 0 && locaTable[i].origOffset < locaTable[i-1].origOffset) {
+      unsortedLoca = gTrue;
+    }
+    locaTable[i].idx = i;
+  }
+
+  // check for zero-length tables
+  nZeroLengthTables = 0;
+  for (i = 0; i < nTables; ++i) {
+    if (tables[i].len == 0) {
+      ++nZeroLengthTables;
+    }
+  }
+
+  // check for an incorrect cmap table length
+  badCmapLen = gFalse;
+  cmapLen = 0; // make gcc happy
+  if (!missingCmap) {
+    cmapLen = cmaps[0].offset + cmaps[0].len;
+    for (i = 1; i < nCmaps; ++i) {
+      if (cmaps[i].offset + cmaps[i].len > cmapLen) {
+       cmapLen = cmaps[i].offset + cmaps[i].len;
+      }
+    }
+    cmapLen -= tables[cmapIdx].offset;
+    if (cmapLen > tables[cmapIdx].len) {
+      badCmapLen = gTrue;
+    }
+  }
+
+  // if nothing is broken, just write the TTF file as is
+  if (!missingCmap && !missingName && !missingPost && !unsortedLoca &&
+      !badCmapLen && nZeroLengthTables == 0) {
+    (*outputFunc)(outputStream, (char *)file, len);
+    goto done1;
+  }
+
+  // sort the 'loca' table: some (non-compliant) fonts have
+  // out-of-order loca tables; in order to correctly handle the case
+  // where (compliant) fonts have empty entries in the middle of the
+  // table, cmpTrueTypeLocaOffset uses offset as its primary sort key,
+  // and idx as its secondary key (ensuring that adjacent entries with
+  // the same pos value remain in the same order)
+  glyfLen = 0; // make gcc happy
+  if (unsortedLoca) {
+    qsort(locaTable, nGlyphs + 1, sizeof(TrueTypeLoca),
+         &cmpTrueTypeLocaOffset);
+    for (i = 0; i < nGlyphs; ++i) {
+      locaTable[i].len = locaTable[i+1].origOffset - locaTable[i].origOffset;
+    }
+    locaTable[nGlyphs].len = 0;
+    qsort(locaTable, nGlyphs + 1, sizeof(TrueTypeLoca),
+         &cmpTrueTypeLocaIdx);
+    pos = 0;
+    for (i = 0; i <= nGlyphs; ++i) {
+      locaTable[i].newOffset = pos;
+      pos += locaTable[i].len;
+      if (pos & 3) {
+       pos += 4 - (pos & 3);
+      }
+    }
+    glyfLen = pos;
+  }
+
+  // construct the new table directory:
+  // - keep all original tables with non-zero length
+  // - fix the cmap table's length, if necessary
+  // - add missing tables
+  // - sort the table by tag
+  // - compute new table positions, including 4-byte alignment
+  nNewTables = nTables - nZeroLengthTables +
+               (missingCmap ? 1 : 0) + (missingName ? 1 : 0) +
+               (missingPost ? 1 : 0);
+  newTables = (TrueTypeTable *)gmalloc(nNewTables * sizeof(TrueTypeTable));
+  j = 0;
+  for (i = 0; i < nTables; ++i) {
+    if (tables[i].len > 0) {
+      newTables[j] = tables[i];
+      newTables[j].origOffset = tables[i].offset;
+      if (newTables[j].tag == cmapTag && badCmapLen) {
+       newTables[j].len = cmapLen;
+      } else if (newTables[j].tag == locaTag && unsortedLoca) {
+       newTables[j].len = (nGlyphs + 1) * (locaFmt ? 4 : 2);
+      } else if (newTables[j].tag == glyfTag && unsortedLoca) {
+       newTables[j].len = glyfLen;
+      }
+      ++j;
+    }
+  }
+  if (missingCmap) {
+    newTables[j].tag = cmapTag;
+    newTables[j].checksum = 0; //~ should compute the checksum
+    newTables[j].len = sizeof(cmapTab);
+    ++j;
+  }
+  if (missingName) {
+    newTables[j].tag = nameTag;
+    newTables[j].checksum = 0; //~ should compute the checksum
+    newTables[j].len = sizeof(nameTab);
+    ++j;
+  }
+  if (missingPost) {
+    newTables[j].tag = postTag;
+    newTables[j].checksum = 0; //~ should compute the checksum
+    newTables[j].len = sizeof(postTab);
+    ++j;
+  }
+  qsort(newTables, nNewTables, sizeof(TrueTypeTable),
+       &cmpTrueTypeTableTag);
+  pos = 12 + nNewTables * 16;
+  for (i = 0; i < nNewTables; ++i) {
+    newTables[i].offset = pos;
+    pos += newTables[i].len;
+    if (pos & 3) {
+      pos += 4 - (pos & 3);
+    }
+  }
+
+  // write the table directory
+  tableDir = (char *)gmalloc(12 + nNewTables * 16);
+  tableDir[0] = 0x00;                                  // sfnt version
+  tableDir[1] = 0x01;
+  tableDir[2] = 0x00;
+  tableDir[3] = 0x00;
+  tableDir[4] = (char)((nNewTables >> 8) & 0xff);      // numTables
+  tableDir[5] = (char)(nNewTables & 0xff);
+  for (i = -1, t = (Guint)nNewTables; t; ++i, t >>= 1) ;
+  t = 1 << (4 + i);
+  tableDir[6] = (char)((t >> 8) & 0xff);               // searchRange
+  tableDir[7] = (char)(t & 0xff);
+  tableDir[8] = (char)((i >> 8) & 0xff);               // entrySelector
+  tableDir[9] = (char)(i & 0xff);
+  t = nNewTables * 16 - t;
+  tableDir[10] = (char)((t >> 8) & 0xff);              // rangeShift
+  tableDir[11] = (char)(t & 0xff);
+  pos = 12;
+  for (i = 0; i < nNewTables; ++i) {
+    tableDir[pos   ] = (char)(newTables[i].tag >> 24);
+    tableDir[pos+ 1] = (char)(newTables[i].tag >> 16);
+    tableDir[pos+ 2] = (char)(newTables[i].tag >>  8);
+    tableDir[pos+ 3] = (char) newTables[i].tag;
+    tableDir[pos+ 4] = (char)(newTables[i].checksum >> 24);
+    tableDir[pos+ 5] = (char)(newTables[i].checksum >> 16);
+    tableDir[pos+ 6] = (char)(newTables[i].checksum >>  8);
+    tableDir[pos+ 7] = (char) newTables[i].checksum;
+    tableDir[pos+ 8] = (char)(newTables[i].offset >> 24);
+    tableDir[pos+ 9] = (char)(newTables[i].offset >> 16);
+    tableDir[pos+10] = (char)(newTables[i].offset >>  8);
+    tableDir[pos+11] = (char) newTables[i].offset;
+    tableDir[pos+12] = (char)(newTables[i].len >> 24);
+    tableDir[pos+13] = (char)(newTables[i].len >> 16);
+    tableDir[pos+14] = (char)(newTables[i].len >>  8);
+    tableDir[pos+15] = (char) newTables[i].len;
+    pos += 16;
+  }
+  (*outputFunc)(outputStream, tableDir, 12 + nNewTables * 16);
+
+  // write the tables
+  for (i = 0; i < nNewTables; ++i) {
+    if (newTables[i].tag == cmapTag && missingCmap) {
+      (*outputFunc)(outputStream, cmapTab, newTables[i].len);
+    } else if (newTables[i].tag == nameTag && missingName) {
+      (*outputFunc)(outputStream, nameTab, newTables[i].len);
+    } else if (newTables[i].tag == postTag && missingPost) {
+      (*outputFunc)(outputStream, postTab, newTables[i].len);
+    } else if (newTables[i].tag == locaTag && unsortedLoca) {
+      for (j = 0; j <= nGlyphs; ++j) {
+       if (locaFmt) {
+         locaBuf[0] = (char)(locaTable[j].newOffset >> 24);
+         locaBuf[1] = (char)(locaTable[j].newOffset >> 16);
+         locaBuf[2] = (char)(locaTable[j].newOffset >>  8);
+         locaBuf[3] = (char) locaTable[j].newOffset;
+         (*outputFunc)(outputStream, locaBuf, 4);
+       } else {
+         locaBuf[0] = (char)(locaTable[j].newOffset >> 9);
+         locaBuf[1] = (char)(locaTable[j].newOffset >> 1);
+         (*outputFunc)(outputStream, locaBuf, 2);
+       }
+      }
+    } else if (newTables[i].tag == glyfTag && unsortedLoca) {
+      pos = tables[seekTable("glyf")].offset;
+      for (j = 0; j < nGlyphs; ++j) {
+       n = locaTable[j].len;
+       if (n > 0) {
+         k = locaTable[j].origOffset;
+         if (checkRegion(pos + k, n)) {
+           (*outputFunc)(outputStream, (char *)file + pos + k, n);
+         } else {
+           for (k = 0; k < n; ++k) {
+             (*outputFunc)(outputStream, "\0", 1);
+           }
+         }
+         if ((k = locaTable[j].len & 3)) {
+           (*outputFunc)(outputStream, "\0\0\0\0", 4 - k);
+         }
+       }
+      }
+    } else {
+      if (checkRegion(newTables[i].origOffset, newTables[i].len)) {
+       (*outputFunc)(outputStream, (char *)file + newTables[i].origOffset,
+                     newTables[i].len);
+      } else {
+       for (j = 0; j < newTables[i].len; ++j) {
+         (*outputFunc)(outputStream, "\0", 1);
+       }
+      }
+    }
+    if (newTables[i].len & 3) {
+      (*outputFunc)(outputStream, "\0\0\0", 4 - (newTables[i].len & 3));
+    }
+  }
+
+  gfree(tableDir);
+  gfree(newTables);
+ done1:
+  gfree(locaTable);
+}
+
+void FoFiTrueType::cvtEncoding(char **encoding,
+                              FoFiOutputFunc outputFunc,
+                              void *outputStream) {
+  char *name;
+  char buf[64];
+  int i;
+
+  (*outputFunc)(outputStream, "/Encoding 256 array\n", 20);
+  if (encoding) {
+    for (i = 0; i < 256; ++i) {
+      if (!(name = encoding[i])) {
+       name = ".notdef";
+      }
+      sprintf(buf, "dup %d /", i);
+      (*outputFunc)(outputStream, buf, strlen(buf));
+      (*outputFunc)(outputStream, name, strlen(name));
+      (*outputFunc)(outputStream, " put\n", 5);
+    }
+  } else {
+    for (i = 0; i < 256; ++i) {
+      sprintf(buf, "dup %d /c%02x put\n", i, i);
+      (*outputFunc)(outputStream, buf, strlen(buf));
+    }
+  }
+  (*outputFunc)(outputStream, "readonly def\n", 13);
+}
+
+void FoFiTrueType::cvtCharStrings(char **encoding,
+                                 Gushort *codeToGID,
+                                 FoFiOutputFunc outputFunc,
+                                 void *outputStream) {
+  char *name;
+  char buf[64], buf2[16];
+  int i, k;
+
+  // always define '.notdef'
+  (*outputFunc)(outputStream, "/CharStrings 256 dict dup begin\n", 32);
+  (*outputFunc)(outputStream, "/.notdef 0 def\n", 15);
+
+  // if there's no 'cmap' table, punt
+  if (nCmaps == 0) {
+    goto err;
+  }
+
+  // map char name to glyph index:
+  // 1. use encoding to map name to char code
+  // 2. use codeToGID to map char code to glyph index
+  // N.B. We do this in reverse order because font subsets can have
+  //      weird encodings that use the same character name twice, and
+  //      the first definition is probably the one we want.
+  k = 0; // make gcc happy
+  for (i = 255; i >= 0; --i) {
+    if (encoding) {
+      name = encoding[i];
+    } else {
+      sprintf(buf2, "c%02x", i);
+      name = buf2;
+    }
+    if (name && strcmp(name, ".notdef")) {
+      k = codeToGID[i];
+      // note: Distiller (maybe Adobe's PS interpreter in general)
+      // doesn't like TrueType fonts that have CharStrings entries
+      // which point to nonexistent glyphs, hence the (k < nGlyphs)
+      // test
+      if (k > 0 && k < nGlyphs) {
+       (*outputFunc)(outputStream, "/", 1);
+       (*outputFunc)(outputStream, name, strlen(name));
+       sprintf(buf, " %d def\n", k);
+       (*outputFunc)(outputStream, buf, strlen(buf));
+      }
+    }
+  }
+
+ err:
+  (*outputFunc)(outputStream, "end readonly def\n", 17);
+}
+
+void FoFiTrueType::cvtSfnts(FoFiOutputFunc outputFunc,
+                           void *outputStream, GString *name) {
+  Guchar headData[54];
+  TrueTypeLoca *locaTable;
+  Guchar *locaData;
+  TrueTypeTable newTables[nT42Tables];
+  Guchar tableDir[12 + nT42Tables*16];
+  GBool ok;
+  Guint checksum;
+  int nNewTables;
+  int length, pos, glyfPos, i, j, k;
+
+  // construct the 'head' table, zero out the font checksum
+  i = seekTable("head");
+  pos = tables[i].offset;
+  if (!checkRegion(pos, 54)) {
+    return;
+  }
+  memcpy(headData, file + pos, 54);
+  headData[8] = headData[9] = headData[10] = headData[11] = (Guchar)0;
+
+  // read the original 'loca' table, pad entries out to 4 bytes, and
+  // sort it into proper order -- some (non-compliant) fonts have
+  // out-of-order loca tables; in order to correctly handle the case
+  // where (compliant) fonts have empty entries in the middle of the
+  // table, cmpTrueTypeLocaPos uses offset as its primary sort key,
+  // and idx as its secondary key (ensuring that adjacent entries with
+  // the same pos value remain in the same order)
+  locaTable = (TrueTypeLoca *)gmalloc((nGlyphs + 1) * sizeof(TrueTypeLoca));
+  i = seekTable("loca");
+  pos = tables[i].offset;
+  ok = gTrue;
+  for (i = 0; i <= nGlyphs; ++i) {
+    locaTable[i].idx = i;
+    if (locaFmt) {
+      locaTable[i].origOffset = (int)getU32BE(pos + i*4, &ok);
+    } else {
+      locaTable[i].origOffset = 2 * getU16BE(pos + i*2, &ok);
+    }
+  }
+  qsort(locaTable, nGlyphs + 1, sizeof(TrueTypeLoca),
+       &cmpTrueTypeLocaOffset);
+  for (i = 0; i < nGlyphs; ++i) {
+    locaTable[i].len = locaTable[i+1].origOffset - locaTable[i].origOffset;
+  }
+  locaTable[nGlyphs].len = 0;
+  qsort(locaTable, nGlyphs + 1, sizeof(TrueTypeLoca),
+       &cmpTrueTypeLocaIdx);
+  pos = 0;
+  for (i = 0; i <= nGlyphs; ++i) {
+    locaTable[i].newOffset = pos;
+    pos += locaTable[i].len;
+    if (pos & 3) {
+      pos += 4 - (pos & 3);
+    }
+  }
+
+  // construct the new 'loca' table
+  locaData = (Guchar *)gmalloc((nGlyphs + 1) * (locaFmt ? 4 : 2));
+  for (i = 0; i <= nGlyphs; ++i) {
+    pos = locaTable[i].newOffset;
+    if (locaFmt) {
+      locaData[4*i  ] = (Guchar)(pos >> 24);
+      locaData[4*i+1] = (Guchar)(pos >> 16);
+      locaData[4*i+2] = (Guchar)(pos >>  8);
+      locaData[4*i+3] = (Guchar) pos;
+    } else {
+      locaData[2*i  ] = (Guchar)(pos >> 9);
+      locaData[2*i+1] = (Guchar)(pos >> 1);
+    }
+  }
+
+  // count the number of tables
+  nNewTables = 0;
+  for (i = 0; i < nT42Tables; ++i) {
+    if (t42Tables[i].required ||
+       seekTable(t42Tables[i].tag) >= 0) {
+      ++nNewTables;
+    }
+  }
+
+  // construct the new table headers, including table checksums
+  // (pad each table out to a multiple of 4 bytes)
+  pos = 12 + nNewTables*16;
+  k = 0;
+  for (i = 0; i < nT42Tables; ++i) {
+    length = -1;
+    checksum = 0; // make gcc happy
+    if (i == t42HeadTable) {
+      length = 54;
+      checksum = computeTableChecksum(headData, 54);
+    } else if (i == t42LocaTable) {
+      length = (nGlyphs + 1) * (locaFmt ? 4 : 2);
+      checksum = computeTableChecksum(locaData, length);
+    } else if (i == t42GlyfTable) {
+      length = 0;
+      checksum = 0;
+      glyfPos = tables[seekTable("glyf")].offset;
+      for (j = 0; j < nGlyphs; ++j) {
+       length += locaTable[j].len;
+       if (length & 3) {
+         length += 4 - (length & 3);
+       }
+       if (checkRegion(glyfPos + locaTable[j].origOffset, locaTable[j].len)) {
+         checksum +=
+             computeTableChecksum(file + glyfPos + locaTable[j].origOffset,
+                                  locaTable[j].len);
+       }
+      }
+    } else {
+      if ((j = seekTable(t42Tables[i].tag)) >= 0) {
+       length = tables[j].len;
+       if (checkRegion(tables[j].offset, length)) {
+         checksum = computeTableChecksum(file + tables[j].offset, length);
+       }
+      } else if (t42Tables[i].required) {
+       //~ error(-1, "Embedded TrueType font is missing a required table ('%s')",
+       //~       t42Tables[i].tag);
+       length = 0;
+       checksum = 0;
+      }
+    }
+    if (length >= 0) {
+      newTables[k].tag = ((t42Tables[i].tag[0] & 0xff) << 24) |
+                        ((t42Tables[i].tag[1] & 0xff) << 16) |
+                        ((t42Tables[i].tag[2] & 0xff) <<  8) |
+                         (t42Tables[i].tag[3] & 0xff);
+      newTables[k].checksum = checksum;
+      newTables[k].offset = pos;
+      newTables[k].len = length;
+      pos += length;
+      if (pos & 3) {
+       pos += 4 - (length & 3);
+      }
+      ++k;
+    }
+  }
+
+  // construct the table directory
+  tableDir[0] = 0x00;          // sfnt version
+  tableDir[1] = 0x01;
+  tableDir[2] = 0x00;
+  tableDir[3] = 0x00;
+  tableDir[4] = 0;             // numTables
+  tableDir[5] = nNewTables;
+  tableDir[6] = 0;             // searchRange
+  tableDir[7] = (Guchar)128;
+  tableDir[8] = 0;             // entrySelector
+  tableDir[9] = 3;
+  tableDir[10] = 0;            // rangeShift
+  tableDir[11] = (Guchar)(16 * nNewTables - 128);
+  pos = 12;
+  for (i = 0; i < nNewTables; ++i) {
+    tableDir[pos   ] = (Guchar)(newTables[i].tag >> 24);
+    tableDir[pos+ 1] = (Guchar)(newTables[i].tag >> 16);
+    tableDir[pos+ 2] = (Guchar)(newTables[i].tag >>  8);
+    tableDir[pos+ 3] = (Guchar) newTables[i].tag;
+    tableDir[pos+ 4] = (Guchar)(newTables[i].checksum >> 24);
+    tableDir[pos+ 5] = (Guchar)(newTables[i].checksum >> 16);
+    tableDir[pos+ 6] = (Guchar)(newTables[i].checksum >>  8);
+    tableDir[pos+ 7] = (Guchar) newTables[i].checksum;
+    tableDir[pos+ 8] = (Guchar)(newTables[i].offset >> 24);
+    tableDir[pos+ 9] = (Guchar)(newTables[i].offset >> 16);
+    tableDir[pos+10] = (Guchar)(newTables[i].offset >>  8);
+    tableDir[pos+11] = (Guchar) newTables[i].offset;
+    tableDir[pos+12] = (Guchar)(newTables[i].len >> 24);
+    tableDir[pos+13] = (Guchar)(newTables[i].len >> 16);
+    tableDir[pos+14] = (Guchar)(newTables[i].len >>  8);
+    tableDir[pos+15] = (Guchar) newTables[i].len;
+    pos += 16;
+  }
+
+  // compute the font checksum and store it in the head table
+  checksum = computeTableChecksum(tableDir, 12 + nNewTables*16);
+  for (i = 0; i < nNewTables; ++i) {
+    checksum += newTables[i].checksum;
+  }
+  checksum = 0xb1b0afba - checksum; // because the TrueType spec says so
+  headData[ 8] = (Guchar)(checksum >> 24);
+  headData[ 9] = (Guchar)(checksum >> 16);
+  headData[10] = (Guchar)(checksum >>  8);
+  headData[11] = (Guchar) checksum;
+
+  // start the sfnts array
+  if (name) {
+    (*outputFunc)(outputStream, "/", 1);
+    (*outputFunc)(outputStream, name->getCString(), name->getLength());
+    (*outputFunc)(outputStream, " [\n", 3);
+  } else {
+    (*outputFunc)(outputStream, "/sfnts [\n", 9);
+  }
+
+  // write the table directory
+  dumpString(tableDir, 12 + nNewTables*16, outputFunc, outputStream);
+
+  // write the tables
+  for (i = 0; i < nNewTables; ++i) {
+    if (i == t42HeadTable) {
+      dumpString(headData, 54, outputFunc, outputStream);
+    } else if (i == t42LocaTable) {
+      length = (nGlyphs + 1) * (locaFmt ? 4 : 2);
+      dumpString(locaData, length, outputFunc, outputStream);
+    } else if (i == t42GlyfTable) {
+      glyfPos = tables[seekTable("glyf")].offset;
+      for (j = 0; j < nGlyphs; ++j) {
+       if (locaTable[j].len > 0 &&
+           checkRegion(glyfPos + locaTable[j].origOffset, locaTable[j].len)) {
+         dumpString(file + glyfPos + locaTable[j].origOffset,
+                    locaTable[j].len, outputFunc, outputStream);
+       }
+      }
+    } else {
+      // length == 0 means the table is missing and the error was
+      // already reported during the construction of the table
+      // headers
+      if ((length = newTables[i].len) > 0) {
+       if ((j = seekTable(t42Tables[i].tag)) >= 0 &&
+           checkRegion(tables[j].offset, tables[j].len)) {
+         dumpString(file + tables[j].offset, tables[j].len,
+                    outputFunc, outputStream);
+       }
+      }
+    }
+  }
+
+  // end the sfnts array
+  (*outputFunc)(outputStream, "] def\n", 6);
+
+  gfree(locaData);
+  gfree(locaTable);
+}
+
+void FoFiTrueType::dumpString(Guchar *s, int length,
+                             FoFiOutputFunc outputFunc,
+                             void *outputStream) {
+  char buf[64];
+  int pad, i, j;
+
+  (*outputFunc)(outputStream, "<", 1);
+  for (i = 0; i < length; i += 32) {
+    for (j = 0; j < 32 && i+j < length; ++j) {
+      sprintf(buf, "%02X", s[i+j] & 0xff);
+      (*outputFunc)(outputStream, buf, strlen(buf));
+    }
+    if (i % (65536 - 32) == 65536 - 64) {
+      (*outputFunc)(outputStream, ">\n<", 3);
+    } else if (i+32 < length) {
+      (*outputFunc)(outputStream, "\n", 1);
+    }
+  }
+  if (length & 3) {
+    pad = 4 - (length & 3);
+    for (i = 0; i < pad; ++i) {
+      (*outputFunc)(outputStream, "00", 2);
+    }
+  }
+  // add an extra zero byte because the Adobe Type 42 spec says so
+  (*outputFunc)(outputStream, "00>\n", 4);
+}
+
+Guint FoFiTrueType::computeTableChecksum(Guchar *data, int length) {
+  Guint checksum, word;
+  int i;
+
+  checksum = 0;
+  for (i = 0; i+3 < length; i += 4) {
+    word = ((data[i  ] & 0xff) << 24) +
+           ((data[i+1] & 0xff) << 16) +
+           ((data[i+2] & 0xff) <<  8) +
+            (data[i+3] & 0xff);
+    checksum += word;
+  }
+  if (length & 3) {
+    word = 0;
+    i = length & ~3;
+    switch (length & 3) {
+    case 3:
+      word |= (data[i+2] & 0xff) <<  8;
+    case 2:
+      word |= (data[i+1] & 0xff) << 16;
+    case 1:
+      word |= (data[i  ] & 0xff) << 24;
+      break;
+    }
+    checksum += word;
+  }
+  return checksum;
+}
+
+void FoFiTrueType::parse() {
+  int pos, i, j;
+
+  parsedOk = gTrue;
+
+  // read the table directory
+  nTables = getU16BE(4, &parsedOk);
+  if (!parsedOk) {
+    return;
+  }
+  tables = (TrueTypeTable *)gmalloc(nTables * sizeof(TrueTypeTable));
+  pos = 12;
+  for (i = 0; i < nTables; ++i) {
+    tables[i].tag = getU32BE(pos, &parsedOk);
+    tables[i].checksum = getU32BE(pos + 4, &parsedOk);
+    tables[i].offset = (int)getU32BE(pos + 8, &parsedOk);
+    tables[i].len = (int)getU32BE(pos + 12, &parsedOk);
+    if (tables[i].offset + tables[i].len < tables[i].offset ||
+       tables[i].offset + tables[i].len > len) {
+      parsedOk = gFalse;
+    }
+    pos += 16;
+  }
+  if (!parsedOk) {
+    return;
+  }
+
+  // check for tables that are required by both the TrueType spec and
+  // the Type 42 spec
+  if (seekTable("head") < 0 ||
+      seekTable("hhea") < 0 ||
+      seekTable("loca") < 0 ||
+      seekTable("maxp") < 0 ||
+      seekTable("glyf") < 0 ||
+      seekTable("hmtx") < 0) {
+    parsedOk = gFalse;
+    return;
+  }
+
+  // read the cmaps
+  if ((i = seekTable("cmap")) >= 0) {
+    pos = tables[i].offset + 2;
+    nCmaps = getU16BE(pos, &parsedOk);
+    pos += 2;
+    if (!parsedOk) {
+      return;
+    }
+    cmaps = (TrueTypeCmap *)gmalloc(nCmaps * sizeof(TrueTypeCmap));
+    for (j = 0; j < nCmaps; ++j) {
+      cmaps[j].platform = getU16BE(pos, &parsedOk);
+      cmaps[j].encoding = getU16BE(pos + 2, &parsedOk);
+      cmaps[j].offset = tables[i].offset + getU32BE(pos + 4, &parsedOk);
+      pos += 8;
+      cmaps[j].fmt = getU16BE(cmaps[j].offset, &parsedOk);
+      cmaps[j].len = getU16BE(cmaps[j].offset + 2, &parsedOk);
+    }
+    if (!parsedOk) {
+      return;
+    }
+  } else {
+    nCmaps = 0;
+  }
+
+  // get the number of glyphs from the maxp table
+  i = seekTable("maxp");
+  nGlyphs = getU16BE(tables[i].offset + 4, &parsedOk);
+  if (!parsedOk) {
+    return;
+  }
+
+  // get the bbox and loca table format from the head table
+  i = seekTable("head");
+  bbox[0] = getS16BE(tables[i].offset + 36, &parsedOk);
+  bbox[1] = getS16BE(tables[i].offset + 38, &parsedOk);
+  bbox[2] = getS16BE(tables[i].offset + 40, &parsedOk);
+  bbox[3] = getS16BE(tables[i].offset + 42, &parsedOk);
+  locaFmt = getS16BE(tables[i].offset + 50, &parsedOk);
+  if (!parsedOk) {
+    return;
+  }
+
+  // read the post table
+  readPostTable();
+  if (!parsedOk) {
+    return;
+  }
+}
+
+void FoFiTrueType::readPostTable() {
+  GString *name;
+  int tablePos, postFmt, stringIdx, stringPos;
+  int i, j, n, m;
+
+  if ((i = seekTable("post")) < 0) {
+    return;
+  }
+  tablePos = tables[i].offset;
+  postFmt = getU32BE(tablePos, &parsedOk);
+  if (!parsedOk) {
+    return;
+  }
+  if (postFmt == 0x00010000) {
+    nameToGID = new GHash(gTrue);
+    for (i = 0; i < 258; ++i) {
+      nameToGID->add(new GString(macGlyphNames[i]), i);
+    }
+  } else if (postFmt == 0x00020000) {
+    nameToGID = new GHash(gTrue);
+    n = getU16BE(tablePos + 32, &parsedOk);
+    if (!parsedOk) {
+      return;
+    }
+    if (n > nGlyphs) {
+      n = nGlyphs;
+    }
+    stringIdx = 0;
+    stringPos = tablePos + 34 + 2*n;
+    for (i = 0; i < n; ++i) {
+      j = getU16BE(tablePos + 34 + 2*i, &parsedOk);
+      if (j < 258) {
+       nameToGID->removeInt(macGlyphNames[j]);
+       nameToGID->add(new GString(macGlyphNames[j]), i);
+      } else {
+       j -= 258;
+       if (j != stringIdx) {
+         for (stringIdx = 0, stringPos = tablePos + 34 + 2*n;
+              stringIdx < j;
+              ++stringIdx, stringPos += 1 + getU8(stringPos, &parsedOk)) ;
+         if (!parsedOk) {
+           return;
+         }
+       }
+       m = getU8(stringPos, &parsedOk);
+       if (!parsedOk || !checkRegion(stringPos + 1, m)) {
+         parsedOk = gFalse;
+         return;
+       }
+       name = new GString((char *)&file[stringPos + 1], m);
+       nameToGID->removeInt(name);
+       nameToGID->add(name, i);
+       ++stringIdx;
+       stringPos += 1 + m;
+      }
+    }
+  } else if (postFmt == 0x00028000) {
+    nameToGID = new GHash(gTrue);
+    for (i = 0; i < nGlyphs; ++i) {
+      j = getU8(tablePos + 32 + i, &parsedOk);
+      if (!parsedOk) {
+       return;
+      }
+      if (j < 258) {
+       nameToGID->removeInt(macGlyphNames[j]);
+       nameToGID->add(new GString(macGlyphNames[j]), i);
+      }
+    }
+  }
+}
+
+int FoFiTrueType::seekTable(char *tag) {
+  Guint tagI;
+  int i;
+
+  tagI = ((tag[0] & 0xff) << 24) |
+         ((tag[1] & 0xff) << 16) |
+         ((tag[2] & 0xff) << 8) |
+          (tag[3] & 0xff);
+  for (i = 0; i < nTables; ++i) {
+    if (tables[i].tag == tagI) {
+      return i;
+    }
+  }
+  return -1;
+}
diff --git a/pdf/fofi/FoFiTrueType.h b/pdf/fofi/FoFiTrueType.h
new file mode 100644 (file)
index 0000000..ea05eec
--- /dev/null
@@ -0,0 +1,133 @@
+//========================================================================
+//
+// FoFiTrueType.h
+//
+// Copyright 1999-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef FOFITRUETYPE_H
+#define FOFITRUETYPE_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#include "gtypes.h"
+#include "FoFiBase.h"
+
+class GHash;
+struct TrueTypeTable;
+struct TrueTypeCmap;
+
+//------------------------------------------------------------------------
+// FoFiTrueType
+//------------------------------------------------------------------------
+
+class FoFiTrueType: public FoFiBase {
+public:
+
+  // Create a FoFiTrueType object from a memory buffer.
+  static FoFiTrueType *make(char *fileA, int lenA);
+
+  // Create a FoFiTrueType object from a file on disk.
+  static FoFiTrueType *load(char *fileName);
+
+  virtual ~FoFiTrueType();
+
+  // Return the number of cmaps defined by this font.
+  int getNumCmaps();
+
+  // Return the platform ID of the <i>th cmap.
+  int getCmapPlatform(int i);
+
+  // Return the encoding ID of the <i>th cmap.
+  int getCmapEncoding(int i);
+
+  // Return the index of the cmap for <platform>, <encoding>.  Returns
+  // -1 if there is no corresponding cmap.
+  int findCmap(int platform, int encoding);
+
+  // Return the GID corresponding to <c> according to the <i>th cmap.
+  Gushort mapCodeToGID(int i, int c);
+
+  // Returns the GID corresponding to <name> according to the post
+  // table.  Returns 0 if there is no mapping for <name> or if the
+  // font does not have a post table.
+  int mapNameToGID(char *name);
+
+  // Returns the least restrictive embedding licensing right (as
+  // defined by the TrueType spec):
+  // * 4: OS/2 table is missing or invalid
+  // * 3: installable embedding
+  // * 2: editable embedding
+  // * 1: preview & print embedding
+  // * 0: restricted license embedding
+  int getEmbeddingRights();
+
+  // Convert to a Type 42 font, suitable for embedding in a PostScript
+  // file.  <psName> will be used as the PostScript font name (so we
+  // don't need to depend on the 'name' table in the font).  The
+  // <encoding> array specifies the mapping from char codes to names.
+  // If <encoding> is NULL, the encoding is unknown or undefined.  The
+  // <codeToGID> array specifies the mapping from char codes to GIDs.
+  void convertToType42(char *psName, char **encoding,
+                      Gushort *codeToGID,
+                      FoFiOutputFunc outputFunc, void *outputStream);
+
+  // Convert to a Type 2 CIDFont, suitable for embedding in a
+  // PostScript file.  <psName> will be used as the PostScript font
+  // name (so we don't need to depend on the 'name' table in the
+  // font).  The <cidMap> array maps CIDs to GIDs; it has <nCIDs>
+  // entries.
+  void convertToCIDType2(char *psName, Gushort *cidMap, int nCIDs,
+                        FoFiOutputFunc outputFunc, void *outputStream);
+
+  // Convert to a Type 0 (but non-CID) composite font, suitable for
+  // embedding in a PostScript file.  <psName> will be used as the
+  // PostScript font name (so we don't need to depend on the 'name'
+  // table in the font).  The <cidMap> array maps CIDs to GIDs; it has
+  // <nCIDs> entries.
+  void convertToType0(char *psName, Gushort *cidMap, int nCIDs,
+                     FoFiOutputFunc outputFunc, void *outputStream);
+
+  // Write a clean TTF file, filling in missing tables and correcting
+  // various other errors.  If the font is complete and correct, it
+  // will be written unmodified.
+  void writeTTF(FoFiOutputFunc outputFunc, void *outputStream);
+
+private:
+
+  FoFiTrueType(char *fileA, int lenA, GBool freeFileDataA);
+  void cvtEncoding(char **encoding,
+                  FoFiOutputFunc outputFunc,
+                  void *outputStream);
+  void cvtCharStrings(char **encoding,
+                     Gushort *codeToGID,
+                     FoFiOutputFunc outputFunc,
+                     void *outputStream);
+  void cvtSfnts(FoFiOutputFunc outputFunc,
+               void *outputStream, GString *name);
+  void dumpString(Guchar *s, int length,
+                 FoFiOutputFunc outputFunc,
+                 void *outputStream);
+  Guint computeTableChecksum(Guchar *data, int length);
+  void parse();
+  void readPostTable();
+  int seekTable(char *tag);
+
+  TrueTypeTable *tables;
+  int nTables;
+  TrueTypeCmap *cmaps;
+  int nCmaps;
+  int nGlyphs;
+  int locaFmt;
+  int bbox[4];
+  GHash *nameToGID;
+
+  GBool parsedOk;
+};
+
+#endif
diff --git a/pdf/fofi/FoFiType1.cc b/pdf/fofi/FoFiType1.cc
new file mode 100644 (file)
index 0000000..fe54a63
--- /dev/null
@@ -0,0 +1,207 @@
+//========================================================================
+//
+// FoFiType1.cc
+//
+// Copyright 1999-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+#include "gmem.h"
+#include "FoFiEncodings.h"
+#include "FoFiType1.h"
+
+//------------------------------------------------------------------------
+// FoFiType1
+//------------------------------------------------------------------------
+
+FoFiType1 *FoFiType1::make(char *fileA, int lenA) {
+  return new FoFiType1(fileA, lenA, gFalse);
+}
+
+FoFiType1 *FoFiType1::load(char *fileName) {
+  char *fileA;
+  int lenA;
+
+  if (!(fileA = FoFiBase::readFile(fileName, &lenA))) {
+    return NULL;
+  }
+  return new FoFiType1(fileA, lenA, gTrue);
+}
+
+FoFiType1::FoFiType1(char *fileA, int lenA, GBool freeFileDataA):
+  FoFiBase(fileA, lenA, freeFileDataA)
+{
+  name = NULL;
+  encoding = NULL;
+  parsed = gFalse;
+}
+
+FoFiType1::~FoFiType1() {
+  int i;
+
+  if (name) {
+    gfree(name);
+  }
+  if (encoding && encoding != fofiType1StandardEncoding) {
+    for (i = 0; i < 256; ++i) {
+      gfree(encoding[i]);
+    }
+    gfree(encoding);
+  }
+}
+
+char *FoFiType1::getName() {
+  if (!parsed) {
+    parse();
+  }
+  return name;
+}
+
+char **FoFiType1::getEncoding() {
+  if (!parsed) {
+    parse();
+  }
+  return encoding;
+}
+
+void FoFiType1::writeEncoded(char **newEncoding,
+                            FoFiOutputFunc outputFunc, void *outputStream) {
+  char buf[512];
+  char *line;
+  int i;
+
+  // copy everything up to the encoding
+  for (line = (char *)file;
+       line && strncmp(line, "/Encoding", 9);
+       line = getNextLine(line)) ;
+  if (!line) {
+    // no encoding - just copy the whole font file
+    (*outputFunc)(outputStream, (char *)file, len);
+    return;
+  }
+  (*outputFunc)(outputStream, (char *)file, line - (char *)file);
+
+  // write the new encoding
+  (*outputFunc)(outputStream, "/Encoding 256 array\n", 20);
+  (*outputFunc)(outputStream,
+               "0 1 255 {1 index exch /.notdef put} for\n", 40);
+  for (i = 0; i < 256; ++i) {
+    if (newEncoding[i]) {
+      sprintf(buf, "dup %d /%s put\n", i, newEncoding[i]);
+      (*outputFunc)(outputStream, buf, strlen(buf));
+    }
+  }
+  (*outputFunc)(outputStream, "readonly def\n", 13);
+  
+  // copy everything after the encoding
+  if (!strncmp(line, "/Encoding StandardEncoding def", 30)) {
+    line = getNextLine(line);
+  } else {
+    for (line = getNextLine(line);
+        line && strncmp(line, "readonly def", 12);
+        line = getNextLine(line)) ;
+  }
+  if (line) {
+    (*outputFunc)(outputStream, line, ((char *)file + len) - line);
+  }
+}
+
+char *FoFiType1::getNextLine(char *line) {
+  while (line < (char *)file + len && *line != '\x0a' && *line != '\x0d') {
+    ++line;
+  }
+  if (line < (char *)file + len && *line == '\x0d') {
+    ++line;
+  }
+  if (line < (char *)file + len && *line == '\x0a') {
+    ++line;
+  }
+  if (line >= (char *)file + len) {
+    return NULL;
+  }
+  return line;
+}
+
+void FoFiType1::parse() {
+  char *line, *line1, *p, *p2;
+  char buf[256];
+  char c;
+  int n, code, i, j;
+
+  for (i = 1, line = (char *)file;
+       i <= 100 && line && (!name || !encoding);
+       ++i) {
+
+    // get font name
+    if (!name && !strncmp(line, "/FontName", 9)) {
+      strncpy(buf, line, 255);
+      buf[255] = '\0';
+      if ((p = strchr(buf+9, '/')) &&
+         (p = strtok(p+1, " \t\n\r"))) {
+       name = copyString(p);
+      }
+      line = getNextLine(line);
+
+    // get encoding
+    } else if (!encoding &&
+              !strncmp(line, "/Encoding StandardEncoding def", 30)) {
+      encoding = fofiType1StandardEncoding;
+    } else if (!encoding &&
+              !strncmp(line, "/Encoding 256 array", 19)) {
+      encoding = (char **)gmalloc(256 * sizeof(char *));
+      for (j = 0; j < 256; ++j) {
+       encoding[j] = NULL;
+      }
+      line = getNextLine(line);
+      for (j = 0; j < 300 && line; ++j) {
+       line1 = getNextLine(line);
+       if ((n = line1 - line) > 255) {
+         n = 255;
+       }
+       strncpy(buf, line, n);
+       buf[n] = '\0';
+       for (p = buf; *p == ' ' || *p == '\t'; ++p) ;
+       if (!strncmp(p, "dup", 3)) {
+         for (p += 3; *p == ' ' || *p == '\t'; ++p) ;
+         for (p2 = p; *p2 >= '0' && *p2 <= '9'; ++p2) ;
+         if (*p2) {
+           c = *p2;
+           *p2 = '\0';
+           if ((code = atoi(p)) < 256) {
+             *p2 = c;
+             for (p = p2; *p == ' ' || *p == '\t'; ++p) ;
+             if (*p == '/') {
+               ++p;
+               for (p2 = p; *p2 && *p2 != ' ' && *p2 != '\t'; ++p2) ;
+               *p2 = '\0';
+               encoding[code] = copyString(p);
+             }
+           }
+         }
+       } else {
+         if (strtok(buf, " \t") &&
+             (p = strtok(NULL, " \t\n\r")) && !strcmp(p, "def")) {
+           break;
+         }
+       }
+       line = line1;
+      }
+      //~ check for getinterval/putinterval junk
+
+    } else {
+      line = getNextLine(line);
+    }
+
+    ++i;
+  }
+
+  parsed = gTrue;
+}
diff --git a/pdf/fofi/FoFiType1.h b/pdf/fofi/FoFiType1.h
new file mode 100644 (file)
index 0000000..843352b
--- /dev/null
@@ -0,0 +1,59 @@
+//========================================================================
+//
+// FoFiType1.h
+//
+// Copyright 1999-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef FOFITYPE1_H
+#define FOFITYPE1_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#include "gtypes.h"
+#include "FoFiBase.h"
+
+//------------------------------------------------------------------------
+// FoFiType1
+//------------------------------------------------------------------------
+
+class FoFiType1: public FoFiBase {
+public:
+
+  // Create a FoFiType1 object from a memory buffer.
+  static FoFiType1 *make(char *fileA, int lenA);
+
+  // Create a FoFiType1 object from a file on disk.
+  static FoFiType1 *load(char *fileName);
+
+  virtual ~FoFiType1();
+
+  // Return the font name.
+  char *getName();
+
+  // Return the encoding, as an array of 256 names (any of which may
+  // be NULL).
+  char **getEncoding();
+
+  // Write a version of the Type 1 font file with a new encoding.
+  void writeEncoded(char **newEncoding,
+                   FoFiOutputFunc outputFunc, void *outputStream);
+
+private:
+
+  FoFiType1(char *fileA, int lenA, GBool freeFileDataA);
+
+  char *getNextLine(char *line);
+  void parse();
+
+  char *name;
+  char **encoding;
+  GBool parsed;
+};
+
+#endif
diff --git a/pdf/fofi/FoFiType1C.cc b/pdf/fofi/FoFiType1C.cc
new file mode 100644 (file)
index 0000000..91a21ad
--- /dev/null
@@ -0,0 +1,2385 @@
+//========================================================================
+//
+// FoFiType1C.cc
+//
+// Copyright 1999-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+#include "gmem.h"
+#include "GString.h"
+#include "FoFiEncodings.h"
+#include "FoFiType1C.h"
+
+//------------------------------------------------------------------------
+
+static char hexChars[17] = "0123456789ABCDEF";
+
+//------------------------------------------------------------------------
+// FoFiType1C
+//------------------------------------------------------------------------
+
+FoFiType1C *FoFiType1C::make(char *fileA, int lenA) {
+  FoFiType1C *ff;
+
+  ff = new FoFiType1C(fileA, lenA, gFalse);
+  if (!ff->parse()) {
+    delete ff;
+    return NULL;
+  }
+  return ff;
+}
+
+FoFiType1C *FoFiType1C::load(char *fileName) {
+  FoFiType1C *ff;
+  char *fileA;
+  int lenA;
+
+  if (!(fileA = FoFiBase::readFile(fileName, &lenA))) {
+    return NULL;
+  }
+  ff = new FoFiType1C(fileA, lenA, gTrue);
+  if (!ff->parse()) {
+    delete ff;
+    return NULL;
+  }
+  return ff;
+}
+
+FoFiType1C::FoFiType1C(char *fileA, int lenA, GBool freeFileDataA):
+  FoFiBase(fileA, lenA, freeFileDataA)
+{
+  name = NULL;
+  encoding = NULL;
+  privateDicts = NULL;
+  fdSelect = NULL;
+  charset = NULL;
+}
+
+FoFiType1C::~FoFiType1C() {
+  int i;
+
+  if (name) {
+    delete name;
+  }
+  if (encoding &&
+      encoding != fofiType1StandardEncoding &&
+      encoding != fofiType1ExpertEncoding) {
+    for (i = 0; i < 256; ++i) {
+      gfree(encoding[i]);
+    }
+    gfree(encoding);
+  }
+  if (privateDicts) {
+    gfree(privateDicts);
+  }
+  if (fdSelect) {
+    gfree(fdSelect);
+  }
+  if (charset &&
+      charset != fofiType1CISOAdobeCharset &&
+      charset != fofiType1CExpertCharset &&
+      charset != fofiType1CExpertSubsetCharset) {
+    gfree(charset);
+  }
+}
+
+char *FoFiType1C::getName() {
+  return name ? name->getCString() : (char *)NULL;
+}
+
+char **FoFiType1C::getEncoding() {
+  return encoding;
+}
+
+Gushort *FoFiType1C::getCIDToGIDMap(int *nCIDs) {
+  Gushort *map;
+  int n, i;
+
+  // a CID font's top dict has ROS as the first operator
+  if (topDict.firstOp != 0x0c1e) {
+    *nCIDs = 0;
+    return NULL;
+  }
+
+  // in a CID font, the charset data is the GID-to-CID mapping, so all
+  // we have to do is reverse it
+  n = 0;
+  for (i = 0; i < nGlyphs; ++i) {
+    if (charset[i] > n) {
+      n = charset[i];
+    }
+  }
+  ++n;
+  map = (Gushort *)gmalloc(n * sizeof(Gushort));
+  memset(map, 0, n * sizeof(Gushort));
+  for (i = 0; i < nGlyphs; ++i) {
+    map[charset[i]] = i;
+  }
+  *nCIDs = n;
+  return map;
+}
+
+void FoFiType1C::convertToType1(char **newEncoding, GBool ascii,
+                               FoFiOutputFunc outputFunc,
+                               void *outputStream) {
+  Type1CEexecBuf eb;
+  Type1CIndex subrIdx;
+  Type1CIndexVal val;
+  char buf[512];
+  char **enc;
+  GBool ok;
+  int i;
+
+  // write header and font dictionary, up to encoding
+  ok = gTrue;
+  (*outputFunc)(outputStream, "%!FontType1-1.0: ", 17);
+  (*outputFunc)(outputStream, name->getCString(), name->getLength());
+  if (topDict.versionSID != 0) {
+    getString(topDict.versionSID, buf, &ok);
+    (*outputFunc)(outputStream, buf, strlen(buf));
+  }
+  (*outputFunc)(outputStream, "\n", 1);
+  // the dictionary needs room for 12 entries: the following 9, plus
+  // Private and CharStrings (in the eexec section) and FID (which is
+  // added by definefont)
+  (*outputFunc)(outputStream, "12 dict begin\n", 14);
+  (*outputFunc)(outputStream, "/FontInfo 10 dict dup begin\n", 28);
+  if (topDict.versionSID != 0) {
+    (*outputFunc)(outputStream, "/version (", 10);
+    (*outputFunc)(outputStream, buf, strlen(buf));
+    (*outputFunc)(outputStream, ") readonly def\n", 15);
+  }
+  if (topDict.noticeSID != 0) {
+    getString(topDict.noticeSID, buf, &ok);
+    (*outputFunc)(outputStream, "/Notice (", 9);
+    (*outputFunc)(outputStream, buf, strlen(buf));
+    (*outputFunc)(outputStream, ") readonly def\n", 15);
+  }
+  if (topDict.copyrightSID != 0) {
+    getString(topDict.copyrightSID, buf, &ok);
+    (*outputFunc)(outputStream, "/Copyright (", 12);
+    (*outputFunc)(outputStream, buf, strlen(buf));
+    (*outputFunc)(outputStream, ") readonly def\n", 15);
+  }
+  if (topDict.fullNameSID != 0) {
+    getString(topDict.fullNameSID, buf, &ok);
+    (*outputFunc)(outputStream, "/FullName (", 11);
+    (*outputFunc)(outputStream, buf, strlen(buf));
+    (*outputFunc)(outputStream, ") readonly def\n", 15);
+  }
+  if (topDict.familyNameSID != 0) {
+    getString(topDict.familyNameSID, buf, &ok);
+    (*outputFunc)(outputStream, "/FamilyName (", 13);
+    (*outputFunc)(outputStream, buf, strlen(buf));
+    (*outputFunc)(outputStream, ") readonly def\n", 15);
+  }
+  if (topDict.weightSID != 0) {
+    getString(topDict.weightSID, buf, &ok);
+    (*outputFunc)(outputStream, "/Weight (", 9);
+    (*outputFunc)(outputStream, buf, strlen(buf));
+    (*outputFunc)(outputStream, ") readonly def\n", 15);
+  }
+  if (topDict.isFixedPitch) {
+    (*outputFunc)(outputStream, "/isFixedPitch true def\n", 23);
+  } else {
+    (*outputFunc)(outputStream, "/isFixedPitch false def\n", 24);
+  }
+  sprintf(buf, "/ItalicAngle %g def\n", topDict.italicAngle);
+  (*outputFunc)(outputStream, buf, strlen(buf));
+  sprintf(buf, "/UnderlinePosition %g def\n", topDict.underlinePosition);
+  (*outputFunc)(outputStream, buf, strlen(buf));
+  sprintf(buf, "/UnderlineThickness %g def\n", topDict.underlineThickness);
+  (*outputFunc)(outputStream, buf, strlen(buf));
+  (*outputFunc)(outputStream, "end readonly def\n", 17);
+  (*outputFunc)(outputStream, "/FontName /", 11);
+  (*outputFunc)(outputStream, name->getCString(), name->getLength());
+  (*outputFunc)(outputStream, " def\n", 5);
+  sprintf(buf, "/PaintType %d def\n", topDict.paintType);
+  (*outputFunc)(outputStream, buf, strlen(buf));
+  (*outputFunc)(outputStream, "/FontType 1 def\n", 16);
+  sprintf(buf, "/FontMatrix [%g %g %g %g %g %g] readonly def\n",
+         topDict.fontMatrix[0], topDict.fontMatrix[1], topDict.fontMatrix[2],
+         topDict.fontMatrix[3], topDict.fontMatrix[4], topDict.fontMatrix[5]);
+  (*outputFunc)(outputStream, buf, strlen(buf));
+  sprintf(buf, "/FontBBox [%g %g %g %g] readonly def\n",
+         topDict.fontBBox[0], topDict.fontBBox[1],
+         topDict.fontBBox[2], topDict.fontBBox[3]);
+  (*outputFunc)(outputStream, buf, strlen(buf));
+  sprintf(buf, "/StrokeWidth %g def\n", topDict.strokeWidth);
+  (*outputFunc)(outputStream, buf, strlen(buf));
+  if (topDict.uniqueID != 0) {
+    sprintf(buf, "/UniqueID %d def\n", topDict.uniqueID);
+    (*outputFunc)(outputStream, buf, strlen(buf));
+  }
+
+  // write the encoding
+  (*outputFunc)(outputStream, "/Encoding ", 10);
+  if (!newEncoding && encoding == fofiType1StandardEncoding) {
+    (*outputFunc)(outputStream, "StandardEncoding def\n", 21);
+  } else {
+    (*outputFunc)(outputStream, "256 array\n", 10);
+    (*outputFunc)(outputStream,
+                 "0 1 255 {1 index exch /.notdef put} for\n", 40);
+    enc = newEncoding ? newEncoding : encoding;
+    for (i = 0; i < 256; ++i) {
+      if (enc[i]) {
+       sprintf(buf, "dup %d /%s put\n", i, enc[i]);
+       (*outputFunc)(outputStream, buf, strlen(buf));
+      }
+    }
+    (*outputFunc)(outputStream, "readonly def\n", 13);
+  }
+  (*outputFunc)(outputStream, "currentdict end\n", 16);
+
+  // start the binary section
+  (*outputFunc)(outputStream, "currentfile eexec\n", 18);
+  eb.outputFunc = outputFunc;
+  eb.outputStream = outputStream;
+  eb.ascii = ascii;
+  eb.r1 = 55665;
+  eb.line = 0;
+
+  // write the private dictionary
+  eexecWrite(&eb, "\x83\xca\x73\xd5");
+  eexecWrite(&eb, "dup /Private 32 dict dup begin\n");
+  eexecWrite(&eb, "/RD {string currentfile exch readstring pop}"
+            " executeonly def\n");
+  eexecWrite(&eb, "/ND {noaccess def} executeonly def\n");
+  eexecWrite(&eb, "/NP {noaccess put} executeonly def\n");
+  eexecWrite(&eb, "/MinFeature {16 16} def\n");
+  eexecWrite(&eb, "/password 5839 def\n");
+  if (privateDicts[0].nBlueValues) {
+    eexecWrite(&eb, "/BlueValues [");
+    for (i = 0; i < privateDicts[0].nBlueValues; ++i) {
+      sprintf(buf, "%s%d", i > 0 ? " " : "", privateDicts[0].blueValues[i]);
+      eexecWrite(&eb, buf);
+    }
+    eexecWrite(&eb, "] def\n");
+  }
+  if (privateDicts[0].nOtherBlues) {
+    eexecWrite(&eb, "/OtherBlues [");
+    for (i = 0; i < privateDicts[0].nOtherBlues; ++i) {
+      sprintf(buf, "%s%d", i > 0 ? " " : "", privateDicts[0].otherBlues[i]);
+      eexecWrite(&eb, buf);
+    }
+    eexecWrite(&eb, "] def\n");
+  }
+  if (privateDicts[0].nFamilyBlues) {
+    eexecWrite(&eb, "/FamilyBlues [");
+    for (i = 0; i < privateDicts[0].nFamilyBlues; ++i) {
+      sprintf(buf, "%s%d", i > 0 ? " " : "", privateDicts[0].familyBlues[i]);
+      eexecWrite(&eb, buf);
+    }
+    eexecWrite(&eb, "] def\n");
+  }
+  if (privateDicts[0].nFamilyOtherBlues) {
+    eexecWrite(&eb, "/FamilyOtherBlues [");
+    for (i = 0; i < privateDicts[0].nFamilyOtherBlues; ++i) {
+      sprintf(buf, "%s%d", i > 0 ? " " : "",
+             privateDicts[0].familyOtherBlues[i]);
+      eexecWrite(&eb, buf);
+    }
+    eexecWrite(&eb, "] def\n");
+  }
+  if (privateDicts[0].blueScale != 0.039625) {
+    sprintf(buf, "/BlueScale %g def\n", privateDicts[0].blueScale);
+    eexecWrite(&eb, buf);
+  }
+  if (privateDicts[0].blueShift != 7) {
+    sprintf(buf, "/BlueShift %d def\n", privateDicts[0].blueShift);
+    eexecWrite(&eb, buf);
+  }
+  if (privateDicts[0].blueFuzz != 1) {
+    sprintf(buf, "/BlueFuzz %d def\n", privateDicts[0].blueFuzz);
+    eexecWrite(&eb, buf);
+  }
+  if (privateDicts[0].hasStdHW) {
+    sprintf(buf, "/StdHW [%g] def\n", privateDicts[0].stdHW);
+    eexecWrite(&eb, buf);
+  }
+  if (privateDicts[0].hasStdVW) {
+    sprintf(buf, "/StdVW [%g] def\n", privateDicts[0].stdVW);
+    eexecWrite(&eb, buf);
+  }
+  if (privateDicts[0].nStemSnapH) {
+    eexecWrite(&eb, "/StemSnapH [");
+    for (i = 0; i < privateDicts[0].nStemSnapH; ++i) {
+      sprintf(buf, "%s%g", i > 0 ? " " : "", privateDicts[0].stemSnapH[i]);
+      eexecWrite(&eb, buf);
+    }
+    eexecWrite(&eb, "] def\n");
+  }
+  if (privateDicts[0].nStemSnapV) {
+    eexecWrite(&eb, "/StemSnapV [");
+    for (i = 0; i < privateDicts[0].nStemSnapV; ++i) {
+      sprintf(buf, "%s%g", i > 0 ? " " : "", privateDicts[0].stemSnapV[i]);
+      eexecWrite(&eb, buf);
+    }
+    eexecWrite(&eb, "] def\n");
+  }
+  if (privateDicts[0].hasForceBold) {
+    sprintf(buf, "/ForceBold %s def\n",
+           privateDicts[0].forceBold ? "true" : "false");
+    eexecWrite(&eb, buf);
+  }
+  if (privateDicts[0].forceBoldThreshold != 0) {
+    sprintf(buf, "/ForceBoldThreshold %g def\n",
+           privateDicts[0].forceBoldThreshold);
+    eexecWrite(&eb, buf);
+  }
+  if (privateDicts[0].languageGroup != 0) {
+    sprintf(buf, "/LanguageGroup %d def\n", privateDicts[0].languageGroup);
+    eexecWrite(&eb, buf);
+  }
+  if (privateDicts[0].expansionFactor != 0.06) {
+    sprintf(buf, "/ExpansionFactor %g def\n", privateDicts[0].expansionFactor);
+    eexecWrite(&eb, buf);
+  }
+
+  // set up subroutines
+  ok = gTrue;
+  getIndex(privateDicts[0].subrsOffset, &subrIdx, &ok);
+  if (!ok) {
+    subrIdx.pos = -1;
+  }
+
+  // write the CharStrings
+  sprintf(buf, "2 index /CharStrings %d dict dup begin\n", nGlyphs);
+  eexecWrite(&eb, buf);
+  for (i = 0; i < nGlyphs; ++i) {
+    ok = gTrue;
+    getIndexVal(&charStringsIdx, i, &val, &ok);
+    if (ok) {
+      getString(charset[i], buf, &ok);
+      if (ok) {
+       eexecCvtGlyph(&eb, buf, val.pos, val.len, &subrIdx, &privateDicts[0]);
+      }
+    }
+  }
+  eexecWrite(&eb, "end\n");
+  eexecWrite(&eb, "end\n");
+  eexecWrite(&eb, "readonly put\n");
+  eexecWrite(&eb, "noaccess put\n");
+  eexecWrite(&eb, "dup /FontName get exch definefont pop\n");
+  eexecWrite(&eb, "mark currentfile closefile\n");
+
+  // trailer
+  if (ascii && eb.line > 0) {
+    (*outputFunc)(outputStream, "\n", 1);
+  }
+  for (i = 0; i < 8; ++i) {
+    (*outputFunc)(outputStream, "0000000000000000000000000000000000000000000000000000000000000000\n", 65);
+  }
+  (*outputFunc)(outputStream, "cleartomark\n", 12);
+}
+
+void FoFiType1C::convertToCIDType0(char *psName,
+                                  FoFiOutputFunc outputFunc,
+                                  void *outputStream) {
+  int *cidMap;
+  GString *charStrings;
+  int *charStringOffsets;
+  Type1CIndex subrIdx;
+  Type1CIndexVal val;
+  int nCIDs, gdBytes;
+  char buf[512], buf2[512];
+  GBool ok;
+  int gid, offset, n, i, j, k;
+
+  // compute the CID count and build the CID-to-GID mapping
+  nCIDs = 0;
+  for (i = 0; i < nGlyphs; ++i) {
+    if (charset[i] >= nCIDs) {
+      nCIDs = charset[i] + 1;
+    }
+  }
+  cidMap = (int *)gmalloc(nCIDs * sizeof(int));
+  for (i = 0; i < nCIDs; ++i) {
+    cidMap[i] = -1;
+  }
+  for (i = 0; i < nGlyphs; ++i) {
+    cidMap[charset[i]] = i;
+  }
+
+  // build the charstrings
+  charStrings = new GString();
+  charStringOffsets = (int *)gmalloc((nCIDs + 1) * sizeof(int));
+  for (i = 0; i < nCIDs; ++i) {
+    charStringOffsets[i] = charStrings->getLength();
+    if ((gid = cidMap[i]) >= 0) {
+      ok = gTrue;
+      getIndexVal(&charStringsIdx, gid, &val, &ok);
+      if (ok) {
+       getIndex(privateDicts[fdSelect[gid]].subrsOffset, &subrIdx, &ok);
+       if (!ok) {
+         subrIdx.pos = -1;
+       }
+       cvtGlyph(val.pos, val.len, charStrings,
+                &subrIdx, &privateDicts[fdSelect[gid]], gTrue);
+      }
+    }
+  }
+  charStringOffsets[nCIDs] = charStrings->getLength();
+
+  // compute gdBytes = number of bytes needed for charstring offsets
+  // (offset size needs to account for the charstring offset table,
+  // with a worst case of five bytes per entry, plus the charstrings
+  // themselves)
+  i = (nCIDs + 1) * 5 + charStrings->getLength();
+  if (i < 0x100) {
+    gdBytes = 1;
+  } else if (i < 0x10000) {
+    gdBytes = 2;
+  } else if (i < 0x1000000) {
+    gdBytes = 3;
+  } else {
+    gdBytes = 4;
+  }
+
+  // begin the font dictionary
+  (*outputFunc)(outputStream, "/CIDInit /ProcSet findresource begin\n", 37);
+  (*outputFunc)(outputStream, "20 dict begin\n", 14);
+  (*outputFunc)(outputStream, "/CIDFontName /", 14);
+  (*outputFunc)(outputStream, psName, strlen(psName));
+  (*outputFunc)(outputStream, " def\n", 5);
+  (*outputFunc)(outputStream, "/CIDFontType 0 def\n", 19);
+  (*outputFunc)(outputStream, "/CIDSystemInfo 3 dict dup begin\n", 32);
+  if (topDict.registrySID > 0 && topDict.orderingSID > 0) {
+    ok = gTrue;
+    getString(topDict.registrySID, buf, &ok);
+    if (ok) {
+      (*outputFunc)(outputStream, "  /Registry (", 13);
+      (*outputFunc)(outputStream, buf, strlen(buf));
+      (*outputFunc)(outputStream, ") def\n", 6);
+    }
+    ok = gTrue;
+    getString(topDict.orderingSID, buf, &ok);
+    if (ok) {
+      (*outputFunc)(outputStream, "  /Ordering (", 13);
+      (*outputFunc)(outputStream, buf, strlen(buf));
+      (*outputFunc)(outputStream, ") def\n", 6);
+    }
+  } else {
+    (*outputFunc)(outputStream, "  /Registry (Adobe) def\n", 24);
+    (*outputFunc)(outputStream, "  /Ordering (Identity) def\n", 27);
+  }
+  sprintf(buf, "  /Supplement %d def\n", topDict.supplement);
+  (*outputFunc)(outputStream, buf, strlen(buf));
+  (*outputFunc)(outputStream, "end def\n", 8);
+  sprintf(buf, "/FontMatrix [%g %g %g %g %g %g] def\n",
+         topDict.fontMatrix[0], topDict.fontMatrix[1], topDict.fontMatrix[2],
+         topDict.fontMatrix[3], topDict.fontMatrix[4], topDict.fontMatrix[5]);
+  (*outputFunc)(outputStream, buf, strlen(buf));
+  sprintf(buf, "/FontBBox [%g %g %g %g] def\n",
+         topDict.fontBBox[0], topDict.fontBBox[1],
+         topDict.fontBBox[2], topDict.fontBBox[3]);
+  (*outputFunc)(outputStream, buf, strlen(buf));
+  (*outputFunc)(outputStream, "/FontInfo 1 dict dup begin\n", 27);
+  (*outputFunc)(outputStream, "  /FSType 8 def\n", 16);
+  (*outputFunc)(outputStream, "end def\n", 8);
+
+  // CIDFont-specific entries
+  sprintf(buf, "/CIDCount %d def\n", nCIDs);
+  (*outputFunc)(outputStream, buf, strlen(buf));
+  (*outputFunc)(outputStream, "/FDBytes 1 def\n", 15);
+  sprintf(buf, "/GDBytes %d def\n", gdBytes);
+  (*outputFunc)(outputStream, buf, strlen(buf));
+  (*outputFunc)(outputStream, "/CIDMapOffset 0 def\n", 20);
+  if (topDict.paintType != 0) {
+    sprintf(buf, "/PaintType %d def\n", topDict.paintType);
+    (*outputFunc)(outputStream, buf, strlen(buf));
+    sprintf(buf, "/StrokeWidth %g def\n", topDict.strokeWidth);
+    (*outputFunc)(outputStream, buf, strlen(buf));
+  }
+
+  // FDArray entry
+  sprintf(buf, "/FDArray %d array\n", nFDs);
+  (*outputFunc)(outputStream, buf, strlen(buf));
+  for (i = 0; i < nFDs; ++i) {
+    sprintf(buf, "dup %d 10 dict begin\n", i);
+    (*outputFunc)(outputStream, buf, strlen(buf));
+    (*outputFunc)(outputStream, "/FontType 1 def\n", 16);
+    (*outputFunc)(outputStream, "/FontMatrix [1 0 0 1 0 0] def\n", 30);
+    sprintf(buf, "/PaintType %d def\n", topDict.paintType);
+    (*outputFunc)(outputStream, buf, strlen(buf));
+    (*outputFunc)(outputStream, "/Private 32 dict begin\n", 23);
+    if (privateDicts[i].nBlueValues) {
+      (*outputFunc)(outputStream, "/BlueValues [", 13);
+      for (j = 0; j < privateDicts[i].nBlueValues; ++j) {
+       sprintf(buf, "%s%d", j > 0 ? " " : "", privateDicts[i].blueValues[j]);
+       (*outputFunc)(outputStream, buf, strlen(buf));
+      }
+      (*outputFunc)(outputStream, "] def\n", 6);
+    }
+    if (privateDicts[i].nOtherBlues) {
+      (*outputFunc)(outputStream, "/OtherBlues [", 13);
+      for (j = 0; j < privateDicts[i].nOtherBlues; ++j) {
+       sprintf(buf, "%s%d", j > 0 ? " " : "", privateDicts[i].otherBlues[j]);
+       (*outputFunc)(outputStream, buf, strlen(buf));
+      }
+      (*outputFunc)(outputStream, "] def\n", 6);
+    }
+    if (privateDicts[i].nFamilyBlues) {
+      (*outputFunc)(outputStream, "/FamilyBlues [", 14);
+      for (j = 0; j < privateDicts[i].nFamilyBlues; ++j) {
+       sprintf(buf, "%s%d", j > 0 ? " " : "", privateDicts[i].familyBlues[j]);
+       (*outputFunc)(outputStream, buf, strlen(buf));
+      }
+      (*outputFunc)(outputStream, "] def\n", 6);
+    }
+    if (privateDicts[i].nFamilyOtherBlues) {
+      (*outputFunc)(outputStream, "/FamilyOtherBlues [", 19);
+      for (j = 0; j < privateDicts[i].nFamilyOtherBlues; ++j) {
+       sprintf(buf, "%s%d", j > 0 ? " " : "",
+               privateDicts[i].familyOtherBlues[j]);
+       (*outputFunc)(outputStream, buf, strlen(buf));
+      }
+      (*outputFunc)(outputStream, "] def\n", 6);
+    }
+    if (privateDicts[i].blueScale != 0.039625) {
+      sprintf(buf, "/BlueScale %g def\n", privateDicts[i].blueScale);
+      (*outputFunc)(outputStream, buf, strlen(buf));
+    }
+    if (privateDicts[i].blueShift != 7) {
+      sprintf(buf, "/BlueShift %d def\n", privateDicts[i].blueShift);
+      (*outputFunc)(outputStream, buf, strlen(buf));
+    }
+    if (privateDicts[i].blueFuzz != 1) {
+      sprintf(buf, "/BlueFuzz %d def\n", privateDicts[i].blueFuzz);
+      (*outputFunc)(outputStream, buf, strlen(buf));
+    }
+    if (privateDicts[i].hasStdHW) {
+      sprintf(buf, "/StdHW [%g] def\n", privateDicts[i].stdHW);
+      (*outputFunc)(outputStream, buf, strlen(buf));
+    }
+    if (privateDicts[i].hasStdVW) {
+      sprintf(buf, "/StdVW [%g] def\n", privateDicts[i].stdVW);
+      (*outputFunc)(outputStream, buf, strlen(buf));
+    }
+    if (privateDicts[i].nStemSnapH) {
+      (*outputFunc)(outputStream, "/StemSnapH [", 12);
+      for (j = 0; j < privateDicts[i].nStemSnapH; ++j) {
+       sprintf(buf, "%s%g", j > 0 ? " " : "", privateDicts[i].stemSnapH[j]);
+       (*outputFunc)(outputStream, buf, strlen(buf));
+      }
+      (*outputFunc)(outputStream, "] def\n", 6);
+    }
+    if (privateDicts[i].nStemSnapV) {
+      (*outputFunc)(outputStream, "/StemSnapV [", 12);
+      for (j = 0; j < privateDicts[i].nStemSnapV; ++j) {
+       sprintf(buf, "%s%g", j > 0 ? " " : "", privateDicts[i].stemSnapV[j]);
+       (*outputFunc)(outputStream, buf, strlen(buf));
+      }
+      (*outputFunc)(outputStream, "] def\n", 6);
+    }
+    if (privateDicts[i].hasForceBold) {
+      sprintf(buf, "/ForceBold %s def\n",
+             privateDicts[i].forceBold ? "true" : "false");
+      (*outputFunc)(outputStream, buf, strlen(buf));
+    }
+    if (privateDicts[i].forceBoldThreshold != 0) {
+      sprintf(buf, "/ForceBoldThreshold %g def\n",
+             privateDicts[i].forceBoldThreshold);
+      (*outputFunc)(outputStream, buf, strlen(buf));
+    }
+    if (privateDicts[i].languageGroup != 0) {
+      sprintf(buf, "/LanguageGroup %d def\n", privateDicts[i].languageGroup);
+      (*outputFunc)(outputStream, buf, strlen(buf));
+    }
+    if (privateDicts[i].expansionFactor != 0.06) {
+      sprintf(buf, "/ExpansionFactor %g def\n",
+             privateDicts[i].expansionFactor);
+      (*outputFunc)(outputStream, buf, strlen(buf));
+    }
+    (*outputFunc)(outputStream, "currentdict end def\n", 20);
+    (*outputFunc)(outputStream, "currentdict end put\n", 20);
+  }
+  (*outputFunc)(outputStream, "def\n", 4);
+
+  // start the binary section
+  offset = (nCIDs + 1) * (1 + gdBytes);
+  sprintf(buf, "(Hex) %d StartData\n",
+         offset + charStrings->getLength());
+  (*outputFunc)(outputStream, buf, strlen(buf));
+
+  // write the charstring offset (CIDMap) table
+  for (i = 0; i <= nCIDs; i += 6) {
+    for (j = 0; j < 6 && i+j <= nCIDs; ++j) {
+      if (i+j < nCIDs && cidMap[i+j] >= 0) {
+       buf[0] = (char)fdSelect[cidMap[i+j]];
+      } else {
+       buf[0] = (char)0;
+      }
+      n = offset + charStringOffsets[i+j];
+      for (k = gdBytes; k >= 1; --k) {
+       buf[k] = (char)(n & 0xff);
+       n >>= 8;
+      }
+      for (k = 0; k <= gdBytes; ++k) {
+       sprintf(buf2, "%02x", buf[k] & 0xff);
+       (*outputFunc)(outputStream, buf2, 2);
+      }
+    }
+    (*outputFunc)(outputStream, "\n", 1);
+  }
+
+  // write the charstring data
+  n = charStrings->getLength();
+  for (i = 0; i < n; i += 32) {
+    for (j = 0; j < 32 && i+j < n; ++j) {
+      sprintf(buf, "%02x", charStrings->getChar(i+j) & 0xff);
+      (*outputFunc)(outputStream, buf, strlen(buf));
+    }
+    if (i + 32 >= n) {
+      (*outputFunc)(outputStream, ">", 1);
+    }
+    (*outputFunc)(outputStream, "\n", 1);
+  }
+
+  gfree(charStringOffsets);
+  delete charStrings;
+  gfree(cidMap);
+}
+
+void FoFiType1C::convertToType0(char *psName,
+                               FoFiOutputFunc outputFunc,
+                               void *outputStream) {
+  int *cidMap;
+  Type1CIndex subrIdx;
+  Type1CIndexVal val;
+  int nCIDs;
+  char buf[512];
+  Type1CEexecBuf eb;
+  GBool ok;
+  int fd, i, j, k;
+
+  // compute the CID count and build the CID-to-GID mapping
+  nCIDs = 0;
+  for (i = 0; i < nGlyphs; ++i) {
+    if (charset[i] >= nCIDs) {
+      nCIDs = charset[i] + 1;
+    }
+  }
+  cidMap = (int *)gmalloc(nCIDs * sizeof(int));
+  for (i = 0; i < nCIDs; ++i) {
+    cidMap[i] = -1;
+  }
+  for (i = 0; i < nGlyphs; ++i) {
+    cidMap[charset[i]] = i;
+  }
+
+  // write the descendant Type 1 fonts
+  for (i = 0; i < nCIDs; i += 256) {
+
+    //~ this assumes that all CIDs in this block have the same FD --
+    //~ to handle multiple FDs correctly, need to somehow divide the
+    //~ font up by FD
+    fd = 0;
+    for (j = 0; j < 256 && i+j < nCIDs; ++j) {
+      if (cidMap[i+j] >= 0) {
+       fd = fdSelect[cidMap[i+j]];
+       break;
+      }
+    }
+
+    // font dictionary (unencrypted section)
+    (*outputFunc)(outputStream, "16 dict begin\n", 14);
+    (*outputFunc)(outputStream, "/FontName /", 11);
+    (*outputFunc)(outputStream, psName, strlen(psName));
+    sprintf(buf, "_%02x def\n", i >> 8);
+    (*outputFunc)(outputStream, buf, strlen(buf));
+    (*outputFunc)(outputStream, "/FontType 1 def\n", 16);
+    sprintf(buf, "/FontMatrix [%g %g %g %g %g %g] def\n",
+           topDict.fontMatrix[0], topDict.fontMatrix[1],
+           topDict.fontMatrix[2], topDict.fontMatrix[3],
+           topDict.fontMatrix[4], topDict.fontMatrix[5]);
+    (*outputFunc)(outputStream, buf, strlen(buf));
+    sprintf(buf, "/FontBBox [%g %g %g %g] def\n",
+           topDict.fontBBox[0], topDict.fontBBox[1],
+           topDict.fontBBox[2], topDict.fontBBox[3]);
+    (*outputFunc)(outputStream, buf, strlen(buf));
+    sprintf(buf, "/PaintType %d def\n", topDict.paintType);
+    (*outputFunc)(outputStream, buf, strlen(buf));
+    if (topDict.paintType != 0) {
+      sprintf(buf, "/StrokeWidth %g def\n", topDict.strokeWidth);
+      (*outputFunc)(outputStream, buf, strlen(buf));
+    }
+    (*outputFunc)(outputStream, "/Encoding 256 array\n", 20);
+    for (j = 0; j < 256 && i+j < nCIDs; ++j) {
+      sprintf(buf, "dup %d /c%02x put\n", j, j);
+      (*outputFunc)(outputStream, buf, strlen(buf));
+    }
+    if (j < 256) {
+      sprintf(buf, "%d 1 255 { 1 index exch /.notdef put } for\n", j);
+      (*outputFunc)(outputStream, buf, strlen(buf));
+    }
+    (*outputFunc)(outputStream, "readonly def\n", 13);
+    (*outputFunc)(outputStream, "currentdict end\n", 16);
+
+    // start the binary section
+    (*outputFunc)(outputStream, "currentfile eexec\n", 18);
+    eb.outputFunc = outputFunc;
+    eb.outputStream = outputStream;
+    eb.ascii = gTrue;
+    eb.r1 = 55665;
+    eb.line = 0;
+
+    // start the private dictionary
+    eexecWrite(&eb, "\x83\xca\x73\xd5");
+    eexecWrite(&eb, "dup /Private 32 dict dup begin\n");
+    eexecWrite(&eb, "/RD {string currentfile exch readstring pop}"
+              " executeonly def\n");
+    eexecWrite(&eb, "/ND {noaccess def} executeonly def\n");
+    eexecWrite(&eb, "/NP {noaccess put} executeonly def\n");
+    eexecWrite(&eb, "/MinFeature {16 16} def\n");
+    eexecWrite(&eb, "/password 5839 def\n");
+    if (privateDicts[fd].nBlueValues) {
+      eexecWrite(&eb, "/BlueValues [");
+      for (k = 0; k < privateDicts[fd].nBlueValues; ++k) {
+       sprintf(buf, "%s%d", k > 0 ? " " : "", privateDicts[fd].blueValues[k]);
+       eexecWrite(&eb, buf);
+      }
+      eexecWrite(&eb, "] def\n");
+    }
+    if (privateDicts[fd].nOtherBlues) {
+      eexecWrite(&eb, "/OtherBlues [");
+      for (k = 0; k < privateDicts[fd].nOtherBlues; ++k) {
+       sprintf(buf, "%s%d", k > 0 ? " " : "", privateDicts[fd].otherBlues[k]);
+       eexecWrite(&eb, buf);
+      }
+      eexecWrite(&eb, "] def\n");
+    }
+    if (privateDicts[fd].nFamilyBlues) {
+      eexecWrite(&eb, "/FamilyBlues [");
+      for (k = 0; k < privateDicts[fd].nFamilyBlues; ++k) {
+       sprintf(buf, "%s%d", k > 0 ? " " : "",
+               privateDicts[fd].familyBlues[k]);
+       eexecWrite(&eb, buf);
+      }
+      eexecWrite(&eb, "] def\n");
+    }
+    if (privateDicts[fd].nFamilyOtherBlues) {
+      eexecWrite(&eb, "/FamilyOtherBlues [");
+      for (k = 0; k < privateDicts[fd].nFamilyOtherBlues; ++k) {
+       sprintf(buf, "%s%d", k > 0 ? " " : "",
+               privateDicts[fd].familyOtherBlues[k]);
+       eexecWrite(&eb, buf);
+      }
+      eexecWrite(&eb, "] def\n");
+    }
+    if (privateDicts[fd].blueScale != 0.039625) {
+      sprintf(buf, "/BlueScale %g def\n", privateDicts[fd].blueScale);
+      eexecWrite(&eb, buf);
+    }
+    if (privateDicts[fd].blueShift != 7) {
+      sprintf(buf, "/BlueShift %d def\n", privateDicts[fd].blueShift);
+      eexecWrite(&eb, buf);
+    }
+    if (privateDicts[fd].blueFuzz != 1) {
+      sprintf(buf, "/BlueFuzz %d def\n", privateDicts[fd].blueFuzz);
+      eexecWrite(&eb, buf);
+    }
+    if (privateDicts[fd].hasStdHW) {
+      sprintf(buf, "/StdHW [%g] def\n", privateDicts[fd].stdHW);
+      eexecWrite(&eb, buf);
+    }
+    if (privateDicts[fd].hasStdVW) {
+      sprintf(buf, "/StdVW [%g] def\n", privateDicts[fd].stdVW);
+      eexecWrite(&eb, buf);
+    }
+    if (privateDicts[fd].nStemSnapH) {
+      eexecWrite(&eb, "/StemSnapH [");
+      for (k = 0; k < privateDicts[fd].nStemSnapH; ++k) {
+       sprintf(buf, "%s%g", k > 0 ? " " : "", privateDicts[fd].stemSnapH[k]);
+       eexecWrite(&eb, buf);
+      }
+      eexecWrite(&eb, "] def\n");
+    }
+    if (privateDicts[fd].nStemSnapV) {
+      eexecWrite(&eb, "/StemSnapV [");
+      for (k = 0; k < privateDicts[fd].nStemSnapV; ++k) {
+       sprintf(buf, "%s%g", k > 0 ? " " : "", privateDicts[fd].stemSnapV[k]);
+       eexecWrite(&eb, buf);
+      }
+      eexecWrite(&eb, "] def\n");
+    }
+    if (privateDicts[fd].hasForceBold) {
+      sprintf(buf, "/ForceBold %s def\n",
+             privateDicts[fd].forceBold ? "true" : "false");
+      eexecWrite(&eb, buf);
+    }
+    if (privateDicts[fd].forceBoldThreshold != 0) {
+      sprintf(buf, "/ForceBoldThreshold %g def\n",
+             privateDicts[fd].forceBoldThreshold);
+      eexecWrite(&eb, buf);
+    }
+    if (privateDicts[fd].languageGroup != 0) {
+      sprintf(buf, "/LanguageGroup %d def\n", privateDicts[fd].languageGroup);
+      eexecWrite(&eb, buf);
+    }
+    if (privateDicts[fd].expansionFactor != 0.06) {
+      sprintf(buf, "/ExpansionFactor %g def\n",
+             privateDicts[fd].expansionFactor);
+      eexecWrite(&eb, buf);
+    }
+
+    // set up the subroutines
+    ok = gTrue;
+    getIndex(privateDicts[fd].subrsOffset, &subrIdx, &ok);
+    if (!ok) {
+      subrIdx.pos = -1;
+    }
+
+    // start the CharStrings
+    sprintf(buf, "2 index /CharStrings 256 dict dup begin\n");
+    eexecWrite(&eb, buf);
+
+    // write the .notdef CharString
+    ok = gTrue;
+    getIndexVal(&charStringsIdx, 0, &val, &ok);
+    if (ok) {
+      eexecCvtGlyph(&eb, ".notdef", val.pos, val.len,
+                   &subrIdx, &privateDicts[fd]);
+    }
+
+    // write the CharStrings
+    for (j = 0; j < 256 && i+j < nCIDs; ++j) {
+      if (cidMap[i+j] >= 0) {
+       ok = gTrue;
+       getIndexVal(&charStringsIdx, cidMap[i+j], &val, &ok);
+       if (ok) {
+         sprintf(buf, "c%02x", j);
+         eexecCvtGlyph(&eb, buf, val.pos, val.len,
+                       &subrIdx, &privateDicts[fd]);
+       }
+      }
+    }
+    eexecWrite(&eb, "end\n");
+    eexecWrite(&eb, "end\n");
+    eexecWrite(&eb, "readonly put\n");
+    eexecWrite(&eb, "noaccess put\n");
+    eexecWrite(&eb, "dup /FontName get exch definefont pop\n");
+    eexecWrite(&eb, "mark currentfile closefile\n");
+
+    // trailer
+    if (eb.line > 0) {
+      (*outputFunc)(outputStream, "\n", 1);
+    }
+    for (j = 0; j < 8; ++j) {
+      (*outputFunc)(outputStream, "0000000000000000000000000000000000000000000000000000000000000000\n", 65);
+    }
+    (*outputFunc)(outputStream, "cleartomark\n", 12);
+  }
+
+  // write the Type 0 parent font
+  (*outputFunc)(outputStream, "16 dict begin\n", 14);
+  (*outputFunc)(outputStream, "/FontName /", 11);
+  (*outputFunc)(outputStream, psName, strlen(psName));
+  (*outputFunc)(outputStream, " def\n", 5);
+  (*outputFunc)(outputStream, "/FontType 0 def\n", 16);
+  (*outputFunc)(outputStream, "/FontMatrix [1 0 0 1 0 0] def\n", 30);
+  (*outputFunc)(outputStream, "/FMapType 2 def\n", 16);
+  (*outputFunc)(outputStream, "/Encoding [\n", 12);
+  for (i = 0; i < nCIDs; i += 256) {
+    sprintf(buf, "%d\n", i >> 8);
+    (*outputFunc)(outputStream, buf, strlen(buf));
+  }
+  (*outputFunc)(outputStream, "] def\n", 6);
+  (*outputFunc)(outputStream, "/FDepVector [\n", 14);
+  for (i = 0; i < nCIDs; i += 256) {
+    (*outputFunc)(outputStream, "/", 1);
+    (*outputFunc)(outputStream, psName, strlen(psName));
+    sprintf(buf, "_%02x findfont\n", i >> 8);
+    (*outputFunc)(outputStream, buf, strlen(buf));
+  }
+  (*outputFunc)(outputStream, "] def\n", 6);
+  (*outputFunc)(outputStream, "FontName currentdict end definefont pop\n", 40);
+
+  gfree(cidMap);
+}
+
+void FoFiType1C::eexecCvtGlyph(Type1CEexecBuf *eb, char *glyphName,
+                              int offset, int nBytes,
+                              Type1CIndex *subrIdx,
+                              Type1CPrivateDict *pDict) {
+  char buf[512];
+  GString *charBuf;
+
+  // generate the charstring
+  charBuf = new GString();
+  cvtGlyph(offset, nBytes, charBuf, subrIdx, pDict, gTrue);
+
+  sprintf(buf, "/%s %d RD ", glyphName, charBuf->getLength());
+  eexecWrite(eb, buf);
+  eexecWriteCharstring(eb, (Guchar *)charBuf->getCString(),
+                      charBuf->getLength());
+  eexecWrite(eb, " ND\n");
+
+  delete charBuf;
+}
+
+void FoFiType1C::cvtGlyph(int offset, int nBytes, GString *charBuf,
+                         Type1CIndex *subrIdx, Type1CPrivateDict *pDict,
+                         GBool top) {
+  Type1CIndexVal val;
+  GBool ok, dFP;
+  double d, dx, dy;
+  Gushort r2;
+  Guchar byte;
+  int pos, subrBias, start, i, k;
+
+  start = charBuf->getLength();
+  if (top) {
+    charBuf->append((char)73);
+    charBuf->append((char)58);
+    charBuf->append((char)147);
+    charBuf->append((char)134);
+    nOps = 0;
+    nHints = 0;
+    firstOp = gTrue;
+  }
+
+  pos = offset;
+  while (pos < offset + nBytes) {
+    ok = gTrue;
+    pos = getOp(pos, gTrue, &ok);
+    if (!ok) {
+      break;
+    }
+    if (!ops[nOps - 1].isNum) {
+      --nOps; // drop the operator
+      switch (ops[nOps].op) {
+      case 0x0001:             // hstem
+       if (firstOp) {
+         cvtGlyphWidth(nOps & 1, charBuf, pDict);
+         firstOp = gFalse;
+       }
+       if (nOps & 1) {
+         //~ error(-1, "Wrong number of args (%d) to Type 2 hstem", nOps);
+       }
+       d = 0;
+       dFP = gFalse;
+       for (k = 0; k < nOps; k += 2) {
+         if (ops[k+1].num < 0) {
+           d += ops[k].num + ops[k+1].num;
+           dFP |= ops[k].isFP | ops[k+1].isFP;
+           cvtNum(d, dFP, charBuf);
+           cvtNum(-ops[k+1].num, ops[k+1].isFP, charBuf);
+         } else {
+           d += ops[k].num;
+           dFP |= ops[k].isFP;
+           cvtNum(d, dFP, charBuf);
+           cvtNum(ops[k+1].num, ops[k+1].isFP, charBuf);
+           d += ops[k+1].num;
+           dFP |= ops[k+1].isFP;
+         }
+         charBuf->append((char)1);
+       }
+       nHints += nOps / 2;
+       nOps = 0;
+       break;
+      case 0x0003:             // vstem
+       if (firstOp) {
+         cvtGlyphWidth(nOps & 1, charBuf, pDict);
+         firstOp = gFalse;
+       }
+       if (nOps & 1) {
+         //~ error(-1, "Wrong number of args (%d) to Type 2 vstem", nOps);
+       }
+       d = 0;
+       dFP = gFalse;
+       for (k = 0; k < nOps; k += 2) {
+         if (ops[k+1].num < 0) {
+           d += ops[k].num + ops[k+1].num;
+           dFP |= ops[k].isFP | ops[k+1].isFP;
+           cvtNum(d, dFP, charBuf);
+           cvtNum(-ops[k+1].num, ops[k+1].isFP, charBuf);
+         } else {
+           d += ops[k].num;
+           dFP |= ops[k].isFP;
+           cvtNum(d, dFP, charBuf);
+           cvtNum(ops[k+1].num, ops[k+1].isFP, charBuf);
+           d += ops[k+1].num;
+           dFP |= ops[k+1].isFP;
+         }
+         charBuf->append((char)3);
+       }
+       nHints += nOps / 2;
+       nOps = 0;
+       break;
+      case 0x0004:             // vmoveto
+       if (firstOp) {
+         cvtGlyphWidth(nOps == 2, charBuf, pDict);
+         firstOp = gFalse;
+       }
+       if (nOps != 1) {
+         //~ error(-1, "Wrong number of args (%d) to Type 2 vmoveto", nOps);
+       }
+       cvtNum(ops[0].num, ops[0].isFP, charBuf);
+       charBuf->append((char)4);
+       nOps = 0;
+       break;
+      case 0x0005:             // rlineto
+       if (nOps < 2 || nOps % 2 != 0) {
+         //~ error(-1, "Wrong number of args (%d) to Type 2 rlineto", nOps);
+       }
+       for (k = 0; k < nOps; k += 2) {
+         cvtNum(ops[k].num, ops[k].isFP, charBuf);
+         cvtNum(ops[k+1].num, ops[k+1].isFP, charBuf);
+         charBuf->append((char)5);
+       }
+       nOps = 0;
+       break;
+      case 0x0006:             // hlineto
+       if (nOps < 1) {
+         //~ error(-1, "Wrong number of args (%d) to Type 2 hlineto", nOps);
+       }
+       for (k = 0; k < nOps; ++k) {
+         cvtNum(ops[k].num, ops[k].isFP, charBuf);
+         charBuf->append((char)((k & 1) ? 7 : 6));
+       }
+       nOps = 0;
+       break;
+      case 0x0007:             // vlineto
+       if (nOps < 1) {
+         //~ error(-1, "Wrong number of args (%d) to Type 2 vlineto", nOps);
+       }
+       for (k = 0; k < nOps; ++k) {
+         cvtNum(ops[k].num, ops[k].isFP, charBuf);
+         charBuf->append((char)((k & 1) ? 6 : 7));
+       }
+       nOps = 0;
+       break;
+      case 0x0008:             // rrcurveto
+       if (nOps < 6 || nOps % 6 != 0) {
+         //~ error(-1, "Wrong number of args (%d) to Type 2 rrcurveto", nOps);
+       }
+       for (k = 0; k < nOps; k += 6) {
+         cvtNum(ops[k].num, ops[k].isFP, charBuf);
+         cvtNum(ops[k+1].num, ops[k+1].isFP, charBuf);
+         cvtNum(ops[k+2].num, ops[k+2].isFP, charBuf);
+         cvtNum(ops[k+3].num, ops[k+3].isFP, charBuf);
+         cvtNum(ops[k+4].num, ops[k+4].isFP, charBuf);
+         cvtNum(ops[k+5].num, ops[k+5].isFP, charBuf);
+         charBuf->append((char)8);
+       }
+       nOps = 0;
+       break;
+      case 0x000a:             // callsubr
+       if (nOps >= 1) {
+         subrBias = (subrIdx->len < 1240)
+                      ? 107 : (subrIdx->len < 33900) ? 1131 : 32768;
+         k = subrBias + (int)ops[nOps - 1].num;
+         --nOps;
+         ok = gTrue;
+         getIndexVal(subrIdx, k, &val, &ok);
+         if (ok) {
+           cvtGlyph(val.pos, val.len, charBuf, subrIdx, pDict, gFalse);
+         }
+       } else {
+         //~ error(-1, "Too few args to Type 2 callsubr");
+       }
+       // don't clear the stack
+       break;
+      case 0x000b:             // return
+       // don't clear the stack
+       break;
+      case 0x000e:             // endchar / seac
+       if (firstOp) {
+         cvtGlyphWidth(nOps == 1 || nOps == 5, charBuf, pDict);
+         firstOp = gFalse;
+       }
+       if (nOps == 4) {
+         cvtNum(0, gFalse, charBuf);
+         cvtNum(ops[0].num, ops[0].isFP, charBuf);
+         cvtNum(ops[1].num, ops[1].isFP, charBuf);
+         cvtNum(ops[2].num, ops[2].isFP, charBuf);
+         cvtNum(ops[3].num, ops[3].isFP, charBuf);
+         charBuf->append((char)12)->append((char)6);
+       } else if (nOps == 0) {
+         charBuf->append((char)14);
+       } else {
+         //~ error(-1, "Wrong number of args (%d) to Type 2 endchar", nOps);
+       }
+       nOps = 0;
+       break;
+      case 0x000f:             // (obsolete)
+       // this op is ignored, but we need the glyph width
+       if (firstOp) {
+         cvtGlyphWidth(nOps > 0, charBuf, pDict);
+         firstOp = gFalse;
+       }
+       nOps = 0;
+       break;
+      case 0x0010:             // blend
+       //~ error(-1, "Unimplemented Type 2 charstring op: %d", file[i]);
+       nOps = 0;
+       break;
+      case 0x0012:             // hstemhm
+       // ignored
+       if (firstOp) {
+         cvtGlyphWidth(nOps & 1, charBuf, pDict);
+         firstOp = gFalse;
+       }
+       if (nOps & 1) {
+         //~ error(-1, "Wrong number of args (%d) to Type 2 hstemhm", nOps);
+       }
+       nHints += nOps / 2;
+       nOps = 0;
+       break;
+      case 0x0013:             // hintmask
+       // ignored
+       if (firstOp) {
+         cvtGlyphWidth(nOps & 1, charBuf, pDict);
+         firstOp = gFalse;
+       }
+       if (nOps > 0) {
+         if (nOps & 1) {
+           //~ error(-1, "Wrong number of args (%d) to Type 2 hintmask/vstemhm",
+           //~       nOps);
+         }
+         nHints += nOps / 2;
+       }
+       pos += (nHints + 7) >> 3;
+       nOps = 0;
+       break;
+      case 0x0014:             // cntrmask
+       // ignored
+       if (firstOp) {
+         cvtGlyphWidth(nOps & 1, charBuf, pDict);
+         firstOp = gFalse;
+       }
+       if (nOps > 0) {
+         if (nOps & 1) {
+           //~ error(-1, "Wrong number of args (%d) to Type 2 cntrmask/vstemhm",
+           //~       nOps);
+         }
+         nHints += nOps / 2;
+       }
+       pos += (nHints + 7) >> 3;
+       nOps = 0;
+       break;
+      case 0x0015:             // rmoveto
+       if (firstOp) {
+         cvtGlyphWidth(nOps == 3, charBuf, pDict);
+         firstOp = gFalse;
+       }
+       if (nOps != 2) {
+         //~ error(-1, "Wrong number of args (%d) to Type 2 rmoveto", nOps);
+       }
+       cvtNum(ops[0].num, ops[0].isFP, charBuf);
+       cvtNum(ops[1].num, ops[1].isFP, charBuf);
+       charBuf->append((char)21);
+       nOps = 0;
+       break;
+      case 0x0016:             // hmoveto
+       if (firstOp) {
+         cvtGlyphWidth(nOps == 2, charBuf, pDict);
+         firstOp = gFalse;
+       }
+       if (nOps != 1) {
+         //~ error(-1, "Wrong number of args (%d) to Type 2 hmoveto", nOps);
+       }
+       cvtNum(ops[0].num, ops[0].isFP, charBuf);
+       charBuf->append((char)22);
+       nOps = 0;
+       break;
+      case 0x0017:             // vstemhm
+       // ignored
+       if (firstOp) {
+         cvtGlyphWidth(nOps & 1, charBuf, pDict);
+         firstOp = gFalse;
+       }
+       if (nOps & 1) {
+         //~ error(-1, "Wrong number of args (%d) to Type 2 vstemhm", nOps);
+       }
+       nHints += nOps / 2;
+       nOps = 0;
+       break;
+      case 0x0018:             // rcurveline
+       if (nOps < 8 || (nOps - 2) % 6 != 0) {
+         //~ error(-1, "Wrong number of args (%d) to Type 2 rcurveline", nOps);
+       }
+       for (k = 0; k < nOps - 2; k += 6) {
+         cvtNum(ops[k].num, ops[k].isFP, charBuf);
+         cvtNum(ops[k+1].num, ops[k+1].isFP, charBuf);
+         cvtNum(ops[k+2].num, ops[k+2].isFP, charBuf);
+         cvtNum(ops[k+3].num, ops[k+3].isFP, charBuf);
+         cvtNum(ops[k+4].num, ops[k+4].isFP, charBuf);
+         cvtNum(ops[k+5].num, ops[k+5].isFP, charBuf);
+         charBuf->append((char)8);
+       }
+       cvtNum(ops[k].num, ops[k].isFP, charBuf);
+       cvtNum(ops[k+1].num, ops[k].isFP, charBuf);
+       charBuf->append((char)5);
+       nOps = 0;
+       break;
+      case 0x0019:             // rlinecurve
+       if (nOps < 8 || (nOps - 6) % 2 != 0) {
+         //~ error(-1, "Wrong number of args (%d) to Type 2 rlinecurve", nOps);
+       }
+       for (k = 0; k < nOps - 6; k += 2) {
+         cvtNum(ops[k].num, ops[k].isFP, charBuf);
+         cvtNum(ops[k+1].num, ops[k].isFP, charBuf);
+         charBuf->append((char)5);
+       }
+       cvtNum(ops[k].num, ops[k].isFP, charBuf);
+       cvtNum(ops[k+1].num, ops[k+1].isFP, charBuf);
+       cvtNum(ops[k+2].num, ops[k+2].isFP, charBuf);
+       cvtNum(ops[k+3].num, ops[k+3].isFP, charBuf);
+       cvtNum(ops[k+4].num, ops[k+4].isFP, charBuf);
+       cvtNum(ops[k+5].num, ops[k+5].isFP, charBuf);
+       charBuf->append((char)8);
+       nOps = 0;
+       break;
+      case 0x001a:             // vvcurveto
+       if (nOps < 4 || !(nOps % 4 == 0 || (nOps-1) % 4 == 0)) {
+         //~ error(-1, "Wrong number of args (%d) to Type 2 vvcurveto", nOps);
+       }
+       if (nOps % 2 == 1) {
+         cvtNum(ops[0].num, ops[0].isFP, charBuf);
+         cvtNum(ops[1].num, ops[1].isFP, charBuf);
+         cvtNum(ops[2].num, ops[2].isFP, charBuf);
+         cvtNum(ops[3].num, ops[3].isFP, charBuf);
+         cvtNum(0, gFalse, charBuf);
+         cvtNum(ops[4].num, ops[4].isFP, charBuf);
+         charBuf->append((char)8);
+         k = 5;
+       } else {
+         k = 0;
+       }
+       for (; k < nOps; k += 4) {
+         cvtNum(0, gFalse, charBuf);
+         cvtNum(ops[k].num, ops[k].isFP, charBuf);
+         cvtNum(ops[k+1].num, ops[k+1].isFP, charBuf);
+         cvtNum(ops[k+2].num, ops[k+2].isFP, charBuf);
+         cvtNum(0, gFalse, charBuf);
+         cvtNum(ops[k+3].num, ops[k+3].isFP, charBuf);
+         charBuf->append((char)8);
+       }
+       nOps = 0;
+       break;
+      case 0x001b:             // hhcurveto
+       if (nOps < 4 || !(nOps % 4 == 0 || (nOps-1) % 4 == 0)) {
+         //~ error(-1, "Wrong number of args (%d) to Type 2 hhcurveto", nOps);
+       }
+       if (nOps % 2 == 1) {
+         cvtNum(ops[1].num, ops[1].isFP, charBuf);
+         cvtNum(ops[0].num, ops[0].isFP, charBuf);
+         cvtNum(ops[2].num, ops[2].isFP, charBuf);
+         cvtNum(ops[3].num, ops[3].isFP, charBuf);
+         cvtNum(ops[4].num, ops[4].isFP, charBuf);
+         cvtNum(0, gFalse, charBuf);
+         charBuf->append((char)8);
+         k = 5;
+       } else {
+         k = 0;
+       }
+       for (; k < nOps; k += 4) {
+         cvtNum(ops[k].num, ops[k].isFP, charBuf);
+         cvtNum(0, gFalse, charBuf);
+         cvtNum(ops[k+1].num, ops[k+1].isFP, charBuf);
+         cvtNum(ops[k+2].num, ops[k+2].isFP, charBuf);
+         cvtNum(ops[k+3].num, ops[k+3].isFP, charBuf);
+         cvtNum(0, gFalse, charBuf);
+         charBuf->append((char)8);
+       }
+       nOps = 0;
+       break;
+      case 0x001d:             // callgsubr
+       if (nOps >= 1) {
+         k = gsubrBias + (int)ops[nOps - 1].num;
+         --nOps;
+         ok = gTrue;
+         getIndexVal(&gsubrIdx, k, &val, &ok);
+         if (ok) {
+           cvtGlyph(val.pos, val.len, charBuf, subrIdx, pDict, gFalse);
+         }
+       } else {
+         //~ error(-1, "Too few args to Type 2 callgsubr");
+       }
+       // don't clear the stack
+       break;
+      case 0x001e:             // vhcurveto
+       if (nOps < 4 || !(nOps % 4 == 0 || (nOps-1) % 4 == 0)) {
+         //~ error(-1, "Wrong number of args (%d) to Type 2 vhcurveto", nOps);
+       }
+       for (k = 0; k < nOps && k != nOps-5; k += 4) {
+         if (k % 8 == 0) {
+           cvtNum(ops[k].num, ops[k].isFP, charBuf);
+           cvtNum(ops[k+1].num, ops[k+1].isFP, charBuf);
+           cvtNum(ops[k+2].num, ops[k+2].isFP, charBuf);
+           cvtNum(ops[k+3].num, ops[k+3].isFP, charBuf);
+           charBuf->append((char)30);
+         } else {
+           cvtNum(ops[k].num, ops[k].isFP, charBuf);
+           cvtNum(ops[k+1].num, ops[k+1].isFP, charBuf);
+           cvtNum(ops[k+2].num, ops[k+2].isFP, charBuf);
+           cvtNum(ops[k+3].num, ops[k+3].isFP, charBuf);
+           charBuf->append((char)31);
+         }
+       }
+       if (k == nOps-5) {
+         if (k % 8 == 0) {
+           cvtNum(0, gFalse, charBuf);
+           cvtNum(ops[k].num, ops[k].isFP, charBuf);
+           cvtNum(ops[k+1].num, ops[k+1].isFP, charBuf);
+           cvtNum(ops[k+2].num, ops[k+2].isFP, charBuf);
+           cvtNum(ops[k+3].num, ops[k+3].isFP, charBuf);
+           cvtNum(ops[k+4].num, ops[k+4].isFP, charBuf);
+         } else {
+           cvtNum(ops[k].num, ops[k].isFP, charBuf);
+           cvtNum(0, gFalse, charBuf);
+           cvtNum(ops[k+1].num, ops[k+1].isFP, charBuf);
+           cvtNum(ops[k+2].num, ops[k+2].isFP, charBuf);
+           cvtNum(ops[k+4].num, ops[k+4].isFP, charBuf);
+           cvtNum(ops[k+3].num, ops[k+3].isFP, charBuf);
+         }
+         charBuf->append((char)8);
+       }
+       nOps = 0;
+       break;
+      case 0x001f:             // hvcurveto
+       if (nOps < 4 || !(nOps % 4 == 0 || (nOps-1) % 4 == 0)) {
+         //~ error(-1, "Wrong number of args (%d) to Type 2 hvcurveto", nOps);
+       }
+       for (k = 0; k < nOps && k != nOps-5; k += 4) {
+         if (k % 8 == 0) {
+           cvtNum(ops[k].num, ops[k].isFP, charBuf);
+           cvtNum(ops[k+1].num, ops[k+1].isFP, charBuf);
+           cvtNum(ops[k+2].num, ops[k+2].isFP, charBuf);
+           cvtNum(ops[k+3].num, ops[k+3].isFP, charBuf);
+           charBuf->append((char)31);
+         } else {
+           cvtNum(ops[k].num, ops[k].isFP, charBuf);
+           cvtNum(ops[k+1].num, ops[k+1].isFP, charBuf);
+           cvtNum(ops[k+2].num, ops[k+2].isFP, charBuf);
+           cvtNum(ops[k+3].num, ops[k+3].isFP, charBuf);
+           charBuf->append((char)30);
+         }
+       }
+       if (k == nOps-5) {
+         if (k % 8 == 0) {
+           cvtNum(ops[k].num, ops[k].isFP, charBuf);
+           cvtNum(0, gFalse, charBuf);
+           cvtNum(ops[k+1].num, ops[k+1].isFP, charBuf);
+           cvtNum(ops[k+2].num, ops[k+2].isFP, charBuf);
+           cvtNum(ops[k+4].num, ops[k+4].isFP, charBuf);
+           cvtNum(ops[k+3].num, ops[k+3].isFP, charBuf);
+         } else {
+           cvtNum(0, gFalse, charBuf);
+           cvtNum(ops[k].num, ops[k].isFP, charBuf);
+           cvtNum(ops[k+1].num, ops[k+1].isFP, charBuf);
+           cvtNum(ops[k+2].num, ops[k+2].isFP, charBuf);
+           cvtNum(ops[k+3].num, ops[k+3].isFP, charBuf);
+           cvtNum(ops[k+4].num, ops[k+4].isFP, charBuf);
+         }
+         charBuf->append((char)8);
+       }
+       nOps = 0;
+       break;
+      case 0x0c00:             // dotsection (should be Type 1 only?)
+       // ignored
+       nOps = 0;
+       break;
+      case 0x0c03:             // and
+      case 0x0c04:             // or
+      case 0x0c05:             // not
+      case 0x0c08:             // store
+      case 0x0c09:             // abs
+      case 0x0c0a:             // add
+      case 0x0c0b:             // sub
+      case 0x0c0c:             // div
+      case 0x0c0d:             // load
+      case 0x0c0e:             // neg
+      case 0x0c0f:             // eq
+      case 0x0c12:             // drop
+      case 0x0c14:             // put
+      case 0x0c15:             // get
+      case 0x0c16:             // ifelse
+      case 0x0c17:             // random
+      case 0x0c18:             // mul
+      case 0x0c1a:             // sqrt
+      case 0x0c1b:             // dup
+      case 0x0c1c:             // exch
+      case 0x0c1d:             // index
+      case 0x0c1e:             // roll
+       //~ error(-1, "Unimplemented Type 2 charstring op: 12.%d", file[i+1]);
+       nOps = 0;
+       break;
+      case 0x0c22:             // hflex
+       if (nOps != 7) {
+         //~ error(-1, "Wrong number of args (%d) to Type 2 hflex", nOps);
+       }
+       cvtNum(ops[0].num, ops[0].isFP, charBuf);
+       cvtNum(0, gFalse, charBuf);
+       cvtNum(ops[1].num, ops[1].isFP, charBuf);
+       cvtNum(ops[2].num, ops[2].isFP, charBuf);
+       cvtNum(ops[3].num, ops[3].isFP, charBuf);
+       cvtNum(0, gFalse, charBuf);
+       charBuf->append((char)8);
+       cvtNum(ops[4].num, ops[4].isFP, charBuf);
+       cvtNum(0, gFalse, charBuf);
+       cvtNum(ops[5].num, ops[5].isFP, charBuf);
+       cvtNum(-ops[2].num, ops[2].isFP, charBuf);
+       cvtNum(ops[6].num, ops[6].isFP, charBuf);
+       cvtNum(0, gFalse, charBuf);
+       charBuf->append((char)8);
+       nOps = 0;
+       break;
+      case 0x0c23:             // flex
+       if (nOps != 13) {
+         //~ error(-1, "Wrong number of args (%d) to Type 2 flex", nOps);
+       }
+       cvtNum(ops[0].num, ops[0].isFP, charBuf);
+       cvtNum(ops[1].num, ops[1].isFP, charBuf);
+       cvtNum(ops[2].num, ops[2].isFP, charBuf);
+       cvtNum(ops[3].num, ops[3].isFP, charBuf);
+       cvtNum(ops[4].num, ops[4].isFP, charBuf);
+       cvtNum(ops[5].num, ops[5].isFP, charBuf);
+       charBuf->append((char)8);
+       cvtNum(ops[6].num, ops[6].isFP, charBuf);
+       cvtNum(ops[7].num, ops[7].isFP, charBuf);
+       cvtNum(ops[8].num, ops[8].isFP, charBuf);
+       cvtNum(ops[9].num, ops[9].isFP, charBuf);
+       cvtNum(ops[10].num, ops[10].isFP, charBuf);
+       cvtNum(ops[11].num, ops[11].isFP, charBuf);
+       charBuf->append((char)8);
+       nOps = 0;
+       break;
+      case 0x0c24:             // hflex1
+       if (nOps != 9) {
+         //~ error(-1, "Wrong number of args (%d) to Type 2 hflex1", nOps);
+       }
+       cvtNum(ops[0].num, ops[0].isFP, charBuf);
+       cvtNum(ops[1].num, ops[1].isFP, charBuf);
+       cvtNum(ops[2].num, ops[2].isFP, charBuf);
+       cvtNum(ops[3].num, ops[3].isFP, charBuf);
+       cvtNum(ops[4].num, ops[4].isFP, charBuf);
+       cvtNum(0, gFalse, charBuf);
+       charBuf->append((char)8);
+       cvtNum(ops[5].num, ops[5].isFP, charBuf);
+       cvtNum(0, gFalse, charBuf);
+       cvtNum(ops[6].num, ops[6].isFP, charBuf);
+       cvtNum(ops[7].num, ops[7].isFP, charBuf);
+       cvtNum(ops[8].num, ops[8].isFP, charBuf);
+       cvtNum(-(ops[1].num + ops[3].num + ops[7].num),
+              ops[1].isFP | ops[3].isFP | ops[7].isFP, charBuf);
+       charBuf->append((char)8);
+       nOps = 0;
+       break;
+      case 0x0c25:             // flex1
+       if (nOps != 11) {
+         //~ error(-1, "Wrong number of args (%d) to Type 2 flex1", nOps);
+       }
+       cvtNum(ops[0].num, ops[0].isFP, charBuf);
+       cvtNum(ops[1].num, ops[1].isFP, charBuf);
+       cvtNum(ops[2].num, ops[2].isFP, charBuf);
+       cvtNum(ops[3].num, ops[3].isFP, charBuf);
+       cvtNum(ops[4].num, ops[4].isFP, charBuf);
+       cvtNum(ops[5].num, ops[5].isFP, charBuf);
+       charBuf->append((char)8);
+       cvtNum(ops[6].num, ops[6].isFP, charBuf);
+       cvtNum(ops[7].num, ops[7].isFP, charBuf);
+       cvtNum(ops[8].num, ops[8].isFP, charBuf);
+       cvtNum(ops[9].num, ops[9].isFP, charBuf);
+       dx = ops[0].num + ops[2].num + ops[4].num + ops[6].num + ops[8].num;
+       dy = ops[1].num + ops[3].num + ops[5].num + ops[7].num + ops[9].num;
+       if (fabs(dx) > fabs(dy)) {
+         cvtNum(ops[10].num, ops[10].isFP, charBuf);
+         cvtNum(-dy, ops[1].isFP | ops[3].isFP | ops[5].isFP |
+                     ops[7].isFP | ops[9].isFP, charBuf);
+       } else {
+         cvtNum(-dx, ops[0].isFP | ops[2].isFP | ops[4].isFP |
+                     ops[6].isFP | ops[8].isFP, charBuf);
+         cvtNum(ops[10].num, ops[10].isFP, charBuf);
+       }
+       charBuf->append((char)8);
+       nOps = 0;
+       break;
+      default:
+       //~ error(-1, "Illegal Type 2 charstring op: %04x",
+       //~       ops[nOps].op);
+       nOps = 0;
+       break;
+      }
+    }
+  }
+
+  // charstring encryption
+  if (top) {
+    r2 = 4330;
+    for (i = start; i < charBuf->getLength(); ++i) {
+      byte = charBuf->getChar(i) ^ (r2 >> 8);
+      charBuf->setChar(i, byte);
+      r2 = (byte + r2) * 52845 + 22719;
+    }
+  }
+}
+
+void FoFiType1C::cvtGlyphWidth(GBool useOp, GString *charBuf,
+                              Type1CPrivateDict *pDict) {
+  double w;
+  GBool wFP;
+  int i;
+
+  if (useOp) {
+    w = pDict->nominalWidthX + ops[0].num;
+    wFP = pDict->nominalWidthXFP | ops[0].isFP;
+    for (i = 1; i < nOps; ++i) {
+      ops[i-1] = ops[i];
+    }
+    --nOps;
+  } else {
+    w = pDict->defaultWidthX;
+    wFP = pDict->defaultWidthXFP;
+  }
+  cvtNum(0, gFalse, charBuf);
+  cvtNum(w, wFP, charBuf);
+  charBuf->append((char)13);
+}
+
+void FoFiType1C::cvtNum(double x, GBool isFP, GString *charBuf) {
+  Guchar buf[12];
+  int y, n;
+
+  n = 0;
+  if (isFP) {
+    if (x >= -32768 && x < 32768) {
+      y = (int)(x * 256.0);
+      buf[0] = 255;
+      buf[1] = (Guchar)(y >> 24);
+      buf[2] = (Guchar)(y >> 16);
+      buf[3] = (Guchar)(y >> 8);
+      buf[4] = (Guchar)y;
+      buf[5] = 255;
+      buf[6] = 0;
+      buf[7] = 0;
+      buf[8] = 1;
+      buf[9] = 0;
+      buf[10] = 12;
+      buf[11] = 12;
+      n = 12;
+    } else {
+      //~ error(-1, "Type 2 fixed point constant out of range");
+    }
+  } else {
+    y = (int)x;
+    if (y >= -107 && y <= 107) {
+      buf[0] = (Guchar)(y + 139);
+      n = 1;
+    } else if (y > 107 && y <= 1131) {
+      y -= 108;
+      buf[0] = (Guchar)((y >> 8) + 247);
+      buf[1] = (Guchar)(y & 0xff);
+      n = 2;
+    } else if (y < -107 && y >= -1131) {
+      y = -y - 108;
+      buf[0] = (Guchar)((y >> 8) + 251);
+      buf[1] = (Guchar)(y & 0xff);
+      n = 2;
+    } else {
+      buf[0] = 255;
+      buf[1] = (Guchar)(y >> 24);
+      buf[2] = (Guchar)(y >> 16);
+      buf[3] = (Guchar)(y >> 8);
+      buf[4] = (Guchar)y;
+      n = 5;
+    }
+  }
+  charBuf->append((char *)buf, n);
+}
+
+void FoFiType1C::eexecWrite(Type1CEexecBuf *eb, char *s) {
+  Guchar *p;
+  Guchar x;
+
+  for (p = (Guchar *)s; *p; ++p) {
+    x = *p ^ (eb->r1 >> 8);
+    eb->r1 = (x + eb->r1) * 52845 + 22719;
+    if (eb->ascii) {
+      (*eb->outputFunc)(eb->outputStream, &hexChars[x >> 4], 1);
+      (*eb->outputFunc)(eb->outputStream, &hexChars[x & 0x0f], 1);
+      eb->line += 2;
+      if (eb->line == 64) {
+       (*eb->outputFunc)(eb->outputStream, "\n", 1);
+       eb->line = 0;
+      }
+    } else {
+      (*eb->outputFunc)(eb->outputStream, (char *)&x, 1);
+    }
+  }
+}
+
+void FoFiType1C::eexecWriteCharstring(Type1CEexecBuf *eb,
+                                     Guchar *s, int n) {
+  Guchar x;
+  int i;
+
+  // eexec encryption
+  for (i = 0; i < n; ++i) {
+    x = s[i] ^ (eb->r1 >> 8);
+    eb->r1 = (x + eb->r1) * 52845 + 22719;
+    if (eb->ascii) {
+      (*eb->outputFunc)(eb->outputStream, &hexChars[x >> 4], 1);
+      (*eb->outputFunc)(eb->outputStream, &hexChars[x & 0x0f], 1);
+      eb->line += 2;
+      if (eb->line == 64) {
+       (*eb->outputFunc)(eb->outputStream, "\n", 1);
+       eb->line = 0;
+      }
+    } else {
+      (*eb->outputFunc)(eb->outputStream, (char *)&x, 1);
+    }
+  }
+}
+
+GBool FoFiType1C::parse() {
+  Type1CIndex fdIdx;
+  Type1CIndexVal val;
+  int i;
+
+  parsedOk = gTrue;
+
+  // some tools embed Type 1C fonts with an extra whitespace char at
+  // the beginning
+  if (len > 0 && file[0] != '\x01') {
+    ++file;
+    --len;
+  }
+
+  // find the indexes
+  getIndex(getU8(2, &parsedOk), &nameIdx, &parsedOk);
+  getIndex(nameIdx.endPos, &topDictIdx, &parsedOk);
+  getIndex(topDictIdx.endPos, &stringIdx, &parsedOk);
+  getIndex(stringIdx.endPos, &gsubrIdx, &parsedOk);
+  if (!parsedOk) {
+    return gFalse;
+  }
+  gsubrBias = (gsubrIdx.len < 1240) ? 107
+                                    : (gsubrIdx.len < 33900) ? 1131 : 32768;
+
+  // read the first font name
+  getIndexVal(&nameIdx, 0, &val, &parsedOk);
+  if (!parsedOk) {
+    return gFalse;
+  }
+  name = new GString((char *)&file[val.pos], val.len);
+
+  // read the top dict for the first font
+  readTopDict();
+
+  // for CID fonts: read the FDArray dicts and private dicts
+  if (topDict.firstOp == 0x0c1e) {
+    if (topDict.fdArrayOffset == 0) {
+      nFDs = 1;
+      privateDicts = (Type1CPrivateDict *)gmalloc(sizeof(Type1CPrivateDict));
+      readPrivateDict(0, 0, &privateDicts[0]);
+    } else {
+      getIndex(topDict.fdArrayOffset, &fdIdx, &parsedOk);
+      if (!parsedOk) {
+       return gFalse;
+      }
+      nFDs = fdIdx.len;
+      privateDicts = (Type1CPrivateDict *)
+                        gmalloc(nFDs * sizeof(Type1CPrivateDict));
+      for (i = 0; i < nFDs; ++i) {
+       getIndexVal(&fdIdx, i, &val, &parsedOk);
+       if (!parsedOk) {
+         return gFalse;
+       }
+       readFD(val.pos, val.len, &privateDicts[i]);
+      }
+    }
+
+  // for 8-bit fonts: read the private dict
+  } else {
+    privateDicts = (Type1CPrivateDict *)gmalloc(sizeof(Type1CPrivateDict));
+    readPrivateDict(topDict.privateOffset, topDict.privateSize,
+                   &privateDicts[0]);
+  }
+
+  // check for parse errors in the private dict(s)
+  if (!parsedOk) {
+    return gFalse;
+  }
+
+  // get the charstrings index
+  if (topDict.charStringsOffset <= 0) {
+    parsedOk = gFalse;
+    return gFalse;
+  }
+  getIndex(topDict.charStringsOffset, &charStringsIdx, &parsedOk);
+  if (!parsedOk) {
+    return gFalse;
+  }
+  nGlyphs = charStringsIdx.len;
+
+  // for CID fonts: read the FDSelect table
+  if (topDict.firstOp == 0x0c1e) {
+    readFDSelect();
+    if (!parsedOk) {
+      return gFalse;
+    }
+  }
+
+  // read the charset
+  if (!readCharset()) {
+    parsedOk = gFalse;
+    return gFalse;
+  }
+
+  // for 8-bit fonts: build the encoding
+  if (topDict.firstOp != 0x0c14 && topDict.firstOp != 0x0c1e) {
+    buildEncoding();
+    if (!parsedOk) {
+      return gFalse;
+    }
+  }
+
+  return parsedOk;
+}
+
+void FoFiType1C::readTopDict() {
+  Type1CIndexVal topDictPtr;
+  int pos;
+
+  topDict.firstOp = -1;
+  topDict.versionSID = 0;
+  topDict.noticeSID = 0;
+  topDict.copyrightSID = 0;
+  topDict.fullNameSID = 0;
+  topDict.familyNameSID = 0;
+  topDict.weightSID = 0;
+  topDict.isFixedPitch = 0;
+  topDict.italicAngle = 0;
+  topDict.underlinePosition = -100;
+  topDict.underlineThickness = 50;
+  topDict.paintType = 0;
+  topDict.charstringType = 2;
+  topDict.fontMatrix[0] = 0.001;
+  topDict.fontMatrix[1] = 0;
+  topDict.fontMatrix[2] = 0;
+  topDict.fontMatrix[3] = 0.001;
+  topDict.fontMatrix[4] = 0;
+  topDict.fontMatrix[5] = 0;
+  topDict.uniqueID = 0;
+  topDict.fontBBox[0] = 0;
+  topDict.fontBBox[1] = 0;
+  topDict.fontBBox[2] = 0;
+  topDict.fontBBox[3] = 0;
+  topDict.strokeWidth = 0;
+  topDict.charsetOffset = 0;
+  topDict.encodingOffset = 0;
+  topDict.charStringsOffset = 0;
+  topDict.privateSize = 0;
+  topDict.privateOffset = 0;
+  topDict.registrySID = 0;
+  topDict.orderingSID = 0;
+  topDict.supplement = 0;
+  topDict.fdArrayOffset = 0;
+  topDict.fdSelectOffset = 0;
+
+  getIndexVal(&topDictIdx, 0, &topDictPtr, &parsedOk);
+  pos = topDictPtr.pos;
+  nOps = 0;
+  while (pos < topDictPtr.pos + topDictPtr.len) {
+    pos = getOp(pos, gFalse, &parsedOk);
+    if (!parsedOk) {
+      break;
+    }
+    if (!ops[nOps - 1].isNum) {
+      --nOps; // drop the operator
+      if (topDict.firstOp < 0) {
+       topDict.firstOp = ops[nOps].op;
+      }
+      switch (ops[nOps].op) {
+      case 0x0000: topDict.versionSID = (int)ops[0].num; break;
+      case 0x0001: topDict.noticeSID = (int)ops[0].num; break;
+      case 0x0c00: topDict.copyrightSID = (int)ops[0].num; break;
+      case 0x0002: topDict.fullNameSID = (int)ops[0].num; break;
+      case 0x0003: topDict.familyNameSID = (int)ops[0].num; break;
+      case 0x0004: topDict.weightSID = (int)ops[0].num; break;
+      case 0x0c01: topDict.isFixedPitch = (int)ops[0].num; break;
+      case 0x0c02: topDict.italicAngle = ops[0].num; break;
+      case 0x0c03: topDict.underlinePosition = ops[0].num; break;
+      case 0x0c04: topDict.underlineThickness = ops[0].num; break;
+      case 0x0c05: topDict.paintType = (int)ops[0].num; break;
+      case 0x0c06: topDict.charstringType = (int)ops[0].num; break;
+      case 0x0c07: topDict.fontMatrix[0] = ops[0].num;
+                  topDict.fontMatrix[1] = ops[1].num;
+                  topDict.fontMatrix[2] = ops[2].num;
+                  topDict.fontMatrix[3] = ops[3].num;
+                  topDict.fontMatrix[4] = ops[4].num;
+                  topDict.fontMatrix[5] = ops[5].num; break;
+      case 0x000d: topDict.uniqueID = (int)ops[0].num; break;
+      case 0x0005: topDict.fontBBox[0] = ops[0].num;
+                  topDict.fontBBox[1] = ops[1].num;
+                  topDict.fontBBox[2] = ops[2].num;
+                  topDict.fontBBox[3] = ops[3].num; break;
+      case 0x0c08: topDict.strokeWidth = ops[0].num; break;
+      case 0x000f: topDict.charsetOffset = (int)ops[0].num; break;
+      case 0x0010: topDict.encodingOffset = (int)ops[0].num; break;
+      case 0x0011: topDict.charStringsOffset = (int)ops[0].num; break;
+      case 0x0012: topDict.privateSize = (int)ops[0].num;
+                  topDict.privateOffset = (int)ops[1].num; break;
+      case 0x0c1e: topDict.registrySID = (int)ops[0].num;
+                  topDict.orderingSID = (int)ops[1].num;
+                  topDict.supplement = (int)ops[2].num; break;
+      case 0x0c24: topDict.fdArrayOffset = (int)ops[0].num; break;
+      case 0x0c25: topDict.fdSelectOffset = (int)ops[0].num; break;
+      }
+      nOps = 0;
+    }
+  }
+}
+
+// Read a CID font dict (FD) - this pulls out the private dict
+// pointer, and reads the private dict.
+void FoFiType1C::readFD(int offset, int length, Type1CPrivateDict *pDict) {
+  int pos, pSize, pOffset;
+
+  pSize = pOffset = 0;
+  pos = offset;
+  nOps = 0;
+  while (pos < offset + length) {
+    pos = getOp(pos, gFalse, &parsedOk);
+    if (!parsedOk) {
+      return;
+    }
+    if (!ops[nOps - 1].isNum) {
+      if (ops[nOps - 1].op == 0x0012) {
+       if (nOps < 3) {
+         parsedOk = gFalse;
+         return;
+       }
+       pSize = (int)ops[0].num;
+       pOffset = (int)ops[1].num;
+       break;
+      }
+      nOps = 0;
+    }
+  }
+  readPrivateDict(pOffset, pSize, pDict);
+}
+
+void FoFiType1C::readPrivateDict(int offset, int length,
+                                Type1CPrivateDict *pDict) {
+  int pos;
+
+  pDict->nBlueValues = 0;
+  pDict->nOtherBlues = 0;
+  pDict->nFamilyBlues = 0;
+  pDict->nFamilyOtherBlues = 0;
+  pDict->blueScale = 0.039625;
+  pDict->blueShift = 7;
+  pDict->blueFuzz = 1;
+  pDict->hasStdHW = gFalse;
+  pDict->hasStdVW = gFalse;
+  pDict->nStemSnapH = 0;
+  pDict->nStemSnapV = 0;
+  pDict->hasForceBold = gFalse;
+  pDict->forceBoldThreshold = 0;
+  pDict->languageGroup = 0;
+  pDict->expansionFactor = 0.06;
+  pDict->initialRandomSeed = 0;
+  pDict->subrsOffset = 0;
+  pDict->defaultWidthX = 0;
+  pDict->defaultWidthXFP = 0;
+  pDict->nominalWidthX = 0;
+  pDict->nominalWidthXFP = 0;
+
+  // no dictionary
+  if (offset == 0 || length == 0) {
+    return;
+  }
+
+  pos = offset;
+  nOps = 0;
+  while (pos < offset + length) {
+    pos = getOp(pos, gFalse, &parsedOk);
+    if (!parsedOk) {
+      break;
+    }
+    if (!ops[nOps - 1].isNum) {
+      --nOps; // drop the operator
+      switch (ops[nOps].op) {
+      case 0x0006:
+       pDict->nBlueValues = getDeltaIntArray(pDict->blueValues,
+                                             type1CMaxBlueValues);
+       break;
+      case 0x0007:
+       pDict->nOtherBlues = getDeltaIntArray(pDict->otherBlues,
+                                             type1CMaxOtherBlues);
+       break;
+      case 0x0008:
+       pDict->nFamilyBlues = getDeltaIntArray(pDict->familyBlues,
+                                              type1CMaxBlueValues);
+       break;
+      case 0x0009:
+       pDict->nFamilyOtherBlues = getDeltaIntArray(pDict->familyOtherBlues,
+                                                   type1CMaxOtherBlues);
+       break;
+      case 0x0c09:
+       pDict->blueScale = ops[0].num;
+       break;
+      case 0x0c0a:
+       pDict->blueShift = (int)ops[0].num;
+       break;
+      case 0x0c0b:
+       pDict->blueFuzz = (int)ops[0].num;
+       break;
+      case 0x000a:
+       pDict->stdHW = ops[0].num;
+       pDict->hasStdHW = gTrue;
+       break;
+      case 0x000b:
+       pDict->stdVW = ops[0].num;
+       pDict->hasStdVW = gTrue;
+       break;
+      case 0x0c0c:
+       pDict->nStemSnapH = getDeltaFPArray(pDict->stemSnapH,
+                                           type1CMaxStemSnap);
+       break;
+      case 0x0c0d:
+       pDict->nStemSnapV = getDeltaFPArray(pDict->stemSnapV,
+                                           type1CMaxStemSnap);
+       break;
+      case 0x0c0e:
+       pDict->forceBold = ops[0].num != 0;
+       pDict->hasForceBold = gTrue;
+       break;
+      case 0x0c0f:
+       pDict->forceBoldThreshold = ops[0].num;
+       break;
+      case 0x0c11:
+       pDict->languageGroup = (int)ops[0].num;
+       break;
+      case 0x0c12:
+       pDict->expansionFactor = ops[0].num;
+       break;
+      case 0x0c13:
+       pDict->initialRandomSeed = (int)ops[0].num;
+       break;
+      case 0x0013:
+       pDict->subrsOffset = offset + (int)ops[0].num;
+       break;
+      case 0x0014:
+       pDict->defaultWidthX = ops[0].num;
+       break;
+      case 0x0015:
+       pDict->nominalWidthX = ops[0].num;
+       break;
+      }
+      nOps = 0;
+    }
+  }
+}
+
+void FoFiType1C::readFDSelect() {
+  int fdSelectFmt, pos, nRanges, gid0, gid1, fd, i, j;
+
+  fdSelect = (Guchar *)gmalloc(nGlyphs);
+  if (topDict.fdSelectOffset == 0) {
+    for (i = 0; i < nGlyphs; ++i) {
+      fdSelect[i] = 0;
+    }
+  } else {
+    pos = topDict.fdSelectOffset;
+    fdSelectFmt = getU8(pos++, &parsedOk);
+    if (!parsedOk) {
+      return;
+    }
+    if (fdSelectFmt == 0) {
+      if (!checkRegion(pos, nGlyphs)) {
+       parsedOk = gFalse;
+       return;
+      }
+      memcpy(fdSelect, file + pos, nGlyphs);
+    } else if (fdSelectFmt == 3) {
+      nRanges = getU16BE(pos, &parsedOk);
+      pos += 2;
+      gid0 = getU16BE(pos, &parsedOk);
+      pos += 2;
+      for (i = 1; i <= nRanges; ++i) {
+       fd = getU8(pos++, &parsedOk);
+       gid1 = getU16BE(pos, &parsedOk);
+       if (!parsedOk) {
+         return;
+       }
+       pos += 2;
+       if (gid0 > gid1 || gid1 > nGlyphs) {
+         //~ error(-1, "Bad FDSelect table in CID font");
+         parsedOk = gFalse;
+         return;
+       }
+       for (j = gid0; j < gid1; ++j) {
+         fdSelect[j] = fd;
+       }
+       gid0 = gid1;
+      }
+    } else {
+      //~ error(-1, "Unknown FDSelect table format in CID font");
+      for (i = 0; i < nGlyphs; ++i) {
+       fdSelect[i] = 0;
+      }
+    }
+  }
+}
+
+void FoFiType1C::buildEncoding() {
+  char buf[256];
+  int nCodes, nRanges, encFormat;
+  int pos, c, sid, nLeft, nSups, i, j;
+
+  if (topDict.encodingOffset == 0) {
+    encoding = fofiType1StandardEncoding;
+
+  } else if (topDict.encodingOffset == 1) {
+    encoding = fofiType1ExpertEncoding;
+
+  } else {
+    encoding = (char **)gmalloc(256 * sizeof(char *));
+    for (i = 0; i < 256; ++i) {
+      encoding[i] = NULL;
+    }
+    pos = topDict.encodingOffset;
+    encFormat = getU8(pos++, &parsedOk);
+    if (!parsedOk) {
+      return;
+    }
+    if ((encFormat & 0x7f) == 0) {
+      nCodes = 1 + getU8(pos++, &parsedOk);
+      if (!parsedOk) {
+       return;
+      }
+      if (nCodes > nGlyphs) {
+       nCodes = nGlyphs;
+      }
+      for (i = 1; i < nCodes; ++i) {
+       c = getU8(pos++, &parsedOk);
+       if (!parsedOk) {
+         return;
+       }
+       if (encoding[c]) {
+         gfree(encoding[c]);
+       }
+       encoding[c] = copyString(getString(charset[i], buf, &parsedOk));
+      }
+    } else if ((encFormat & 0x7f) == 1) {
+      nRanges = getU8(pos++, &parsedOk);
+      if (!parsedOk) {
+       return;
+      }
+      nCodes = 1;
+      for (i = 0; i < nRanges; ++i) {
+       c = getU8(pos++, &parsedOk);
+       nLeft = getU8(pos++, &parsedOk);
+       if (!parsedOk) {
+         return;
+       }
+       for (j = 0; j <= nLeft && nCodes < nGlyphs; ++j) {
+         if (c < 256) {
+           if (encoding[c]) {
+             gfree(encoding[c]);
+           }
+           encoding[c] = copyString(getString(charset[nCodes], buf,
+                                              &parsedOk));
+         }
+         ++nCodes;
+         ++c;
+       }
+      }
+    }
+    if (encFormat & 0x80) {
+      nSups = getU8(pos++, &parsedOk);
+      if (!parsedOk) {
+       return;
+      }
+      for (i = 0; i < nSups; ++i) {
+       c = getU8(pos++, &parsedOk);;
+       if (!parsedOk) {
+         return;;
+       }
+       sid = getU16BE(pos, &parsedOk);
+       pos += 2;
+       if (!parsedOk) {
+         return;
+       }
+       if (encoding[c]) {
+         gfree(encoding[c]);
+       }
+       encoding[c] = copyString(getString(sid, buf, &parsedOk));
+      }
+    }
+  }
+}
+
+GBool FoFiType1C::readCharset() {
+  int charsetFormat, c, pos;
+  int nLeft, i, j;
+
+  if (topDict.charsetOffset == 0) {
+    charset = fofiType1CISOAdobeCharset;
+  } else if (topDict.charsetOffset == 1) {
+    charset = fofiType1CExpertCharset;
+  } else if (topDict.charsetOffset == 2) {
+    charset = fofiType1CExpertSubsetCharset;
+  } else {
+    charset = (Gushort *)gmalloc(nGlyphs * sizeof(Gushort));
+    for (i = 0; i < nGlyphs; ++i) {
+      charset[i] = 0;
+    }
+    pos = topDict.charsetOffset;
+    charsetFormat = getU8(pos++, &parsedOk);
+    if (charsetFormat == 0) {
+      for (i = 1; i < nGlyphs; ++i) {
+       charset[i] = (Gushort)getU16BE(pos, &parsedOk);
+       pos += 2;
+       if (!parsedOk) {
+         break;
+       }
+      }
+    } else if (charsetFormat == 1) {
+      i = 1;
+      while (i < nGlyphs) {
+       c = getU16BE(pos, &parsedOk);
+       pos += 2;
+       nLeft = getU8(pos++, &parsedOk);
+       if (!parsedOk) {
+         break;
+       }
+       for (j = 0; j <= nLeft && i < nGlyphs; ++j) {
+         charset[i++] = (Gushort)c++;
+       }
+      }
+    } else if (charsetFormat == 2) {
+      i = 1;
+      while (i < nGlyphs) {
+       c = getU16BE(pos, &parsedOk);
+       pos += 2;
+       nLeft = getU16BE(pos, &parsedOk);
+       pos += 2;
+       if (!parsedOk) {
+         break;
+       }
+       for (j = 0; j <= nLeft && i < nGlyphs; ++j) {
+         charset[i++] = (Gushort)c++;
+       }
+      }
+    }
+    if (!parsedOk) {
+      gfree(charset);
+      charset = NULL;
+      return gFalse;
+    }
+  }
+  return gTrue;
+}
+
+int FoFiType1C::getOp(int pos, GBool charstring, GBool *ok) {
+  static char nybChars[16] = "0123456789.ee -";
+  Type1COp op;
+  char buf[65];
+  int b0, b1, nyb0, nyb1, x, i;
+
+  b0 = getU8(pos++, ok);
+  op.isNum = gTrue;
+  op.isFP = gFalse;
+
+  if (b0 == 28) {
+    x = getU8(pos++, ok);
+    x = (x << 8) | getU8(pos++, ok);
+    if (x & 0x8000) {
+      x |= ~0xffff;
+    }
+    op.num = x;
+
+  } else if (!charstring && b0 == 29) {
+    x = getU8(pos++, ok);
+    x = (x << 8) | getU8(pos++, ok);
+    x = (x << 8) | getU8(pos++, ok);
+    x = (x << 8) | getU8(pos++, ok);
+    if (x & 0x80000000) {
+      x |= ~0xffffffff;
+    }
+    op.num = x;
+
+  } else if (!charstring && b0 == 30) {
+    i = 0;
+    do {
+      b1 = getU8(pos++, ok);
+      nyb0 = b1 >> 4;
+      nyb1 = b1 & 0x0f;
+      if (nyb0 == 0xf) {
+       break;
+      }
+      buf[i++] = nybChars[nyb0];
+      if (i == 64) {
+       break;
+      }
+      if (nyb0 == 0xc) {
+       buf[i++] = '-';
+      }
+      if (i == 64) {
+       break;
+      }
+      if (nyb1 == 0xf) {
+       break;
+      }
+      buf[i++] = nybChars[nyb1];
+      if (i == 64) {
+       break;
+      }
+      if (nyb1 == 0xc) {
+       buf[i++] = '-';
+      }
+    } while (i < 64);
+    buf[i] = '\0';
+    op.num = atof(buf);
+    op.isFP = gTrue;
+
+  } else if (b0 >= 32 && b0 <= 246) {
+    op.num = b0 - 139;
+
+  } else if (b0 >= 247 && b0 <= 250) {
+    op.num = ((b0 - 247) << 8) + getU8(pos++, ok) + 108;
+
+  } else if (b0 >= 251 && b0 <= 254) {
+    op.num = -((b0 - 251) << 8) - getU8(pos++, ok) - 108;
+
+  } else if (charstring && b0 == 255) {
+    x = getU8(pos++, ok);
+    x = (x << 8) | getU8(pos++, ok);
+    x = (x << 8) | getU8(pos++, ok);
+    x = (x << 8) | getU8(pos++, ok);
+    if (x & 0x80000000) {
+      x |= ~0xffffffff;
+    }
+    op.num = (double)x / 65536.0;
+    op.isFP = gTrue;
+
+  } else if (b0 == 12) {
+    op.isNum = gFalse;
+    op.op = 0x0c00 + getU8(pos++, ok);
+
+  } else {
+    op.isNum = gFalse;
+    op.op = b0;
+  }
+
+  if (nOps < 49) {
+    ops[nOps++] = op;
+  }
+
+  return pos;
+}
+
+// Convert the delta-encoded ops array to an array of ints.
+int FoFiType1C::getDeltaIntArray(int *arr, int maxLen) {
+  int x;
+  int n, i;
+
+  if ((n = nOps) > maxLen) {
+    n = maxLen;
+  }
+  x = 0;
+  for (i = 0; i < n; ++i) {
+    x += (int)ops[i].num;
+    arr[i] = x;
+  }
+  return n;
+}
+
+// Convert the delta-encoded ops array to an array of doubles.
+int FoFiType1C::getDeltaFPArray(double *arr, int maxLen) {
+  double x;
+  int n, i;
+
+  if ((n = nOps) > maxLen) {
+    n = maxLen;
+  }
+  x = 0;
+  for (i = 0; i < n; ++i) {
+    x += ops[i].num;
+    arr[i] = x;
+  }
+  return n;
+}
+
+void FoFiType1C::getIndex(int pos, Type1CIndex *idx, GBool *ok) {
+  idx->pos = pos;
+  idx->len = getU16BE(pos, ok);
+  if (idx->len == 0) {
+    // empty indexes are legal
+    idx->offSize = 0;
+    idx->startPos = idx->endPos = 0;
+  } else {
+    idx->offSize = getU8(pos + 2, ok);
+    if (idx->offSize < 1 || idx->offSize > 4) {
+      *ok = gFalse;
+    }
+    idx->startPos = pos + 3 + (idx->len + 1) * idx->offSize - 1;
+    if (idx->startPos < 0 || idx->startPos >= len) {
+      *ok = gFalse;
+    }
+    idx->endPos = idx->startPos + getUVarBE(pos + 3 + idx->len * idx->offSize,
+                                           idx->offSize, ok);
+    if (idx->endPos < idx->startPos || idx->endPos > len) {
+      *ok = gFalse;
+    }
+  }
+}
+
+void FoFiType1C::getIndexVal(Type1CIndex *idx, int i,
+                            Type1CIndexVal *val, GBool *ok) {
+  int pos0, pos1;
+
+  if (i < 0 || i >= idx->len) {
+    *ok = gFalse;
+    return;
+  }
+  pos0 = idx->startPos + getUVarBE(idx->pos + 3 + i * idx->offSize,
+                                  idx->offSize, ok);
+  pos1 = idx->startPos + getUVarBE(idx->pos + 3 + (i + 1) * idx->offSize,
+                                  idx->offSize, ok);
+  if (pos0 < idx->startPos || pos0 >= idx->endPos ||
+      pos1 <= idx->startPos || pos1 > idx->endPos ||
+      pos1 < pos0) {
+    *ok = gFalse;
+  }
+  val->pos = pos0;
+  val->len = pos1 - pos0;
+}
+
+char *FoFiType1C::getString(int sid, char *buf, GBool *ok) {
+  Type1CIndexVal val;
+  int n;
+
+  if (sid < 391) {
+    strcpy(buf, fofiType1CStdStrings[sid]);
+  } else {
+    sid -= 391;
+    getIndexVal(&stringIdx, sid, &val, ok);
+    if (ok) {
+      if ((n = val.len) > 255) {
+       n = 255;
+      }
+      strncpy(buf, (char *)&file[val.pos], n);
+      buf[n] = '\0';
+    } else {
+      buf[0] = '\0';
+    }
+  }
+  return buf;
+}
diff --git a/pdf/fofi/FoFiType1C.h b/pdf/fofi/FoFiType1C.h
new file mode 100644 (file)
index 0000000..e6f2b64
--- /dev/null
@@ -0,0 +1,226 @@
+//========================================================================
+//
+// FoFiType1C.h
+//
+// Copyright 1999-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef FOFITYPE1C_H
+#define FOFITYPE1C_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#include "gtypes.h"
+#include "FoFiBase.h"
+
+class GString;
+
+//------------------------------------------------------------------------
+
+struct Type1CIndex {
+  int pos;                     // absolute position in file
+  int len;                     // length (number of entries)
+  int offSize;                 // offset size
+  int startPos;                        // position of start of index data - 1
+  int endPos;                  // position one byte past end of the index
+};
+
+struct Type1CIndexVal {
+  int pos;                     // absolute position in file
+  int len;                     // length, in bytes
+};
+
+struct Type1CTopDict {
+  int firstOp;
+
+  int versionSID;
+  int noticeSID;
+  int copyrightSID;
+  int fullNameSID;
+  int familyNameSID;
+  int weightSID;
+  int isFixedPitch;
+  double italicAngle;
+  double underlinePosition;
+  double underlineThickness;
+  int paintType;
+  int charstringType;
+  double fontMatrix[6];
+  int uniqueID;
+  double fontBBox[4];
+  double strokeWidth;
+  int charsetOffset;
+  int encodingOffset;
+  int charStringsOffset;
+  int privateSize;
+  int privateOffset;
+
+  // CIDFont entries
+  int registrySID;
+  int orderingSID;
+  int supplement;
+  int fdArrayOffset;
+  int fdSelectOffset;
+};
+
+#define type1CMaxBlueValues 14
+#define type1CMaxOtherBlues 10
+#define type1CMaxStemSnap   12
+
+struct Type1CPrivateDict {
+  int blueValues[type1CMaxBlueValues];
+  int nBlueValues;
+  int otherBlues[type1CMaxOtherBlues];
+  int nOtherBlues;
+  int familyBlues[type1CMaxBlueValues];
+  int nFamilyBlues;
+  int familyOtherBlues[type1CMaxOtherBlues];
+  int nFamilyOtherBlues;
+  double blueScale;
+  int blueShift;
+  int blueFuzz;
+  double stdHW;
+  GBool hasStdHW;
+  double stdVW;
+  GBool hasStdVW;
+  double stemSnapH[type1CMaxStemSnap];
+  int nStemSnapH;
+  double stemSnapV[type1CMaxStemSnap];
+  int nStemSnapV;
+  GBool forceBold;
+  GBool hasForceBold;
+  double forceBoldThreshold;
+  int languageGroup;
+  double expansionFactor;
+  int initialRandomSeed;
+  int subrsOffset;
+  double defaultWidthX;
+  GBool defaultWidthXFP;
+  double nominalWidthX;
+  GBool nominalWidthXFP;
+};
+
+struct Type1COp {
+  GBool isNum;                 // true -> number, false -> operator
+  GBool isFP;                  // true -> floating point number, false -> int
+  union {
+    double num;                        // if num is true
+    int op;                    // if num is false
+  };
+};
+
+struct Type1CEexecBuf {
+  FoFiOutputFunc outputFunc;
+  void *outputStream;
+  GBool ascii;                 // ASCII encoding?
+  Gushort r1;                  // eexec encryption key
+  int line;                    // number of eexec chars left on current line
+};
+
+//------------------------------------------------------------------------
+// FoFiType1C
+//------------------------------------------------------------------------
+
+class FoFiType1C: public FoFiBase {
+public:
+
+  // Create a FoFiType1C object from a memory buffer.
+  static FoFiType1C *make(char *fileA, int lenA);
+
+  // Create a FoFiType1C object from a file on disk.
+  static FoFiType1C *load(char *fileName);
+
+  virtual ~FoFiType1C();
+
+  // Return the font name.
+  char *getName();
+
+  // Return the encoding, as an array of 256 names (any of which may
+  // be NULL).  This is only useful with 8-bit fonts.
+  char **getEncoding();
+
+  // Return the mapping from CIDs to GIDs, and return the number of
+  // CIDs in *<nCIDs>.  This is only useful for CID fonts.
+  Gushort *getCIDToGIDMap(int *nCIDs);
+
+  // Convert to a Type 1 font, suitable for embedding in a PostScript
+  // file.  This is only useful with 8-bit fonts.  If <newEncoding> is
+  // not NULL, it will be used in place of the encoding in the Type 1C
+  // font.  If <ascii> is true the eexec section will be hex-encoded,
+  // otherwise it will be left as binary data.
+  void convertToType1(char **newEncoding, GBool ascii,
+                     FoFiOutputFunc outputFunc, void *outputStream);
+
+  // Convert to a Type 0 CIDFont, suitable for embedding in a
+  // PostScript file.  <psName> will be used as the PostScript font
+  // name.
+  void convertToCIDType0(char *psName,
+                        FoFiOutputFunc outputFunc, void *outputStream);
+
+  // Convert to a Type 0 (but non-CID) composite font, suitable for
+  // embedding in a PostScript file.  <psName> will be used as the
+  // PostScript font name.
+  void convertToType0(char *psName,
+                     FoFiOutputFunc outputFunc, void *outputStream);
+
+private:
+
+  FoFiType1C(char *fileA, int lenA, GBool freeFileDataA);
+  void eexecCvtGlyph(Type1CEexecBuf *eb, char *glyphName,
+                    int offset, int nBytes,
+                    Type1CIndex *subrIdx,
+                    Type1CPrivateDict *pDict);
+  void cvtGlyph(int offset, int nBytes, GString *charBuf,
+               Type1CIndex *subrIdx, Type1CPrivateDict *pDict,
+               GBool top);
+  void cvtGlyphWidth(GBool useOp, GString *charBuf,
+                    Type1CPrivateDict *pDict);
+  void cvtNum(double x, GBool isFP, GString *charBuf);
+  void eexecWrite(Type1CEexecBuf *eb, char *s);
+  void eexecWriteCharstring(Type1CEexecBuf *eb, Guchar *s, int n);
+  GBool parse();
+  void readTopDict();
+  void readFD(int offset, int length, Type1CPrivateDict *pDict);
+  void readPrivateDict(int offset, int length, Type1CPrivateDict *pDict);
+  void readFDSelect();
+  void buildEncoding();
+  GBool readCharset();
+  int getOp(int pos, GBool charstring, GBool *ok);
+  int getDeltaIntArray(int *arr, int maxLen);
+  int getDeltaFPArray(double *arr, int maxLen);
+  void getIndex(int pos, Type1CIndex *idx, GBool *ok);
+  void getIndexVal(Type1CIndex *idx, int i, Type1CIndexVal *val, GBool *ok);
+  char *getString(int sid, char *buf, GBool *ok);
+
+  GString *name;
+  char **encoding;
+
+  Type1CIndex nameIdx;
+  Type1CIndex topDictIdx;
+  Type1CIndex stringIdx;
+  Type1CIndex gsubrIdx;
+  Type1CIndex charStringsIdx;
+
+  Type1CTopDict topDict;
+  Type1CPrivateDict *privateDicts;
+
+  int nGlyphs;
+  int nFDs;
+  Guchar *fdSelect;
+  Gushort *charset;
+  int gsubrBias;
+
+  GBool parsedOk;
+
+  Type1COp ops[49];            // operands and operator
+  int nOps;                    // number of operands
+  int nHints;                  // number of hints for the current glyph
+  GBool firstOp;               // true if we haven't hit the first op yet
+};
+
+#endif
diff --git a/pdf/fofi/Makefile.dep b/pdf/fofi/Makefile.dep
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/pdf/fofi/Makefile.in b/pdf/fofi/Makefile.in
new file mode 100644 (file)
index 0000000..d0ac7a7
--- /dev/null
@@ -0,0 +1,69 @@
+#========================================================================
+#
+# FoFi library Makefile
+#
+# Copyright 2003 Glyph & Cog, LLC
+#
+#========================================================================
+
+SHELL = /bin/sh
+
+srcdir = @srcdir@
+VPATH = @srcdir@
+
+GOOSRCDIR = $(srcdir)/../goo
+GOOLIBDIR = ../goo
+
+CXXFLAGS = @CXXFLAGS@ @DEFS@ -I.. -I$(GOOSRCDIR) -I$(srcdir)
+
+CXX = @CXX@
+AR = @AR@
+RANLIB = @RANLIB@
+
+LIBPREFIX = @LIBPREFIX@
+
+#------------------------------------------------------------------------
+
+.SUFFIXES: .cc
+
+.cc.o:
+       $(CXX) $(CXXFLAGS) -c $<
+
+#------------------------------------------------------------------------
+
+CXX_SRC = \
+       $(srcdir)/FoFiBase.cc \
+       $(srcdir)/FoFiEncodings.cc \
+       $(srcdir)/FoFiTrueType.cc \
+       $(srcdir)/FoFiType1.cc \
+       $(srcdir)/FoFiType1C.cc
+
+#------------------------------------------------------------------------
+
+all: $(LIBPREFIX)fofi.a
+
+#------------------------------------------------------------------------
+
+FOFI_OBJS = \
+       FoFiBase.o \
+       FoFiEncodings.o \
+       FoFiTrueType.o \
+       FoFiType1.o \
+       FoFiType1C.o
+
+$(LIBPREFIX)fofi.a: $(FOFI_OBJS)
+       rm -f $(LIBPREFIX)fofi.a
+       $(AR) $(LIBPREFIX)fofi.a $(FOFI_OBJS)
+       $(RANLIB) $(LIBPREFIX)fofi.a
+
+#------------------------------------------------------------------------
+
+clean:
+       rm -f $(FOFI_OBJS) $(LIBPREFIX)fofi.a
+
+#------------------------------------------------------------------------
+
+depend:
+       $(CXX) $(CXXFLAGS) -MM $(CXX_SRC) >Makefile.dep
+
+include Makefile.dep
diff --git a/pdf/fofi/vms_make.com b/pdf/fofi/vms_make.com
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/pdf/splash/Makefile.dep b/pdf/splash/Makefile.dep
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/pdf/splash/Makefile.in b/pdf/splash/Makefile.in
new file mode 100644 (file)
index 0000000..66c449b
--- /dev/null
@@ -0,0 +1,99 @@
+#========================================================================
+#
+# Splash library Makefile
+#
+# Copyright 2003 Glyph & Cog, LLC
+#
+#========================================================================
+
+SHELL = /bin/sh
+
+srcdir = @srcdir@
+VPATH = @srcdir@
+
+GOOSRCDIR = $(srcdir)/../goo
+GOOLIBDIR = ../goo
+FOFISRCDIR = $(srcdir)/../fofi
+FOFILIBDIR = ../fofi
+
+CXXFLAGS = @CXXFLAGS@ @DEFS@ -I.. -I$(GOOSRCDIR) -I$(FOFISRCDIR) -I$(srcdir) @t1_CFLAGS@ @freetype2_CFLAGS@
+
+CXX = @CXX@
+AR = @AR@
+RANLIB = @RANLIB@
+
+LIBPREFIX = @LIBPREFIX@
+
+#------------------------------------------------------------------------
+
+.SUFFIXES: .cc
+
+.cc.o:
+       $(CXX) $(CXXFLAGS) -c $<
+
+#------------------------------------------------------------------------
+
+CXX_SRC = \
+       $(srcdir)/Splash.cc \
+       $(srcdir)/SplashBitmap.cc \
+       $(srcdir)/SplashClip.cc \
+       $(srcdir)/SplashFTFont.cc \
+       $(srcdir)/SplashFTFontEngine.cc \
+       $(srcdir)/SplashFTFontFile.cc \
+       $(srcdir)/SplashFont.cc \
+       $(srcdir)/SplashFontEngine.cc \
+       $(srcdir)/SplashFontFile.cc \
+       $(srcdir)/SplashFontFileID.cc \
+       $(srcdir)/SplashPath.cc \
+       $(srcdir)/SplashPattern.cc \
+       $(srcdir)/SplashScreen.cc \
+       $(srcdir)/SplashState.cc \
+       $(srcdir)/SplashT1Font.cc \
+       $(srcdir)/SplashT1FontEngine.cc \
+       $(srcdir)/SplashT1FontFile.cc \
+       $(srcdir)/SplashXPath.cc \
+       $(srcdir)/SplashXPathScanner.cc
+
+#------------------------------------------------------------------------
+
+all: $(LIBPREFIX)splash.a
+
+#------------------------------------------------------------------------
+
+SPLASH_OBJS = \
+       Splash.o \
+       SplashBitmap.o \
+       SplashClip.o \
+       SplashFTFont.o \
+       SplashFTFontEngine.o \
+       SplashFTFontFile.o \
+       SplashFont.o \
+       SplashFontEngine.o \
+       SplashFontFile.o \
+       SplashFontFileID.o \
+       SplashPath.o \
+       SplashPattern.o \
+       SplashScreen.o \
+       SplashState.o \
+       SplashT1Font.o \
+       SplashT1FontEngine.o \
+       SplashT1FontFile.o \
+       SplashXPath.o \
+       SplashXPathScanner.o
+
+$(LIBPREFIX)splash.a: $(SPLASH_OBJS)
+       rm -f $(LIBPREFIX)splash.a
+       $(AR) $(LIBPREFIX)splash.a $(SPLASH_OBJS)
+       $(RANLIB) $(LIBPREFIX)splash.a
+
+#------------------------------------------------------------------------
+
+clean:
+       rm -f $(SPLASH_OBJS) $(LIBPREFIX)splash.a
+
+#------------------------------------------------------------------------
+
+depend:
+       $(CXX) $(CXXFLAGS) -MM $(CXX_SRC) >Makefile.dep
+
+include Makefile.dep
diff --git a/pdf/splash/Splash.cc b/pdf/splash/Splash.cc
new file mode 100644 (file)
index 0000000..f86156d
--- /dev/null
@@ -0,0 +1,1648 @@
+//========================================================================
+//
+// Splash.cc
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+#include "gmem.h"
+#include "SplashErrorCodes.h"
+#include "SplashMath.h"
+#include "SplashBitmap.h"
+#include "SplashState.h"
+#include "SplashPath.h"
+#include "SplashXPath.h"
+#include "SplashXPathScanner.h"
+#include "SplashPattern.h"
+#include "SplashScreen.h"
+#include "SplashClip.h"
+#include "SplashFont.h"
+#include "SplashGlyphBitmap.h"
+#include "Splash.h"
+
+//------------------------------------------------------------------------
+// Splash
+//------------------------------------------------------------------------
+
+Splash::Splash(SplashBitmap *bitmapA) {
+  bitmap = bitmapA;
+  state = new SplashState(bitmap->width, bitmap->height);
+  debugMode = gFalse;
+}
+
+Splash::~Splash() {
+  while (state->next) {
+    restoreState();
+  }
+  delete state;
+}
+
+//------------------------------------------------------------------------
+// state read
+//------------------------------------------------------------------------
+
+
+SplashPattern *Splash::getStrokePattern() {
+  return state->strokePattern;
+}
+
+SplashPattern *Splash::getFillPattern() {
+  return state->fillPattern;
+}
+
+SplashScreen *Splash::getScreen() {
+  return state->screen;
+}
+
+SplashCoord Splash::getLineWidth() {
+  return state->lineWidth;
+}
+
+int Splash::getLineCap() {
+  return state->lineCap;
+}
+
+int Splash::getLineJoin() {
+  return state->lineJoin;
+}
+
+SplashCoord Splash::getMiterLimit() {
+  return state->miterLimit;
+}
+
+SplashCoord Splash::getFlatness() {
+  return state->flatness;
+}
+
+SplashCoord *Splash::getLineDash() {
+  return state->lineDash;
+}
+
+int Splash::getLineDashLength() {
+  return state->lineDashLength;
+}
+
+SplashCoord Splash::getLineDashPhase() {
+  return state->lineDashPhase;
+}
+
+SplashClip *Splash::getClip() {
+  return state->clip;
+}
+
+//------------------------------------------------------------------------
+// state write
+//------------------------------------------------------------------------
+
+void Splash::setStrokePattern(SplashPattern *strokePattern) {
+  state->setStrokePattern(strokePattern);
+}
+
+void Splash::setFillPattern(SplashPattern *fillPattern) {
+  state->setFillPattern(fillPattern);
+}
+
+void Splash::setScreen(SplashScreen *screen) {
+  state->setScreen(screen);
+}
+
+void Splash::setLineWidth(SplashCoord lineWidth) {
+  state->lineWidth = lineWidth;
+}
+
+void Splash::setLineCap(int lineCap) {
+  state->lineCap = lineCap;
+}
+
+void Splash::setLineJoin(int lineJoin) {
+  state->lineJoin = lineJoin;
+}
+
+void Splash::setMiterLimit(SplashCoord miterLimit) {
+  state->miterLimit = miterLimit;
+}
+
+void Splash::setFlatness(SplashCoord flatness) {
+  if (flatness < 1) {
+    state->flatness = 1;
+  } else {
+    state->flatness = flatness;
+  }
+}
+
+void Splash::setLineDash(SplashCoord *lineDash, int lineDashLength,
+                        SplashCoord lineDashPhase) {
+  state->setLineDash(lineDash, lineDashLength, lineDashPhase);
+}
+
+void Splash::clipResetToRect(SplashCoord x0, SplashCoord y0,
+                            SplashCoord x1, SplashCoord y1) {
+  state->clip->resetToRect(x0, y0, x1, y1);
+}
+
+SplashError Splash::clipToRect(SplashCoord x0, SplashCoord y0,
+                              SplashCoord x1, SplashCoord y1) {
+  return state->clip->clipToRect(x0, y0, x1, y1);
+}
+
+SplashError Splash::clipToPath(SplashPath *path, GBool eo) {
+  return state->clip->clipToPath(path, state->flatness, eo);
+}
+
+//------------------------------------------------------------------------
+// state save/restore
+//------------------------------------------------------------------------
+
+void Splash::saveState() {
+  SplashState *newState;
+
+  newState = state->copy();
+  newState->next = state;
+  state = newState;
+}
+
+SplashError Splash::restoreState() {
+  SplashState *oldState;
+
+  if (!state->next) {
+    return splashErrNoSave;
+  }
+  oldState = state;
+  state = state->next;
+  delete oldState;
+  return splashOk;
+}
+
+//------------------------------------------------------------------------
+// drawing operations
+//------------------------------------------------------------------------
+
+void Splash::clear(SplashColor color) {
+  SplashMono1P *mono1;
+  SplashMono8 *mono8;
+  SplashRGB8 *rgb8;
+  SplashBGR8P *bgr8line, *bgr8;
+  SplashMono1 data;
+  int n, i, x, y;
+
+  switch (bitmap->mode) {
+  case splashModeMono1:
+    n = ((bitmap->width + 7) >> 3) * bitmap->height;
+    data = color.mono1 ? 0xff : 0x00;
+    for (i = 0, mono1 = bitmap->data.mono1; i < n; ++i, ++mono1) {
+      *mono1 = data;
+    }
+    break;
+  case splashModeMono8:
+    n = bitmap->width * bitmap->height;
+    for (i = 0, mono8 = bitmap->data.mono8; i < n; ++i, ++mono8) {
+      *mono8 = color.mono8;
+    }
+    break;
+  case splashModeRGB8:
+    n = bitmap->width * bitmap->height;
+    for (i = 0, rgb8 = bitmap->data.rgb8; i < n; ++i, ++rgb8) {
+      *rgb8 = color.rgb8;
+    }
+    break;
+  case splashModeBGR8Packed:
+    bgr8line = bitmap->data.bgr8;
+    for (y = 0; y < bitmap->height; ++y) {
+      bgr8 = bgr8line;
+      for (x = 0; x < bitmap->width; ++x) {
+       bgr8[2] = splashBGR8R(color.bgr8);
+       bgr8[1] = splashBGR8G(color.bgr8);
+       bgr8[0] = splashBGR8B(color.bgr8);
+       bgr8 += 3;
+      }
+      bgr8line += bitmap->rowSize;
+    }
+    break;
+  }
+}
+
+SplashError Splash::stroke(SplashPath *path) {
+  SplashXPath *xPath, *xPath2;
+
+  if (debugMode) {
+    printf("stroke [dash:%d] [width:%.2f]:\n",
+          state->lineDashLength, state->lineWidth);
+    dumpPath(path);
+  }
+  if (path->length == 0) {
+    return splashErrEmptyPath;
+  }
+  xPath = new SplashXPath(path, state->flatness, gFalse);
+  if (state->lineDashLength > 0) {
+    xPath2 = makeDashedPath(xPath);
+    delete xPath;
+    xPath = xPath2;
+  }
+  if (state->lineWidth <= 1) {
+    strokeNarrow(xPath);
+  } else {
+    strokeWide(xPath);
+  }
+  delete xPath;
+  return splashOk;
+}
+
+void Splash::strokeNarrow(SplashXPath *xPath) {
+  SplashXPathSeg *seg;
+  int x0, x1, x2, x3, y0, y1, x, y, t;
+  SplashCoord dx, dy, dxdy;
+  SplashClipResult clipRes;
+  int i;
+
+  for (i = 0, seg = xPath->segs; i < xPath->length; ++i, ++seg) {
+
+    x0 = splashFloor(seg->x0);
+    x1 = splashFloor(seg->x1);
+    y0 = splashFloor(seg->y0);
+    y1 = splashFloor(seg->y1);
+
+    // horizontal segment
+    if (y0 == y1) {
+      if (x0 > x1) {
+       t = x0; x0 = x1; x1 = t;
+      }
+      if ((clipRes = state->clip->testSpan(x0, x1, y0))
+         != splashClipAllOutside) {
+       drawSpan(x0, x1, y0, state->strokePattern,
+                clipRes == splashClipAllInside);
+      }
+
+    // segment with |dx| > |dy|
+    } else if (splashAbs(seg->dxdy) > 1) {
+      dx = seg->x1 - seg->x0;
+      dy = seg->y1 - seg->y0;
+      dxdy = seg->dxdy;
+      if (y0 > y1) {
+       t = y0; y0 = y1; y1 = t;
+       t = x0; x0 = x1; x1 = t;
+       dx = -dx;
+       dy = -dy;
+      }
+      if ((clipRes = state->clip->testRect(x0 <= x1 ? x0 : x1, y0,
+                                          x0 <= x1 ? x1 : x0, y1))
+         != splashClipAllOutside) {
+       if (dx > 0) {
+         x2 = x0;
+         for (y = y0; y < y1; ++y) {
+           x3 = splashFloor(seg->x0 + (y + 1 - seg->y0) * dxdy);
+           drawSpan(x2, x3 - 1, y, state->strokePattern,
+                    clipRes == splashClipAllInside);
+           x2 = x3;
+         }
+         drawSpan(x2, x1, y, state->strokePattern,
+                  clipRes == splashClipAllInside);
+       } else {
+         x2 = x0;
+         for (y = y0; y < y1; ++y) {
+           x3 = splashFloor(seg->x0 + (y + 1 - seg->y0) * dxdy);
+           drawSpan(x3 + 1, x2, y, state->strokePattern,
+                    clipRes == splashClipAllInside);
+           x2 = x3;
+         }
+         drawSpan(x1, x2, y, state->strokePattern,
+                  clipRes == splashClipAllInside);
+       }
+      }
+
+    // segment with |dy| > |dx|
+    } else {
+      dxdy = seg->dxdy;
+      if (y0 > y1) {
+       t = y0; y0 = y1; y1 = t;
+      }
+      if ((clipRes = state->clip->testRect(x0 <= x1 ? x0 : x1, y0,
+                                          x0 <= x1 ? x1 : x0, y1))
+         != splashClipAllOutside) {
+       for (y = y0; y <= y1; ++y) {
+         x = splashFloor(seg->x0 + (y - seg->y0) * dxdy);
+         drawPixel(x, y, state->strokePattern,
+                   clipRes == splashClipAllInside);
+       }
+      }
+    }
+  }
+}
+
+void Splash::strokeWide(SplashXPath *xPath) {
+  SplashXPathSeg *seg, *seg2;
+  SplashPath *widePath;
+  SplashCoord d, dx, dy, wdx, wdy, dxPrev, dyPrev, wdxPrev, wdyPrev;
+  SplashCoord dotprod, miter;
+  int i, j;
+
+  dx = dy = wdx = wdy = 0; // make gcc happy
+  dxPrev = dyPrev = wdxPrev = wdyPrev = 0; // make gcc happy
+
+  for (i = 0, seg = xPath->segs; i < xPath->length; ++i, ++seg) {
+
+    // save the deltas for the previous segment; if this is the first
+    // segment on a subpath, compute the deltas for the last segment
+    // on the subpath (which may be used to draw a line join)
+    if (seg->flags & splashXPathFirst) {
+      for (j = i + 1, seg2 = &xPath->segs[j]; j < xPath->length; ++j, ++seg2) {
+       if (seg2->flags & splashXPathLast) {
+         d = splashDist(seg2->x0, seg2->y0, seg2->x1, seg2->y1);
+         if (d == 0) {
+           //~ not clear what the behavior should be for joins with d==0
+           dxPrev = 0;
+           dyPrev = 1;
+         } else {
+           d = 1 / d;
+           dxPrev = d * (seg2->x1 - seg2->x0);
+           dyPrev = d * (seg2->y1 - seg2->y0);
+         }
+         wdxPrev = 0.5 * state->lineWidth * dxPrev;
+         wdyPrev = 0.5 * state->lineWidth * dyPrev;
+         break;
+       }
+      }
+    } else {
+      dxPrev = dx;
+      dyPrev = dy;
+      wdxPrev = wdx;
+      wdyPrev = wdy;
+    }
+
+    // compute deltas for this line segment
+    d = splashDist(seg->x0, seg->y0, seg->x1, seg->y1);
+    if (d == 0) {
+      // we need to draw end caps on zero-length lines
+      //~ not clear what the behavior should be for splashLineCapButt with d==0
+      dx = 0;
+      dy = 1;
+    } else {
+      d = 1 / d;
+      dx = d * (seg->x1 - seg->x0);
+      dy = d * (seg->y1 - seg->y0);
+    }
+    wdx = 0.5 * state->lineWidth * dx;
+    wdy = 0.5 * state->lineWidth * dy;
+
+    // initialize the path (which will be filled)
+    widePath = new SplashPath();
+    widePath->moveTo(seg->x0 - wdy, seg->y0 + wdx);
+
+    // draw the start cap
+    if (seg->flags & splashXPathEnd0) {
+      switch (state->lineCap) {
+      case splashLineCapButt:
+       widePath->lineTo(seg->x0 + wdy, seg->y0 - wdx);
+       break;
+      case splashLineCapRound:
+       widePath->arcCWTo(seg->x0 + wdy, seg->y0 - wdx, seg->x0, seg->y0);
+       break;
+      case splashLineCapProjecting:
+       widePath->lineTo(seg->x0 - wdx - wdy, seg->y0 + wdx - wdy);
+       widePath->lineTo(seg->x0 - wdx + wdy, seg->y0 - wdx - wdy);
+       widePath->lineTo(seg->x0 + wdy, seg->y0 - wdx);
+       break;
+      }
+    } else {
+      widePath->lineTo(seg->x0 + wdy, seg->y0 - wdx);
+    }
+
+    // draw the left side of the segment
+    widePath->lineTo(seg->x1 + wdy, seg->y1 - wdx);
+
+    // draw the end cap
+    if (seg->flags & splashXPathEnd1) {
+      switch (state->lineCap) {
+      case splashLineCapButt:
+       widePath->lineTo(seg->x1 - wdy, seg->y1 + wdx);
+       break;
+      case splashLineCapRound:
+       widePath->arcCWTo(seg->x1 - wdy, seg->y1 + wdx, seg->x1, seg->y1);
+       break;
+      case splashLineCapProjecting:
+       widePath->lineTo(seg->x1 + wdx + wdy, seg->y1 - wdx + wdy);
+       widePath->lineTo(seg->x1 + wdx - wdy, seg->y1 + wdx + wdy);
+       widePath->lineTo(seg->x1 - wdy, seg->y1 + wdx);
+       break;
+      }
+    } else {
+      widePath->lineTo(seg->x1 - wdy, seg->y1 + wdx);
+    }
+
+    // draw the right side of the segment
+    widePath->lineTo(seg->x0 - wdy, seg->y0 + wdx);
+
+    // fill the segment
+    fillWithPattern(widePath, gTrue, state->strokePattern);
+    delete widePath;
+
+    // draw the line join
+    if (!(seg->flags & splashXPathEnd0)) {
+      widePath = NULL;
+      switch (state->lineJoin) {
+      case splashLineJoinMiter:
+       dotprod = -(dx * dxPrev + dy * dyPrev);
+       if (dotprod != 1) {
+         widePath = new SplashPath();
+         widePath->moveTo(seg->x0, seg->y0);
+         miter = 2 / (1 - dotprod);
+         if (splashSqrt(miter) <= state->miterLimit) {
+           miter = splashSqrt(miter - 1);
+           if (dy * dxPrev > dx * dyPrev) {
+             widePath->lineTo(seg->x0 + wdyPrev, seg->y0 - wdxPrev);
+             widePath->lineTo(seg->x0 + wdy - miter * wdx,
+                              seg->y0 - wdx - miter * wdy);
+             widePath->lineTo(seg->x0 + wdy, seg->y0 - wdx);
+           } else {
+             widePath->lineTo(seg->x0 - wdyPrev, seg->y0 + wdxPrev);
+             widePath->lineTo(seg->x0 - wdy - miter * wdx,
+                              seg->y0 + wdx - miter * wdy);
+             widePath->lineTo(seg->x0 - wdy, seg->y0 + wdx);
+           }
+         } else {
+           if (dy * dxPrev > dx * dyPrev) {
+             widePath->lineTo(seg->x0 + wdyPrev, seg->y0 - wdxPrev);
+             widePath->lineTo(seg->x0 + wdy, seg->y0 - wdx);
+           } else {
+             widePath->lineTo(seg->x0 - wdyPrev, seg->y0 + wdxPrev);
+             widePath->lineTo(seg->x0 - wdy, seg->y0 + wdx);
+           }
+         }
+       }
+       break;
+      case splashLineJoinRound:
+       widePath = new SplashPath();
+       widePath->moveTo(seg->x0 + wdy, seg->y0 - wdx);
+       widePath->arcCWTo(seg->x0 + wdy, seg->y0 - wdx, seg->x0, seg->y0);
+       break;
+      case splashLineJoinBevel:
+       widePath = new SplashPath();
+       widePath->moveTo(seg->x0, seg->y0);
+       if (dy * dxPrev > dx * dyPrev) {
+         widePath->lineTo(seg->x0 + wdyPrev, seg->y0 - wdxPrev);
+         widePath->lineTo(seg->x0 + wdy, seg->y0 - wdx);
+       } else {
+         widePath->lineTo(seg->x0 - wdyPrev, seg->y0 + wdxPrev);
+         widePath->lineTo(seg->x0 - wdy, seg->y0 + wdx);
+       }
+       break;
+      }
+      if (widePath) {
+       fillWithPattern(widePath, gTrue, state->strokePattern);
+       delete widePath;
+      }
+    }
+  }
+}
+
+SplashXPath *Splash::makeDashedPath(SplashXPath *xPath) {
+  SplashXPath *dPath;
+  GBool lineDashStartOn, lineDashOn;
+  GBool atSegStart, atSegEnd, atDashStart, atDashEnd;
+  int lineDashStartIdx, lineDashIdx, subpathStart;
+  SplashCoord lineDashTotal, lineDashStartPhase, lineDashDist;
+  int segIdx;
+  SplashXPathSeg *seg;
+  SplashCoord sx0, sy0, sx1, sy1, ax0, ay0, ax1, ay1, dist;
+  int i;
+
+  dPath = new SplashXPath();
+
+  lineDashTotal = 0;
+  for (i = 0; i < state->lineDashLength; ++i) {
+    lineDashTotal += state->lineDash[i];
+  }
+  lineDashStartPhase = state->lineDashPhase;
+  i = splashFloor(lineDashStartPhase / lineDashTotal);
+  lineDashStartPhase -= i * lineDashTotal;
+  lineDashStartOn = gTrue;
+  lineDashStartIdx = 0;
+  while (lineDashStartPhase >= state->lineDash[lineDashStartIdx]) {
+    lineDashStartOn = !lineDashStartOn;
+    lineDashStartPhase -= state->lineDash[lineDashStartIdx];
+    ++lineDashStartIdx;
+  }
+
+  segIdx = 0;
+  seg = xPath->segs;
+  sx0 = seg->x0;
+  sy0 = seg->y0;
+  sx1 = seg->x1;
+  sy1 = seg->y1;
+  dist = splashDist(sx0, sy0, sx1, sy1);
+  lineDashOn = lineDashStartOn;
+  lineDashIdx = lineDashStartIdx;
+  lineDashDist = state->lineDash[lineDashIdx] - lineDashStartPhase;
+  atSegStart = gTrue;
+  atDashStart = gTrue;
+  subpathStart = dPath->length;
+
+  while (segIdx < xPath->length) {
+
+    ax0 = sx0;
+    ay0 = sy0;
+    if (dist <= lineDashDist) {
+      ax1 = sx1;
+      ay1 = sy1;
+      lineDashDist -= dist;
+      dist = 0;
+      atSegEnd = gTrue;
+      atDashEnd = lineDashDist == 0 || (seg->flags & splashXPathLast);
+    } else {
+      ax1 = sx0 + (lineDashDist / dist) * (sx1 - sx0);
+      ay1 = sy0 + (lineDashDist / dist) * (sy1 - sy0);
+      sx0 = ax1;
+      sy0 = ay1;
+      dist -= lineDashDist;
+      lineDashDist = 0;
+      atSegEnd = gFalse;
+      atDashEnd = gTrue;
+    }
+
+    if (lineDashOn) {
+      dPath->addSegment(ax0, ay0, ax1, ay1,
+                       atDashStart, atDashEnd,
+                       atDashStart, atDashEnd);
+      // end of closed subpath
+      if (atSegEnd &&
+         (seg->flags & splashXPathLast) &&
+         !(seg->flags & splashXPathEnd1)) {
+       dPath->segs[subpathStart].flags &= ~splashXPathEnd0;
+       dPath->segs[dPath->length - 1].flags &= ~splashXPathEnd1;
+      }
+    }
+
+    if (atDashEnd) {
+      lineDashOn = !lineDashOn;
+      if (++lineDashIdx == state->lineDashLength) {
+       lineDashIdx = 0;
+      }
+      lineDashDist = state->lineDash[lineDashIdx];
+      atDashStart = gTrue;
+    } else {
+      atDashStart = gFalse;
+    }
+    if (atSegEnd) {
+      if (++segIdx < xPath->length) {
+       ++seg;
+       sx0 = seg->x0;
+       sy0 = seg->y0;
+       sx1 = seg->x1;
+       sy1 = seg->y1;
+       dist = splashDist(sx0, sy0, sx1, sy1);
+       if (seg->flags & splashXPathFirst) {
+         lineDashOn = lineDashStartOn;
+         lineDashIdx = lineDashStartIdx;
+         lineDashDist = state->lineDash[lineDashIdx] - lineDashStartPhase;
+         atDashStart = gTrue;
+         subpathStart = dPath->length;
+       }
+      }
+      atSegStart = gTrue;
+    } else {
+      atSegStart = gFalse;
+    }
+  }
+
+  return dPath;
+}
+
+SplashError Splash::fill(SplashPath *path, GBool eo) {
+  if (debugMode) {
+    printf("fill [eo:%d]:\n", eo);
+    dumpPath(path);
+  }
+  return fillWithPattern(path, eo, state->fillPattern);
+}
+
+SplashError Splash::fillWithPattern(SplashPath *path, GBool eo,
+                                   SplashPattern *pattern) {
+  SplashXPath *xPath;
+  SplashXPathScanner *scanner;
+  int xMinI, yMinI, xMaxI, yMaxI, x0, x1, y;
+  SplashClipResult clipRes, clipRes2;
+
+  if (path->length == 0) {
+    return splashErrEmptyPath;
+  }
+  xPath = new SplashXPath(path, state->flatness, gTrue);
+  xPath->sort();
+  scanner = new SplashXPathScanner(xPath, eo);
+
+  // get the min and max x and y values
+  scanner->getBBox(&xMinI, &yMinI, &xMaxI, &yMaxI);
+
+  // check clipping
+  if ((clipRes = state->clip->testRect(xMinI, yMinI, xMaxI, yMaxI))
+      != splashClipAllOutside) {
+
+    // draw the spans
+    for (y = yMinI; y <= yMaxI; ++y) {
+      while (scanner->getNextSpan(y, &x0, &x1)) {
+       if (clipRes == splashClipAllInside) {
+         drawSpan(x0, x1, y, pattern, gTrue);
+       } else {
+         clipRes2 = state->clip->testSpan(x0, x1, y);
+         drawSpan(x0, x1, y, pattern, clipRes2 == splashClipAllInside);
+       }
+      }
+    }
+  }
+
+  delete scanner;
+  delete xPath;
+  return splashOk;
+}
+
+SplashError Splash::xorFill(SplashPath *path, GBool eo) {
+  SplashXPath *xPath;
+  SplashXPathScanner *scanner;
+  int xMinI, yMinI, xMaxI, yMaxI, x0, x1, y;
+  SplashClipResult clipRes, clipRes2;
+
+  if (path->length == 0) {
+    return splashErrEmptyPath;
+  }
+  xPath = new SplashXPath(path, state->flatness, gTrue);
+  xPath->sort();
+  scanner = new SplashXPathScanner(xPath, eo);
+
+  // get the min and max x and y values
+  scanner->getBBox(&xMinI, &yMinI, &xMaxI, &yMaxI);
+
+  // check clipping
+  if ((clipRes = state->clip->testRect(xMinI, yMinI, xMaxI, yMaxI))
+      != splashClipAllOutside) {
+
+    // draw the spans
+    for (y = yMinI; y <= yMaxI; ++y) {
+      while (scanner->getNextSpan(y, &x0, &x1)) {
+       if (clipRes == splashClipAllInside) {
+         xorSpan(x0, x1, y, state->fillPattern, gTrue);
+       } else {
+         clipRes2 = state->clip->testSpan(x0, x1, y);
+         xorSpan(x0, x1, y, state->fillPattern,
+                 clipRes2 == splashClipAllInside);
+       }
+      }
+    }
+  }
+
+  delete scanner;
+  delete xPath;
+  return splashOk;
+}
+
+void Splash::drawPixel(int x, int y, SplashColor *color, GBool noClip) {
+  SplashMono1P *mono1;
+  SplashBGR8P *bgr8;
+
+  if (noClip || state->clip->test(x, y)) {
+    switch (bitmap->mode) {
+    case splashModeMono1:
+      mono1 = &bitmap->data.mono8[y * bitmap->rowSize + (x >> 3)];
+      if (color->mono1) {
+       *mono1 |= 0x80 >> (x & 7);
+      } else {
+       *mono1 &= ~(0x80 >> (x & 7));
+      }
+      break;
+    case splashModeMono8:
+      bitmap->data.mono8[y * bitmap->width + x] = color->mono8;
+      break;
+    case splashModeRGB8:
+      bitmap->data.rgb8[y * bitmap->width + x] = color->rgb8;
+      break;
+    case splashModeBGR8Packed:
+      bgr8 = &bitmap->data.bgr8[y * bitmap->rowSize + 3 * x];
+      bgr8[2] = splashBGR8R(color->bgr8);
+      bgr8[1] = splashBGR8G(color->bgr8);
+      bgr8[0] = splashBGR8B(color->bgr8);
+      break;
+    }
+  }
+}
+
+void Splash::drawPixel(int x, int y, SplashPattern *pattern, GBool noClip) {
+  SplashColor color;
+  SplashMono1P *mono1;
+  SplashBGR8P *bgr8;
+
+  if (noClip || state->clip->test(x, y)) {
+    color = pattern->getColor(x, y);
+    switch (bitmap->mode) {
+    case splashModeMono1:
+      mono1 = &bitmap->data.mono8[y * bitmap->rowSize + (x >> 3)];
+      if (color.mono1) {
+       *mono1 |= 0x80 >> (x & 7);
+      } else {
+       *mono1 &= ~(0x80 >> (x & 7));
+      }
+      break;
+    case splashModeMono8:
+      bitmap->data.mono8[y * bitmap->width + x] = color.mono8;
+      break;
+    case splashModeRGB8:
+      bitmap->data.rgb8[y * bitmap->width + x] = color.rgb8;
+      break;
+    case splashModeBGR8Packed:
+      bgr8 = &bitmap->data.bgr8[y * bitmap->rowSize + 3 * x];
+      bgr8[2] = splashBGR8R(color.bgr8);
+      bgr8[1] = splashBGR8G(color.bgr8);
+      bgr8[0] = splashBGR8B(color.bgr8);
+      break;
+    }
+  }
+}
+
+void Splash::drawSpan(int x0, int x1, int y, SplashPattern *pattern,
+                     GBool noClip) {
+  SplashColor color;
+  SplashMono1P *mono1;
+  SplashMono8 *mono8;
+  SplashRGB8 *rgb8;
+  SplashBGR8P *bgr8;
+  SplashMono1 mask1;
+  int i, j, n;
+
+  n = x1 - x0 + 1;
+
+  switch (bitmap->mode) {
+  case splashModeMono1:
+    mono1 = &bitmap->data.mono8[y * bitmap->rowSize + (x0 >> 3)];
+    i = 0;
+    if ((j = x0 & 7)) {
+      mask1 = 0x80 >> j;
+      for (j = x0 & 7; j < 8 && i < n; ++i, ++j) {
+       if (noClip || state->clip->test(x0 + i, y)) {
+         color = pattern->getColor(x0 + i, y);
+         if (color.mono1) {
+           *mono1 |= mask1;
+         } else {
+           *mono1 &= ~mask1;
+         }
+       }
+       mask1 >>= 1;
+      }
+      ++mono1;
+    }
+    while (i < n) {
+      mask1 = 0x80;
+      for (j = 0; j < 8 && i < n; ++i, ++j) {
+       if (noClip || state->clip->test(x0 + i, y)) {
+         color = pattern->getColor(x0 + i, y);
+         if (color.mono1) {
+           *mono1 |= mask1;
+         } else {
+           *mono1 &= ~mask1;
+         }
+       }
+       mask1 >>= 1;
+      }
+      ++mono1;
+    }
+    break;
+
+  case splashModeMono8:
+    mono8 = &bitmap->data.mono8[y * bitmap->width + x0];
+    for (i = 0; i < n; ++i) {
+      if (noClip || state->clip->test(x0 + i, y)) {
+       color = pattern->getColor(x0 + i, y);
+       *mono8 = color.mono8;
+      }
+      ++mono8;
+    }
+    break;
+
+  case splashModeRGB8:
+    rgb8 = &bitmap->data.rgb8[y * bitmap->width + x0];
+    for (i = 0; i < n; ++i) {
+      if (noClip || state->clip->test(x0 + i, y)) {
+       color = pattern->getColor(x0 + i, y);
+       *rgb8 = color.rgb8;
+      }
+      ++rgb8;
+    }
+    break;
+
+  case splashModeBGR8Packed:
+    bgr8 = &bitmap->data.bgr8[y * bitmap->rowSize + 3 * x0];
+    for (i = 0; i < n; ++i) {
+      if (noClip || state->clip->test(x0 + i, y)) {
+       color = pattern->getColor(x0 + i, y);
+       bgr8[2] = splashBGR8R(color.bgr8);
+       bgr8[1] = splashBGR8G(color.bgr8);
+       bgr8[0] = splashBGR8B(color.bgr8);
+      }
+      bgr8 += 3;
+    }
+    break;
+  }
+}
+
+void Splash::xorSpan(int x0, int x1, int y, SplashPattern *pattern,
+                    GBool noClip) {
+  SplashColor color;
+  SplashMono1P *mono1;
+  SplashMono8 *mono8;
+  SplashRGB8 *rgb8;
+  SplashBGR8P *bgr8;
+  SplashMono1 mask1;
+  int i, j, n;
+
+  n = x1 - x0 + 1;
+
+  switch (bitmap->mode) {
+  case splashModeMono1:
+    mono1 = &bitmap->data.mono8[y * bitmap->rowSize + (x0 >> 3)];
+    i = 0;
+    if ((j = x0 & 7)) {
+      mask1 = 0x80 >> j;
+      for (j = x0 & 7; j < 8 && i < n; ++i, ++j) {
+       if (noClip || state->clip->test(x0 + i, y)) {
+         color = pattern->getColor(x0 + i, y);
+         if (color.mono1) {
+           *mono1 ^= mask1;
+         }
+       }
+       mask1 >>= 1;
+      }
+      ++mono1;
+    }
+    while (i < n) {
+      mask1 = 0x80;
+      for (j = 0; j < 8 && i < n; ++i, ++j) {
+       if (noClip || state->clip->test(x0 + i, y)) {
+         color = pattern->getColor(x0 + i, y);
+         if (color.mono1) {
+           *mono1 ^= mask1;
+         }
+       }
+       mask1 >>= 1;
+      }
+      ++mono1;
+    }
+    break;
+
+  case splashModeMono8:
+    mono8 = &bitmap->data.mono8[y * bitmap->width + x0];
+    for (i = 0; i < n; ++i) {
+      if (noClip || state->clip->test(x0 + i, y)) {
+       color = pattern->getColor(x0 + i, y);
+       *mono8 ^= color.mono8;
+      }
+      ++mono8;
+    }
+    break;
+
+  case splashModeRGB8:
+    rgb8 = &bitmap->data.rgb8[y * bitmap->width + x0];
+    for (i = 0; i < n; ++i) {
+      if (noClip || state->clip->test(x0 + i, y)) {
+       color = pattern->getColor(x0 + i, y);
+       *rgb8 ^= color.rgb8;
+      }
+      ++rgb8;
+    }
+    break;
+
+  case splashModeBGR8Packed:
+    bgr8 = &bitmap->data.bgr8[y * bitmap->rowSize + 3 * x0];
+    for (i = 0; i < n; ++i) {
+      if (noClip || state->clip->test(x0 + i, y)) {
+       color = pattern->getColor(x0 + i, y);
+       bgr8[2] ^= splashBGR8R(color.bgr8);
+       bgr8[1] ^= splashBGR8G(color.bgr8);
+       bgr8[0] ^= splashBGR8B(color.bgr8);
+      }
+      bgr8 += 3;
+    }
+    break;
+  }
+}
+
+void Splash::getPixel(int x, int y, SplashColor *pixel) {
+  SplashBGR8P *bgr8;
+
+  if (y < 0 || y >= bitmap->height || x < 0 || x >= bitmap->width) {
+    return;
+  }
+  switch (bitmap->mode) {
+  case splashModeMono1:
+    pixel->mono1 = (bitmap->data.mono1[y * bitmap->rowSize + (x >> 3)]
+                   >> (7 - (x & 7))) & 1;
+    break;
+  case splashModeMono8:
+    pixel->mono8 = bitmap->data.mono8[y * bitmap->width + x];
+    break;
+  case splashModeRGB8:
+    pixel->rgb8 = bitmap->data.rgb8[y * bitmap->width + x];
+    break;
+  case splashModeBGR8Packed:
+    bgr8 = &bitmap->data.bgr8[y * bitmap->rowSize + 3 * x];
+    pixel->bgr8 = splashMakeBGR8(bgr8[2], bgr8[1], bgr8[0]);
+    break;
+  }
+}
+
+SplashError Splash::fillChar(SplashCoord x, SplashCoord y,
+                            int c, SplashFont *font) {
+  SplashGlyphBitmap glyph;
+  int x0, y0, xFrac, yFrac;
+  SplashError err;
+
+  if (debugMode) {
+    printf("fillChar: x=%.2f y=%.2f c=%3d=0x%02x='%c'\n",
+          x, y, c, c, c);
+  }
+  x0 = splashFloor(x);
+  xFrac = splashFloor((x - x0) * splashFontFraction);
+  y0 = splashFloor(y);
+  yFrac = splashFloor((y - y0) * splashFontFraction);
+  if (!font->getGlyph(c, xFrac, yFrac, &glyph)) {
+    return splashErrNoGlyph;
+  }
+  err = fillGlyph(x, y, &glyph);
+  if (glyph.freeData) {
+    gfree(glyph.data);
+  }
+  return err;
+}
+
+SplashError Splash::fillGlyph(SplashCoord x, SplashCoord y,
+                             SplashGlyphBitmap *glyph) {
+  int alpha, ialpha;
+  Guchar *p;
+  SplashColor fg;
+  SplashMono1P *mono1Ptr;
+  SplashMono8 *mono8Ptr;
+  SplashRGB8 *rgb8Ptr;
+  SplashBGR8P *bgr8Ptr;
+  SplashMono8 bgMono8;
+  int bgR, bgG, bgB;
+  SplashClipResult clipRes;
+  GBool noClip;
+  int x0, y0, x1, y1, xx, xx1, yy;
+
+  x0 = splashFloor(x);
+  y0 = splashFloor(y);
+
+  if ((clipRes = state->clip->testRect(x0 - glyph->x,
+                                      y0 - glyph->y,
+                                      x0 - glyph->x + glyph->w - 1,
+                                      y0 - glyph->y + glyph->h - 1))
+      != splashClipAllOutside) {
+    noClip = clipRes == splashClipAllInside;
+
+    //~ optimize this
+    if (glyph->aa) {
+      p = glyph->data;
+      for (yy = 0, y1 = y0 - glyph->y; yy < glyph->h; ++yy, ++y1) {
+       for (xx = 0, x1 = x0 - glyph->x; xx < glyph->w; ++xx, ++x1) {
+         alpha = *p++;
+         if (alpha > 0) {
+           if (noClip || state->clip->test(x1, y1)) {
+             ialpha = 255 - alpha;
+             fg = state->fillPattern->getColor(x1, y1);
+             switch (bitmap->mode) {
+             case splashModeMono1:
+               if (alpha >= 0x80) {
+                 mono1Ptr = &bitmap->data.mono1[y1 * bitmap->rowSize +
+                                                (x1 >> 3)];
+                 if (fg.mono1) {
+                   *mono1Ptr |= 0x80 >> (x1 & 7);
+                 } else {
+                   *mono1Ptr &= ~(0x80 >> (x1 & 7));
+                 }
+               }
+               break;
+             case splashModeMono8:
+               mono8Ptr = &bitmap->data.mono8[y1 * bitmap->width + x1];
+               bgMono8 = *mono8Ptr;
+               // note: floor(x / 255) = x >> 8 (for 16-bit x)
+               *mono8Ptr = (alpha * fg.mono8 + ialpha * bgMono8) >> 8;
+               break;
+             case splashModeRGB8:
+               rgb8Ptr = &bitmap->data.rgb8[y1 * bitmap->width + x1];
+               bgR = splashRGB8R(*rgb8Ptr);
+               bgG = splashRGB8G(*rgb8Ptr);
+               bgB = splashRGB8B(*rgb8Ptr);
+               *rgb8Ptr = splashMakeRGB8((alpha * splashRGB8R(fg.rgb8) +
+                                          ialpha * bgR) >> 8,
+                                         (alpha * splashRGB8G(fg.rgb8) +
+                                          ialpha * bgG) >> 8,
+                                         (alpha * splashRGB8B(fg.rgb8) +
+                                          ialpha * bgB) >> 8);
+               break;
+             case splashModeBGR8Packed:
+               bgr8Ptr = &bitmap->data.bgr8[y1 * bitmap->rowSize + 3 * x1];
+               bgr8Ptr[2] =
+                   (alpha * splashBGR8R(fg.bgr8) + ialpha * bgr8Ptr[2]) >> 8;
+               bgr8Ptr[1] =
+                   (alpha * splashBGR8G(fg.bgr8) + ialpha * bgr8Ptr[1]) >> 8;
+               bgr8Ptr[0] =
+                   (alpha * splashBGR8B(fg.bgr8) + ialpha * bgr8Ptr[0]) >> 8;
+               break;
+             }
+           }
+         }
+       }
+      }
+
+    } else {
+      p = glyph->data;
+      for (yy = 0, y1 = y0 - glyph->y; yy < glyph->h; ++yy, ++y1) {
+       for (xx = 0, x1 = x0 - glyph->x; xx < glyph->w; xx += 8) {
+         alpha = *p++;
+         for (xx1 = 0; xx1 < 8 && xx + xx1 < glyph->w; ++xx1, ++x1) {
+           if (alpha & 0x80) {
+             if (noClip || state->clip->test(x1, y1)) {
+               fg = state->fillPattern->getColor(x1, y1);
+               switch (bitmap->mode) {
+               case splashModeMono1:
+                 mono1Ptr = &bitmap->data.mono1[y1 * bitmap->rowSize +
+                                                (x1 >> 3)];
+                 if (fg.mono1) {
+                   *mono1Ptr |= 0x80 >> (x1 & 7);
+                 } else {
+                   *mono1Ptr &= ~(0x80 >> (x1 & 7));
+                 }
+                 break;
+               case splashModeMono8:
+                 bitmap->data.mono8[y1 * bitmap->width + x1] = fg.mono8;
+                 break;
+               case splashModeRGB8:
+                 bitmap->data.rgb8[y1 * bitmap->width + x1] = fg.rgb8;
+                 break;
+               case splashModeBGR8Packed:
+                 bgr8Ptr = &bitmap->data.bgr8[y1 * bitmap->rowSize + 3 * x1];
+                 bgr8Ptr[2] = splashBGR8R(fg.bgr8);
+                 bgr8Ptr[1] = splashBGR8G(fg.bgr8);
+                 bgr8Ptr[0] = splashBGR8B(fg.bgr8);
+                 break;
+               }
+             }
+           }
+           alpha <<= 1;
+         }
+       }
+      }
+    }
+  }
+
+  return splashOk;
+}
+
+SplashError Splash::fillImageMask(SplashImageMaskSource src, void *srcData,
+                                 int w, int h, SplashCoord *mat) {
+  GBool rot;
+  SplashCoord xScale, yScale, xShear, yShear;
+  int tx, ty, scaledWidth, scaledHeight, xSign, ySign;
+  int ulx, uly, llx, lly, urx, ury, lrx, lry;
+  int ulx1, uly1, llx1, lly1, urx1, ury1, lrx1, lry1;
+  int xMin, xMax, yMin, yMax;
+  SplashClipResult clipRes, clipRes2;
+  int yp, yq, yt, yStep, lastYStep;
+  int xp, xq, xt, xStep, xSrc;
+  int k1, spanXMin, spanXMax, spanY;
+  SplashMono1 *pixBuf;
+  SplashMono1 *p;
+  int pixAcc;
+  SplashCoord alpha;
+  SplashColor fg, bg, pix;
+  int x, y, x1, y1, x2, y2;
+  int n, m, i, j;
+
+  if (debugMode) {
+    printf("fillImageMask: w=%d h=%d mat=[%.2f %.2f %.2f %.2f %.2f %.2f]\n",
+          w, h, mat[0], mat[1], mat[2], mat[3], mat[4], mat[5]);
+  }
+
+  // check for singular matrix
+  if (splashAbs(mat[0] * mat[3] - mat[1] * mat[2]) < 0.000001) {
+    return splashErrSingularMatrix;
+  }
+
+  // compute scale, shear, rotation, translation parameters
+  rot = splashAbs(mat[1]) > splashAbs(mat[0]);
+  if (rot) {
+    xScale = -mat[1];
+    yScale = mat[2] - (mat[0] * mat[3]) / mat[1];
+    xShear = -mat[3] / yScale;
+    yShear = -mat[0] / mat[1];
+  } else {
+    xScale = mat[0];
+    yScale = mat[3] - (mat[1] * mat[2]) / mat[0];
+    xShear = mat[2] / yScale;
+    yShear = mat[1] / mat[0];
+  }
+  tx = splashRound(mat[4]);
+  ty = splashRound(mat[5]);
+  scaledWidth = abs(splashRound(mat[4] + xScale) - tx) + 1;
+  scaledHeight = abs(splashRound(mat[5] + yScale) - ty) + 1;
+  xSign = (xScale < 0) ? -1 : 1;
+  ySign = (yScale < 0) ? -1 : 1;
+
+  // clipping
+  ulx1 = 0;
+  uly1 = 0;
+  urx1 = xSign * (scaledWidth - 1);
+  ury1 = splashRound(yShear * urx1);
+  llx1 = splashRound(xShear * ySign * (scaledHeight - 1));
+  lly1 = ySign * (scaledHeight - 1) + splashRound(yShear * llx1);
+  lrx1 = xSign * (scaledWidth - 1) +
+           splashRound(xShear * ySign * (scaledHeight - 1));
+  lry1 = ySign * (scaledHeight - 1) + splashRound(yShear * lrx1);
+  if (rot) {
+    ulx = tx + uly1;    uly = ty - ulx1;
+    urx = tx + ury1;    ury = ty - urx1;
+    llx = tx + lly1;    lly = ty - llx1;
+    lrx = tx + lry1;    lry = ty - lrx1;
+  } else {
+    ulx = tx + ulx1;    uly = ty + uly1;
+    urx = tx + urx1;    ury = ty + ury1;
+    llx = tx + llx1;    lly = ty + lly1;
+    lrx = tx + lrx1;    lry = ty + lry1;
+  }
+  xMin = (ulx < urx) ? (ulx < llx) ? (ulx < lrx) ? ulx : lrx
+                                   : (llx < lrx) ? llx : lrx
+                    : (urx < llx) ? (urx < lrx) ? urx : lrx
+                                   : (llx < lrx) ? llx : lrx;
+  xMax = (ulx > urx) ? (ulx > llx) ? (ulx > lrx) ? ulx : lrx
+                                   : (llx > lrx) ? llx : lrx
+                    : (urx > llx) ? (urx > lrx) ? urx : lrx
+                                   : (llx > lrx) ? llx : lrx;
+  yMin = (uly < ury) ? (uly < lly) ? (uly < lry) ? uly : lry
+                                   : (lly < lry) ? lly : lry
+                    : (ury < lly) ? (ury < lry) ? ury : lry
+                                   : (lly < lry) ? lly : lry;
+  yMax = (uly > ury) ? (uly > lly) ? (uly > lry) ? uly : lry
+                                   : (lly > lry) ? lly : lry
+                    : (ury > lly) ? (ury > lry) ? ury : lry
+                                   : (lly > lry) ? lly : lry;
+  clipRes = state->clip->testRect(xMin, yMin, xMax, yMax);
+
+  // compute Bresenham parameters for x and y scaling
+  yp = h / scaledHeight;
+  yq = h % scaledHeight;
+  xp = w / scaledWidth;
+  xq = w % scaledWidth;
+
+  // allocate pixel buffer
+  pixBuf = (SplashMono1 *)gmalloc((yp + 1) * w * sizeof(SplashMono1));
+
+  // init y scale Bresenham
+  yt = 0;
+  lastYStep = 1;
+
+  for (y = 0; y < scaledHeight; ++y) {
+
+    // y scale Bresenham
+    yStep = yp;
+    yt += yq;
+    if (yt >= scaledHeight) {
+      yt -= scaledHeight;
+      ++yStep;
+    }
+
+    // read row(s) from image
+    n = (yp > 0) ? yStep : lastYStep;
+    if (n > 0) {
+      p = pixBuf;
+      for (i = 0; i < n; ++i) {
+       for (j = 0; j < w; ++j) {
+         (*src)(srcData, p++);
+       }
+      }
+    }
+    lastYStep = yStep;
+
+    // loop-invariant constants
+    k1 = splashRound(xShear * ySign * y);
+
+    // clipping test
+    if (clipRes != splashClipAllInside &&
+       !rot &&
+       splashRound(yShear * k1) ==
+         splashRound(yShear * (xSign * (scaledWidth - 1) + k1))) {
+      if (xSign > 0) {
+       spanXMin = tx + k1;
+       spanXMax = spanXMin + (scaledWidth - 1);
+      } else {
+       spanXMax = tx + k1;
+       spanXMin = spanXMax - (scaledWidth - 1);
+      }
+      spanY = ty + ySign * y + splashRound(xShear * ySign * y);
+      clipRes2 = state->clip->testSpan(spanXMin, spanXMax, spanY);
+      if (clipRes2 == splashClipAllOutside) {
+       continue;
+      }
+    } else {
+      clipRes2 = clipRes;
+    }
+
+    // init x scale Bresenham
+    xt = 0;
+    xSrc = 0;
+
+    for (x = 0; x < scaledWidth; ++x) {
+
+      // x scale Bresenham
+      xStep = xp;
+      xt += xq;
+      if (xt >= scaledWidth) {
+       xt -= scaledWidth;
+       ++xStep;
+      }
+
+      // x shear
+      x1 = xSign * x + k1;
+
+      // y shear
+      y1 = ySign * y + splashRound(yShear * x1);
+
+      // rotation
+      if (rot) {
+       x2 = y1;
+       y2 = -x1;
+      } else {
+       x2 = x1;
+       y2 = y1;
+      }
+
+      // compute the alpha value for (x,y) after the x and y scaling
+      // operations
+      n = yStep > 0 ? yStep : 1;
+      m = xStep > 0 ? xStep : 1;
+      p = pixBuf + xSrc;
+      pixAcc = 0;
+      for (i = 0; i < n; ++i) {
+       for (j = 0; j < m; ++j) {
+         pixAcc += *p++;
+       }
+       p += w - m;
+      }
+
+      // blend fill color with background
+      if (pixAcc != 0) {
+       fg = state->fillPattern->getColor(tx + x2, ty + y2);
+       if (pixAcc == n * m) {
+         pix = fg;
+       } else {
+         getPixel(tx + x2, ty + y2, &bg);
+         alpha = (SplashCoord)pixAcc / (SplashCoord)(n * m);
+         switch (bitmap->mode) {
+         case splashModeMono1:
+           pix.mono1 = splashRound(alpha * fg.mono1 +
+                                   (1 - alpha) * bg.mono1);
+           break;
+         case splashModeMono8:
+           pix.mono8 = splashRound(alpha * fg.mono8 +
+                                   (1 - alpha) * bg.mono8);
+           break;
+         case splashModeRGB8:
+           pix.rgb8 = splashMakeRGB8(
+                          splashRound(alpha * splashRGB8R(fg.rgb8) +
+                                      (1 - alpha) * splashRGB8R(bg.rgb8)),
+                          splashRound(alpha * splashRGB8G(fg.rgb8) +
+                                      (1 - alpha) * splashRGB8G(bg.rgb8)),
+                          splashRound(alpha * splashRGB8B(fg.rgb8) +
+                                      (1 - alpha) * splashRGB8B(bg.rgb8)));
+         case splashModeBGR8Packed:
+           pix.bgr8 = splashMakeBGR8(
+                          splashRound(alpha * splashBGR8R(fg.bgr8) +
+                                      (1 - alpha) * splashBGR8R(bg.bgr8)),
+                          splashRound(alpha * splashBGR8G(fg.bgr8) +
+                                      (1 - alpha) * splashBGR8G(bg.bgr8)),
+                          splashRound(alpha * splashBGR8B(fg.bgr8) +
+                                      (1 - alpha) * splashBGR8B(bg.bgr8)));
+           break;
+         }
+       }
+       drawPixel(tx + x2, ty + y2, &pix, clipRes2 == splashClipAllInside);
+      }
+
+      // x scale Bresenham
+      xSrc += xStep;
+    }
+  }
+
+  // free memory
+  gfree(pixBuf);
+
+  return splashOk;
+}
+
+SplashError Splash::drawImage(SplashImageSource src, void *srcData,
+                             SplashColorMode srcMode,
+                             int w, int h, SplashCoord *mat) {
+  GBool ok, rot, halftone;
+  SplashCoord xScale, yScale, xShear, yShear;
+  int tx, ty, scaledWidth, scaledHeight, xSign, ySign;
+  int ulx, uly, llx, lly, urx, ury, lrx, lry;
+  int ulx1, uly1, llx1, lly1, urx1, ury1, lrx1, lry1;
+  int xMin, xMax, yMin, yMax;
+  SplashClipResult clipRes, clipRes2;
+  int yp, yq, yt, yStep, lastYStep;
+  int xp, xq, xt, xStep, xSrc;
+  int k1, spanXMin, spanXMax, spanY;
+  SplashColor *pixBuf, *p;
+  Guchar *alphaBuf, *q;
+  SplashColor pix;
+  SplashCoord pixAcc[splashMaxColorComps];
+  int alphaAcc;
+  SplashCoord pixMul, alphaMul, alpha;
+  int x, y, x1, y1, x2, y2;
+  int n, m, i, j;
+
+  if (debugMode) {
+    printf("drawImage: srcMode=%d w=%d h=%d mat=[%.2f %.2f %.2f %.2f %.2f %.2f]\n",
+          srcMode, w, h, mat[0], mat[1], mat[2], mat[3], mat[4], mat[5]);
+  }
+
+  // check color modes
+  ok = gFalse; // make gcc happy
+  switch (bitmap->mode) {
+  case splashModeMono1:
+    ok = srcMode == splashModeMono1 || srcMode == splashModeMono8;
+    break;
+  case splashModeMono8:
+    ok = srcMode == splashModeMono8;
+    break;
+  case splashModeRGB8:
+    ok = srcMode == splashModeRGB8;
+    break;
+  case splashModeBGR8Packed:
+    ok = srcMode == splashModeBGR8Packed;
+    break;
+  }
+  if (!ok) {
+    return splashErrModeMismatch;
+  }
+  halftone = bitmap->mode == splashModeMono1 && srcMode == splashModeMono8;
+
+  // check for singular matrix
+  if (splashAbs(mat[0] * mat[3] - mat[1] * mat[2]) < 0.000001) {
+    return splashErrSingularMatrix;
+  }
+
+  // compute scale, shear, rotation, translation parameters
+  rot = splashAbs(mat[1]) > splashAbs(mat[0]);
+  if (rot) {
+    xScale = -mat[1];
+    yScale = mat[2] - (mat[0] * mat[3]) / mat[1];
+    xShear = -mat[3] / yScale;
+    yShear = -mat[0] / mat[1];
+  } else {
+    xScale = mat[0];
+    yScale = mat[3] - (mat[1] * mat[2]) / mat[0];
+    xShear = mat[2] / yScale;
+    yShear = mat[1] / mat[0];
+  }
+  tx = splashRound(mat[4]);
+  ty = splashRound(mat[5]);
+  scaledWidth = abs(splashRound(mat[4] + xScale) - tx) + 1;
+  scaledHeight = abs(splashRound(mat[5] + yScale) - ty) + 1;
+  xSign = (xScale < 0) ? -1 : 1;
+  ySign = (yScale < 0) ? -1 : 1;
+
+  // clipping
+  ulx1 = 0;
+  uly1 = 0;
+  urx1 = xSign * (scaledWidth - 1);
+  ury1 = splashRound(yShear * urx1);
+  llx1 = splashRound(xShear * ySign * (scaledHeight - 1));
+  lly1 = ySign * (scaledHeight - 1) + splashRound(yShear * llx1);
+  lrx1 = xSign * (scaledWidth - 1) +
+           splashRound(xShear * ySign * (scaledHeight - 1));
+  lry1 = ySign * (scaledHeight - 1) + splashRound(yShear * lrx1);
+  if (rot) {
+    ulx = tx + uly1;    uly = ty - ulx1;
+    urx = tx + ury1;    ury = ty - urx1;
+    llx = tx + lly1;    lly = ty - llx1;
+    lrx = tx + lry1;    lry = ty - lrx1;
+  } else {
+    ulx = tx + ulx1;    uly = ty + uly1;
+    urx = tx + urx1;    ury = ty + ury1;
+    llx = tx + llx1;    lly = ty + lly1;
+    lrx = tx + lrx1;    lry = ty + lry1;
+  }
+  xMin = (ulx < urx) ? (ulx < llx) ? (ulx < lrx) ? ulx : lrx
+                                   : (llx < lrx) ? llx : lrx
+                    : (urx < llx) ? (urx < lrx) ? urx : lrx
+                                   : (llx < lrx) ? llx : lrx;
+  xMax = (ulx > urx) ? (ulx > llx) ? (ulx > lrx) ? ulx : lrx
+                                   : (llx > lrx) ? llx : lrx
+                    : (urx > llx) ? (urx > lrx) ? urx : lrx
+                                   : (llx > lrx) ? llx : lrx;
+  yMin = (uly < ury) ? (uly < lly) ? (uly < lry) ? uly : lry
+                                   : (lly < lry) ? lly : lry
+                    : (ury < lly) ? (ury < lry) ? ury : lry
+                                   : (lly < lry) ? lly : lry;
+  yMax = (uly > ury) ? (uly > lly) ? (uly > lry) ? uly : lry
+                                   : (lly > lry) ? lly : lry
+                    : (ury > lly) ? (ury > lry) ? ury : lry
+                                   : (lly > lry) ? lly : lry;
+  if ((clipRes = state->clip->testRect(xMin, yMin, xMax, yMax))
+      == splashClipAllOutside) {
+    return splashOk;
+  }
+
+  // compute Bresenham parameters for x and y scaling
+  yp = h / scaledHeight;
+  yq = h % scaledHeight;
+  xp = w / scaledWidth;
+  xq = w % scaledWidth;
+
+  // allocate pixel buffer
+  pixBuf = (SplashColor *)gmalloc((yp + 1) * w * sizeof(SplashColor));
+  alphaBuf = (Guchar *)gmalloc((yp + 1) * w * sizeof(Guchar));
+
+  // init y scale Bresenham
+  yt = 0;
+  lastYStep = 1;
+
+  for (y = 0; y < scaledHeight; ++y) {
+
+    // y scale Bresenham
+    yStep = yp;
+    yt += yq;
+    if (yt >= scaledHeight) {
+      yt -= scaledHeight;
+      ++yStep;
+    }
+
+    // read row(s) from image
+    n = (yp > 0) ? yStep : lastYStep;
+    if (n > 0) {
+      p = pixBuf;
+      q = alphaBuf;
+      for (i = 0; i < n; ++i) {
+       for (j = 0; j < w; ++j) {
+         (*src)(srcData, p++, q++);
+       }
+      }
+    }
+    lastYStep = yStep;
+
+    // loop-invariant constants
+    k1 = splashRound(xShear * ySign * y);
+
+    // clipping test
+    if (clipRes != splashClipAllInside &&
+       !rot &&
+       splashRound(yShear * k1) ==
+         splashRound(yShear * (xSign * (scaledWidth - 1) + k1))) {
+      if (xSign > 0) {
+       spanXMin = tx + k1;
+       spanXMax = spanXMin + (scaledWidth - 1);
+      } else {
+       spanXMax = tx + k1;
+       spanXMin = spanXMax - (scaledWidth - 1);
+      }
+      spanY = ty + ySign * y + splashRound(xShear * ySign * y);
+      clipRes2 = state->clip->testSpan(spanXMin, spanXMax, spanY);
+      if (clipRes2 == splashClipAllOutside) {
+       continue;
+      }
+    } else {
+      clipRes2 = clipRes;
+    }
+
+    // init x scale Bresenham
+    xt = 0;
+    xSrc = 0;
+
+    for (x = 0; x < scaledWidth; ++x) {
+
+      // x scale Bresenham
+      xStep = xp;
+      xt += xq;
+      if (xt >= scaledWidth) {
+       xt -= scaledWidth;
+       ++xStep;
+      }
+
+      // x shear
+      x1 = xSign * x + k1;
+
+      // y shear
+      y1 = ySign * y + splashRound(yShear * x1);
+
+      // rotation
+      if (rot) {
+       x2 = y1;
+       y2 = -x1;
+      } else {
+       x2 = x1;
+       y2 = y1;
+      }
+
+      // compute the filtered pixel at (x,y) after the x and y scaling
+      // operations
+      n = yStep > 0 ? yStep : 1;
+      m = xStep > 0 ? xStep : 1;
+      p = pixBuf + xSrc;
+      q = alphaBuf + xSrc;
+      for (i = 0; i < splashMaxColorComps; ++i) {
+       pixAcc[i] = 0;
+      }
+      alphaAcc = 0;
+      for (i = 0; i < n; ++i) {
+       for (j = 0; j < m; ++j) {
+         switch (srcMode) {
+         case splashModeMono1:
+           pixAcc[0] += p->mono1;
+           break;
+         case splashModeMono8:
+           pixAcc[0] += p->mono8;
+           break;
+         case splashModeRGB8:
+           pixAcc[0] += splashRGB8R(p->rgb8);
+           pixAcc[1] += splashRGB8G(p->rgb8);
+           pixAcc[2] += splashRGB8B(p->rgb8);
+           break;
+         case splashModeBGR8Packed:
+           pixAcc[0] += splashBGR8R(p->bgr8);
+           pixAcc[1] += splashBGR8G(p->bgr8);
+           pixAcc[2] += splashBGR8B(p->bgr8);
+           break;
+         }
+         ++p;
+         alphaAcc += *q++;
+       }
+       p += w - m;
+       q += w - m;
+      }
+      alphaMul = 1 / (SplashCoord)(n * m);
+      if (halftone) {
+       pixMul = (SplashCoord)alphaMul / 256.0;
+      } else {
+       pixMul = alphaMul;
+      }
+      alpha = (SplashCoord)alphaAcc * alphaMul;
+
+      //~ this should blend if 0 < alpha < 1
+      if (alpha > 0.75) {
+
+       // mono8 -> mono1 conversion, with halftoning
+       if (halftone) {
+         pix.mono1 = state->screen->test(tx + x2, ty + y2,
+                                         pixAcc[0] * pixMul);
+
+       // no conversion, no halftoning
+       } else {
+         switch (bitmap->mode) {
+         case splashModeMono1:
+           pix.mono1 = splashRound(pixAcc[0] * pixMul);
+           break;
+         case splashModeMono8:
+           pix.mono8 = splashRound(pixAcc[0] * pixMul);
+           break;
+         case splashModeRGB8:
+           pix.rgb8 = splashMakeRGB8(splashRound(pixAcc[0] * pixMul),
+                                     splashRound(pixAcc[1] * pixMul),
+                                     splashRound(pixAcc[2] * pixMul));
+           break;
+         case splashModeBGR8Packed:
+           pix.bgr8 = splashMakeBGR8(splashRound(pixAcc[0] * pixMul),
+                                     splashRound(pixAcc[1] * pixMul),
+                                     splashRound(pixAcc[2] * pixMul));
+           break;
+         }
+       }
+
+       // set pixel
+       drawPixel(tx + x2, ty + y2, &pix, clipRes2 == splashClipAllInside);
+      }
+
+      // x scale Bresenham
+      xSrc += xStep;
+    }
+  }
+
+  gfree(pixBuf);
+  gfree(alphaBuf);
+
+  return splashOk;
+}
+
+void Splash::dumpPath(SplashPath *path) {
+  int i;
+
+  for (i = 0; i < path->length; ++i) {
+    printf("  %3d: x=%8.2f y=%8.2f%s%s%s%s%s\n",
+          i, path->pts[i].x, path->pts[i].y,
+          (path->flags[i] & splashPathFirst) ? " first" : "",
+          (path->flags[i] & splashPathLast) ? " last" : "",
+          (path->flags[i] & splashPathClosed) ? " closed" : "",
+          (path->flags[i] & splashPathCurve) ? " curve" : "",
+          (path->flags[i] & splashPathArcCW) ? " arcCW" : "");
+  }
+}
diff --git a/pdf/splash/Splash.h b/pdf/splash/Splash.h
new file mode 100644 (file)
index 0000000..efcbdab
--- /dev/null
@@ -0,0 +1,176 @@
+//========================================================================
+//
+// Splash.h
+//
+//========================================================================
+
+#ifndef SPLASH_H
+#define SPLASH_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#include "SplashTypes.h"
+
+class SplashBitmap;
+class SplashGlyphBitmap;
+class SplashState;
+class SplashPattern;
+class SplashScreen;
+class SplashPath;
+class SplashXPath;
+class SplashClip;
+class SplashFont;
+
+//------------------------------------------------------------------------
+
+// Retrieves the next pixel in an image mask.  Normally, fills in
+// *<pixel> and returns true.  If the image stream is exhausted,
+// returns false.
+typedef GBool (*SplashImageMaskSource)(void *data, SplashMono1 *pixel);
+
+// Retrieves the next pixel in an image.  Normally, fills in *<pixel>
+// (pixel color) and *<alpha> (1 for opaque, 0 for transparent), and
+// returns true.  If the image stream is exhausted, returns false.
+typedef GBool (*SplashImageSource)(void *data, SplashColor *pixel,
+                                  Guchar *alpha);
+
+//------------------------------------------------------------------------
+// Splash
+//------------------------------------------------------------------------
+
+class Splash {
+public:
+
+  // Create a new rasterizer object.
+  Splash(SplashBitmap *bitmapA);
+
+  ~Splash();
+
+  //----- state read
+
+  SplashPattern *getStrokePattern();
+  SplashPattern *getFillPattern();
+  SplashScreen *getScreen();
+  SplashCoord getLineWidth();
+  int getLineCap();
+  int getLineJoin();
+  SplashCoord getMiterLimit();
+  SplashCoord getFlatness();
+  SplashCoord *getLineDash();
+  int getLineDashLength();
+  SplashCoord getLineDashPhase();
+  SplashClip *getClip();
+
+  //----- state write
+
+  void setStrokePattern(SplashPattern *strokeColor);
+  void setFillPattern(SplashPattern *fillColor);
+  void setScreen(SplashScreen *screen);
+  void setLineWidth(SplashCoord lineWidth);
+  void setLineCap(int lineCap);
+  void setLineJoin(int lineJoin);
+  void setMiterLimit(SplashCoord miterLimit);
+  void setFlatness(SplashCoord flatness);
+  // the <lineDash> array will be copied
+  void setLineDash(SplashCoord *lineDash, int lineDashLength,
+                  SplashCoord lineDashPhase);
+  void clipResetToRect(SplashCoord x0, SplashCoord y0,
+                      SplashCoord x1, SplashCoord y1);
+  SplashError clipToRect(SplashCoord x0, SplashCoord y0,
+                        SplashCoord x1, SplashCoord y1);
+  SplashError clipToPath(SplashPath *path, GBool eo);
+
+  //----- state save/restore
+
+  void saveState();
+  SplashError restoreState();
+
+  //----- drawing operations
+
+  // Fill the bitmap with <color>.  This is not subject to clipping.
+  void clear(SplashColor color);
+
+  // Stroke a path using the current stroke pattern.
+  SplashError stroke(SplashPath *path);
+
+  // Fill a path using the current fill pattern.
+  SplashError fill(SplashPath *path, GBool eo);
+
+  // Fill a path, XORing with the current fill pattern.
+  SplashError xorFill(SplashPath *path, GBool eo);
+
+  // Draw a character, using the current fill pattern.
+  SplashError fillChar(SplashCoord x, SplashCoord y, int c, SplashFont *font);
+
+  // Draw a glyph, using the current fill pattern.  This function does
+  // not free any data, i.e., it ignores glyph->freeData.
+  SplashError fillGlyph(SplashCoord x, SplashCoord y,
+                       SplashGlyphBitmap *glyph);
+
+  // Draws an image mask using the fill color.  This will read <w>*<h>
+  // pixels from <src>, in raster order, starting with the top line.
+  // "1" pixels will be drawn with the current fill color; "0" pixels
+  // are transparent.  The matrix:
+  //    [ mat[0] mat[1] 0 ]
+  //    [ mat[2] mat[3] 0 ]
+  //    [ mat[4] mat[5] 1 ]
+  // maps a unit square to the desired destination for the image, in
+  // PostScript style:
+  //    [x' y' 1] = [x y 1] * mat
+  // Note that the Splash y axis points downward, and the image source
+  // is assumed to produce pixels in raster order, starting from the
+  // top line.
+  SplashError fillImageMask(SplashImageMaskSource src, void *srcData,
+                           int w, int h, SplashCoord *mat);
+
+  // Draw an image.  This will read <w>*<h> pixels from <src>, in
+  // raster order, starting with the top line.  These pixels are
+  // assumed to be in the source mode, <srcMode>.  The following
+  // combinations of source and target modes are supported:
+  //    source       target
+  //    ------       ------
+  //    Mono1        Mono1
+  //    Mono8        Mono1   -- with dithering
+  //    Mono8        Mono8
+  //    RGB8         RGB8
+  //    BGR8packed   BGR8Packed
+  // The matrix behaves as for fillImageMask.
+  SplashError drawImage(SplashImageSource src, void *srcData,
+                       SplashColorMode srcMode,
+                       int w, int h, SplashCoord *mat);
+
+  //~ drawMaskedImage
+
+  //----- misc
+
+  // Return the associated bitmap.
+  SplashBitmap *getBitmap() { return bitmap; }
+
+  // Toggle debug mode on or off.
+  void setDebugMode(GBool debugModeA) { debugMode = debugModeA; }
+
+private:
+
+  void strokeNarrow(SplashXPath *xPath);
+  void strokeWide(SplashXPath *xPath);
+  SplashXPath *makeDashedPath(SplashXPath *xPath);
+  SplashError fillWithPattern(SplashPath *path, GBool eo,
+                             SplashPattern *pattern);
+  void drawPixel(int x, int y, SplashColor *color, GBool noClip);
+  void drawPixel(int x, int y, SplashPattern *pattern, GBool noClip);
+  void drawSpan(int x0, int x1, int y, SplashPattern *pattern, GBool noClip);
+  void xorSpan(int x0, int x1, int y, SplashPattern *pattern, GBool noClip);
+  void putPixel(int x, int y, SplashColor *pixel);
+  void getPixel(int x, int y, SplashColor *pixel);
+  void dumpPath(SplashPath *path);
+
+  SplashBitmap *bitmap;
+  SplashState *state;
+  GBool debugMode;
+};
+
+#endif
diff --git a/pdf/splash/SplashBitmap.cc b/pdf/splash/SplashBitmap.cc
new file mode 100644 (file)
index 0000000..5ea304f
--- /dev/null
@@ -0,0 +1,134 @@
+//========================================================================
+//
+// SplashBitmap.cc
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <stdio.h>
+#include "gmem.h"
+#include "SplashErrorCodes.h"
+#include "SplashBitmap.h"
+
+//------------------------------------------------------------------------
+// SplashBitmap
+//------------------------------------------------------------------------
+
+SplashBitmap::SplashBitmap(int widthA, int heightA, SplashColorMode modeA) {
+  width = widthA;
+  height = heightA;
+  mode = modeA;
+  switch (mode) {
+  case splashModeMono1:
+    rowSize = (width + 7) >> 3;
+    data.mono1 = (SplashMono1P *)
+                   gmalloc(rowSize * height * sizeof(SplashMono1P));
+    break;
+  case splashModeMono8:
+    rowSize = width;
+    data.mono8 = (SplashMono8 *)
+                   gmalloc(width * height * sizeof(SplashMono8));
+    break;
+  case splashModeRGB8:
+    rowSize = width << 2;
+    data.rgb8 = (SplashRGB8 *)
+                  gmalloc(width * height * sizeof(SplashRGB8));
+    break;
+  case splashModeBGR8Packed:
+    rowSize = (width * 3 + 3) & ~3;
+    data.bgr8 = (SplashBGR8P *)
+                  gmalloc(rowSize * height * sizeof(SplashMono1P));
+  }
+}
+
+
+SplashBitmap::~SplashBitmap() {
+  switch (mode) {
+  case splashModeMono1:
+    gfree(data.mono1);
+    break;
+  case splashModeMono8:
+    gfree(data.mono8);
+    break;
+  case splashModeRGB8:
+    gfree(data.rgb8);
+    break;
+  case splashModeBGR8Packed:
+    gfree(data.bgr8);
+    break;
+  }
+}
+
+SplashError SplashBitmap::writePNMFile(char *fileName) {
+  FILE *f;
+  SplashMono1P *mono1;
+  SplashMono8 *mono8;
+  SplashRGB8 *rgb8;
+  SplashBGR8P *bgr8line, *bgr8;
+  int x, y;
+
+  if (!(f = fopen(fileName, "wb"))) {
+    return splashErrOpenFile;
+  }
+
+  switch (mode) {
+
+  case splashModeMono1:
+    fprintf(f, "P4\n%d %d\n", width, height);
+    mono1 = data.mono1;
+    for (y = 0; y < height; ++y) {
+      for (x = 0; x < width; x += 8) {
+       fputc(*mono1 ^ 0xff, f);
+       ++mono1;
+      }
+    }
+    break;
+
+  case splashModeMono8:
+    fprintf(f, "P5\n%d %d\n255\n", width, height);
+    mono8 = data.mono8;
+    for (y = 0; y < height; ++y) {
+      for (x = 0; x < width; ++x) {
+       fputc(*mono8, f);
+       ++mono8;
+      }
+    }
+    break;
+
+  case splashModeRGB8:
+    fprintf(f, "P6\n%d %d\n255\n", width, height);
+    rgb8 = data.rgb8;
+    for (y = 0; y < height; ++y) {
+      for (x = 0; x < width; ++x) {
+       fputc(splashRGB8R(*rgb8), f);
+       fputc(splashRGB8G(*rgb8), f);
+       fputc(splashRGB8B(*rgb8), f);
+       ++rgb8;
+      }
+    }
+    break;
+
+  case splashModeBGR8Packed:
+    fprintf(f, "P6\n%d %d\n255\n", width, height);
+    bgr8line = data.bgr8;
+    for (y = 0; y < height; ++y) {
+      bgr8 = bgr8line;
+      for (x = 0; x < width; ++x) {
+       fputc(bgr8[2], f);
+       fputc(bgr8[1], f);
+       fputc(bgr8[0], f);
+       bgr8 += 3;
+      }
+      bgr8line += rowSize;
+    }
+    break;
+  }
+
+  fclose(f);
+  return splashOk;
+}
diff --git a/pdf/splash/SplashBitmap.h b/pdf/splash/SplashBitmap.h
new file mode 100644 (file)
index 0000000..75e3217
--- /dev/null
@@ -0,0 +1,48 @@
+//========================================================================
+//
+// SplashBitmap.h
+//
+//========================================================================
+
+#ifndef SPLASHBITMAP_H
+#define SPLASHBITMAP_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#include "SplashTypes.h"
+
+//------------------------------------------------------------------------
+// SplashBitmap
+//------------------------------------------------------------------------
+
+class SplashBitmap {
+public:
+
+  // Create a new bitmap.
+  SplashBitmap(int widthA, int heightA, SplashColorMode modeA);
+
+  ~SplashBitmap();
+
+  int getWidth() { return width; }
+  int getHeight() { return height; }
+  int getRowSize() { return rowSize; }
+  SplashColorMode getMode() { return mode; }
+  SplashColorPtr getDataPtr() { return data; }
+
+  SplashError writePNMFile(char *fileName);
+
+private:
+
+  int width, height;           // size of bitmap
+  int rowSize;                 // size of one row of data, in bytes
+  SplashColorMode mode;                // color mode
+  SplashColorPtr data;
+
+  friend class Splash;
+};
+
+#endif
diff --git a/pdf/splash/SplashClip.cc b/pdf/splash/SplashClip.cc
new file mode 100644 (file)
index 0000000..4c70c03
--- /dev/null
@@ -0,0 +1,270 @@
+//========================================================================
+//
+// SplashClip.cc
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+#include "gmem.h"
+#include "SplashErrorCodes.h"
+#include "SplashMath.h"
+#include "SplashPath.h"
+#include "SplashXPath.h"
+#include "SplashXPathScanner.h"
+#include "SplashClip.h"
+
+//------------------------------------------------------------------------
+// SplashClip.flags
+//------------------------------------------------------------------------
+
+#define splashClipEO       0x01        // use even-odd rule
+
+//------------------------------------------------------------------------
+// SplashClip
+//------------------------------------------------------------------------
+
+SplashClip::SplashClip(SplashCoord x0, SplashCoord y0,
+                      SplashCoord x1, SplashCoord y1) {
+  if (x0 < x1) {
+    xMin = splashFloor(x0);
+    xMax = splashFloor(x1);
+  } else {
+    xMin = splashFloor(x1);
+    xMax = splashFloor(x0);
+  }
+  if (y0 < y1) {
+    yMin = splashFloor(y0);
+    yMax = splashFloor(y1);
+  } else {
+    yMin = splashFloor(y1);
+    yMax = splashFloor(y0);
+  }
+  paths = NULL;
+  flags = NULL;
+  scanners = NULL;
+  length = size = 0;
+}
+
+SplashClip::SplashClip(SplashClip *clip) {
+  int i;
+
+  xMin = clip->xMin;
+  yMin = clip->yMin;
+  xMax = clip->xMax;
+  yMax = clip->yMax;
+  length = clip->length;
+  size = clip->size;
+  paths = (SplashXPath **)gmalloc(size * sizeof(SplashXPath *));
+  flags = (Guchar *)gmalloc(size * sizeof(Guchar));
+  scanners = (SplashXPathScanner **)
+                 gmalloc(size * sizeof(SplashXPathScanner *));
+  for (i = 0; i < length; ++i) {
+    paths[i] = clip->paths[i]->copy();
+    flags[i] = clip->flags[i];
+    scanners[i] = new SplashXPathScanner(paths[i], flags[i] & splashClipEO);
+  }
+}
+
+SplashClip::~SplashClip() {
+  int i;
+
+  for (i = 0; i < length; ++i) {
+    delete paths[i];
+    delete scanners[i];
+  }
+  gfree(paths);
+  gfree(flags);
+  gfree(scanners);
+}
+
+void SplashClip::grow(int nPaths) {
+  if (length + nPaths > size) {
+    if (size == 0) {
+      size = 32;
+    }
+    while (size < length + nPaths) {
+      size *= 2;
+    }
+    paths = (SplashXPath **)grealloc(paths, size * sizeof(SplashXPath *));
+    flags = (Guchar *)grealloc(flags, size * sizeof(Guchar));
+    scanners = (SplashXPathScanner **)
+                   grealloc(scanners, size * sizeof(SplashXPathScanner *));
+  }
+}
+
+void SplashClip::resetToRect(SplashCoord x0, SplashCoord y0,
+                            SplashCoord x1, SplashCoord y1) {
+  int i;
+
+  for (i = 0; i < length; ++i) {
+    delete paths[i];
+    delete scanners[i];
+  }
+  gfree(paths);
+  gfree(flags);
+  gfree(scanners);
+  paths = NULL;
+  flags = NULL;
+  scanners = NULL;
+  length = size = 0;
+
+  if (x0 < x1) {
+    xMin = splashFloor(x0);
+    xMax = splashFloor(x1);
+  } else {
+    xMin = splashFloor(x1);
+    xMax = splashFloor(x0);
+  }
+  if (y0 < y1) {
+    yMin = splashFloor(y0);
+    yMax = splashFloor(y1);
+  } else {
+    yMin = splashFloor(y1);
+    yMax = splashFloor(y0);
+  }
+}
+
+SplashError SplashClip::clipToRect(SplashCoord x0, SplashCoord y0,
+                                  SplashCoord x1, SplashCoord y1) {
+  int x0I, y0I, x1I, y1I;
+
+  if (x0 < x1) {
+    x0I = splashFloor(x0);
+    x1I = splashFloor(x1);
+  } else {
+    x0I = splashFloor(x1);
+    x1I = splashFloor(x0);
+  }
+  if (x0I > xMin) {
+    xMin = x0I;
+  }
+  if (x1I < xMax) {
+    xMax = x1I;
+  }
+  if (y0 < y1) {
+    y0I = splashFloor(y0);
+    y1I = splashFloor(y1);
+  } else {
+    y0I = splashFloor(y1);
+    y1I = splashFloor(y0);
+  }
+  if (y0I > yMin) {
+    yMin = y0I;
+  }
+  if (y1I < yMax) {
+    yMax = y1I;
+  }
+  return splashOk;
+}
+
+SplashError SplashClip::clipToPath(SplashPath *path, SplashCoord flatness,
+                                  GBool eo) {
+  SplashXPath *xPath;
+
+  xPath = new SplashXPath(path, flatness, gTrue);
+
+  // check for an empty path
+  if (xPath->length == 0) {
+    xMax = xMin - 1;
+    yMax = yMin - 1;
+    delete xPath;
+
+  // check for a rectangle
+  } else if (xPath->length == 4 &&
+      ((xPath->segs[0].x0 == xPath->segs[0].x1 &&
+       xPath->segs[0].x0 == xPath->segs[1].x0 &&
+       xPath->segs[0].x0 == xPath->segs[3].x1 &&
+       xPath->segs[2].x0 == xPath->segs[2].x1 &&
+       xPath->segs[2].x0 == xPath->segs[1].x1 &&
+       xPath->segs[2].x0 == xPath->segs[3].x0 &&
+       xPath->segs[1].y0 == xPath->segs[1].y1 &&
+       xPath->segs[1].y0 == xPath->segs[0].y1 &&
+       xPath->segs[1].y0 == xPath->segs[2].y0 &&
+       xPath->segs[3].y0 == xPath->segs[3].y1 &&
+       xPath->segs[3].y0 == xPath->segs[0].y0 &&
+       xPath->segs[3].y0 == xPath->segs[2].y1) ||
+       (xPath->segs[0].y0 == xPath->segs[0].y1 &&
+       xPath->segs[0].y0 == xPath->segs[1].y0 &&
+       xPath->segs[0].y0 == xPath->segs[3].y1 &&
+       xPath->segs[2].y0 == xPath->segs[2].y1 &&
+       xPath->segs[2].y0 == xPath->segs[1].y1 &&
+       xPath->segs[2].y0 == xPath->segs[3].y0 &&
+       xPath->segs[1].x0 == xPath->segs[1].x1 &&
+       xPath->segs[1].x0 == xPath->segs[0].x1 &&
+       xPath->segs[1].x0 == xPath->segs[2].x0 &&
+       xPath->segs[3].x0 == xPath->segs[3].x1 &&
+       xPath->segs[3].x0 == xPath->segs[0].x0 &&
+       xPath->segs[3].x0 == xPath->segs[2].x1))) {
+    clipToRect(xPath->segs[0].x0, xPath->segs[0].y0,
+              xPath->segs[2].x0, xPath->segs[2].y0);
+    delete xPath;
+
+  } else {
+    grow(1);
+    xPath->sort();
+    paths[length] = xPath;
+    flags[length] = eo ? splashClipEO : 0;
+    scanners[length] = new SplashXPathScanner(xPath, eo);
+    ++length;
+  }
+
+  return splashOk;
+}
+
+GBool SplashClip::test(int x, int y) {
+  int i;
+
+  // check the rectangle
+  if (x < xMin || x > xMax || y < yMin || y > yMax) {
+    return gFalse;
+  }
+
+  // check the paths
+  for (i = 0; i < length; ++i) {
+    if (!scanners[i]->test(x, y)) {
+      return gFalse;
+    }
+  }
+
+  return gTrue;
+}
+
+SplashClipResult SplashClip::testRect(int rectXMin, int rectYMin,
+                                     int rectXMax, int rectYMax) {
+  if (rectXMax < xMin || rectXMin > xMax ||
+      rectYMax < yMin || rectYMin > yMax) {
+    return splashClipAllOutside;
+  }
+  if (rectXMin >= xMin && rectXMax <= xMax &&
+      rectYMin >= yMin && rectYMax <= yMax &&
+      length == 0) {
+    return splashClipAllInside;
+  }
+  return splashClipPartial;
+}
+
+SplashClipResult SplashClip::testSpan(int spanXMin, int spanXMax, int spanY) {
+  int i;
+
+  if (spanXMax < xMin || spanXMin > xMax ||
+      spanY < yMin || spanY > yMax) {
+    return splashClipAllOutside;
+  }
+  if (!(spanXMin >= xMin && spanXMax <= xMax &&
+       spanY >= yMin && spanY <= yMax)) {
+    return splashClipPartial;
+  }
+  for (i = 0; i < length; ++i) {
+    if (!scanners[i]->testSpan(xMin, xMax, spanY)) {
+      return splashClipPartial;
+    }
+  }
+  return splashClipAllInside;
+}
diff --git a/pdf/splash/SplashClip.h b/pdf/splash/SplashClip.h
new file mode 100644 (file)
index 0000000..34a4cc5
--- /dev/null
@@ -0,0 +1,88 @@
+//========================================================================
+//
+// SplashClip.h
+//
+//========================================================================
+
+#ifndef SPLASHCLIP_H
+#define SPLASHCLIP_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#include "SplashTypes.h"
+
+class SplashPath;
+class SplashXPath;
+class SplashXPathScanner;
+
+//------------------------------------------------------------------------
+
+enum SplashClipResult {
+  splashClipAllInside,
+  splashClipAllOutside,
+  splashClipPartial
+};
+
+//------------------------------------------------------------------------
+// SplashClip
+//------------------------------------------------------------------------
+
+class SplashClip {
+public:
+
+  // Create a clip, for the given rectangle.
+  SplashClip(SplashCoord x0, SplashCoord y0,
+            SplashCoord x1, SplashCoord y1);
+
+  // Copy a clip.
+  SplashClip *copy() { return new SplashClip(this); }
+
+  ~SplashClip();
+
+  // Reset the clip to a rectangle.
+  void resetToRect(SplashCoord x0, SplashCoord y0,
+                  SplashCoord x1, SplashCoord y1);
+
+  // Intersect the clip with a rectangle.
+  SplashError clipToRect(SplashCoord x0, SplashCoord y0,
+                        SplashCoord x1, SplashCoord y1);
+
+  // Interesect the clip with <path>.
+  SplashError clipToPath(SplashPath *path, SplashCoord flatness,
+                        GBool eo);
+
+  // Returns true if (<x>,<y>) is inside the clip.
+  GBool test(int x, int y);
+
+  // Tests a rectangle against the clipping region.  Returns one of:
+  //   - splashClipAllInside if the entire rectangle is inside the
+  //     clipping region, i.e., all pixels in the rectangle are
+  //     visible
+  //   - splashClipAllOutside if the entire rectangle is outside the
+  //     clipping region, i.e., all the pixels in the rectangle are
+  //     clipped
+  //   - splashClipPartial if the rectangle is part inside and part
+  //     outside the clipping region
+  SplashClipResult testRect(int rectXMin, int rectYMin,
+                           int rectXMax, int rectYMax);
+
+  // Similar to testRect, but tests a horizontal span.
+  SplashClipResult testSpan(int spanXMin, int spanXMax, int spanY);
+
+private:
+
+  SplashClip(SplashClip *clip);
+  void grow(int nPaths);
+
+  int xMin, yMin, xMax, yMax;
+  SplashXPath **paths;
+  Guchar *flags;
+  SplashXPathScanner **scanners;
+  int length, size;
+};
+
+#endif
diff --git a/pdf/splash/SplashErrorCodes.h b/pdf/splash/SplashErrorCodes.h
new file mode 100644 (file)
index 0000000..2a70d4b
--- /dev/null
@@ -0,0 +1,32 @@
+//========================================================================
+//
+// SplashErrorCodes.h
+//
+//========================================================================
+
+#ifndef SPLASHERRORCODES_H
+#define SPLASHERRORCODES_H
+
+#include <aconf.h>
+
+//------------------------------------------------------------------------
+
+#define splashOk                 0     // no error
+
+#define splashErrNoCurPt         1     // no current point
+
+#define splashErrEmptyPath       2     // zero points in path
+
+#define splashErrBogusPath       3     // only one point in subpath
+
+#define splashErrNoSave                 4      // state stack is empty
+
+#define splashErrOpenFile        5     // couldn't open file
+
+#define splashErrNoGlyph         6     // couldn't get the requested glyph
+
+#define splashErrModeMismatch    7     // invalid combination of color modes
+
+#define splashErrSingularMatrix  8     // matrix is singular
+
+#endif
diff --git a/pdf/splash/SplashFTFont.cc b/pdf/splash/SplashFTFont.cc
new file mode 100644 (file)
index 0000000..a7d0396
--- /dev/null
@@ -0,0 +1,288 @@
+//========================================================================
+//
+// SplashFTFont.cc
+//
+//========================================================================
+
+#include <aconf.h>
+
+#if HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include "freetype/ftoutln.h"
+#include "freetype/internal/ftobjs.h" // needed for FT_New_Size decl
+#include "gmem.h"
+#include "SplashMath.h"
+#include "SplashGlyphBitmap.h"
+#include "SplashPath.h"
+#include "SplashFTFontEngine.h"
+#include "SplashFTFontFile.h"
+#include "SplashFTFont.h"
+
+//------------------------------------------------------------------------
+
+static int glyphPathMoveTo(FT_Vector *pt, void *path);
+static int glyphPathLineTo(FT_Vector *pt, void *path);
+static int glyphPathConicTo(FT_Vector *ctrl, FT_Vector *pt, void *path);
+static int glyphPathCubicTo(FT_Vector *ctrl1, FT_Vector *ctrl2,
+                           FT_Vector *pt, void *path);
+
+//------------------------------------------------------------------------
+// SplashFTFont
+//------------------------------------------------------------------------
+
+SplashFTFont::SplashFTFont(SplashFTFontFile *fontFileA, SplashCoord *matA):
+  SplashFont(fontFileA, matA, fontFileA->engine->aa)
+{
+  FT_Face face;
+  SplashCoord size, div;
+  int x, y;
+
+  face = fontFileA->face;
+  if (FT_New_Size(face, &sizeObj)) {
+    return;
+  }
+  face->size = sizeObj;
+  size = splashSqrt(mat[2]*mat[2] + mat[3]*mat[3]);
+  if (FT_Set_Pixel_Sizes(face, 0, (int)size)) {
+    return;
+  }
+
+  div = face->bbox.xMax > 20000 ? 65536 : 1;
+
+  // transform the four corners of the font bounding box -- the min
+  // and max values form the bounding box of the transformed font
+  x = (int)((mat[0] * face->bbox.xMin + mat[2] * face->bbox.yMin) /
+           (div * face->units_per_EM));
+  xMin = xMax = x;
+  y = (int)((mat[1] * face->bbox.xMin + mat[3] * face->bbox.yMin) /
+           (div * face->units_per_EM));
+  yMin = yMax = y;
+  x = (int)((mat[0] * face->bbox.xMin + mat[2] * face->bbox.yMax) /
+           (div * face->units_per_EM));
+  if (x < xMin) {
+    xMin = x;
+  } else if (x > xMax) {
+    xMax = x;
+  }
+  y = (int)((mat[1] * face->bbox.xMin + mat[3] * face->bbox.yMax) /
+           (div * face->units_per_EM));
+  if (y < yMin) {
+    yMin = y;
+  } else if (y > yMax) {
+    yMax = y;
+  }
+  x = (int)((mat[0] * face->bbox.xMax + mat[2] * face->bbox.yMin) /
+           (div * face->units_per_EM));
+  if (x < xMin) {
+    xMin = x;
+  } else if (x > xMax) {
+    xMax = x;
+  }
+  y = (int)((mat[1] * face->bbox.xMax + mat[3] * face->bbox.yMin) /
+           (div * face->units_per_EM));
+  if (y < yMin) {
+    yMin = y;
+  } else if (y > yMax) {
+    yMax = y;
+  }
+  x = (int)((mat[0] * face->bbox.xMax + mat[2] * face->bbox.yMax) /
+           (div * face->units_per_EM));
+  if (x < xMin) {
+    xMin = x;
+  } else if (x > xMax) {
+    xMax = x;
+  }
+  y = (int)((mat[1] * face->bbox.xMax + mat[3] * face->bbox.yMax) /
+           (div * face->units_per_EM));
+  if (y < yMin) {
+    yMin = y;
+  } else if (y > yMax) {
+    yMax = y;
+  }
+  // This is a kludge: some buggy PDF generators embed fonts with
+  // zero bounding boxes.
+  if (xMax == xMin) {
+    xMin = 0;
+    xMax = (int)size;
+  }
+  if (yMax == yMin) {
+    yMin = 0;
+    yMax = (int)(1.2 * size);
+  }
+
+  // compute the transform matrix
+  matrix.xx = (FT_Fixed)((mat[0] / size) * 65536);
+  matrix.yx = (FT_Fixed)((mat[1] / size) * 65536);
+  matrix.xy = (FT_Fixed)((mat[2] / size) * 65536);
+  matrix.yy = (FT_Fixed)((mat[3] / size) * 65536);
+}
+
+SplashFTFont::~SplashFTFont() {
+}
+
+GBool SplashFTFont::getGlyph(int c, int xFrac, int yFrac,
+                            SplashGlyphBitmap *bitmap) {
+  return SplashFont::getGlyph(c, xFrac, 0, bitmap);
+}
+
+GBool SplashFTFont::makeGlyph(int c, int xFrac, int yFrac,
+                             SplashGlyphBitmap *bitmap) {
+  SplashFTFontFile *ff;
+  FT_Vector offset;
+  FT_GlyphSlot slot;
+  FT_UInt gid;
+  int rowSize;
+  Guchar *p, *q;
+  int i;
+
+  ff = (SplashFTFontFile *)fontFile;
+
+  ff->face->size = sizeObj;
+  offset.x = (FT_Pos)(xFrac * splashFontFractionMul * 64);
+  offset.y = 0;
+  FT_Set_Transform(ff->face, &matrix, &offset);
+  slot = ff->face->glyph;
+
+  if (ff->codeToGID && c < ff->codeToGIDLen) {
+    gid = (FT_UInt)ff->codeToGID[c];
+  } else {
+    gid = (FT_UInt)c;
+  }
+
+  // if we have the FT2 bytecode interpreter, autohinting won't be used
+#ifdef TT_CONFIG_OPTION_BYTECODE_INTERPRETER
+  if (FT_Load_Glyph(ff->face, gid,
+                   aa ? FT_LOAD_NO_BITMAP : FT_LOAD_DEFAULT)) {
+    return gFalse;
+  }
+#else
+  // FT2's autohinting doesn't always work very well (especially with
+  // font subsets), so turn it off if anti-aliasing is enabled; if
+  // anti-aliasing is disabled, this seems to be a tossup - some fonts
+  // look better with hinting, some without, so leave hinting on
+  if (FT_Load_Glyph(ff->face, gid,
+                   aa ? FT_LOAD_NO_HINTING | FT_LOAD_NO_BITMAP
+                       : FT_LOAD_DEFAULT)) {
+    return gFalse;
+  }
+#endif
+  if (FT_Render_Glyph(slot, aa ? ft_render_mode_normal
+                              : ft_render_mode_mono)) {
+    return gFalse;
+  }
+
+  bitmap->x = -slot->bitmap_left;
+  bitmap->y = slot->bitmap_top;
+  bitmap->w = slot->bitmap.width;
+  bitmap->h = slot->bitmap.rows;
+  bitmap->aa = aa;
+  if (aa) {
+    rowSize = bitmap->w;
+  } else {
+    rowSize = (bitmap->w + 7) >> 3;
+  }
+  bitmap->data = (Guchar *)gmalloc(rowSize * bitmap->h);
+  bitmap->freeData = gTrue;
+  for (i = 0, p = bitmap->data, q = slot->bitmap.buffer;
+       i < bitmap->h;
+       ++i, p += rowSize, q += slot->bitmap.pitch) {
+    memcpy(p, q, rowSize);
+  }
+
+  return gTrue;
+}
+
+SplashPath *SplashFTFont::getGlyphPath(int c) {
+  static FT_Outline_Funcs outlineFuncs = {
+    &glyphPathMoveTo,
+    &glyphPathLineTo,
+    &glyphPathConicTo,
+    &glyphPathCubicTo,
+    0, 0
+  };
+  SplashFTFontFile *ff;
+  SplashPath *path;
+  FT_GlyphSlot slot;
+  FT_UInt gid;
+  FT_Glyph glyph;
+
+  ff = (SplashFTFontFile *)fontFile;
+  ff->face->size = sizeObj;
+  FT_Set_Transform(ff->face, &matrix, NULL);
+  slot = ff->face->glyph;
+  if (ff->codeToGID && c < ff->codeToGIDLen) {
+    gid = ff->codeToGID[c];
+  } else {
+    gid = (FT_UInt)c;
+  }
+  if (FT_Load_Glyph(ff->face, gid, FT_LOAD_DEFAULT)) {
+    return NULL;
+  }
+  if (FT_Get_Glyph(slot, &glyph)) {
+    return NULL;
+  }
+  path = new SplashPath();
+  FT_Outline_Decompose(&((FT_OutlineGlyph)glyph)->outline,
+                      &outlineFuncs, path);
+  return path;
+}
+
+static int glyphPathMoveTo(FT_Vector *pt, void *path) {
+  ((SplashPath *)path)->moveTo(pt->x / 64.0, -pt->y / 64.0);
+  return 0;
+}
+
+static int glyphPathLineTo(FT_Vector *pt, void *path) {
+  ((SplashPath *)path)->lineTo(pt->x / 64.0, -pt->y / 64.0);
+  return 0;
+}
+
+static int glyphPathConicTo(FT_Vector *ctrl, FT_Vector *pt, void *path) {
+  SplashCoord x0, y0, x1, y1, x2, y2, x3, y3, xc, yc;
+
+  if (!((SplashPath *)path)->getCurPt(&x0, &y0)) {
+    return 0;
+  }
+  xc = ctrl->x / 64.0;
+  yc = -ctrl->y / 64.0;
+  x3 = pt->x / 64.0;
+  y3 = -pt->y / 64.0;
+
+  // A second-order Bezier curve is defined by two endpoints, p0 and
+  // p3, and one control point, pc:
+  //
+  //     p(t) = (1-t)^2*p0 + t*(1-t)*pc + t^2*p3
+  //
+  // A third-order Bezier curve is defined by the same two endpoints,
+  // p0 and p3, and two control points, p1 and p2:
+  //
+  //     p(t) = (1-t)^3*p0 + 3t*(1-t)^2*p1 + 3t^2*(1-t)*p2 + t^3*p3
+  //
+  // Applying some algebra, we can convert a second-order curve to a
+  // third-order curve:
+  //
+  //     p1 = (1/3) * (p0 + 2pc)
+  //     p2 = (1/3) * (2pc + p3)
+
+  x1 = (1.0 / 3.0) * (x0 + 2 * xc);
+  y1 = (1.0 / 3.0) * (y0 + 2 * yc);
+  x2 = (1.0 / 3.0) * (2 * xc + x3);
+  y2 = (1.0 / 3.0) * (2 * yc + y3);
+
+  ((SplashPath *)path)->curveTo(x1, y1, x2, y2, x3, y3);
+  return 0;
+}
+
+static int glyphPathCubicTo(FT_Vector *ctrl1, FT_Vector *ctrl2,
+                           FT_Vector *pt, void *path) {
+  ((SplashPath *)path)->curveTo(ctrl1->x / 64.0, -ctrl1->y / 64.0,
+                               ctrl2->x / 64.0, -ctrl2->y / 64.0,
+                               pt->x / 64.0, -pt->y / 64.0);
+  return 0;
+}
+
+#endif // HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H
diff --git a/pdf/splash/SplashFTFont.h b/pdf/splash/SplashFTFont.h
new file mode 100644 (file)
index 0000000..7628a2b
--- /dev/null
@@ -0,0 +1,54 @@
+//========================================================================
+//
+// SplashFTFont.h
+//
+//========================================================================
+
+#ifndef SPLASHFTFONT_H
+#define SPLASHFTFONT_H
+
+#include <aconf.h>
+
+#if HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#include <freetype/freetype.h>
+#include "SplashFont.h"
+
+class SplashFTFontFile;
+
+//------------------------------------------------------------------------
+// SplashFTFont
+//------------------------------------------------------------------------
+
+class SplashFTFont: public SplashFont {
+public:
+
+  SplashFTFont(SplashFTFontFile *fontFileA, SplashCoord *matA);
+
+  virtual ~SplashFTFont();
+
+  // Munge xFrac and yFrac before calling SplashFont::getGlyph.
+  virtual GBool getGlyph(int c, int xFrac, int yFrac,
+                        SplashGlyphBitmap *bitmap);
+
+  // Rasterize a glyph.  The <xFrac> and <yFrac> values are the same
+  // as described for getGlyph.
+  virtual GBool makeGlyph(int c, int xFrac, int yFrac,
+                         SplashGlyphBitmap *bitmap);
+
+  // Return the path for a glyph.
+  virtual SplashPath *getGlyphPath(int c);
+
+private:
+
+  FT_Size sizeObj;
+  FT_Matrix matrix;
+};
+
+#endif // HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H
+
+#endif
diff --git a/pdf/splash/SplashFTFontEngine.cc b/pdf/splash/SplashFTFontEngine.cc
new file mode 100644 (file)
index 0000000..64dbc75
--- /dev/null
@@ -0,0 +1,134 @@
+//========================================================================
+//
+// SplashFTFontEngine.cc
+//
+//========================================================================
+
+#include <aconf.h>
+
+#if HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <stdio.h>
+#ifndef WIN32
+#  include <unistd.h>
+#endif
+#include "gmem.h"
+#include "GString.h"
+#include "gfile.h"
+#include "FoFiTrueType.h"
+#include "FoFiType1C.h"
+#include "SplashFTFontFile.h"
+#include "SplashFTFontEngine.h"
+
+#ifdef VMS
+#if (__VMS_VER < 70000000)
+extern "C" int unlink(char *filename);
+#endif
+#endif
+
+//------------------------------------------------------------------------
+
+static void fileWrite(void *stream, char *data, int len) {
+  fwrite(data, 1, len, (FILE *)stream);
+}
+
+//------------------------------------------------------------------------
+// SplashFTFontEngine
+//------------------------------------------------------------------------
+
+SplashFTFontEngine::SplashFTFontEngine(GBool aaA, FT_Library libA) {
+  aa = aaA;
+  lib = libA;
+}
+
+SplashFTFontEngine *SplashFTFontEngine::init(GBool aaA) {
+  FT_Library libA;
+
+  if (FT_Init_FreeType(&libA)) {
+    return NULL;
+  }
+  return new SplashFTFontEngine(aaA, libA);
+}
+
+SplashFTFontEngine::~SplashFTFontEngine() {
+  FT_Done_FreeType(lib);
+}
+
+SplashFontFile *SplashFTFontEngine::loadType1Font(SplashFontFileID *idA,
+                                                 char *fileName,
+                                                 GBool deleteFile,
+                                                 char **enc) {
+  return SplashFTFontFile::loadType1Font(this, idA, fileName, deleteFile, enc);
+}
+
+SplashFontFile *SplashFTFontEngine::loadType1CFont(SplashFontFileID *idA,
+                                                  char *fileName,
+                                                  GBool deleteFile,
+                                                  char **enc) {
+  return SplashFTFontFile::loadType1Font(this, idA, fileName, deleteFile, enc);
+}
+
+SplashFontFile *SplashFTFontEngine::loadCIDFont(SplashFontFileID *idA,
+                                               char *fileName,
+                                               GBool deleteFile) {
+  FoFiType1C *ff;
+  Gushort *cidToGIDMap;
+  int nCIDs;
+  SplashFontFile *ret;
+
+  // check for a CFF font
+  if ((ff = FoFiType1C::load(fileName))) {
+    cidToGIDMap = ff->getCIDToGIDMap(&nCIDs);
+    delete ff;
+  } else {
+    cidToGIDMap = NULL;
+    nCIDs = 0;
+  }
+  ret = SplashFTFontFile::loadCIDFont(this, idA, fileName, deleteFile,
+                                     cidToGIDMap, nCIDs);
+  if (!ret) {
+    gfree(cidToGIDMap);
+  }
+  return ret;
+}
+
+SplashFontFile *SplashFTFontEngine::loadTrueTypeFont(SplashFontFileID *idA,
+                                                    char *fileName,
+                                                    GBool deleteFile,
+                                                    Gushort *codeToGID,
+                                                    int codeToGIDLen) {
+  FoFiTrueType *ff;
+  GString *tmpFileName;
+  FILE *tmpFile;
+  SplashFontFile *ret;
+
+  if (!(ff = FoFiTrueType::load(fileName))) {
+    return NULL;
+  }
+  tmpFileName = NULL;
+  if (!openTempFile(&tmpFileName, &tmpFile, "wb", NULL)) {
+    delete ff;
+    return NULL;
+  }
+  ff->writeTTF(&fileWrite, tmpFile);
+  delete ff;
+  fclose(tmpFile);
+  ret = SplashFTFontFile::loadTrueTypeFont(this, idA,
+                                          tmpFileName->getCString(),
+                                          gTrue, codeToGID, codeToGIDLen);
+  if (ret) {
+    if (deleteFile) {
+      unlink(fileName);
+    }
+  } else {
+    unlink(tmpFileName->getCString());
+  }
+  delete tmpFileName;
+  return ret;
+}
+
+#endif // HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H
diff --git a/pdf/splash/SplashFTFontEngine.h b/pdf/splash/SplashFTFontEngine.h
new file mode 100644 (file)
index 0000000..0497a46
--- /dev/null
@@ -0,0 +1,59 @@
+//========================================================================
+//
+// SplashFTFontEngine.h
+//
+//========================================================================
+
+#ifndef SPLASHFTFONTENGINE_H
+#define SPLASHFTFONTENGINE_H
+
+#include <aconf.h>
+
+#if HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#include <freetype/freetype.h>
+#include "gtypes.h"
+
+class SplashFontFile;
+class SplashFontFileID;
+
+//------------------------------------------------------------------------
+// SplashFTFontEngine
+//------------------------------------------------------------------------
+
+class SplashFTFontEngine {
+public:
+
+  static SplashFTFontEngine *init(GBool aaA);
+
+  ~SplashFTFontEngine();
+
+  // Load fonts.
+  SplashFontFile *loadType1Font(SplashFontFileID *idA, char *fileName,
+                               GBool deleteFile, char **enc);
+  SplashFontFile *loadType1CFont(SplashFontFileID *idA, char *fileName,
+                                GBool deleteFile, char **enc);
+  SplashFontFile *loadCIDFont(SplashFontFileID *idA, char *fileName,
+                             GBool deleteFile);
+  SplashFontFile *loadTrueTypeFont(SplashFontFileID *idA, char *fileName,
+                                  GBool deleteFile,
+                                  Gushort *codeToGID, int codeToGIDLen);
+
+private:
+
+  SplashFTFontEngine(GBool aaA, FT_Library libA);
+
+  GBool aa;
+  FT_Library lib;
+
+  friend class SplashFTFontFile;
+  friend class SplashFTFont;
+};
+
+#endif // HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H
+
+#endif
diff --git a/pdf/splash/SplashFTFontFile.cc b/pdf/splash/SplashFTFontFile.cc
new file mode 100644 (file)
index 0000000..a750966
--- /dev/null
@@ -0,0 +1,111 @@
+//========================================================================
+//
+// SplashFTFontFile.cc
+//
+//========================================================================
+
+#include <aconf.h>
+
+#if HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include "gmem.h"
+#include "SplashFTFontEngine.h"
+#include "SplashFTFont.h"
+#include "SplashFTFontFile.h"
+
+//------------------------------------------------------------------------
+// SplashFTFontFile
+//------------------------------------------------------------------------
+
+SplashFontFile *SplashFTFontFile::loadType1Font(SplashFTFontEngine *engineA,
+                                               SplashFontFileID *idA,
+                                               char *fileNameA,
+                                               GBool deleteFileA,
+                                               char **encA) {
+  FT_Face faceA;
+  Gushort *codeToGIDA;
+  char *name;
+  int i;
+
+  if (FT_New_Face(engineA->lib, fileNameA, 0, &faceA)) {
+    return NULL;
+  }
+  codeToGIDA = (Gushort *)gmalloc(256 * sizeof(int));
+  for (i = 0; i < 256; ++i) {
+    codeToGIDA[i] = 0;
+    if ((name = encA[i])) {
+      codeToGIDA[i] = (Gushort)FT_Get_Name_Index(faceA, name);
+    }
+  }
+
+  return new SplashFTFontFile(engineA, idA, fileNameA, deleteFileA,
+                             faceA, codeToGIDA, 256);
+}
+
+SplashFontFile *SplashFTFontFile::loadCIDFont(SplashFTFontEngine *engineA,
+                                             SplashFontFileID *idA,
+                                             char *fileNameA,
+                                             GBool deleteFileA,
+                                             Gushort *codeToGIDA,
+                                             int codeToGIDLenA) {
+  FT_Face faceA;
+
+  if (FT_New_Face(engineA->lib, fileNameA, 0, &faceA)) {
+    return NULL;
+  }
+
+  return new SplashFTFontFile(engineA, idA, fileNameA, deleteFileA,
+                             faceA, codeToGIDA, codeToGIDLenA);
+}
+
+SplashFontFile *SplashFTFontFile::loadTrueTypeFont(SplashFTFontEngine *engineA,
+                                                  SplashFontFileID *idA,
+                                                  char *fileNameA,
+                                                  GBool deleteFileA,
+                                                  Gushort *codeToGIDA,
+                                                  int codeToGIDLenA) {
+  FT_Face faceA;
+
+  if (FT_New_Face(engineA->lib, fileNameA, 0, &faceA)) {
+    return NULL;
+  }
+
+  return new SplashFTFontFile(engineA, idA, fileNameA, deleteFileA,
+                             faceA, codeToGIDA, codeToGIDLenA);
+}
+
+SplashFTFontFile::SplashFTFontFile(SplashFTFontEngine *engineA,
+                                  SplashFontFileID *idA,
+                                  char *fileNameA, GBool deleteFileA,
+                                  FT_Face faceA,
+                                  Gushort *codeToGIDA, int codeToGIDLenA):
+  SplashFontFile(idA, fileNameA, deleteFileA)
+{
+  engine = engineA;
+  face = faceA;
+  codeToGID = codeToGIDA;
+  codeToGIDLen = codeToGIDLenA;
+}
+
+SplashFTFontFile::~SplashFTFontFile() {
+  if (face) {
+    FT_Done_Face(face);
+  }
+  if (codeToGID) {
+    gfree(codeToGID);
+  }
+}
+
+SplashFont *SplashFTFontFile::makeFont(SplashCoord *mat) {
+  SplashFont *font;
+
+  font = new SplashFTFont(this, mat);
+  font->initCache();
+  return font;
+}
+
+#endif // HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H
diff --git a/pdf/splash/SplashFTFontFile.h b/pdf/splash/SplashFTFontFile.h
new file mode 100644 (file)
index 0000000..522d055
--- /dev/null
@@ -0,0 +1,69 @@
+//========================================================================
+//
+// SplashFTFontFile.h
+//
+//========================================================================
+
+#ifndef SPLASHFTFONTFILE_H
+#define SPLASHFTFONTFILE_H
+
+#include <aconf.h>
+
+#if HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#include <freetype/freetype.h>
+#include "SplashFontFile.h"
+
+class SplashFontFileID;
+class SplashFTFontEngine;
+
+//------------------------------------------------------------------------
+// SplashFTFontFile
+//------------------------------------------------------------------------
+
+class SplashFTFontFile: public SplashFontFile {
+public:
+
+  static SplashFontFile *loadType1Font(SplashFTFontEngine *engineA,
+                                      SplashFontFileID *idA, char *fileNameA,
+                                      GBool deleteFileA, char **encA);
+  static SplashFontFile *loadCIDFont(SplashFTFontEngine *engineA,
+                                    SplashFontFileID *idA, char *fileNameA,
+                                    GBool deleteFileA,
+                                    Gushort *codeToCIDA, int codeToGIDLenA);
+  static SplashFontFile *loadTrueTypeFont(SplashFTFontEngine *engineA,
+                                         SplashFontFileID *idA,
+                                         char *fileNameA,
+                                         GBool deleteFileA,
+                                         Gushort *codeToGIDA,
+                                         int codeToGIDLenA);
+
+  virtual ~SplashFTFontFile();
+
+  // Create a new SplashFTFont, i.e., a scaled instance of this font
+  // file.
+  virtual SplashFont *makeFont(SplashCoord *mat);
+
+private:
+
+  SplashFTFontFile(SplashFTFontEngine *engineA,
+                  SplashFontFileID *idA,
+                  char *fileNameA, GBool deleteFileA,
+                  FT_Face faceA,
+                  Gushort *codeToGIDA, int codeToGIDLenA);
+
+  SplashFTFontEngine *engine;
+  FT_Face face;
+  Gushort *codeToGID;
+  int codeToGIDLen;
+
+  friend class SplashFTFont;
+};
+
+#endif // HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H
+
+#endif
diff --git a/pdf/splash/SplashFont.cc b/pdf/splash/SplashFont.cc
new file mode 100644 (file)
index 0000000..461c981
--- /dev/null
@@ -0,0 +1,166 @@
+//========================================================================
+//
+// SplashFont.cc
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <string.h>
+#include "gmem.h"
+#include "SplashMath.h"
+#include "SplashGlyphBitmap.h"
+#include "SplashFontFile.h"
+#include "SplashFont.h"
+
+//------------------------------------------------------------------------
+
+struct SplashFontCacheTag {
+  int c;
+  short xFrac, yFrac;          // x and y fractions
+  int mru;                     // valid bit (0x80000000) and MRU index
+  int x, y, w, h;              // offset and size of glyph
+};
+
+//------------------------------------------------------------------------
+// SplashFont
+//------------------------------------------------------------------------
+
+SplashFont::SplashFont(SplashFontFile *fontFileA, SplashCoord *matA,
+                      GBool aaA) {
+  fontFile = fontFileA;
+  fontFile->incRefCnt();
+  mat[0] = matA[0];
+  mat[1] = matA[1];
+  mat[2] = matA[2];
+  mat[3] = matA[3];
+  aa = aaA;
+
+  cache = NULL;
+  cacheTags = NULL;
+
+  xMin = yMin = xMax = yMax = 0;
+}
+
+void SplashFont::initCache() {
+  int i;
+
+  // this should be (max - min + 1), but we add some padding to
+  // deal with rounding errors
+  glyphW = xMax - xMin + 3;
+  glyphH = yMax - yMin + 3;
+  if (aa) {
+    glyphSize = glyphW * glyphH;
+  } else {
+    glyphSize = ((glyphW + 7) >> 3) * glyphH;
+  }
+
+  // set up the glyph pixmap cache
+  cacheAssoc = 8;
+  if (glyphSize <= 256) {
+    cacheSets = 8;
+  } else if (glyphSize <= 512) {
+    cacheSets = 4;
+  } else if (glyphSize <= 1024) {
+    cacheSets = 2;
+  } else {
+    cacheSets = 1;
+  }
+  cache = (Guchar *)gmalloc(cacheSets * cacheAssoc * glyphSize);
+  cacheTags = (SplashFontCacheTag *)gmalloc(cacheSets * cacheAssoc *
+                                             sizeof(SplashFontCacheTag));
+  for (i = 0; i < cacheSets * cacheAssoc; ++i) {
+    cacheTags[i].mru = i & (cacheAssoc - 1);
+  }
+}
+
+SplashFont::~SplashFont() {
+  fontFile->decRefCnt();
+  if (cache) {
+    gfree(cache);
+  }
+  if (cacheTags) {
+    gfree(cacheTags);
+  }
+}
+
+GBool SplashFont::getGlyph(int c, int xFrac, int yFrac,
+                          SplashGlyphBitmap *bitmap) {
+  SplashGlyphBitmap bitmap2;
+  int size;
+  Guchar *p;
+  int i, j, k;
+
+  // check the cache
+  i = (c & (cacheSets - 1)) * cacheAssoc;
+  for (j = 0; j < cacheAssoc; ++j) {
+    if ((cacheTags[i+j].mru & 0x80000000) &&
+       cacheTags[i+j].c == c &&
+       (int)cacheTags[i+j].xFrac == xFrac &&
+       (int)cacheTags[i+j].yFrac == yFrac) {
+      bitmap->x = cacheTags[i+j].x;
+      bitmap->y = cacheTags[i+j].y;
+      bitmap->w = cacheTags[i+j].w;
+      bitmap->h = cacheTags[i+j].h;
+      for (k = 0; k < cacheAssoc; ++k) {
+       if (k != j &&
+           (cacheTags[i+k].mru & 0x7fffffff) <
+             (cacheTags[i+j].mru & 0x7fffffff)) {
+         ++cacheTags[i+k].mru;
+       }
+      }
+      cacheTags[i+j].mru = 0x80000000;
+      bitmap->aa = aa;
+      bitmap->data = cache + (i+j) * glyphSize;
+      bitmap->freeData = gFalse;
+      return gTrue;
+    }
+  }
+
+  // generate the glyph bitmap
+  if (!makeGlyph(c, xFrac, yFrac, &bitmap2)) {
+    return gFalse;
+  }
+
+  // if the glyph doesn't fit in the bounding box, return a temporary
+  // uncached bitmap
+  if (bitmap2.w > glyphW || bitmap2.h > glyphH) {
+    *bitmap = bitmap2;
+    return gTrue;
+  }
+
+  // insert glyph pixmap in cache
+  if (aa) {
+    size = bitmap2.w * bitmap2.h;
+  } else {
+    size = ((bitmap2.w + 7) >> 3) * bitmap2.h;
+  }
+  p = NULL; // make gcc happy
+  for (j = 0; j < cacheAssoc; ++j) {
+    if ((cacheTags[i+j].mru & 0x7fffffff) == cacheAssoc - 1) {
+      cacheTags[i+j].mru = 0x80000000;
+      cacheTags[i+j].c = c;
+      cacheTags[i+j].xFrac = (short)xFrac;
+      cacheTags[i+j].yFrac = (short)yFrac;
+      cacheTags[i+j].x = bitmap2.x;
+      cacheTags[i+j].y = bitmap2.y;
+      cacheTags[i+j].w = bitmap2.w;
+      cacheTags[i+j].h = bitmap2.h;
+      p = cache + (i+j) * glyphSize;
+      memcpy(p, bitmap2.data, size);
+    } else {
+      ++cacheTags[i+j].mru;
+    }
+  }
+  *bitmap = bitmap2;
+  bitmap->data = p;
+  bitmap->freeData = gFalse;
+  if (bitmap2.freeData) {
+    gfree(bitmap2.data);
+  }
+  return gTrue;
+}
diff --git a/pdf/splash/SplashFont.h b/pdf/splash/SplashFont.h
new file mode 100644 (file)
index 0000000..49e36c2
--- /dev/null
@@ -0,0 +1,89 @@
+//========================================================================
+//
+// SplashFont.h
+//
+//========================================================================
+
+#ifndef SPLASHFONT_H
+#define SPLASHFONT_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#include "gtypes.h"
+#include "SplashTypes.h"
+
+struct SplashGlyphBitmap;
+struct SplashFontCacheTag;
+class SplashFontFile;
+class SplashPath;
+
+//------------------------------------------------------------------------
+
+// Fractional positioning uses this many bits to the right of the
+// decimal points.
+#define splashFontFractionBits 2
+#define splashFontFraction     (1 << splashFontFractionBits)
+#define splashFontFractionMul  (1 / (SplashCoord)splashFontFraction)
+
+//------------------------------------------------------------------------
+// SplashFont
+//------------------------------------------------------------------------
+
+class SplashFont {
+public:
+
+  SplashFont(SplashFontFile *fontFileA, SplashCoord *matA, GBool aaA);
+
+  // This must be called after the constructor, so that the subclass
+  // constructor has a chance to compute the bbox.
+  void initCache();
+
+  virtual ~SplashFont();
+
+  SplashFontFile *getFontFile() { return fontFile; }
+
+  // Return true if <this> matches the specified font file and matrix.
+  GBool matches(SplashFontFile *fontFileA, SplashCoord *matA) {
+    return fontFileA == fontFile &&
+           matA[0] == mat[0] && matA[1] == mat[1] &&
+           matA[2] == mat[2] && matA[3] == mat[3];
+  }
+
+  // Get a glyph - this does a cache lookup first, and if not found,
+  // creates a new bitmap and adds it to the cache.  The <xFrac> and
+  // <yFrac> values are splashFontFractionBits bits each, representing
+  // the numerators of fractions in [0, 1), where the denominator is
+  // splashFontFraction = 1 << splashFontFractionBits.  Subclasses
+  // should override this to zero out xFrac and/or yFrac if they don't
+  // support fractional coordinates.
+  virtual GBool getGlyph(int c, int xFrac, int yFrac,
+                        SplashGlyphBitmap *bitmap);
+
+  // Rasterize a glyph.  The <xFrac> and <yFrac> values are the same
+  // as described for getGlyph.
+  virtual GBool makeGlyph(int c, int xFrac, int yFrac,
+                         SplashGlyphBitmap *bitmap) = 0;
+
+  // Return the path for a glyph.
+  virtual SplashPath *getGlyphPath(int c) = 0;
+
+protected:
+
+  SplashFontFile *fontFile;
+  SplashCoord mat[4];          // font transform matrix
+  GBool aa;                    // anti-aliasing
+  int xMin, yMin, xMax, yMax;  // glyph bounding box
+  Guchar *cache;               // glyph bitmap cache
+  SplashFontCacheTag *         // cache tags
+    cacheTags;
+  int glyphW, glyphH;          // size of glyph bitmaps
+  int glyphSize;               // size of glyph bitmaps, in bytes
+  int cacheSets;               // number of sets in cache
+  int cacheAssoc;              // cache associativity (glyphs per set)
+};
+
+#endif
diff --git a/pdf/splash/SplashFontEngine.cc b/pdf/splash/SplashFontEngine.cc
new file mode 100644 (file)
index 0000000..b8aef70
--- /dev/null
@@ -0,0 +1,245 @@
+//========================================================================
+//
+// SplashFontEngine.cc
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#if HAVE_T1LIB_H
+#include <t1lib.h>
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#ifndef WIN32
+#  include <unistd.h>
+#endif
+#include "gmem.h"
+#include "GString.h"
+#include "SplashT1FontEngine.h"
+#include "SplashFTFontEngine.h"
+#include "SplashFontFile.h"
+#include "SplashFontFileID.h"
+#include "SplashFont.h"
+#include "SplashFontEngine.h"
+
+#ifdef VMS
+#if (__VMS_VER < 70000000)
+extern "C" int unlink(char *filename);
+#endif
+#endif
+
+//------------------------------------------------------------------------
+// SplashFontEngine
+//------------------------------------------------------------------------
+
+SplashFontEngine::SplashFontEngine(
+#if HAVE_T1LIB_H
+                                  GBool enableT1lib,
+#endif
+#if HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H
+                                  GBool enableFreeType,
+#endif
+                                  GBool aa) {
+  int i;
+
+  for (i = 0; i < splashFontCacheSize; ++i) {
+    fontCache[i] = NULL;
+  }
+
+#if HAVE_T1LIB_H
+  if (enableT1lib) {
+    t1Engine = SplashT1FontEngine::init(aa);
+  } else {
+    t1Engine = NULL;
+  }
+#endif
+#if HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H
+  if (enableFreeType) {
+    ftEngine = SplashFTFontEngine::init(aa);
+  } else {
+    ftEngine = NULL;
+  }
+#endif
+}
+
+SplashFontEngine::~SplashFontEngine() {
+  int i;
+
+  for (i = 0; i < splashFontCacheSize; ++i) {
+    if (fontCache[i]) {
+      delete fontCache[i];
+    }
+  }
+
+#if HAVE_T1LIB_H
+  if (t1Engine) {
+    delete t1Engine;
+  }
+#endif
+#if HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H
+  if (ftEngine) {
+    delete ftEngine;
+  }
+#endif
+}
+
+SplashFontFile *SplashFontEngine::getFontFile(SplashFontFileID *id) {
+  SplashFontFile *fontFile;
+  int i;
+
+  for (i = 0; i < splashFontCacheSize; ++i) {
+    if (fontCache[i]) {
+      fontFile = fontCache[i]->getFontFile();
+      if (fontFile && fontFile->getID()->matches(id)) {
+       return fontFile;
+      }
+    }
+  }
+  return NULL;
+}
+
+SplashFontFile *SplashFontEngine::loadType1Font(SplashFontFileID *idA,
+                                               char *fileName,
+                                               GBool deleteFile, char **enc) {
+  SplashFontFile *fontFile;
+
+  fontFile = NULL;
+#if HAVE_T1LIB_H
+  if (!fontFile && t1Engine) {
+    fontFile = t1Engine->loadType1Font(idA, fileName, deleteFile, enc);
+  }
+#endif
+#if HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H
+  if (!fontFile && ftEngine) {
+    fontFile = ftEngine->loadType1Font(idA, fileName, deleteFile, enc);
+  }
+#endif
+
+  // delete the (temporary) font file -- with Unix hard link
+  // semantics, this will remove the last link; otherwise it will
+  // return an error, leaving the file to be deleted later (if
+  // loadXYZFont failed, the file will always be deleted)
+  if (deleteFile) {
+    unlink(fontFile ? fontFile->fileName->getCString() : fileName);
+  }
+
+  return fontFile;
+}
+
+SplashFontFile *SplashFontEngine::loadType1CFont(SplashFontFileID *idA,
+                                                char *fileName,
+                                                GBool deleteFile,
+                                                char **enc) {
+  SplashFontFile *fontFile;
+
+  fontFile = NULL;
+#if HAVE_T1LIB_H
+  if (!fontFile && t1Engine) {
+    fontFile = t1Engine->loadType1CFont(idA, fileName, deleteFile, enc);
+  }
+#endif
+#if HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H
+  if (!fontFile && ftEngine) {
+    fontFile = ftEngine->loadType1CFont(idA, fileName, deleteFile, enc);
+  }
+#endif
+
+  // delete the (temporary) font file -- with Unix hard link
+  // semantics, this will remove the last link; otherwise it will
+  // return an error, leaving the file to be deleted later (if
+  // loadXYZFont failed, the file will always be deleted)
+  if (deleteFile) {
+    unlink(fontFile ? fontFile->fileName->getCString() : fileName);
+  }
+
+  return fontFile;
+}
+
+SplashFontFile *SplashFontEngine::loadCIDFont(SplashFontFileID *idA,
+                                             char *fileName,
+                                             GBool deleteFile) {
+  SplashFontFile *fontFile;
+
+  fontFile = NULL;
+#if HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H
+  if (!fontFile && ftEngine) {
+    fontFile = ftEngine->loadCIDFont(idA, fileName, deleteFile);
+  }
+#endif
+
+  // delete the (temporary) font file -- with Unix hard link
+  // semantics, this will remove the last link; otherwise it will
+  // return an error, leaving the file to be deleted later (if
+  // loadXYZFont failed, the file will always be deleted)
+  if (deleteFile) {
+    unlink(fontFile ? fontFile->fileName->getCString() : fileName);
+  }
+
+  return fontFile;
+}
+
+SplashFontFile *SplashFontEngine::loadTrueTypeFont(SplashFontFileID *idA,
+                                                  char *fileName,
+                                                  GBool deleteFile,
+                                                  Gushort *codeToGID,
+                                                  int codeToGIDLen) {
+  SplashFontFile *fontFile;
+
+  fontFile = NULL;
+#if HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H
+  if (!fontFile && ftEngine) {
+    fontFile = ftEngine->loadTrueTypeFont(idA, fileName, deleteFile,
+                                         codeToGID, codeToGIDLen);
+  }
+#endif
+
+  if (!fontFile) {
+    gfree(codeToGID);
+  }
+
+  // delete the (temporary) font file -- with Unix hard link
+  // semantics, this will remove the last link; otherwise it will
+  // return an error, leaving the file to be deleted later (if
+  // loadXYZFont failed, the file will always be deleted)
+  if (deleteFile) {
+    unlink(fontFile ? fontFile->fileName->getCString() : fileName);
+  }
+
+  return fontFile;
+}
+
+SplashFont *SplashFontEngine::getFont(SplashFontFile *fontFile,
+                                     SplashCoord *mat) {
+  SplashFont *font;
+  int i, j;
+
+  font = fontCache[0];
+  if (font && font->matches(fontFile, mat)) {
+    return font;
+  }
+  for (i = 1; i < splashFontCacheSize; ++i) {
+    font = fontCache[i];
+    if (font && font->matches(fontFile, mat)) {
+      for (j = i; j > 0; --j) {
+       fontCache[j] = fontCache[j-1];
+      }
+      fontCache[0] = font;
+      return font;
+    }
+  }
+  font = fontFile->makeFont(mat);
+  if (fontCache[splashFontCacheSize - 1]) {
+    delete fontCache[splashFontCacheSize - 1];
+  }
+  for (j = splashFontCacheSize - 1; j > 0; --j) {
+    fontCache[j] = fontCache[j-1];
+  }
+  fontCache[0] = font;
+  return font;
+}
diff --git a/pdf/splash/SplashFontEngine.h b/pdf/splash/SplashFontEngine.h
new file mode 100644 (file)
index 0000000..2548221
--- /dev/null
@@ -0,0 +1,85 @@
+//========================================================================
+//
+// SplashFontEngine.h
+//
+//========================================================================
+
+#ifndef SPLASHFONTENGINE_H
+#define SPLASHFONTENGINE_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#include "gtypes.h"
+
+class SplashT1FontEngine;
+class SplashFTFontEngine;
+class SplashDTFontEngine;
+class SplashFontFile;
+class SplashFontFileID;
+class SplashFont;
+
+//------------------------------------------------------------------------
+
+#define splashFontCacheSize 16
+
+//------------------------------------------------------------------------
+// SplashFontEngine
+//------------------------------------------------------------------------
+
+class SplashFontEngine {
+public:
+
+  // Create a font engine.
+  SplashFontEngine(
+#if HAVE_T1LIB_H
+                  GBool enableT1lib,
+#endif
+#if HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H
+                  GBool enableFreeType,
+#endif
+                  GBool aa);
+
+  ~SplashFontEngine();
+
+  // Get a font file from the cache.  Returns NULL if there is no
+  // matching entry in the cache.
+  SplashFontFile *getFontFile(SplashFontFileID *id);
+
+  // Load fonts - these create new SplashFontFile objects.
+  SplashFontFile *loadType1Font(SplashFontFileID *idA, char *fileName,
+                               GBool deleteFile, char **enc);
+  SplashFontFile *loadType1CFont(SplashFontFileID *idA, char *fileName,
+                                GBool deleteFile, char **enc);
+  SplashFontFile *loadCIDFont(SplashFontFileID *idA, char *fileName,
+                             GBool deleteFile);
+  SplashFontFile *loadTrueTypeFont(SplashFontFileID *idA, char *fileName,
+                                  GBool deleteFile,
+                                  Gushort *codeToGID, int codeToGIDLen);
+
+  // Get a font - this does a cache lookup first, and if not found,
+  // creates a new SplashFont object and adds it to the cache.  The
+  // matrix:
+  //    [ mat[0] mat[1] ]
+  //    [ mat[2] mat[3] ]
+  // specifies the font transform in PostScript style:
+  //    [x' y'] = [x y] * mat
+  // Note that the Splash y axis points downward.
+  SplashFont *getFont(SplashFontFile *fontFile, SplashCoord *mat);
+
+private:
+
+  SplashFont *fontCache[splashFontCacheSize];
+
+#if HAVE_T1LIB_H
+  SplashT1FontEngine *t1Engine;
+#endif
+#if HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H
+  SplashFTFontEngine *ftEngine;
+#endif
+};
+
+#endif
diff --git a/pdf/splash/SplashFontFile.cc b/pdf/splash/SplashFontFile.cc
new file mode 100644 (file)
index 0000000..acbc12a
--- /dev/null
@@ -0,0 +1,55 @@
+//========================================================================
+//
+// SplashFontFile.cc
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <stdio.h>
+#ifndef WIN32
+#  include <unistd.h>
+#endif
+#include "GString.h"
+#include "SplashFontFile.h"
+#include "SplashFontFileID.h"
+
+#ifdef VMS
+#if (__VMS_VER < 70000000)
+extern "C" int unlink(char *filename);
+#endif
+#endif
+
+//------------------------------------------------------------------------
+// SplashFontFile
+//------------------------------------------------------------------------
+
+SplashFontFile::SplashFontFile(SplashFontFileID *idA, char *fileNameA,
+                              GBool deleteFileA) {
+  id = idA;
+  fileName = new GString(fileNameA);
+  deleteFile = deleteFileA;
+  refCnt = 0;
+}
+
+SplashFontFile::~SplashFontFile() {
+  if (deleteFile) {
+    unlink(fileName->getCString());
+  }
+  delete fileName;
+  delete id;
+}
+
+void SplashFontFile::incRefCnt() {
+  ++refCnt;
+}
+
+void SplashFontFile::decRefCnt() {
+  if (!--refCnt) {
+    delete this;
+  }
+}
diff --git a/pdf/splash/SplashFontFile.h b/pdf/splash/SplashFontFile.h
new file mode 100644 (file)
index 0000000..2fe1def
--- /dev/null
@@ -0,0 +1,60 @@
+//========================================================================
+//
+// SplashFontFile.h
+//
+//========================================================================
+
+#ifndef SPLASHFONTFILE_H
+#define SPLASHFONTFILE_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#include "gtypes.h"
+#include "SplashTypes.h"
+
+class GString;
+class SplashFontEngine;
+class SplashFont;
+class SplashFontFileID;
+
+//------------------------------------------------------------------------
+// SplashFontFile
+//------------------------------------------------------------------------
+
+class SplashFontFile {
+public:
+
+  virtual ~SplashFontFile();
+
+  // Create a new SplashFont, i.e., a scaled instance of this font
+  // file.
+  virtual SplashFont *makeFont(SplashCoord *mat) = 0;
+
+  // Get the font file ID.
+  SplashFontFileID *getID() { return id; }
+
+  // Increment the reference count.
+  void incRefCnt();
+
+  // Decrement the reference count.  If the new value is zero, delete
+  // the SplashFontFile object.
+  void decRefCnt();
+
+protected:
+
+  SplashFontFile(SplashFontFileID *idA, char *fileNameA,
+                GBool deleteFileA);
+
+  SplashFontFileID *id;
+  GString *fileName;
+  GBool deleteFile;
+  int refCnt;
+
+  friend class SplashFontEngine;
+};
+
+#endif
diff --git a/pdf/splash/SplashFontFileID.cc b/pdf/splash/SplashFontFileID.cc
new file mode 100644 (file)
index 0000000..af37cb2
--- /dev/null
@@ -0,0 +1,23 @@
+//========================================================================
+//
+// SplashFontFileID.cc
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include "SplashFontFileID.h"
+
+//------------------------------------------------------------------------
+// SplashFontFileID
+//------------------------------------------------------------------------
+
+SplashFontFileID::SplashFontFileID() {
+}
+
+SplashFontFileID::~SplashFontFileID() {
+}
diff --git a/pdf/splash/SplashFontFileID.h b/pdf/splash/SplashFontFileID.h
new file mode 100644 (file)
index 0000000..bed11d3
--- /dev/null
@@ -0,0 +1,30 @@
+//========================================================================
+//
+// SplashFontFileID.h
+//
+//========================================================================
+
+#ifndef SPLASHFONTFILEID_H
+#define SPLASHFONTFILEID_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#include "gtypes.h"
+
+//------------------------------------------------------------------------
+// SplashFontFileID
+//------------------------------------------------------------------------
+
+class SplashFontFileID {
+public:
+
+  SplashFontFileID();
+  virtual ~SplashFontFileID();
+  virtual GBool matches(SplashFontFileID *id) = 0;
+};
+
+#endif
diff --git a/pdf/splash/SplashGlyphBitmap.h b/pdf/splash/SplashGlyphBitmap.h
new file mode 100644 (file)
index 0000000..044ba4a
--- /dev/null
@@ -0,0 +1,26 @@
+//========================================================================
+//
+// SplashGlyphBitmap.h
+//
+//========================================================================
+
+#ifndef SPLASHGLYPHBITMAP_H
+#define SPLASHGLYPHBITMAP_H
+
+#include <aconf.h>
+
+#include "gtypes.h"
+
+//------------------------------------------------------------------------
+// SplashGlyphBitmap
+//------------------------------------------------------------------------
+
+struct SplashGlyphBitmap {
+  int x, y, w, h;              // offset and size of glyph
+  GBool aa;                    // anti-aliased: true means 8-bit alpha
+                               //   bitmap; false means 1-bit
+  Guchar *data;                        // bitmap data
+  GBool freeData;              // true if data memory should be freed
+};
+
+#endif
diff --git a/pdf/splash/SplashMath.h b/pdf/splash/SplashMath.h
new file mode 100644 (file)
index 0000000..f1bde14
--- /dev/null
@@ -0,0 +1,46 @@
+//========================================================================
+//
+// SplashMath.h
+//
+//========================================================================
+
+#ifndef SPLASHMATH_H
+#define SPLASHMATH_H
+
+#include <aconf.h>
+#include <math.h>
+#include "SplashTypes.h"
+
+static inline SplashCoord splashAbs(SplashCoord x) {
+  return fabs(x);
+}
+
+static inline int splashFloor(SplashCoord x) {
+  return (int)floor(x);
+}
+
+static inline int splashCeil(SplashCoord x) {
+  return (int)ceil(x);
+}
+
+static inline int splashRound(SplashCoord x) {
+  return (int)floor(x + 0.5);
+}
+
+static inline SplashCoord splashSqrt(SplashCoord x) {
+  return sqrt(x);
+}
+
+static inline SplashCoord splashPow(SplashCoord x, SplashCoord y) {
+  return pow(x, y);
+}
+
+static inline SplashCoord splashDist(SplashCoord x0, SplashCoord y0,
+                                    SplashCoord x1, SplashCoord y1) {
+  SplashCoord dx, dy;
+  dx = x1 - x0;
+  dy = y1 - y0;
+  return sqrt(dx * dx + dy * dy);
+}
+
+#endif
diff --git a/pdf/splash/SplashPath.cc b/pdf/splash/SplashPath.cc
new file mode 100644 (file)
index 0000000..465990f
--- /dev/null
@@ -0,0 +1,177 @@
+//========================================================================
+//
+// SplashPath.cc
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <string.h>
+#include "gmem.h"
+#include "SplashErrorCodes.h"
+#include "SplashPath.h"
+
+//------------------------------------------------------------------------
+// SplashPath
+//------------------------------------------------------------------------
+
+// A path can be in three possible states:
+//
+// 1. no current point -- zero or more finished subpaths
+//    [curSubpath == length]
+//
+// 2. one point in subpath
+//    [curSubpath == length - 1]
+//
+// 3. open subpath with two or more points
+//    [curSubpath < length - 1]
+
+SplashPath::SplashPath() {
+  pts = NULL;
+  flags = NULL;
+  length = size = 0;
+  curSubpath = 0;
+}
+
+SplashPath::SplashPath(SplashPath *path) {
+  length = path->length;
+  size = path->size;
+  pts = (SplashPathPoint *)gmalloc(size * sizeof(SplashPathPoint));
+  flags = (Guchar *)gmalloc(size * sizeof(Guchar));
+  memcpy(pts, path->pts, length * sizeof(SplashPathPoint));
+  memcpy(flags, path->flags, length * sizeof(Guchar));
+  curSubpath = path->curSubpath;
+}
+
+SplashPath::~SplashPath() {
+  gfree(pts);
+  gfree(flags);
+}
+
+// Add space for <nPts> more points.
+void SplashPath::grow(int nPts) {
+  if (length + nPts > size) {
+    if (size == 0) {
+      size = 32;
+    }
+    while (size < length + nPts) {
+      size *= 2;
+    }
+    pts = (SplashPathPoint *)grealloc(pts, size * sizeof(SplashPathPoint));
+    flags = (Guchar *)grealloc(flags, size * sizeof(Guchar));
+  }
+}
+
+void SplashPath::append(SplashPath *path) {
+  int i;
+
+  curSubpath = length + path->curSubpath;
+  grow(path->length);
+  for (i = 0; i < path->length; ++i) {
+    pts[length] = path->pts[i];
+    flags[length] = path->flags[i];
+    ++length;
+  }
+}
+
+SplashError SplashPath::moveTo(SplashCoord x, SplashCoord y) {
+  if (onePointSubpath()) {
+    return splashErrBogusPath;
+  }
+  grow(1);
+  pts[length].x = x;
+  pts[length].y = y;
+  flags[length] = splashPathFirst | splashPathLast;
+  curSubpath = length++;
+  return splashOk;
+}
+
+SplashError SplashPath::lineTo(SplashCoord x, SplashCoord y) {
+  if (noCurrentPoint()) {
+    return splashErrNoCurPt;
+  }
+  flags[length-1] &= ~splashPathLast;
+  grow(1);
+  pts[length].x = x;
+  pts[length].y = y;
+  flags[length] = splashPathLast;
+  ++length;
+  return splashOk;
+}
+
+SplashError SplashPath::curveTo(SplashCoord x1, SplashCoord y1,
+                               SplashCoord x2, SplashCoord y2,
+                               SplashCoord x3, SplashCoord y3) {
+  if (noCurrentPoint()) {
+    return splashErrNoCurPt;
+  }
+  flags[length-1] &= ~splashPathLast;
+  grow(3);
+  pts[length].x = x1;
+  pts[length].y = y1;
+  flags[length] = splashPathCurve;
+  ++length;
+  pts[length].x = x2;
+  pts[length].y = y2;
+  flags[length] = splashPathCurve;
+  ++length;
+  pts[length].x = x3;
+  pts[length].y = y3;
+  flags[length] = splashPathLast;
+  ++length;
+  return splashOk;
+}
+
+SplashError SplashPath::arcCWTo(SplashCoord x1, SplashCoord y1,
+                               SplashCoord xc, SplashCoord yc) {
+  if (noCurrentPoint()) {
+    return splashErrNoCurPt;
+  }
+  flags[length-1] &= ~splashPathLast;
+  grow(2);
+  pts[length].x = xc;
+  pts[length].y = yc;
+  flags[length] = splashPathArcCW;
+  ++length;
+  pts[length].x = x1;
+  pts[length].y = y1;
+  flags[length] = splashPathLast;
+  ++length;
+  return splashOk;
+}
+
+SplashError SplashPath::close() {
+  if (noCurrentPoint()) {
+    return splashErrNoCurPt;
+  }
+  if (pts[length - 1].x != pts[curSubpath].x ||
+      pts[length - 1].y != pts[curSubpath].y) {
+    lineTo(pts[curSubpath].x, pts[curSubpath].y);
+  }
+  flags[curSubpath] |= splashPathClosed;
+  flags[length - 1] |= splashPathClosed;
+  curSubpath = length;
+  return splashOk;
+}
+
+void SplashPath::offset(SplashCoord dx, SplashCoord dy) {
+  int i;
+
+  for (i = 0; i < length; ++i) {
+    pts[i].x += dx;
+    pts[i].y += dy;
+  }
+}
+
+GBool SplashPath::getCurPt(SplashCoord *x, SplashCoord *y) {
+  if (noCurrentPoint()) {
+    return gFalse;
+  }
+  *x = pts[length - 1].x;
+  *y = pts[length - 1].y;
+  return gTrue;
+}
diff --git a/pdf/splash/SplashPath.h b/pdf/splash/SplashPath.h
new file mode 100644 (file)
index 0000000..8aa1a8b
--- /dev/null
@@ -0,0 +1,107 @@
+//========================================================================
+//
+// SplashPath.h
+//
+//========================================================================
+
+#ifndef SPLASHPATH_H
+#define SPLASHPATH_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#include "SplashTypes.h"
+
+//------------------------------------------------------------------------
+// SplashPathPoint
+//------------------------------------------------------------------------
+
+struct SplashPathPoint {
+  SplashCoord x, y;
+};
+
+//------------------------------------------------------------------------
+// SplashPath.flags
+//------------------------------------------------------------------------
+
+// first point on each subpath sets this flag
+#define splashPathFirst  0x01
+
+// last point on each subpath sets this flag
+#define splashPathLast   0x02
+
+// if the subpath is closed, its first and last points must be
+// identical, and must set this flag
+#define splashPathClosed 0x04
+
+// curve control points set this flag
+#define splashPathCurve  0x08
+
+// clockwise arc center points set this flag
+#define splashPathArcCW  0x10
+
+//------------------------------------------------------------------------
+// SplashPath
+//------------------------------------------------------------------------
+
+class SplashPath {
+public:
+
+  // Create an empty path.
+  SplashPath();
+
+  // Copy a path.
+  SplashPath *copy() { return new SplashPath(this); }
+
+  ~SplashPath();
+
+  // Append <path> to <this>.
+  void append(SplashPath *path);
+
+  // Start a new subpath.
+  SplashError moveTo(SplashCoord x, SplashCoord y);
+
+  // Add a line segment to the last subpath.
+  SplashError lineTo(SplashCoord x, SplashCoord y);
+
+  // Add a third-order (cubic) Bezier curve segment to the last
+  // subpath.
+  SplashError curveTo(SplashCoord x1, SplashCoord y1,
+                     SplashCoord x2, SplashCoord y2,
+                     SplashCoord x3, SplashCoord y3);
+
+  // Add a clockwise circular arc with center (xc, yc) and endpoint
+  // (x1, y1).
+  SplashError arcCWTo(SplashCoord x1, SplashCoord y1,
+                     SplashCoord xc, SplashCoord yc);
+
+  // Close the last subpath, adding a line segment if necessary.
+  SplashError close();
+
+  // Add (<dx>, <dy>) to every point on this path.
+  void offset(SplashCoord dx, SplashCoord dy);
+
+  // Get the current point.
+  GBool getCurPt(SplashCoord *x, SplashCoord *y);
+
+private:
+
+  SplashPath(SplashPath *path);
+  void grow(int nPts);
+  GBool noCurrentPoint() { return curSubpath == length; }
+  GBool onePointSubpath() { return curSubpath == length - 1; }
+  GBool openSubpath() { return curSubpath < length - 1; }
+
+  SplashPathPoint *pts;                // array of points
+  Guchar *flags;               // array of flags
+  int length, size;            // length/size of the pts and flags arrays
+  int curSubpath;              // index of first point in last subpath
+
+  friend class SplashXPath;
+  friend class Splash;
+};
+
+#endif
diff --git a/pdf/splash/SplashPattern.cc b/pdf/splash/SplashPattern.cc
new file mode 100644 (file)
index 0000000..1cbd8a9
--- /dev/null
@@ -0,0 +1,64 @@
+//========================================================================
+//
+// SplashPattern.cc
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include "SplashMath.h"
+#include "SplashScreen.h"
+#include "SplashPattern.h"
+
+//------------------------------------------------------------------------
+// SplashPattern
+//------------------------------------------------------------------------
+
+SplashPattern::SplashPattern() {
+}
+
+SplashPattern::~SplashPattern() {
+}
+
+//------------------------------------------------------------------------
+// SplashSolidColor
+//------------------------------------------------------------------------
+
+SplashSolidColor::SplashSolidColor(SplashColor colorA) {
+  color = colorA;
+}
+
+SplashSolidColor::~SplashSolidColor() {
+}
+
+SplashColor SplashSolidColor::getColor(int x, int y) {
+  return color;
+}
+
+//------------------------------------------------------------------------
+// SplashHalftone
+//------------------------------------------------------------------------
+
+SplashHalftone::SplashHalftone(SplashColor color0A, SplashColor color1A,
+                              SplashScreen *screenA, SplashCoord valueA) {
+  color0 = color0A;
+  color1 = color1A;
+  screen = screenA;
+  value = valueA;
+}
+
+SplashPattern *SplashHalftone::copy() {
+  return new SplashHalftone(color0, color1, screen->copy(), value);
+}
+
+SplashHalftone::~SplashHalftone() {
+  delete screen;
+}
+
+SplashColor SplashHalftone::getColor(int x, int y) {
+  return screen->test(x, y, value) ? color1 : color0;
+}
diff --git a/pdf/splash/SplashPattern.h b/pdf/splash/SplashPattern.h
new file mode 100644 (file)
index 0000000..4bf4477
--- /dev/null
@@ -0,0 +1,81 @@
+//========================================================================
+//
+// SplashPattern.h
+//
+//========================================================================
+
+#ifndef SPLASHPATTERN_H
+#define SPLASHPATTERN_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#include "SplashTypes.h"
+
+class SplashScreen;
+
+//------------------------------------------------------------------------
+// SplashPattern
+//------------------------------------------------------------------------
+
+class SplashPattern {
+public:
+
+  SplashPattern();
+
+  virtual SplashPattern *copy() = 0;
+
+  virtual ~SplashPattern();
+
+  virtual SplashColor getColor(int x, int y) = 0;
+
+private:
+};
+
+//------------------------------------------------------------------------
+// SplashSolidColor
+//------------------------------------------------------------------------
+
+class SplashSolidColor: public SplashPattern {
+public:
+
+  SplashSolidColor(SplashColor colorA);
+
+  virtual SplashPattern *copy() { return new SplashSolidColor(color); }
+
+  virtual ~SplashSolidColor();
+
+  virtual SplashColor getColor(int x, int y);
+
+private:
+
+  SplashColor color;
+};
+
+//------------------------------------------------------------------------
+// SplashHalftone
+//------------------------------------------------------------------------
+
+class SplashHalftone: public SplashPattern {
+public:
+
+  SplashHalftone(SplashColor color0A, SplashColor color1A,
+                SplashScreen *screenA, SplashCoord valueA);
+
+  virtual SplashPattern *copy();
+
+  virtual ~SplashHalftone();
+
+  virtual SplashColor getColor(int x, int y);
+
+private:
+
+  SplashColor color0, color1;
+  SplashScreen *screen;
+  SplashCoord value;
+};
+
+#endif
diff --git a/pdf/splash/SplashScreen.cc b/pdf/splash/SplashScreen.cc
new file mode 100644 (file)
index 0000000..4230856
--- /dev/null
@@ -0,0 +1,107 @@
+//========================================================================
+//
+// SplashScreen.cc
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include "gmem.h"
+#include "SplashMath.h"
+#include "SplashScreen.h"
+
+//------------------------------------------------------------------------
+// SplashScreen
+//------------------------------------------------------------------------
+
+// This generates a 45 degree screen using a circular dot spot
+// function.  DPI = resolution / ((size / 2) * sqrt(2)).
+// Gamma correction (gamma = 1 / 1.33) is also computed here.
+SplashScreen::SplashScreen(int sizeA) {
+  SplashCoord *dist;
+  SplashCoord u, v, d;
+  int x, y, x1, y1, i;
+
+  size = sizeA >> 1;
+  if (size < 1) {
+    size = 1;
+  }
+
+  // initialize the threshold matrix
+  mat = (SplashCoord *)gmalloc(2 * size * size * sizeof(SplashCoord));
+  for (y = 0; y < 2 * size; ++y) {
+    for (x = 0; x < size; ++x) {
+      mat[y * size + x] = -1;
+    }
+  }
+
+  // build the distance matrix
+  dist = (SplashCoord *)gmalloc(2 * size * size * sizeof(SplashCoord));
+  for (y = 0; y < size; ++y) {
+    for (x = 0; x < size; ++x) {
+      if (x + y < size - 1) {
+       u = (SplashCoord)x + 0.5 - 0;  //~ (-0.5);
+       v = (SplashCoord)y + 0.5 - 0;
+      } else {
+       u = (SplashCoord)x + 0.5 - (SplashCoord)size; //~ ((SplashCoord)size - 0.5);
+       v = (SplashCoord)y + 0.5 - (SplashCoord)size;
+      }
+      dist[y * size + x] = u*u + v*v;
+    }
+  }
+  for (y = 0; y < size; ++y) {
+    for (x = 0; x < size; ++x) {
+      if (x < y) {
+       u = (SplashCoord)x + 0.5 - 0;  //~ (-0.5);
+       v = (SplashCoord)y + 0.5 - (SplashCoord)size;
+      } else {
+       u = (SplashCoord)x + 0.5 - (SplashCoord)size; //~ ((SplashCoord)size - 0.5);
+       v = (SplashCoord)y + 0.5 - 0;
+      }
+      dist[(size + y) * size + x] = u*u + v*v;
+    }
+  }
+
+  // build the threshold matrix
+  x1 = y1 = 0; // make gcc happy
+  for (i = 1; i <= 2 * size * size; ++i) {
+    d = 2 * size * size;
+    for (y = 0; y < 2 * size; ++y) {
+      for (x = 0; x < size; ++x) {
+       if (mat[y * size + x] < 0 &&
+           dist[y * size + x] < d) {
+         x1 = x;
+         y1 = y;
+         d = dist[y1 * size + x1];
+       }
+      }
+    }
+    u = 1.0 - (SplashCoord)i / (SplashCoord)(2 * size * size + 1);
+    mat[y1 * size + x1] = splashPow(u, 1.33);
+  }
+
+  gfree(dist);
+}
+
+SplashScreen::~SplashScreen() {
+  gfree(mat);
+}
+
+int SplashScreen::test(int x, int y, SplashCoord value) {
+  SplashCoord *mat1;
+  int xx, yy;
+
+  xx = x % (2 * size);
+  yy = y % (2 * size);
+  mat1 = mat;
+  if ((xx / size) ^ (yy / size)) {
+    mat1 += size * size;
+  }
+  xx %= size;
+  yy %= size;
+  return value < mat1[yy * size + xx] ? 0 : 1;
+}
diff --git a/pdf/splash/SplashScreen.h b/pdf/splash/SplashScreen.h
new file mode 100644 (file)
index 0000000..33024bb
--- /dev/null
@@ -0,0 +1,40 @@
+//========================================================================
+//
+// SplashScreen.h
+//
+//========================================================================
+
+#ifndef SPLASHSCREEN_H
+#define SPLASHSCREEN_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#include "SplashTypes.h"
+
+//------------------------------------------------------------------------
+// SplashScreen
+//------------------------------------------------------------------------
+
+class SplashScreen {
+public:
+
+  SplashScreen(int sizeA);
+  ~SplashScreen();
+
+  SplashScreen *copy() { return new SplashScreen(size << 1); }
+
+  // Return the computed pixel value (0=black, 1=white) for the gray
+  // level <value> at (<x>, <y>).
+  int test(int x, int y, SplashCoord value);
+
+private:
+
+  SplashCoord *mat;            // threshold matrix
+  int size;                    // size of the threshold matrix
+};
+
+#endif
diff --git a/pdf/splash/SplashState.cc b/pdf/splash/SplashState.cc
new file mode 100644 (file)
index 0000000..7d5dc83
--- /dev/null
@@ -0,0 +1,99 @@
+//========================================================================
+//
+// SplashState.cc
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <string.h>
+#include "gmem.h"
+#include "SplashPattern.h"
+#include "SplashScreen.h"
+#include "SplashClip.h"
+#include "SplashState.h"
+
+//------------------------------------------------------------------------
+// SplashState
+//------------------------------------------------------------------------
+
+SplashState::SplashState(int width, int height) {
+  SplashColor color;
+
+  memset(&color, 0, sizeof(SplashColor));
+  strokePattern = new SplashSolidColor(color);
+  fillPattern = new SplashSolidColor(color);
+  screen = new SplashScreen(10);
+  lineWidth = 0;
+  lineCap = splashLineCapButt;
+  lineJoin = splashLineJoinMiter;
+  miterLimit = 10;
+  flatness = 1;
+  lineDash = NULL;
+  lineDashLength = 0;
+  lineDashPhase = 0;
+  clip = new SplashClip(0, 0, width - 1, height - 1);
+  next = NULL;
+}
+
+SplashState::SplashState(SplashState *state) {
+  strokePattern = state->strokePattern->copy();
+  fillPattern = state->fillPattern->copy();
+  screen = state->screen->copy();
+  lineWidth = state->lineWidth;
+  lineCap = state->lineCap;
+  lineJoin = state->lineJoin;
+  miterLimit = state->miterLimit;
+  flatness = state->flatness;
+  if (state->lineDash) {
+    lineDashLength = state->lineDashLength;
+    lineDash = (SplashCoord *)gmalloc(lineDashLength * sizeof(SplashCoord));
+    memcpy(lineDash, state->lineDash, lineDashLength * sizeof(SplashCoord));
+  } else {
+    lineDash = NULL;
+    lineDashLength = 0;
+  }
+  lineDashPhase = state->lineDashPhase;
+  clip = state->clip->copy();
+  next = NULL;
+}
+
+SplashState::~SplashState() {
+  delete strokePattern;
+  delete fillPattern;
+  delete screen;
+  gfree(lineDash);
+  delete clip;
+}
+
+void SplashState::setStrokePattern(SplashPattern *strokePatternA) {
+  delete strokePattern;
+  strokePattern = strokePatternA;
+}
+
+void SplashState::setFillPattern(SplashPattern *fillPatternA) {
+  delete fillPattern;
+  fillPattern = fillPatternA;
+}
+
+void SplashState::setScreen(SplashScreen *screenA) {
+  delete screen;
+  screen = screenA;
+}
+
+void SplashState::setLineDash(SplashCoord *lineDashA, int lineDashLengthA,
+                             SplashCoord lineDashPhaseA) {
+  gfree(lineDash);
+  lineDashLength = lineDashLengthA;
+  if (lineDashLength > 0) {
+    lineDash = (SplashCoord *)gmalloc(lineDashLength * sizeof(SplashCoord));
+    memcpy(lineDash, lineDashA, lineDashLength * sizeof(SplashCoord));
+  } else {
+    lineDash = NULL;
+  }
+  lineDashPhase = lineDashPhaseA;
+}
diff --git a/pdf/splash/SplashState.h b/pdf/splash/SplashState.h
new file mode 100644 (file)
index 0000000..8fcb54b
--- /dev/null
@@ -0,0 +1,88 @@
+//========================================================================
+//
+// SplashState.h
+//
+//========================================================================
+
+#ifndef SPLASHSTATE_H
+#define SPLASHSTATE_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#include "SplashTypes.h"
+
+class SplashPattern;
+class SplashScreen;
+class SplashClip;
+
+//------------------------------------------------------------------------
+// line cap values
+//------------------------------------------------------------------------
+
+#define splashLineCapButt       0
+#define splashLineCapRound      1
+#define splashLineCapProjecting 2
+
+//------------------------------------------------------------------------
+// line join values
+//------------------------------------------------------------------------
+
+#define splashLineJoinMiter     0
+#define splashLineJoinRound     1
+#define splashLineJoinBevel     2
+
+//------------------------------------------------------------------------
+// SplashState
+//------------------------------------------------------------------------
+
+class SplashState {
+public:
+
+  // Create a new state object, initialized with default settings.
+  SplashState(int width, int height);
+
+  // Copy a state object.
+  SplashState *copy() { return new SplashState(this); }
+
+  ~SplashState();
+
+  // Set the stroke pattern.  This does not copy <strokePatternA>.
+  void setStrokePattern(SplashPattern *strokePatternA);
+
+  // Set the fill pattern.  This does not copy <fillPatternA>.
+  void setFillPattern(SplashPattern *fillPatternA);
+
+  // Set the screen.  This does not copy <screenA>.
+  void setScreen(SplashScreen *screenA);
+
+  // Set the line dash pattern.  This copies the <lineDashA> array.
+  void setLineDash(SplashCoord *lineDashA, int lineDashLengthA,
+                  SplashCoord lineDashPhaseA);
+
+private:
+
+  SplashState(SplashState *state);
+
+  SplashPattern *strokePattern;
+  SplashPattern *fillPattern;
+  SplashScreen *screen;
+  SplashCoord lineWidth;
+  int lineCap;
+  int lineJoin;
+  SplashCoord miterLimit;
+  SplashCoord flatness;
+  SplashCoord *lineDash;
+  int lineDashLength;
+  SplashCoord lineDashPhase;
+  SplashClip *clip;
+
+  SplashState *next;           // used by Splash class
+
+  friend class Splash;
+};
+
+#endif
diff --git a/pdf/splash/SplashT1Font.cc b/pdf/splash/SplashT1Font.cc
new file mode 100644 (file)
index 0000000..8338aba
--- /dev/null
@@ -0,0 +1,251 @@
+//========================================================================
+//
+// SplashT1Font.cc
+//
+//========================================================================
+
+#include <aconf.h>
+
+#if HAVE_T1LIB_H
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <stdlib.h>
+#include <t1lib.h>
+#include "gmem.h"
+#include "SplashMath.h"
+#include "SplashGlyphBitmap.h"
+#include "SplashPath.h"
+#include "SplashT1FontEngine.h"
+#include "SplashT1FontFile.h"
+#include "SplashT1Font.h"
+
+//------------------------------------------------------------------------
+
+static Guchar bitReverse[256] = {
+  0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0,
+  0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0,
+  0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8,
+  0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8,
+  0x04, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4,
+  0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4,
+  0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec,
+  0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc,
+  0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2,
+  0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2,
+  0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea,
+  0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa,
+  0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6,
+  0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6,
+  0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee,
+  0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe,
+  0x01, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1,
+  0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1,
+  0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9,
+  0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9,
+  0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5,
+  0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5,
+  0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed,
+  0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd,
+  0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3,
+  0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3,
+  0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb,
+  0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb,
+  0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7,
+  0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7,
+  0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef,
+  0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff
+};
+
+//------------------------------------------------------------------------
+// SplashT1Font
+//------------------------------------------------------------------------
+
+SplashT1Font::SplashT1Font(SplashT1FontFile *fontFileA, SplashCoord *matA):
+  SplashFont(fontFileA, matA, ((SplashT1FontFile *)fontFileA)->engine->aa)
+{
+  T1_TMATRIX matrix;
+  BBox bbox;
+  SplashCoord bbx0, bby0, bbx1, bby1;
+  int x, y;
+
+  t1libID = T1_CopyFont(fontFileA->t1libID);
+
+  // compute font size
+  size = (float)splashSqrt(mat[2]*mat[2] + mat[3]*mat[3]);
+
+  // transform the four corners of the font bounding box -- the min
+  // and max values form the bounding box of the transformed font
+  bbox = T1_GetFontBBox(t1libID);
+  bbx0 = 0.001 * bbox.llx;
+  bby0 = 0.001 * bbox.lly;
+  bbx1 = 0.001 * bbox.urx;
+  bby1 = 0.001 * bbox.ury;
+  // some fonts are completely broken, so we fake it (with values
+  // large enough that most glyphs should fit)
+  if (bbx0 == 0 && bby0 == 0 && bbx1 == 0 && bby1 == 0) {
+    bbx0 = bby0 = -0.5;
+    bbx1 = bby1 = 1.5;
+  }
+  x = (int)(mat[0] * bbx0 + mat[2] * bby0);
+  xMin = xMax = x;
+  y = (int)(mat[1] * bbx0 + mat[3] * bby0);
+  yMin = yMax = y;
+  x = (int)(mat[0] * bbx0 + mat[2] * bby1);
+  if (x < xMin) {
+    xMin = x;
+  } else if (x > xMax) {
+    xMax = x;
+  }
+  y = (int)(mat[1] * bbx0 + mat[3] * bby1);
+  if (y < yMin) {
+    yMin = y;
+  } else if (y > yMax) {
+    yMax = y;
+  }
+  x = (int)(mat[0] * bbx1 + mat[2] * bby0);
+  if (x < xMin) {
+    xMin = x;
+  } else if (x > xMax) {
+    xMax = x;
+  }
+  y = (int)(mat[1] * bbx1 + mat[3] * bby0);
+  if (y < yMin) {
+    yMin = y;
+  } else if (y > yMax) {
+    yMax = y;
+  }
+  x = (int)(mat[0] * bbx1 + mat[2] * bby1);
+  if (x < xMin) {
+    xMin = x;
+  } else if (x > xMax) {
+    xMax = x;
+  }
+  y = (int)(mat[1] * bbx1 + mat[3] * bby1);
+  if (y < yMin) {
+    yMin = y;
+  } else if (y > yMax) {
+    yMax = y;
+  }
+  // This is a kludge: some buggy PDF generators embed fonts with
+  // zero bounding boxes.
+  if (xMax == xMin) {
+    xMin = 0;
+    xMax = (int)size;
+  }
+  if (yMax == yMin) {
+    yMin = 0;
+    yMax = (int)(1.2 * size);
+  }
+  // Another kludge: an unusually large xMin or yMin coordinate is
+  // probably wrong.
+  if (xMin > 0) {
+    xMin = 0;
+  }
+  if (yMin > 0) {
+    yMin = 0;
+  }
+  // Another kludge: t1lib doesn't correctly handle fonts with
+  // real (non-integer) bounding box coordinates.
+  if (xMax - xMin > 5000) {
+    xMin = 0;
+    xMax = (int)size;
+  }
+  if (yMax - yMin > 5000) {
+    yMin = 0;
+    yMax = (int)(1.2 * size);
+  }
+
+  // transform the font
+  matrix.cxx = mat[0] / size;
+  matrix.cxy = mat[1] / size;
+  matrix.cyx = mat[2] / size;
+  matrix.cyy = mat[3] / size;
+  T1_TransformFont(t1libID, &matrix);
+}
+
+SplashT1Font::~SplashT1Font() {
+  T1_DeleteFont(t1libID);
+}
+
+GBool SplashT1Font::getGlyph(int c, int xFrac, int yFrac,
+                            SplashGlyphBitmap *bitmap) {
+  return SplashFont::getGlyph(c, 0, 0, bitmap);
+}
+
+GBool SplashT1Font::makeGlyph(int c, int xFrac, int yFrac,
+                             SplashGlyphBitmap *bitmap) {
+  GLYPH *glyph;
+  int n, i;
+
+  if (aa) {
+    glyph = T1_AASetChar(t1libID, c, size, NULL);
+  } else {
+    glyph = T1_SetChar(t1libID, c, size, NULL);
+  }
+  if (!glyph) {
+    return gFalse;
+  }
+
+  bitmap->x = -glyph->metrics.leftSideBearing;
+  bitmap->y = glyph->metrics.ascent;
+  bitmap->w = glyph->metrics.rightSideBearing - glyph->metrics.leftSideBearing;
+  bitmap->h = glyph->metrics.ascent - glyph->metrics.descent;
+  bitmap->aa = aa;
+  if (aa) {
+    bitmap->data = (Guchar *)glyph->bits;
+    bitmap->freeData = gFalse;
+  } else {
+    n = bitmap->h * ((bitmap->w + 7) >> 3);
+    bitmap->data = (Guchar *)gmalloc(n);
+    for (i = 0; i < n; ++i) {
+      bitmap->data[i] = bitReverse[glyph->bits[i] & 0xff];
+    }
+    bitmap->freeData = gTrue;
+  }
+
+  return gTrue;
+}
+
+SplashPath *SplashT1Font::getGlyphPath(int c) {
+  SplashPath *path;
+  T1_OUTLINE *outline;
+  T1_PATHSEGMENT *seg;
+  T1_BEZIERSEGMENT *bez;
+  SplashCoord x, y, x1, y1;
+
+  path = new SplashPath();
+  outline = T1_GetCharOutline(t1libID, c, size, NULL);
+  x = 0;
+  y = 0;
+  for (seg = outline; seg; seg = seg->link) {
+    switch (seg->type) {
+    case T1_PATHTYPE_MOVE:
+      x += seg->dest.x / 65536.0;
+      y += seg->dest.y / 65536.0;
+      path->moveTo(x, y);
+      break;
+    case T1_PATHTYPE_LINE:
+      x += seg->dest.x / 65536.0;
+      y += seg->dest.y / 65536.0;
+      path->lineTo(x, y);
+      break;
+    case T1_PATHTYPE_BEZIER:
+      bez = (T1_BEZIERSEGMENT *)seg;
+      x1 = x + bez->dest.x / 65536.0;
+      y1 = y + bez->dest.y / 65536.0;
+      path->curveTo(x + bez->B.x / 65536.0, y + bez->B.y / 65536.0,
+                   x + bez->C.x / 65536.0, y + bez->C.y / 65536.0,
+                   x1, y1);
+      x = x1;
+      y = y1;
+      break;
+    }
+  }
+  T1_FreeOutline(outline);
+  return path;
+}
+
+#endif // HAVE_T1LIB_H
diff --git a/pdf/splash/SplashT1Font.h b/pdf/splash/SplashT1Font.h
new file mode 100644 (file)
index 0000000..e745e07
--- /dev/null
@@ -0,0 +1,51 @@
+//========================================================================
+//
+// SplashT1Font.h
+//
+//========================================================================
+
+#ifndef SPLASHT1FONT_H
+#define SPLASHT1FONT_H
+
+#include <aconf.h>
+
+#if HAVE_T1LIB_H
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#include "SplashFont.h"
+
+//------------------------------------------------------------------------
+// SplashT1Font
+//------------------------------------------------------------------------
+
+class SplashT1Font: public SplashFont {
+public:
+
+  SplashT1Font(SplashT1FontFile *fontFileA, SplashCoord *matA);
+
+  virtual ~SplashT1Font();
+
+  // Munge xFrac and yFrac before calling SplashFont::getGlyph.
+  virtual GBool getGlyph(int c, int xFrac, int yFrac,
+                        SplashGlyphBitmap *bitmap);
+
+  // Rasterize a glyph.  The <xFrac> and <yFrac> values are the same
+  // as described for getGlyph.
+  virtual GBool makeGlyph(int c, int xFrac, int yFrac,
+                         SplashGlyphBitmap *bitmap);
+
+  // Return the path for a glyph.
+  virtual SplashPath *getGlyphPath(int c);
+
+private:
+
+  int t1libID;                 // t1lib font ID
+  float size;
+};
+
+#endif // HAVE_T1LIB_H
+
+#endif
diff --git a/pdf/splash/SplashT1FontEngine.cc b/pdf/splash/SplashT1FontEngine.cc
new file mode 100644 (file)
index 0000000..a1c9342
--- /dev/null
@@ -0,0 +1,124 @@
+//========================================================================
+//
+// SplashT1FontEngine.cc
+//
+//========================================================================
+
+#include <aconf.h>
+
+#if HAVE_T1LIB_H
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#ifndef WIN32
+#  include <unistd.h>
+#endif
+#include <t1lib.h>
+#include "GString.h"
+#include "gfile.h"
+#include "FoFiType1C.h"
+#include "SplashT1FontFile.h"
+#include "SplashT1FontEngine.h"
+
+#ifdef VMS
+#if (__VMS_VER < 70000000)
+extern "C" int unlink(char *filename);
+#endif
+#endif
+
+//------------------------------------------------------------------------
+
+int SplashT1FontEngine::t1libInitCount = 0;
+
+//------------------------------------------------------------------------
+
+static void fileWrite(void *stream, char *data, int len) {
+  fwrite(data, 1, len, (FILE *)stream);
+}
+
+//------------------------------------------------------------------------
+// SplashT1FontEngine
+//------------------------------------------------------------------------
+
+SplashT1FontEngine::SplashT1FontEngine(GBool aaA) {
+  aa = aaA;
+}
+
+SplashT1FontEngine *SplashT1FontEngine::init(GBool aaA) {
+  // grayVals[i] = round(i * 255 / 16)
+  static unsigned long grayVals[17] = {
+    0, 16, 32, 48, 64, 80, 96, 112, 128, 143, 159, 175, 191, 207, 223, 239, 255
+  };
+
+  //~ for multithreading: need a mutex here
+  if (t1libInitCount == 0) {
+    T1_SetBitmapPad(8);
+    if (!T1_InitLib(NO_LOGFILE | IGNORE_CONFIGFILE | IGNORE_FONTDATABASE |
+                   T1_NO_AFM)) {
+      return NULL;
+    }
+    if (aaA) {
+      T1_AASetBitsPerPixel(8);
+      T1_AASetLevel(T1_AA_HIGH);
+      T1_AAHSetGrayValues(grayVals);
+    } else {
+      T1_AANSetGrayValues(0, 1);
+    }
+  }
+  ++t1libInitCount;
+
+  return new SplashT1FontEngine(aaA);
+}
+
+SplashT1FontEngine::~SplashT1FontEngine() {
+  //~ for multithreading: need a mutex here
+  if (--t1libInitCount == 0) {
+    T1_CloseLib();
+  }
+}
+
+SplashFontFile *SplashT1FontEngine::loadType1Font(SplashFontFileID *idA,
+                                                 char *fileName,
+                                                 GBool deleteFile,
+                                                 char **enc) {
+  return SplashT1FontFile::loadType1Font(this, idA, fileName, deleteFile, enc);
+}
+
+SplashFontFile *SplashT1FontEngine::loadType1CFont(SplashFontFileID *idA,
+                                                  char *fileName,
+                                                  GBool deleteFile,
+                                                  char **enc) {
+  FoFiType1C *ff;
+  GString *tmpFileName;
+  FILE *tmpFile;
+  SplashFontFile *ret;
+
+  if (!(ff = FoFiType1C::load(fileName))) {
+    return NULL;
+  }
+  tmpFileName = NULL;
+  if (!openTempFile(&tmpFileName, &tmpFile, "wb", NULL)) {
+    delete ff;
+    return NULL;
+  }
+  ff->convertToType1(NULL, gTrue, &fileWrite, tmpFile);
+  delete ff;
+  fclose(tmpFile);
+  ret = SplashT1FontFile::loadType1Font(this, idA, tmpFileName->getCString(),
+                                       gTrue, enc);
+  if (ret) {
+    if (deleteFile) {
+      unlink(fileName);
+    }
+  } else {
+    unlink(tmpFileName->getCString());
+  }
+  delete tmpFileName;
+  return ret;
+}
+
+#endif // HAVE_T1LIB_H
diff --git a/pdf/splash/SplashT1FontEngine.h b/pdf/splash/SplashT1FontEngine.h
new file mode 100644 (file)
index 0000000..57a0448
--- /dev/null
@@ -0,0 +1,53 @@
+//========================================================================
+//
+// SplashT1FontEngine.h
+//
+//========================================================================
+
+#ifndef SPLASHT1FONTENGINE_H
+#define SPLASHT1FONTENGINE_H
+
+#include <aconf.h>
+
+#if HAVE_T1LIB_H
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#include "gtypes.h"
+
+class SplashFontFile;
+class SplashFontFileID;
+
+//------------------------------------------------------------------------
+// SplashT1FontEngine
+//------------------------------------------------------------------------
+
+class SplashT1FontEngine {
+public:
+
+  static SplashT1FontEngine *init(GBool aaA);
+
+  ~SplashT1FontEngine();
+
+  // Load fonts.
+  SplashFontFile *loadType1Font(SplashFontFileID *idA, char *fileName,
+                               GBool deleteFile, char **enc);
+  SplashFontFile *loadType1CFont(SplashFontFileID *idA, char *fileName,
+                                GBool deleteFile, char **enc);
+
+private:
+
+  SplashT1FontEngine(GBool aaA);
+
+  static int t1libInitCount;
+  GBool aa;
+
+  friend class SplashT1FontFile;
+  friend class SplashT1Font;
+};
+
+#endif // HAVE_T1LIB_H
+
+#endif
diff --git a/pdf/splash/SplashT1FontFile.cc b/pdf/splash/SplashT1FontFile.cc
new file mode 100644 (file)
index 0000000..83eaec9
--- /dev/null
@@ -0,0 +1,96 @@
+//========================================================================
+//
+// SplashT1FontFile.cc
+//
+//========================================================================
+
+#include <aconf.h>
+
+#if HAVE_T1LIB_H
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <string.h>
+#include <t1lib.h>
+#include "gmem.h"
+#include "SplashT1FontEngine.h"
+#include "SplashT1Font.h"
+#include "SplashT1FontFile.h"
+
+//------------------------------------------------------------------------
+// SplashT1FontFile
+//------------------------------------------------------------------------
+
+SplashFontFile *SplashT1FontFile::loadType1Font(SplashT1FontEngine *engineA,
+                                               SplashFontFileID *idA,
+                                               char *fileNameA,
+                                               GBool deleteFileA,
+                                               char **encA) {
+  int t1libIDA;
+  char **encTmp;
+  char *encStrTmp;
+  int encStrSize;
+  char *encPtr;
+  int i;
+
+  // load the font file
+  if ((t1libIDA = T1_AddFont(fileNameA)) < 0) {
+    return NULL;
+  }
+  T1_LoadFont(t1libIDA);
+
+  // reencode it
+  encStrSize = 0;
+  for (i = 0; i < 256; ++i) {
+    if (encA[i]) {
+      encStrSize += strlen(encA[i]) + 1;
+    }
+  }
+  encTmp = (char **)gmalloc(257 * sizeof(char *));
+  encStrTmp = (char *)gmalloc(encStrSize * sizeof(char));
+  encPtr = encStrTmp;
+  for (i = 0; i < 256; ++i) {
+    if (encA[i]) {
+      strcpy(encPtr, encA[i]);
+      encTmp[i] = encPtr;
+      encPtr += strlen(encPtr) + 1;
+    } else {
+      encTmp[i] = ".notdef";
+    }
+  }
+  encTmp[256] = "custom";
+  T1_ReencodeFont(t1libIDA, encTmp);
+
+  return new SplashT1FontFile(engineA, idA, fileNameA, deleteFileA,
+                             t1libIDA, encTmp, encStrTmp);
+}
+
+SplashT1FontFile::SplashT1FontFile(SplashT1FontEngine *engineA,
+                                  SplashFontFileID *idA,
+                                  char *fileNameA, GBool deleteFileA,
+                                  int t1libIDA, char **encA, char *encStrA):
+  SplashFontFile(idA, fileNameA, deleteFileA)
+{
+  engine = engineA;
+  t1libID = t1libIDA;
+  enc = encA;
+  encStr = encStrA;
+}
+
+SplashT1FontFile::~SplashT1FontFile() {
+  gfree(encStr);
+  gfree(enc);
+  T1_DeleteFont(t1libID);
+}
+
+SplashFont *SplashT1FontFile::makeFont(SplashCoord *mat) {
+  SplashFont *font;
+
+  font = new SplashT1Font(this, mat);
+  font->initCache();
+  return font;
+}
+
+#endif // HAVE_T1LIB_H
diff --git a/pdf/splash/SplashT1FontFile.h b/pdf/splash/SplashT1FontFile.h
new file mode 100644 (file)
index 0000000..c220c04
--- /dev/null
@@ -0,0 +1,57 @@
+//========================================================================
+//
+// SplashT1FontFile.h
+//
+//========================================================================
+
+#ifndef SPLASHT1FONTFILE_H
+#define SPLASHT1FONTFILE_H
+
+#include <aconf.h>
+
+#if HAVE_T1LIB_H
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#include "SplashFontFile.h"
+
+class SplashT1FontEngine;
+
+//------------------------------------------------------------------------
+// SplashT1FontFile
+//------------------------------------------------------------------------
+
+class SplashT1FontFile: public SplashFontFile {
+public:
+
+  static SplashFontFile *loadType1Font(SplashT1FontEngine *engineA,
+                                      SplashFontFileID *idA,
+                                      char *fileNameA, GBool deleteFileA,
+                                      char **encA);
+
+  virtual ~SplashT1FontFile();
+
+  // Create a new SplashT1Font, i.e., a scaled instance of this font
+  // file.
+  virtual SplashFont *makeFont(SplashCoord *mat);
+
+private:
+
+  SplashT1FontFile(SplashT1FontEngine *engineA,
+                  SplashFontFileID *idA,
+                  char *fileNameA, GBool deleteFileA,
+                  int t1libIDA, char **encA, char *encStrA);
+
+  SplashT1FontEngine *engine;
+  int t1libID;                 // t1lib font ID
+  char **enc;
+  char *encStr;
+
+  friend class SplashT1Font;
+};
+
+#endif // HAVE_T1LIB_H
+
+#endif
diff --git a/pdf/splash/SplashTypes.h b/pdf/splash/SplashTypes.h
new file mode 100644 (file)
index 0000000..91b120f
--- /dev/null
@@ -0,0 +1,77 @@
+//========================================================================
+//
+// SplashTypes.h
+//
+//========================================================================
+
+#ifndef SPLASHTYPES_H
+#define SPLASHTYPES_H
+
+#include <aconf.h>
+#include "gtypes.h"
+
+//------------------------------------------------------------------------
+// coordinates
+//------------------------------------------------------------------------
+
+typedef double SplashCoord;
+
+//------------------------------------------------------------------------
+// colors
+//------------------------------------------------------------------------
+
+enum SplashColorMode {
+  splashModeMono1,
+  splashModeMono8,
+  splashModeRGB8,
+  splashModeBGR8Packed
+};
+
+// max number of components in any SplashColor
+#define splashMaxColorComps 3
+
+// 1-bit gray or alpha
+typedef Guchar SplashMono1;
+typedef Guchar SplashMono1P; // packed
+
+// 8-bit gray or alpha
+typedef Guchar SplashMono8;
+
+// 3x8-bit RGB: (MSB) 00RRGGBB (LSB)
+typedef Guint SplashRGB8;
+#define splashRGB8R(rgb8) (((rgb8) >> 16) & 0xff)
+#define splashRGB8G(rgb8) (((rgb8) >> 8) & 0xff)
+#define splashRGB8B(rgb8) ((rgb8) & 0xff)
+#define splashMakeRGB8(r, g, b) \
+  ((((r) & 0xff) << 16) | (((g) & 0xff) << 8) | ((b) & 0xff))
+
+// 3x8-bit RGB: (MSB) 00BBGGRR (LSB)
+typedef Guint SplashBGR8;
+typedef Guchar SplashBGR8P; // packed
+#define splashBGR8R(bgr8) ((bgr8) & 0xff)
+#define splashBGR8G(bgr8) (((bgr8) >> 8) & 0xff)
+#define splashBGR8B(bgr8) (((bgr8) >> 16) & 0xff)
+#define splashMakeBGR8(r, g, b) \
+  ((((b) & 0xff) << 16) | (((g) & 0xff) << 8) | ((r) & 0xff))
+
+union SplashColor {
+  SplashMono1 mono1;
+  SplashMono8 mono8;
+  SplashRGB8 rgb8;
+  SplashBGR8 bgr8;
+};
+
+union SplashColorPtr {
+  SplashMono1P *mono1;
+  SplashMono8 *mono8;
+  SplashRGB8 *rgb8;
+  SplashBGR8P *bgr8;
+};
+
+//------------------------------------------------------------------------
+// error results
+//------------------------------------------------------------------------
+
+typedef int SplashError;
+
+#endif
diff --git a/pdf/splash/SplashXPath.cc b/pdf/splash/SplashXPath.cc
new file mode 100644 (file)
index 0000000..e1a3afb
--- /dev/null
@@ -0,0 +1,417 @@
+//========================================================================
+//
+// SplashXPath.cc
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+#include "gmem.h"
+#include "SplashMath.h"
+#include "SplashPath.h"
+#include "SplashXPath.h"
+
+//------------------------------------------------------------------------
+
+#define maxCurveSplits (1 << 10)
+
+//------------------------------------------------------------------------
+// SplashXPath
+//------------------------------------------------------------------------
+
+SplashXPath::SplashXPath() {
+  segs = NULL;
+  length = size = 0;
+}
+
+SplashXPath::SplashXPath(SplashPath *path, SplashCoord flatness,
+                        GBool closeSubpaths) {
+  SplashCoord xc, yc, dx, dy, r, x0, y0, x1, y1;
+  int quad0, quad1, quad;
+  int i, curSubpath;
+  GBool last;
+
+  segs = NULL;
+  length = size = 0;
+
+  i = 0;
+  curSubpath = 0;
+  while (i < path->length) {
+
+    // first point in subpath - skip it
+    if (path->flags[i] & splashPathFirst) {
+      curSubpath = i;
+      ++i;
+
+    } else {
+
+      // curve segment
+      if (path->flags[i] & splashPathCurve) {
+       addCurve(path->pts[i-1].x, path->pts[i-1].y,
+                path->pts[i  ].x, path->pts[i  ].y,
+                path->pts[i+1].x, path->pts[i+1].y,
+                path->pts[i+2].x, path->pts[i+2].y,
+                flatness,
+                (path->flags[i-1] & splashPathFirst),
+                (path->flags[i+2] & splashPathLast),
+                !closeSubpaths &&
+                  (path->flags[i-1] & splashPathFirst) &&
+                  !(path->flags[i-1] & splashPathClosed),
+                !closeSubpaths &&
+                  (path->flags[i+2] & splashPathLast) &&
+                  !(path->flags[i+2] & splashPathClosed));
+       i += 3;
+
+      // clockwise circular arc
+      } else if (path->flags[i] & splashPathArcCW) {
+       xc = path->pts[i].x;
+       yc = path->pts[i].y;
+       dx = path->pts[i+1].x - xc;
+       dy = path->pts[i+1].y - yc;
+       r = splashSqrt(dx * dx + dy * dy);
+       if (path->pts[i-1].x < xc && path->pts[i-1].y <= yc) {
+         quad0 = 0;
+       } else if (path->pts[i-1].x >= xc && path->pts[i-1].y < yc) {
+         quad0 = 1;
+       } else if (path->pts[i-1].x > xc && path->pts[i-1].y >= yc) {
+         quad0 = 2;
+       } else {
+         quad0 = 3;
+       }
+       if (path->pts[i+1].x <= xc && path->pts[i+1].y < yc) {
+         quad1 = 0;
+       } else if (path->pts[i+1].x > xc && path->pts[i+1].y <= yc) {
+         quad1 = 1;
+       } else if (path->pts[i+1].x >= xc && path->pts[i+1].y > yc) {
+         quad1 = 2;
+       } else {
+         quad1 = 3;
+       }
+       x0 = path->pts[i-1].x;
+       y0 = path->pts[i-1].y;
+       x1 = y1 = 0; // make gcc happy
+       quad = quad0;
+       while (1) {
+         switch (quad) {
+         case 0: x1 = xc;     y1 = yc - r; break;
+         case 1: x1 = xc + r; y1 = yc;     break;
+         case 2: x1 = xc;     y1 = yc + r; break;
+         case 3: x1 = xc - r; y1 = yc;     break;
+         }
+         last = gFalse;
+         if (quad == quad1) {
+           switch (quad) {
+           case 0: 
+           case 1: last = path->pts[i+1].x > x0; break;
+           case 2:
+           case 3: last = path->pts[i+1].x < x0; break;
+           }
+         }
+         if (last) {
+           addArc(x0, y0, path->pts[i+1].x, path->pts[i+1].y,
+                  xc, yc, r, quad, flatness,
+                  quad == quad0 && (path->flags[i-1] & splashPathFirst),
+                  (path->flags[i+1] & splashPathLast),
+                  quad == quad0 && !closeSubpaths &&
+                    (path->flags[i-1] & splashPathFirst) &&
+                    !(path->flags[i-1] & splashPathClosed),
+                  !closeSubpaths &&
+                    (path->flags[i+1] & splashPathLast) &&
+                    !(path->flags[i+1] & splashPathClosed));
+           break;
+         } else {
+           addArc(x0, y0, x1, y1,
+                  xc, yc, r, quad, flatness,
+                  quad == quad0 && (path->flags[i-1] & splashPathFirst),
+                  gFalse,
+                  quad == quad0 && !closeSubpaths &&
+                    (path->flags[i-1] & splashPathFirst) &&
+                    !(path->flags[i-1] & splashPathClosed),
+                  gFalse);
+           x0 = x1;
+           y0 = y1;
+           quad = (quad + 1) & 3;
+         }
+       }
+       i += 2;
+
+      // line segment
+      } else {
+       addSegment(path->pts[i-1].x, path->pts[i-1].y,
+                  path->pts[i].x, path->pts[i].y,
+                  path->flags[i-1] & splashPathFirst,
+                  path->flags[i] & splashPathLast,
+                  !closeSubpaths &&
+                    (path->flags[i-1] & splashPathFirst) &&
+                    !(path->flags[i-1] & splashPathClosed),
+                  !closeSubpaths &&
+                    (path->flags[i] & splashPathLast) &&
+                    !(path->flags[i] & splashPathClosed));
+       ++i;
+      }
+
+      // close a subpath
+      if (closeSubpaths &&
+         (path->flags[i-1] & splashPathLast) &&
+         (path->pts[i-1].x != path->pts[curSubpath].x ||
+          path->pts[i-1].y != path->pts[curSubpath]. y)) {
+       addSegment(path->pts[i-1].x, path->pts[i-1].y,
+                  path->pts[curSubpath].x, path->pts[curSubpath].y,
+                  gFalse, gTrue, gFalse, gFalse);
+      }
+    }
+  }
+}
+
+SplashXPath::SplashXPath(SplashXPath *xPath) {
+  length = xPath->length;
+  size = xPath->size;
+  segs = (SplashXPathSeg *)gmalloc(size * sizeof(SplashXPathSeg));
+  memcpy(segs, xPath->segs, length * sizeof(SplashXPathSeg));
+}
+
+SplashXPath::~SplashXPath() {
+  gfree(segs);
+}
+
+// Add space for <nSegs> more segments
+void SplashXPath::grow(int nSegs) {
+  if (length + nSegs > size) {
+    if (size == 0) {
+      size = 32;
+    }
+    while (size < length + nSegs) {
+      size *= 2;
+    }
+    segs = (SplashXPathSeg *)grealloc(segs, size * sizeof(SplashXPathSeg));
+  }
+}
+
+void SplashXPath::addCurve(SplashCoord x0, SplashCoord y0,
+                          SplashCoord x1, SplashCoord y1,
+                          SplashCoord x2, SplashCoord y2,
+                          SplashCoord x3, SplashCoord y3,
+                          SplashCoord flatness,
+                          GBool first, GBool last, GBool end0, GBool end1) {
+  SplashCoord cx[maxCurveSplits + 1][3];
+  SplashCoord cy[maxCurveSplits + 1][3];
+  int cNext[maxCurveSplits + 1];
+  SplashCoord xl0, xl1, xl2, xr0, xr1, xr2, xr3, xx1, xx2, xh;
+  SplashCoord yl0, yl1, yl2, yr0, yr1, yr2, yr3, yy1, yy2, yh;
+  SplashCoord dx, dy, mx, my, d1, d2, flatness2;
+  int p1, p2, p3;
+
+  flatness2 = flatness * flatness;
+
+  // initial segment
+  p1 = 0;
+  p2 = maxCurveSplits;
+  cx[p1][0] = x0;  cy[p1][0] = y0;
+  cx[p1][1] = x1;  cy[p1][1] = y1;
+  cx[p1][2] = x2;  cy[p1][2] = y2;
+  cx[p2][0] = x3;  cy[p2][0] = y3;
+  cNext[p1] = p2;
+
+  while (p1 < maxCurveSplits) {
+
+    // get the next segment
+    xl0 = cx[p1][0];  yl0 = cy[p1][0];
+    xx1 = cx[p1][1];  yy1 = cy[p1][1];
+    xx2 = cx[p1][2];  yy2 = cy[p1][2];
+    p2 = cNext[p1];
+    xr3 = cx[p2][0];  yr3 = cy[p2][0];
+
+    // compute the distances from the control points to the
+    // midpoint of the straight line (this is a bit of a hack, but
+    // it's much faster than computing the actual distances to the
+    // line)
+    mx = (xl0 + xr3) * 0.5;
+    my = (yl0 + yr3) * 0.5;
+    dx = xx1 - mx;
+    dy = yy1 - my;
+    d1 = dx*dx + dy*dy;
+    dx = xx2 - mx;
+    dy = yy2 - my;
+    d2 = dx*dx + dy*dy;
+
+    // if the curve is flat enough, or no more subdivisions are
+    // allowed, add the straight line segment
+    if (p2 - p1 == 1 || (d1 <= flatness2 && d2 <= flatness2)) {
+      addSegment(xl0, yl0, xr3, yr3,
+                p1 == 0 && first,
+                p2 == maxCurveSplits && last,
+                p1 == 0 && end0,
+                p2 == maxCurveSplits && end1);
+      p1 = p2;
+
+    // otherwise, subdivide the curve
+    } else {
+      xl1 = (xl0 + xx1) * 0.5;
+      yl1 = (yl0 + yy1) * 0.5;
+      xh = (xx1 + xx2) * 0.5;
+      yh = (yy1 + yy2) * 0.5;
+      xl2 = (xl1 + xh) * 0.5;
+      yl2 = (yl1 + yh) * 0.5;
+      xr2 = (xx2 + xr3) * 0.5;
+      yr2 = (yy2 + yr3) * 0.5;
+      xr1 = (xh + xr2) * 0.5;
+      yr1 = (yh + yr2) * 0.5;
+      xr0 = (xl2 + xr1) * 0.5;
+      yr0 = (yl2 + yr1) * 0.5;
+      // add the new subdivision points
+      p3 = (p1 + p2) / 2;
+      cx[p1][1] = xl1;  cy[p1][1] = yl1;
+      cx[p1][2] = xl2;  cy[p1][2] = yl2;
+      cNext[p1] = p3;
+      cx[p3][0] = xr0;  cy[p3][0] = yr0;
+      cx[p3][1] = xr1;  cy[p3][1] = yr1;
+      cx[p3][2] = xr2;  cy[p3][2] = yr2;
+      cNext[p3] = p2;
+    }
+  }
+}
+
+void SplashXPath::addArc(SplashCoord x0, SplashCoord y0,
+                        SplashCoord x1, SplashCoord y1,
+                        SplashCoord xc, SplashCoord yc,
+                        SplashCoord r, int quad,
+                        SplashCoord flatness,
+                        GBool first, GBool last, GBool end0, GBool end1) {
+  SplashCoord px[maxCurveSplits + 1];
+  SplashCoord py[maxCurveSplits + 1];
+  int pNext[maxCurveSplits + 1];
+  SplashCoord r2, flatness2;
+  SplashCoord xx0, yy0, xx1, yy1, xm, ym, t, dx, dy;
+  int p1, p2, p3;
+
+  r2 = r * r;
+  flatness2 = flatness * flatness;
+
+  // initial segment
+  p1 = 0;
+  p2 = maxCurveSplits;
+  px[p1] = x0;  py[p1] = y0;
+  px[p2] = x1;  py[p2] = y1;
+  pNext[p1] = p2;
+
+  while (p1 < maxCurveSplits) {
+
+    // get the next segment
+    xx0 = px[p1];  yy0 = py[p1];
+    p2 = pNext[p1];
+    xx1 = px[p2];  yy1 = py[p2];
+
+    // compute the arc midpoint
+    t = (xx0 - xc) * (xx1 - xc) - (yy0 - yc) * (yy1 - yc);
+    xm = splashSqrt(0.5 * (r2 + t));
+    ym = splashSqrt(0.5 * (r2 - t));
+    switch (quad) {
+    case 0: xm = xc - xm;  ym = yc - ym;  break;
+    case 1: xm = xc + xm;  ym = yc - ym;  break;
+    case 2: xm = xc + xm;  ym = yc + ym;  break;
+    case 3: xm = xc - xm;  ym = yc + ym;  break;
+    }
+
+    // compute distance from midpoint of straight segment to midpoint
+    // of arc
+    dx = 0.5 * (xx0 + xx1) - xm;
+    dy = 0.5 * (yy0 + yy1) - ym;
+
+    // if the arc is flat enough, or no more subdivisions are allowed,
+    // add the straight line segment
+    if (p2 - p1 == 1 || dx * dx + dy * dy <= flatness2) {
+      addSegment(xx0, yy0, xx1, yy1,
+                p1 == 0 && first,
+                p2 == maxCurveSplits && last,
+                p1 == 0 && end0,
+                p2 == maxCurveSplits && end1);
+      p1 = p2;
+
+    // otherwise, subdivide the arc
+    } else {
+      p3 = (p1 + p2) / 2;
+      px[p3] = xm;
+      py[p3] = ym;
+      pNext[p1] = p3;
+      pNext[p3] = p2;
+    }
+  }
+}
+
+void SplashXPath::addSegment(SplashCoord x0, SplashCoord y0,
+                            SplashCoord x1, SplashCoord y1,
+                            GBool first, GBool last, GBool end0, GBool end1) {
+  grow(1);
+  segs[length].x0 = x0;
+  segs[length].y0 = y0;
+  segs[length].x1 = x1;
+  segs[length].y1 = y1;
+  segs[length].flags = 0;
+  if (first) {
+    segs[length].flags |= splashXPathFirst;
+  }
+  if (last) {
+    segs[length].flags |= splashXPathLast;
+  }
+  if (end0) {
+    segs[length].flags |= splashXPathEnd0;
+  }
+  if (end1) {
+    segs[length].flags |= splashXPathEnd1;
+  }
+  if (y1 == y0) {
+    segs[length].dxdy = segs[length].dydx = 0;
+    segs[length].flags |= splashXPathHoriz;
+    if (x1 == x0) {
+      segs[length].flags |= splashXPathVert;
+    }
+  } else if (x1 == x0) {
+    segs[length].dxdy = segs[length].dydx = 0;
+    segs[length].flags |= splashXPathVert;
+  } else {
+    segs[length].dxdy = (x1 - x0) / (y1 - y0);
+    segs[length].dydx = 1 / segs[length].dxdy;
+  }
+  if (y0 > y1) {
+    segs[length].flags |= splashXPathFlip;
+  }
+  ++length;
+}
+
+static int cmpXPathSegs(const void *arg0, const void *arg1) {
+  SplashXPathSeg *seg0 = (SplashXPathSeg *)arg0;
+  SplashXPathSeg *seg1 = (SplashXPathSeg *)arg1;
+  SplashCoord x0, y0, x1, y1;
+
+  if (seg0->flags & splashXPathFlip) {
+    x0 = seg0->x1;
+    y0 = seg0->y1;
+  } else {
+    x0 = seg0->x0;
+    y0 = seg0->y0;
+  }
+  if (seg1->flags & splashXPathFlip) {
+    x1 = seg1->x1;
+    y1 = seg1->y1;
+  } else {
+    x1 = seg1->x0;
+    y1 = seg1->y0;
+  }
+  if (y0 != y1) {
+    return (y0 > y1) ? 1 : -1;
+  }
+  if (x0 != x1) {
+    return (x0 > x1) ? 1 : -1;
+  }
+  return 0;
+}
+
+void SplashXPath::sort() {
+  qsort(segs, length, sizeof(SplashXPathSeg), &cmpXPathSegs);
+}
diff --git a/pdf/splash/SplashXPath.h b/pdf/splash/SplashXPath.h
new file mode 100644 (file)
index 0000000..a9fe9a2
--- /dev/null
@@ -0,0 +1,92 @@
+//========================================================================
+//
+// SplashXPath.h
+//
+//========================================================================
+
+#ifndef SPLASHXPATH_H
+#define SPLASHXPATH_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#include "SplashTypes.h"
+
+class SplashPath;
+
+//------------------------------------------------------------------------
+// SplashXPathSeg
+//------------------------------------------------------------------------
+
+struct SplashXPathSeg {
+  SplashCoord x0, y0;          // first endpoint
+  SplashCoord x1, y1;          // second endpoint
+  SplashCoord dxdy;            // slope: delta-x / delta-y
+  SplashCoord dydx;            // slope: delta-y / delta-x
+  Guint flags;
+};
+
+#define splashXPathFirst   0x01        // first segment of a subpath
+#define splashXPathLast    0x02        // last segment of a subpath
+#define splashXPathEnd0    0x04        // first endpoint is end of an open subpath
+#define splashXPathEnd1    0x08 // second endpoint is end of an open subpath
+#define splashXPathHoriz   0x10 // segment is vertical (y0 == y1)
+                               //   (dxdy is undef)
+#define splashXPathVert    0x20 // segment is horizontal (x0 == x1)
+                               //   (dydx is undef)
+#define splashXPathFlip           0x40 // y0 > y1
+
+//------------------------------------------------------------------------
+// SplashXPath
+//------------------------------------------------------------------------
+
+class SplashXPath {
+public:
+
+  // Expands (converts to segments) and flattens (converts curves to
+  // lines) <path>.  If <closeSubpaths> is true, closes all open
+  // subpaths.
+  SplashXPath(SplashPath *path, SplashCoord flatness,
+             GBool closeSubpaths);
+
+  // Copy an expanded path.
+  SplashXPath *copy() { return new SplashXPath(this); }
+
+  ~SplashXPath();
+
+  // Sort by upper coordinate (lower y), in y-major order.
+  void sort();
+
+private:
+
+  SplashXPath();
+  SplashXPath(SplashXPath *xPath);
+  void grow(int nSegs);
+  void addCurve(SplashCoord x0, SplashCoord y0,
+               SplashCoord x1, SplashCoord y1,
+               SplashCoord x2, SplashCoord y2,
+               SplashCoord x3, SplashCoord y3,
+               SplashCoord flatness,
+               GBool first, GBool last, GBool end0, GBool end1);
+  void addArc(SplashCoord x0, SplashCoord y0,
+             SplashCoord x1, SplashCoord y1,
+             SplashCoord xc, SplashCoord yc,
+             SplashCoord r, int quad,
+             SplashCoord flatness,
+             GBool first, GBool last, GBool end0, GBool end1);
+  void addSegment(SplashCoord x0, SplashCoord y0,
+                 SplashCoord x1, SplashCoord y1,
+                 GBool first, GBool last, GBool end0, GBool end1);
+
+  SplashXPathSeg *segs;
+  int length, size;            // length and size of segs array
+
+  friend class SplashXPathScanner;
+  friend class SplashClip;
+  friend class Splash;
+};
+
+#endif
diff --git a/pdf/splash/SplashXPathScanner.cc b/pdf/splash/SplashXPathScanner.cc
new file mode 100644 (file)
index 0000000..c93cc49
--- /dev/null
@@ -0,0 +1,271 @@
+//========================================================================
+//
+// SplashXPathScanner.cc
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <stdlib.h>
+#include "gmem.h"
+#include "SplashMath.h"
+#include "SplashXPath.h"
+#include "SplashXPathScanner.h"
+
+//------------------------------------------------------------------------
+
+struct SplashIntersect {
+  int x0, x1;                  // intersection of segment with [y, y+1)
+  int count;                   // EO/NZWN counter increment
+};
+
+static int cmpIntersect(const void *p0, const void *p1) {
+  return ((SplashIntersect *)p0)->x0 - ((SplashIntersect *)p1)->x0;
+}
+
+//------------------------------------------------------------------------
+// SplashXPathScanner
+//------------------------------------------------------------------------
+
+SplashXPathScanner::SplashXPathScanner(SplashXPath *xPathA, GBool eoA) {
+  SplashXPathSeg *seg;
+  SplashCoord xMinFP, yMinFP, xMaxFP, yMaxFP;
+  int i;
+
+  xPath = xPathA;
+  eo = eoA;
+
+  // compute the bbox
+  seg = &xPath->segs[0];
+  if (seg->x0 <= seg->x1) {
+    xMinFP = seg->x0;
+    xMaxFP = seg->x1;
+  } else {
+    xMinFP = seg->x1;
+    xMaxFP = seg->x0;
+  }
+  if (seg->flags & splashXPathFlip) {
+    yMinFP = seg->y1;
+    yMaxFP = seg->y0;
+  } else {
+    yMinFP = seg->y0;
+    yMaxFP = seg->y1;
+  }
+  for (i = 1; i < xPath->length; ++i) {
+    seg = &xPath->segs[i];
+    if (seg->x0 < xMinFP) {
+      xMinFP = seg->x0;
+    } else if (seg->x0 > xMaxFP) {
+      xMaxFP = seg->x0;
+    }
+    if (seg->x1 < xMinFP) {
+      xMinFP = seg->x1;
+    } else if (seg->x1 > xMaxFP) {
+      xMaxFP = seg->x1;
+    }
+    if (seg->flags & splashXPathFlip) {
+      if (seg->y0 > yMaxFP) {
+       yMaxFP = seg->y0;
+      }
+    } else {
+      if (seg->y1 > yMaxFP) {
+       yMaxFP = seg->y1;
+      }
+    }
+  }
+  xMin = splashFloor(xMinFP);
+  xMax = splashFloor(xMaxFP);
+  yMin = splashFloor(yMinFP);
+  yMax = splashFloor(yMaxFP);
+
+  interY = 0;
+  xPathIdx = 0;
+  inter = NULL;
+  interLen = interSize = 0;
+  computeIntersections(yMin);
+}
+
+SplashXPathScanner::~SplashXPathScanner() {
+  gfree(inter);
+}
+
+void SplashXPathScanner::getSpanBounds(int y, int *spanXMin, int *spanXMax) {
+  if (interY != y) {
+    computeIntersections(y);
+  }
+  if (interLen > 0) {
+    *spanXMin = inter[0].x0;
+    *spanXMax = inter[interLen - 1].x1;
+  } else {
+    *spanXMin = xMax + 1;
+    *spanXMax = xMax;
+  }
+}
+
+GBool SplashXPathScanner::test(int x, int y) {
+  int count, i;
+
+  if (interY != y) {
+    computeIntersections(y);
+  }
+  count = 0;
+  for (i = 0; i < interLen && inter[i].x0 <= x; ++i) {
+    if (x <= inter[i].x1) {
+      return gTrue;
+    }
+    count += inter[i].count;
+  }
+  return eo ? (count & 1) : (count != 0);
+}
+
+GBool SplashXPathScanner::testSpan(int x0, int x1, int y) {
+  int count, xx1, i;
+
+  if (interY != y) {
+    computeIntersections(y);
+  }
+
+  count = 0;
+  for (i = 0; i < interLen && inter[i].x1 < x0; ++i) {
+    count += inter[i].count;
+  }
+
+  // invariant: the subspan [x0,xx1] is inside the path
+  xx1 = x0 - 1;
+  while (xx1 < x1) {
+    if (i >= interLen) {
+      return gFalse;
+    }
+    if (inter[i].x0 > xx1 + 1 &&
+       !(eo ? (count & 1) : (count != 0))) {
+      return gFalse;
+    }
+    if (inter[i].x1 > xx1) {
+      xx1 = inter[i].x1;
+    }
+    count += inter[i].count;
+    ++i;
+  }
+
+  return gTrue;
+}
+
+GBool SplashXPathScanner::getNextSpan(int y, int *x0, int *x1) {
+  int xx0, xx1;
+
+  if (interY != y) {
+    computeIntersections(y);
+  }
+  if (interIdx >= interLen) {
+    return gFalse;
+  }
+  xx0 = inter[interIdx].x0;
+  xx1 = inter[interIdx].x1;
+  interCount += inter[interIdx].count;
+  ++interIdx;
+  while (interIdx < interLen &&
+        (inter[interIdx].x0 <= xx1 ||
+         (eo ? (interCount & 1) : (interCount != 0)))) {
+    if (inter[interIdx].x1 > xx1) {
+      xx1 = inter[interIdx].x1;
+    }
+    interCount += inter[interIdx].count;
+    ++interIdx;
+  }
+  *x0 = xx0;
+  *x1 = xx1;
+  return gTrue;
+}
+
+void SplashXPathScanner::computeIntersections(int y) {
+  SplashCoord ySegMin, ySegMax, xx0, xx1;
+  SplashXPathSeg *seg;
+  int i, j;
+
+  // find the first segment that intersects [y, y+1)
+  i = (y >= interY) ? xPathIdx : 0;
+  while (i < xPath->length &&
+        xPath->segs[i].y0 < y && xPath->segs[i].y1 < y) {
+    ++i;
+  }
+  xPathIdx = i;
+
+  // find all of the segments that intersect [y, y+1) and create an
+  // Intersect element for each one
+  interLen = 0;
+  for (j = i; j < xPath->length; ++j) {
+    seg = &xPath->segs[j];
+    if (seg->flags & splashXPathFlip) {
+      ySegMin = seg->y1;
+      ySegMax = seg->y0;
+    } else {
+      ySegMin = seg->y0;
+      ySegMax = seg->y1;
+    }
+
+    // ensure that:      ySegMin < y+1
+    //              y <= ySegMax
+    if (ySegMin >= y + 1) {
+      break;
+    }
+    if (ySegMax < y) {
+      continue;
+    }
+
+    if (interLen == interSize) {
+      if (interSize == 0) {
+       interSize = 16;
+      } else {
+       interSize *= 2;
+      }
+      inter = (SplashIntersect *)grealloc(inter,
+                                         interSize * sizeof(SplashIntersect));
+    }
+
+    if (seg->flags & splashXPathHoriz) {
+      xx0 = seg->x0;
+      xx1 = seg->x1;
+    } else if (seg->flags & splashXPathVert) {
+      xx0 = xx1 = seg->x0;
+    } else {
+      if (ySegMin <= y) {
+       // intersection with top edge
+       xx0 = seg->x0 + (y - seg->y0) * seg->dxdy;
+      } else {
+       // x coord of segment endpoint with min y coord
+       xx0 = (seg->flags & splashXPathFlip) ? seg->x1 : seg->x0;
+      }
+      if (ySegMax >= y + 1) {
+       // intersection with bottom edge
+       xx1 = seg->x0 + (y + 1 - seg->y0) * seg->dxdy;
+      } else {
+       // x coord of segment endpoint with max y coord
+       xx1 = (seg->flags & splashXPathFlip) ? seg->x0 : seg->x1;
+      }
+    }
+    if (xx0 < xx1) {
+      inter[interLen].x0 = splashFloor(xx0);
+      inter[interLen].x1 = splashFloor(xx1);
+    } else {
+      inter[interLen].x0 = splashFloor(xx1);
+      inter[interLen].x1 = splashFloor(xx0);
+    }
+    if (ySegMin <= y && y < ySegMax && !(seg->flags & splashXPathHoriz)) {
+      inter[interLen].count = eo ? 1
+                                : (seg->flags & splashXPathFlip) ? 1 : -1;
+    } else {
+      inter[interLen].count = 0;
+    }
+    ++interLen;
+  }
+
+  qsort(inter, interLen, sizeof(SplashIntersect), &cmpIntersect);
+
+  interY = y;
+  interIdx = 0;
+  interCount = 0;
+}
diff --git a/pdf/splash/SplashXPathScanner.h b/pdf/splash/SplashXPathScanner.h
new file mode 100644 (file)
index 0000000..62484ba
--- /dev/null
@@ -0,0 +1,74 @@
+//========================================================================
+//
+// SplashXPathScanner.h
+//
+//========================================================================
+
+#ifndef SPLASHXPATHSCANNER_H
+#define SPLASHXPATHSCANNER_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#include "SplashTypes.h"
+
+class SplashXPath;
+struct SplashIntersect;
+
+//------------------------------------------------------------------------
+// SplashXPathScanner
+//------------------------------------------------------------------------
+
+class SplashXPathScanner {
+public:
+
+  // Create a new SplashXPathScanner object.  <xPathA> must be sorted.
+  SplashXPathScanner(SplashXPath *xPathA, GBool eoA);
+
+  ~SplashXPathScanner();
+
+  // Return the path's bounding box.
+  void getBBox(int *xMinA, int *yMinA, int *xMaxA, int *yMaxA)
+    { *xMinA = xMin; *yMinA = yMin; *xMaxA = xMax; *yMaxA = yMax; }
+
+  // Return the min/max x values for the span at <y>.
+  void getSpanBounds(int y, int *spanXMin, int *spanXMax);
+
+  // Returns true if (<x>,<y>) is inside the path.
+  GBool test(int x, int y);
+
+  // Returns true if the entire span ([<x0>,<x1>], <y>) is inside the
+  // path.
+  GBool testSpan(int x0, int x1, int y);
+
+  // Returns the next span inside the path at <y>.  If <y> is
+  // different than the previous call to getNextSpan, this returns the
+  // first span at <y>; otherwise it returns the next span (relative
+  // to the previous call to getNextSpan).  Returns false if there are
+  // no more spans at <y>.
+  GBool getNextSpan(int y, int *x0, int *x1);
+
+private:
+
+  void computeIntersections(int y);
+
+  SplashXPath *xPath;
+  GBool eo;
+  int xMin, yMin, xMax, yMax;
+
+  int interY;                  // current y value
+  int interIdx;                        // current index into <inter> - used by
+                               //   getNextSpan 
+  int interCount;              // current EO/NZWN counter - used by
+                               //   getNextSpan
+  int xPathIdx;                        // current index into <xPath> - used by
+                               //   computeIntersections
+  SplashIntersect *inter;      // intersections array for <interY>
+  int interLen;                        // number of intersections in <inter>
+  int interSize;               // size of the <inter> array
+};
+
+#endif
diff --git a/pdf/splash/vms_make.com b/pdf/splash/vms_make.com
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/pdf/xpdf/JArithmeticDecoder.cc b/pdf/xpdf/JArithmeticDecoder.cc
new file mode 100644 (file)
index 0000000..fd29744
--- /dev/null
@@ -0,0 +1,300 @@
+//========================================================================
+//
+// JArithmeticDecoder.cc
+//
+// Copyright 2002-2004 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include "Object.h"
+#include "Stream.h"
+#include "JArithmeticDecoder.h"
+
+//------------------------------------------------------------------------
+// JArithmeticDecoderStates
+//------------------------------------------------------------------------
+
+JArithmeticDecoderStats::JArithmeticDecoderStats(int contextSizeA) {
+  contextSize = contextSizeA;
+  cxTab = (Guchar *)gmalloc(contextSize * sizeof(Guchar));
+  reset();
+}
+
+JArithmeticDecoderStats::~JArithmeticDecoderStats() {
+  gfree(cxTab);
+}
+
+JArithmeticDecoderStats *JArithmeticDecoderStats::copy() {
+  JArithmeticDecoderStats *stats;
+
+  stats = new JArithmeticDecoderStats(contextSize);
+  memcpy(stats->cxTab, cxTab, contextSize);
+  return stats;
+}
+
+void JArithmeticDecoderStats::reset() {
+  memset(cxTab, 0, contextSize);
+}
+
+void JArithmeticDecoderStats::copyFrom(JArithmeticDecoderStats *stats) {
+  memcpy(cxTab, stats->cxTab, contextSize);
+}
+
+void JArithmeticDecoderStats::setEntry(Guint cx, int i, int mps) {
+  cxTab[cx] = (i << 1) + mps;
+}
+
+//------------------------------------------------------------------------
+// JArithmeticDecoder
+//------------------------------------------------------------------------
+
+Guint JArithmeticDecoder::qeTab[47] = {
+  0x56010000, 0x34010000, 0x18010000, 0x0AC10000,
+  0x05210000, 0x02210000, 0x56010000, 0x54010000,
+  0x48010000, 0x38010000, 0x30010000, 0x24010000,
+  0x1C010000, 0x16010000, 0x56010000, 0x54010000,
+  0x51010000, 0x48010000, 0x38010000, 0x34010000,
+  0x30010000, 0x28010000, 0x24010000, 0x22010000,
+  0x1C010000, 0x18010000, 0x16010000, 0x14010000,
+  0x12010000, 0x11010000, 0x0AC10000, 0x09C10000,
+  0x08A10000, 0x05210000, 0x04410000, 0x02A10000,
+  0x02210000, 0x01410000, 0x01110000, 0x00850000,
+  0x00490000, 0x00250000, 0x00150000, 0x00090000,
+  0x00050000, 0x00010000, 0x56010000
+};
+
+int JArithmeticDecoder::nmpsTab[47] = {
+   1,  2,  3,  4,  5, 38,  7,  8,  9, 10, 11, 12, 13, 29, 15, 16,
+  17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32,
+  33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 45, 46
+};
+
+int JArithmeticDecoder::nlpsTab[47] = {
+   1,  6,  9, 12, 29, 33,  6, 14, 14, 14, 17, 18, 20, 21, 14, 14,
+  15, 16, 17, 18, 19, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
+  30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 46
+};
+
+int JArithmeticDecoder::switchTab[47] = {
+  1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0,
+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+JArithmeticDecoder::JArithmeticDecoder() {
+  str = NULL;
+}
+
+JArithmeticDecoder::~JArithmeticDecoder() {
+  while (dataLen > 0) {
+    readByte();
+  }
+}
+
+inline Guint JArithmeticDecoder::readByte() {
+  if (dataLen == 0) {
+    return 0xff;
+  }
+  if (dataLen > 0) {
+    --dataLen;
+  }
+  return (Guint)str->getChar() & 0xff;
+}
+
+void JArithmeticDecoder::start() {
+  buf0 = readByte();
+  buf1 = readByte();
+
+  // INITDEC
+  c = (buf0 ^ 0xff) << 16;
+  byteIn();
+  c <<= 7;
+  ct -= 7;
+  a = 0x80000000;
+}
+
+int JArithmeticDecoder::decodeBit(Guint context,
+                                 JArithmeticDecoderStats *stats) {
+  int bit;
+  Guint qe;
+  int iCX, mpsCX;
+
+  iCX = stats->cxTab[context] >> 1;
+  mpsCX = stats->cxTab[context] & 1;
+  qe = qeTab[iCX];
+  a -= qe;
+  if (c < a) {
+    if (a & 0x80000000) {
+      bit = mpsCX;
+    } else {
+      // MPS_EXCHANGE
+      if (a < qe) {
+       bit = 1 - mpsCX;
+       if (switchTab[iCX]) {
+         stats->cxTab[context] = (nlpsTab[iCX] << 1) | (1 - mpsCX);
+       } else {
+         stats->cxTab[context] = (nlpsTab[iCX] << 1) | mpsCX;
+       }
+      } else {
+       bit = mpsCX;
+       stats->cxTab[context] = (nmpsTab[iCX] << 1) | mpsCX;
+      }
+      // RENORMD
+      do {
+       if (ct == 0) {
+         byteIn();
+       }
+       a <<= 1;
+       c <<= 1;
+       --ct;
+      } while (!(a & 0x80000000));
+    }
+  } else {
+    c -= a;
+    // LPS_EXCHANGE
+    if (a < qe) {
+      bit = mpsCX;
+      stats->cxTab[context] = (nmpsTab[iCX] << 1) | mpsCX;
+    } else {
+      bit = 1 - mpsCX;
+      if (switchTab[iCX]) {
+       stats->cxTab[context] = (nlpsTab[iCX] << 1) | (1 - mpsCX);
+      } else {
+       stats->cxTab[context] = (nlpsTab[iCX] << 1) | mpsCX;
+      }
+    }
+    a = qe;
+    // RENORMD
+    do {
+      if (ct == 0) {
+       byteIn();
+      }
+      a <<= 1;
+      c <<= 1;
+      --ct;
+    } while (!(a & 0x80000000));
+  }
+  return bit;
+}
+
+int JArithmeticDecoder::decodeByte(Guint context,
+                                  JArithmeticDecoderStats *stats) {
+  int byte;
+  int i;
+
+  byte = 0;
+  for (i = 0; i < 8; ++i) {
+    byte = (byte << 1) | decodeBit(context, stats);
+  }
+  return byte;
+}
+
+GBool JArithmeticDecoder::decodeInt(int *x, JArithmeticDecoderStats *stats) {
+  int s;
+  Guint v;
+  int i;
+
+  prev = 1;
+  s = decodeIntBit(stats);
+  if (decodeIntBit(stats)) {
+    if (decodeIntBit(stats)) {
+      if (decodeIntBit(stats)) {
+       if (decodeIntBit(stats)) {
+         if (decodeIntBit(stats)) {
+           v = 0;
+           for (i = 0; i < 32; ++i) {
+             v = (v << 1) | decodeIntBit(stats);
+           }
+           v += 4436;
+         } else {
+           v = 0;
+           for (i = 0; i < 12; ++i) {
+             v = (v << 1) | decodeIntBit(stats);
+           }
+           v += 340;
+         }
+       } else {
+         v = 0;
+         for (i = 0; i < 8; ++i) {
+           v = (v << 1) | decodeIntBit(stats);
+         }
+         v += 84;
+       }
+      } else {
+       v = 0;
+       for (i = 0; i < 6; ++i) {
+         v = (v << 1) | decodeIntBit(stats);
+       }
+       v += 20;
+      }
+    } else {
+      v = decodeIntBit(stats);
+      v = (v << 1) | decodeIntBit(stats);
+      v = (v << 1) | decodeIntBit(stats);
+      v = (v << 1) | decodeIntBit(stats);
+      v += 4;
+    }
+  } else {
+    v = decodeIntBit(stats);
+    v = (v << 1) | decodeIntBit(stats);
+  }
+
+  if (s) {
+    if (v == 0) {
+      return gFalse;
+    }
+    *x = -(int)v;
+  } else {
+    *x = (int)v;
+  }
+  return gTrue;
+}
+
+int JArithmeticDecoder::decodeIntBit(JArithmeticDecoderStats *stats) {
+  int bit;
+
+  bit = decodeBit(prev, stats);
+  if (prev < 0x100) {
+    prev = (prev << 1) | bit;
+  } else {
+    prev = (((prev << 1) | bit) & 0x1ff) | 0x100;
+  }
+  return bit;
+}
+
+Guint JArithmeticDecoder::decodeIAID(Guint codeLen,
+                                    JArithmeticDecoderStats *stats) {
+  Guint i;
+  int bit;
+
+  prev = 1;
+  for (i = 0; i < codeLen; ++i) {
+    bit = decodeBit(prev, stats);
+    prev = (prev << 1) | bit;
+  }
+  return prev - (1 << codeLen);
+}
+
+void JArithmeticDecoder::byteIn() {
+  if (buf0 == 0xff) {
+    if (buf1 > 0x8f) {
+      ct = 8;
+    } else {
+      buf0 = buf1;
+      buf1 = readByte();
+      c = c + 0xfe00 - (buf0 << 9);
+      ct = 7;
+    }
+  } else {
+    buf0 = buf1;
+    buf1 = readByte();
+    c = c + 0xff00 - (buf0 << 8);
+    ct = 8;
+  }
+}
diff --git a/pdf/xpdf/JArithmeticDecoder.h b/pdf/xpdf/JArithmeticDecoder.h
new file mode 100644 (file)
index 0000000..a348017
--- /dev/null
@@ -0,0 +1,91 @@
+//========================================================================
+//
+// JArithmeticDecoder.h
+//
+// Arithmetic decoder used by the JBIG2 and JPEG2000 decoders.
+//
+// Copyright 2002-2004 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef JARITHMETICDECODER_H
+#define JARITHMETICDECODER_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#include "gtypes.h"
+
+class Stream;
+
+//------------------------------------------------------------------------
+// JArithmeticDecoderStats
+//------------------------------------------------------------------------
+
+class JArithmeticDecoderStats {
+public:
+
+  JArithmeticDecoderStats(int contextSizeA);
+  ~JArithmeticDecoderStats();
+  JArithmeticDecoderStats *copy();
+  void reset();
+  int getContextSize() { return contextSize; }
+  void copyFrom(JArithmeticDecoderStats *stats);
+  void setEntry(Guint cx, int i, int mps);
+
+private:
+
+  Guchar *cxTab;               // cxTab[cx] = (i[cx] << 1) + mps[cx]
+  int contextSize;
+
+  friend class JArithmeticDecoder;
+};
+
+//------------------------------------------------------------------------
+// JArithmeticDecoder
+//------------------------------------------------------------------------
+
+class JArithmeticDecoder {
+public:
+
+  JArithmeticDecoder();
+  ~JArithmeticDecoder();
+  void setStream(Stream *strA)
+    { str = strA; dataLen = -1; }
+  void setStream(Stream *strA, int dataLenA)
+    { str = strA; dataLen = dataLenA; }
+  void start();
+  int decodeBit(Guint context, JArithmeticDecoderStats *stats);
+  int decodeByte(Guint context, JArithmeticDecoderStats *stats);
+
+  // Returns false for OOB, otherwise sets *<x> and returns true.
+  GBool decodeInt(int *x, JArithmeticDecoderStats *stats);
+
+  Guint decodeIAID(Guint codeLen,
+                  JArithmeticDecoderStats *stats);
+
+private:
+
+  Guint readByte();
+  int decodeIntBit(JArithmeticDecoderStats *stats);
+  void byteIn();
+
+  static Guint qeTab[47];
+  static int nmpsTab[47];
+  static int nlpsTab[47];
+  static int switchTab[47];
+
+  Guint buf0, buf1;
+  Guint c, a;
+  int ct;
+
+  Guint prev;                  // for the integer decoder
+
+  Stream *str;
+  int dataLen;
+};
+
+#endif
diff --git a/pdf/xpdf/JPXStream.cc b/pdf/xpdf/JPXStream.cc
new file mode 100644 (file)
index 0000000..defa7d2
--- /dev/null
@@ -0,0 +1,2822 @@
+//========================================================================
+//
+// JPXStream.cc
+//
+// Copyright 2002-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include "gmem.h"
+#include "Error.h"
+#include "JArithmeticDecoder.h"
+#include "JPXStream.h"
+
+//~ to do:
+//  - precincts
+//  - ROI
+//  - progression order changes
+//  - packed packet headers
+//  - support for palettes, channel maps, etc.
+//  - make sure all needed JP2/JPX subboxes are parsed (readBoxes)
+//  - can we assume that QCC segments must come after the QCD segment?
+//  - skip EPH markers (readTilePartData)
+//  - handle tilePartToEOC in readTilePartData
+//  - deal with multiple codeword segments (readTilePartData,
+//    readCodeBlockData)
+//  - progression orders 2, 3, and 4
+//  - in coefficient decoding (readCodeBlockData):
+//    - termination pattern: terminate after every coding pass
+//    - error resilience segmentation symbol
+//    - selective arithmetic coding bypass
+//    - vertically causal context formation
+//    - coeffs longer than 31 bits (should just ignore the extra bits?)
+//  - handle boxes larger than 2^32 bytes
+//  - the fixed-point arithmetic won't handle 16-bit pixels
+
+//------------------------------------------------------------------------
+
+// number of contexts for the arithmetic decoder
+#define jpxNContexts        19
+
+#define jpxContextSigProp    0 // 0 - 8: significance prop and cleanup
+#define jpxContextSign       9 // 9 - 13: sign
+#define jpxContextMagRef    14 // 14 -16: magnitude refinement
+#define jpxContextRunLength 17 // cleanup: run length
+#define jpxContextUniform   18 // cleanup: first signif coeff
+
+//------------------------------------------------------------------------
+
+#define jpxPassSigProp       0
+#define jpxPassMagRef        1
+#define jpxPassCleanup       2
+
+//------------------------------------------------------------------------
+
+// arithmetic decoder context for the significance propagation and
+// cleanup passes:
+//     [horiz][vert][diag][subband]
+// where subband = 0 for HL
+//               = 1 for LH and LL
+//               = 2 for HH
+static Guint sigPropContext[3][3][5][3] = {
+  {{{ 0, 0, 0 },   // horiz=0, vert=0, diag=0
+    { 1, 1, 3 },   // horiz=0, vert=0, diag=1
+    { 2, 2, 6 },   // horiz=0, vert=0, diag=2
+    { 2, 2, 8 },   // horiz=0, vert=0, diag=3
+    { 2, 2, 8 }},  // horiz=0, vert=0, diag=4
+   {{ 5, 3, 1 },   // horiz=0, vert=1, diag=0
+    { 6, 3, 4 },   // horiz=0, vert=1, diag=1
+    { 6, 3, 7 },   // horiz=0, vert=1, diag=2
+    { 6, 3, 8 },   // horiz=0, vert=1, diag=3
+    { 6, 3, 8 }},  // horiz=0, vert=1, diag=4
+   {{ 8, 4, 2 },   // horiz=0, vert=2, diag=0
+    { 8, 4, 5 },   // horiz=0, vert=2, diag=1
+    { 8, 4, 7 },   // horiz=0, vert=2, diag=2
+    { 8, 4, 8 },   // horiz=0, vert=2, diag=3
+    { 8, 4, 8 }}}, // horiz=0, vert=2, diag=4
+  {{{ 3, 5, 1 },   // horiz=1, vert=0, diag=0
+    { 3, 6, 4 },   // horiz=1, vert=0, diag=1
+    { 3, 6, 7 },   // horiz=1, vert=0, diag=2
+    { 3, 6, 8 },   // horiz=1, vert=0, diag=3
+    { 3, 6, 8 }},  // horiz=1, vert=0, diag=4
+   {{ 7, 7, 2 },   // horiz=1, vert=1, diag=0
+    { 7, 7, 5 },   // horiz=1, vert=1, diag=1
+    { 7, 7, 7 },   // horiz=1, vert=1, diag=2
+    { 7, 7, 8 },   // horiz=1, vert=1, diag=3
+    { 7, 7, 8 }},  // horiz=1, vert=1, diag=4
+   {{ 8, 7, 2 },   // horiz=1, vert=2, diag=0
+    { 8, 7, 5 },   // horiz=1, vert=2, diag=1
+    { 8, 7, 7 },   // horiz=1, vert=2, diag=2
+    { 8, 7, 8 },   // horiz=1, vert=2, diag=3
+    { 8, 7, 8 }}}, // horiz=1, vert=2, diag=4
+  {{{ 4, 8, 2 },   // horiz=2, vert=0, diag=0
+    { 4, 8, 5 },   // horiz=2, vert=0, diag=1
+    { 4, 8, 7 },   // horiz=2, vert=0, diag=2
+    { 4, 8, 8 },   // horiz=2, vert=0, diag=3
+    { 4, 8, 8 }},  // horiz=2, vert=0, diag=4
+   {{ 7, 8, 2 },   // horiz=2, vert=1, diag=0
+    { 7, 8, 5 },   // horiz=2, vert=1, diag=1
+    { 7, 8, 7 },   // horiz=2, vert=1, diag=2
+    { 7, 8, 8 },   // horiz=2, vert=1, diag=3
+    { 7, 8, 8 }},  // horiz=2, vert=1, diag=4
+   {{ 8, 8, 2 },   // horiz=2, vert=2, diag=0
+    { 8, 8, 5 },   // horiz=2, vert=2, diag=1
+    { 8, 8, 7 },   // horiz=2, vert=2, diag=2
+    { 8, 8, 8 },   // horiz=2, vert=2, diag=3
+    { 8, 8, 8 }}}  // horiz=2, vert=2, diag=4
+};
+
+// arithmetic decoder context and xor bit for the sign bit in the
+// significance propagation pass:
+//     [horiz][vert][k]
+// where horiz/vert are offset by 2 (i.e., range is -2 .. 2)
+// and k = 0 for the context
+//       = 1 for the xor bit
+static Guint signContext[5][5][2] = {
+  {{ 13, 1 },  // horiz=-2, vert=-2
+   { 13, 1 },  // horiz=-2, vert=-1
+   { 12, 1 },  // horiz=-2, vert= 0
+   { 11, 1 },  // horiz=-2, vert=+1
+   { 11, 1 }}, // horiz=-2, vert=+2
+  {{ 13, 1 },  // horiz=-1, vert=-2
+   { 13, 1 },  // horiz=-1, vert=-1
+   { 12, 1 },  // horiz=-1, vert= 0
+   { 11, 1 },  // horiz=-1, vert=+1
+   { 11, 1 }}, // horiz=-1, vert=+2
+  {{ 10, 1 },  // horiz= 0, vert=-2
+   { 10, 1 },  // horiz= 0, vert=-1
+   {  9, 0 },  // horiz= 0, vert= 0
+   { 10, 0 },  // horiz= 0, vert=+1
+   { 10, 0 }}, // horiz= 0, vert=+2
+  {{ 11, 0 },  // horiz=+1, vert=-2
+   { 11, 0 },  // horiz=+1, vert=-1
+   { 12, 0 },  // horiz=+1, vert= 0
+   { 13, 0 },  // horiz=+1, vert=+1
+   { 13, 0 }}, // horiz=+1, vert=+2
+  {{ 11, 0 },  // horiz=+2, vert=-2
+   { 11, 0 },  // horiz=+2, vert=-1
+   { 12, 0 },  // horiz=+2, vert= 0
+   { 13, 0 },  // horiz=+2, vert=+1
+   { 13, 0 }}, // horiz=+2, vert=+2
+};
+
+//------------------------------------------------------------------------
+
+// constants used in the IDWT
+#define idwtAlpha  -1.586134342059924
+#define idwtBeta   -0.052980118572961
+#define idwtGamma   0.882911075530934
+#define idwtDelta   0.443506852043971
+#define idwtKappa   1.230174104914001
+#define idwtIKappa  (1.0 / idwtKappa)
+
+// number of bits to the right of the decimal point for the fixed
+// point arithmetic used in the IDWT
+#define fracBits 16
+
+//------------------------------------------------------------------------
+
+// floor(x / y)
+#define jpxFloorDiv(x, y) ((x) / (y))
+
+// floor(x / 2^y)
+#define jpxFloorDivPow2(x, y) ((x) >> (y))
+
+// ceil(x / y)
+#define jpxCeilDiv(x, y) (((x) + (y) - 1) / (y))
+
+// ceil(x / 2^y)
+#define jpxCeilDivPow2(x, y) (((x) + (1 << (y)) - 1) >> (y))
+
+//------------------------------------------------------------------------
+
+JPXStream::JPXStream(Stream *strA):
+  FilterStream(strA)
+{
+  nComps = 0;
+  bpc = NULL;
+  width = height = 0;
+  haveCS = gFalse;
+  havePalette = gFalse;
+  haveCompMap = gFalse;
+  haveChannelDefn = gFalse;
+
+  img.tiles = NULL;
+  bitBuf = 0;
+  bitBufLen = 0;
+  bitBufSkip = gFalse;
+  byteCount = 0;
+}
+
+JPXStream::~JPXStream() {
+  JPXTile *tile;
+  JPXTileComp *tileComp;
+  JPXResLevel *resLevel;
+  JPXPrecinct *precinct;
+  JPXSubband *subband;
+  JPXCodeBlock *cb;
+  Guint comp, i, k, r, pre, sb;
+
+  gfree(bpc);
+  if (havePalette) {
+    gfree(palette.bpc);
+    gfree(palette.c);
+  }
+  if (haveCompMap) {
+    gfree(compMap.comp);
+    gfree(compMap.type);
+    gfree(compMap.pComp);
+  }
+  if (haveChannelDefn) {
+    gfree(channelDefn.idx);
+    gfree(channelDefn.type);
+    gfree(channelDefn.assoc);
+  }
+
+  if (img.tiles) {
+    for (i = 0; i < img.nXTiles * img.nYTiles; ++i) {
+      tile = &img.tiles[i];
+      if (tile->tileComps) {
+       for (comp = 0; comp < img.nComps; ++comp) {
+         tileComp = &tile->tileComps[comp];
+         gfree(tileComp->quantSteps);
+         gfree(tileComp->data);
+         gfree(tileComp->buf);
+         if (tileComp->resLevels) {
+           for (r = 0; r <= tileComp->nDecompLevels; ++r) {
+             resLevel = &tileComp->resLevels[r];
+             if (resLevel->precincts) {
+               for (pre = 0; pre < 1; ++pre) {
+                 precinct = &resLevel->precincts[pre];
+                 if (precinct->subbands) {
+                   for (sb = 0; sb < (r == 0 ? 1 : 3); ++sb) {
+                     subband = &precinct->subbands[sb];
+                     gfree(subband->inclusion);
+                     gfree(subband->zeroBitPlane);
+                     if (subband->cbs) {
+                       for (k = 0; k < subband->nXCBs * subband->nYCBs; ++k) {
+                         cb = &subband->cbs[k];
+                         gfree(cb->coeffs);
+                         if (cb->stats) {
+                           delete cb->stats;
+                         }
+                       }
+                       gfree(subband->cbs);
+                     }
+                   }
+                   gfree(precinct->subbands);
+                 }
+               }
+               gfree(img.tiles[i].tileComps[comp].resLevels[r].precincts);
+             }
+           }
+           gfree(img.tiles[i].tileComps[comp].resLevels);
+         }
+       }
+       gfree(img.tiles[i].tileComps);
+      }
+    }
+    gfree(img.tiles);
+  }
+  delete str;
+}
+
+void JPXStream::reset() {
+  str->reset();
+  if (readBoxes()) {
+    curY = img.yOffset;
+  } else {
+    // readBoxes reported an error, so we go immediately to EOF
+    curY = img.ySize;
+  }
+  curX = img.xOffset;
+  curComp = 0;
+  readBufLen = 0;
+}
+
+int JPXStream::getChar() {
+  int c;
+
+  if (readBufLen < 8) {
+    fillReadBuf();
+  }
+  if (readBufLen == 8) {
+    c = readBuf & 0xff;
+    readBufLen = 0;
+  } else if (readBufLen > 8) {
+    c = (readBuf >> (readBufLen - 8)) & 0xff;
+    readBufLen -= 8;
+  } else if (readBufLen == 0) {
+    c = EOF;
+  } else {
+    c = (readBuf << (8 - readBufLen)) & 0xff;
+    readBufLen = 0;
+  }
+  return c;
+}
+
+int JPXStream::lookChar() {
+  int c;
+
+  if (readBufLen < 8) {
+    fillReadBuf();
+  }
+  if (readBufLen == 8) {
+    c = readBuf & 0xff;
+  } else if (readBufLen > 8) {
+    c = (readBuf >> (readBufLen - 8)) & 0xff;
+  } else if (readBufLen == 0) {
+    c = EOF;
+  } else {
+    c = (readBuf << (8 - readBufLen)) & 0xff;
+  }
+  return c;
+}
+
+void JPXStream::fillReadBuf() {
+  JPXTileComp *tileComp;
+  Guint tileIdx, tx, ty;
+  int pix, pixBits;
+
+  do {
+    if (curY >= img.ySize) {
+      return;
+    }
+    tileIdx = ((curY - img.yTileOffset) / img.yTileSize) * img.nXTiles
+              + (curX - img.xTileOffset) / img.xTileSize;
+#if 1 //~ ignore the palette, assume the PDF ColorSpace object is valid
+    tileComp = &img.tiles[tileIdx].tileComps[curComp];
+#else
+    tileComp = &img.tiles[tileIdx].tileComps[havePalette ? 0 : curComp];
+#endif
+    tx = jpxCeilDiv((curX - img.xTileOffset) % img.xTileSize, tileComp->hSep);
+    ty = jpxCeilDiv((curY - img.yTileOffset) % img.yTileSize, tileComp->vSep);
+    pix = (int)tileComp->data[ty * (tileComp->x1 - tileComp->x0) + tx];
+    pixBits = tileComp->prec;
+#if 1 //~ ignore the palette, assume the PDF ColorSpace object is valid
+    if (++curComp == img.nComps) {
+#else
+    if (havePalette) {
+      if (pix >= 0 && pix < palette.nEntries) {
+       pix = palette.c[pix * palette.nComps + curComp];
+      } else {
+       pix = 
+      pixBits = palette.bpc[curComp];
+    }
+    if (++curComp == (Guint)(havePalette ? palette.nComps : img.nComps)) {
+#endif
+      curComp = 0;
+      if (++curX == img.xSize) {
+       curX = img.xOffset;
+       ++curY;
+      }
+    }
+    if (pixBits == 8) {
+      readBuf = (readBuf << 8) | (pix & 0xff);
+    } else {
+      readBuf = (readBuf << pixBits) | (pix & ((1 << pixBits) - 1));
+    }
+    readBufLen += pixBits;
+  } while (readBufLen < 8);
+}
+
+GString *JPXStream::getPSFilter(int psLevel, char *indent) {
+  return NULL;
+}
+
+GBool JPXStream::isBinary(GBool last) {
+  return str->isBinary(gTrue);
+}
+
+GBool JPXStream::readBoxes() {
+  Guint boxType, boxLen, dataLen;
+  Guint bpc1, compression, unknownColorspace, ipr;
+  Guint i, j;
+
+  haveImgHdr = gFalse;
+
+  // check for a naked JPEG 2000 codestream (without the JP2/JPX
+  // wrapper) -- this appears to be a violation of the PDF spec, but
+  // Acrobat allows it
+  if (str->lookChar() == 0xff) {
+    error(getPos(), "Naked JPEG 2000 codestream, missing JP2/JPX wrapper");
+    readCodestream(0);
+    nComps = img.nComps;
+    bpc = (Guint *)gmalloc(nComps * sizeof(Guint));
+    for (i = 0; i < nComps; ++i) {
+      bpc[i] = img.tiles[0].tileComps[i].prec;
+    }
+    width = img.xSize - img.xOffset;
+    height = img.ySize - img.yOffset;
+    return gTrue;
+  }
+
+  while (readBoxHdr(&boxType, &boxLen, &dataLen)) {
+    switch (boxType) {
+    case 0x6a703268:           // JP2 header
+      // this is a grouping box ('superbox') which has no real
+      // contents and doesn't appear to be used consistently, i.e.,
+      // some things which should be subboxes of the JP2 header box
+      // show up outside of it - so we simply ignore the JP2 header
+      // box
+      break;
+    case 0x69686472:           // image header
+      if (!readULong(&height) ||
+         !readULong(&width) ||
+         !readUWord(&nComps) ||
+         !readUByte(&bpc1) ||
+         !readUByte(&compression) ||
+         !readUByte(&unknownColorspace) ||
+         !readUByte(&ipr)) {
+       error(getPos(), "Unexpected EOF in JPX stream");
+       return gFalse;
+      }
+      if (compression != 7) {
+       error(getPos(), "Unknown compression type in JPX stream");
+       return gFalse;
+      }
+      bpc = (Guint *)gmalloc(nComps * sizeof(Guint));
+      for (i = 0; i < nComps; ++i) {
+       bpc[i] = bpc1;
+      }
+      haveImgHdr = gTrue;
+      break;
+    case 0x62706363:           // bits per component
+      if (!haveImgHdr) {
+       error(getPos(), "Found bits per component box before image header box in JPX stream");
+       return gFalse;
+      }
+      if (dataLen != nComps) {
+       error(getPos(), "Invalid bits per component box in JPX stream");
+       return gFalse;
+      }
+      for (i = 0; i < nComps; ++i) {
+       if (!readUByte(&bpc[i])) {
+         error(getPos(), "Unexpected EOF in JPX stream");
+         return gFalse;
+       }
+      }
+      break;
+    case 0x636F6C72:           // color specification
+      if (!readColorSpecBox(dataLen)) {
+       return gFalse;
+      }
+      break;
+    case 0x70636c72:           // palette
+      if (!readUWord(&palette.nEntries) ||
+         !readUByte(&palette.nComps)) {
+       error(getPos(), "Unexpected EOF in JPX stream");
+       return gFalse;
+      }
+      palette.bpc = (Guint *)gmalloc(palette.nComps * sizeof(Guint));
+      palette.c =
+          (int *)gmalloc(palette.nEntries * palette.nComps * sizeof(int));
+      for (i = 0; i < palette.nComps; ++i) {
+       if (!readUByte(&palette.bpc[i])) {
+         error(getPos(), "Unexpected EOF in JPX stream");
+         return gFalse;
+       }
+       ++palette.bpc[i];
+      }
+      for (i = 0; i < palette.nEntries; ++i) {
+       for (j = 0; j < palette.nComps; ++j) {
+         if (!readNBytes(((palette.bpc[j] & 0x7f) + 7) >> 3,
+                         (palette.bpc[j] & 0x80) ? gTrue : gFalse,
+                         &palette.c[i * palette.nComps + j])) {
+           error(getPos(), "Unexpected EOF in JPX stream");
+           return gFalse;
+         }
+       }
+      }
+      havePalette = gTrue;
+      break;
+    case 0x636d6170:           // component mapping
+      compMap.nChannels = dataLen / 4;
+      compMap.comp = (Guint *)gmalloc(compMap.nChannels * sizeof(Guint));
+      compMap.type = (Guint *)gmalloc(compMap.nChannels * sizeof(Guint));
+      compMap.pComp = (Guint *)gmalloc(compMap.nChannels * sizeof(Guint));
+      for (i = 0; i < compMap.nChannels; ++i) {
+       if (!readUWord(&compMap.comp[i]) ||
+           !readUByte(&compMap.type[i]) ||
+           !readUByte(&compMap.pComp[i])) {
+         error(getPos(), "Unexpected EOF in JPX stream");
+         return gFalse;
+       }
+      }
+      haveCompMap = gTrue;
+      break;
+    case 0x63646566:           // channel definition
+      if (!readUWord(&channelDefn.nChannels)) {
+       error(getPos(), "Unexpected EOF in JPX stream");
+       return gFalse;
+      }
+      channelDefn.idx =
+         (Guint *)gmalloc(channelDefn.nChannels * sizeof(Guint));
+      channelDefn.type =
+         (Guint *)gmalloc(channelDefn.nChannels * sizeof(Guint));
+      channelDefn.assoc =
+         (Guint *)gmalloc(channelDefn.nChannels * sizeof(Guint));
+      for (i = 0; i < channelDefn.nChannels; ++i) {
+       if (!readUWord(&channelDefn.idx[i]) ||
+           !readUWord(&channelDefn.type[i]) ||
+           !readUWord(&channelDefn.assoc[i])) {
+         error(getPos(), "Unexpected EOF in JPX stream");
+         return gFalse;
+       }
+      }
+      haveChannelDefn = gTrue;
+      break;
+    case 0x6A703263:           // contiguous codestream
+      if (!bpc) {
+       error(getPos(), "JPX stream is missing the image header box");
+       return gFalse;
+      }
+      if (!haveCS) {
+       error(getPos(), "JPX stream has no supported color spec");
+       return gFalse;
+      }
+      if (!readCodestream(dataLen)) {
+       return gFalse;
+      }
+      break;
+    default:
+      for (i = 0; i < dataLen; ++i) {
+       if (str->getChar() == EOF) {
+         error(getPos(), "Unexpected EOF in JPX stream");
+         return gFalse;
+       }
+      }
+      break;
+    }
+  }
+  return gTrue;
+}
+
+GBool JPXStream::readColorSpecBox(Guint dataLen) {
+  JPXColorSpec newCS;
+  Guint csApprox, csEnum;
+  Guint i;
+  GBool ok;
+
+  ok = gFalse;
+  if (!readUByte(&newCS.meth) ||
+      !readByte(&newCS.prec) ||
+      !readUByte(&csApprox)) {
+    goto err;
+  }
+  switch (newCS.meth) {
+  case 1:                      // enumerated colorspace
+    if (!readULong(&csEnum)) {
+      goto err;
+    }
+    newCS.enumerated.type = (JPXColorSpaceType)csEnum;
+    switch (newCS.enumerated.type) {
+    case jpxCSBiLevel:
+      ok = gTrue;
+      break;
+    case jpxCSYCbCr1:
+      ok = gTrue;
+      break;
+    case jpxCSYCbCr2:
+      ok = gTrue;
+      break;
+    case jpxCSYCBCr3:
+      ok = gTrue;
+      break;
+    case jpxCSPhotoYCC:
+      ok = gTrue;
+      break;
+    case jpxCSCMY:
+      ok = gTrue;
+      break;
+    case jpxCSCMYK:
+      ok = gTrue;
+      break;
+    case jpxCSYCCK:
+      ok = gTrue;
+      break;
+    case jpxCSCIELab:
+      if (dataLen == 3 + 7*4) {
+       if (!readULong(&newCS.enumerated.cieLab.rl) ||
+           !readULong(&newCS.enumerated.cieLab.ol) ||
+           !readULong(&newCS.enumerated.cieLab.ra) ||
+           !readULong(&newCS.enumerated.cieLab.oa) ||
+           !readULong(&newCS.enumerated.cieLab.rb) ||
+           !readULong(&newCS.enumerated.cieLab.ob) ||
+           !readULong(&newCS.enumerated.cieLab.il)) {
+         goto err;
+       }
+      } else if (dataLen == 3) {
+       //~ this assumes the 8-bit case
+       newCS.enumerated.cieLab.rl = 100;
+       newCS.enumerated.cieLab.ol = 0;
+       newCS.enumerated.cieLab.ra = 255;
+       newCS.enumerated.cieLab.oa = 128;
+       newCS.enumerated.cieLab.rb = 255;
+       newCS.enumerated.cieLab.ob = 96;
+       newCS.enumerated.cieLab.il = 0x00443530;
+      } else {
+       goto err;
+      }
+      ok = gTrue;
+      break;
+    case jpxCSsRGB:
+      ok = gTrue;
+      break;
+    case jpxCSGrayscale:
+      ok = gTrue;
+      break;
+    case jpxCSBiLevel2:
+      ok = gTrue;
+      break;
+    case jpxCSCIEJab:
+      // not allowed in PDF
+      goto err;
+    case jpxCSCISesRGB:
+      ok = gTrue;
+      break;
+    case jpxCSROMMRGB:
+      ok = gTrue;
+      break;
+    case jpxCSsRGBYCbCr:
+      ok = gTrue;
+      break;
+    case jpxCSYPbPr1125:
+      ok = gTrue;
+      break;
+    case jpxCSYPbPr1250:
+      ok = gTrue;
+      break;
+    default:
+      goto err;
+    }
+    break;
+  case 2:                      // restricted ICC profile
+  case 3:                      // any ICC profile (JPX)
+  case 4:                      // vendor color (JPX)
+    for (i = 0; i < dataLen - 3; ++i) {
+      if (str->getChar() == EOF) {
+       goto err;
+      }
+    }
+    break;
+  }
+
+  if (ok && (!haveCS || newCS.prec > cs.prec)) {
+    cs = newCS;
+    haveCS = gTrue;
+  }
+
+  return gTrue;
+
+ err:
+  error(getPos(), "Error in JPX color spec");
+  return gFalse;
+}
+
+GBool JPXStream::readCodestream(Guint len) {
+  JPXTile *tile;
+  JPXTileComp *tileComp;
+  int segType;
+  GBool haveSIZ, haveCOD, haveQCD, haveSOT;
+  Guint precinctSize, style;
+  Guint segLen, capabilities, comp, i, j, r;
+
+  //----- main header
+  haveSIZ = haveCOD = haveQCD = haveSOT = gFalse;
+  do {
+    if (!readMarkerHdr(&segType, &segLen)) {
+      error(getPos(), "Error in JPX codestream");
+      return gFalse;
+    }
+    switch (segType) {
+    case 0x4f:                 // SOC - start of codestream
+      // marker only
+      break;
+    case 0x51:                 // SIZ - image and tile size
+      if (!readUWord(&capabilities) ||
+         !readULong(&img.xSize) ||
+         !readULong(&img.ySize) ||
+         !readULong(&img.xOffset) ||
+         !readULong(&img.yOffset) ||
+         !readULong(&img.xTileSize) ||
+         !readULong(&img.yTileSize) ||
+         !readULong(&img.xTileOffset) ||
+         !readULong(&img.yTileOffset) ||
+         !readUWord(&img.nComps)) {
+       error(getPos(), "Error in JPX SIZ marker segment");
+       return gFalse;
+      }
+      if (haveImgHdr && img.nComps != nComps) {
+       error(getPos(), "Different number of components in JPX SIZ marker segment");
+       return gFalse;
+      }
+      img.nXTiles = (img.xSize - img.xTileOffset + img.xTileSize - 1)
+                   / img.xTileSize;
+      img.nYTiles = (img.ySize - img.yTileOffset + img.yTileSize - 1)
+                   / img.yTileSize;
+      img.tiles = (JPXTile *)gmalloc(img.nXTiles * img.nYTiles *
+                                    sizeof(JPXTile));
+      for (i = 0; i < img.nXTiles * img.nYTiles; ++i) {
+       img.tiles[i].tileComps = (JPXTileComp *)gmalloc(img.nComps *
+                                                       sizeof(JPXTileComp));
+       for (comp = 0; comp < img.nComps; ++comp) {
+         img.tiles[i].tileComps[comp].quantSteps = NULL;
+         img.tiles[i].tileComps[comp].data = NULL;
+         img.tiles[i].tileComps[comp].buf = NULL;
+         img.tiles[i].tileComps[comp].resLevels = NULL;
+       }
+      }
+      for (comp = 0; comp < img.nComps; ++comp) {
+       if (!readUByte(&img.tiles[0].tileComps[comp].prec) ||
+           !readUByte(&img.tiles[0].tileComps[comp].hSep) ||
+           !readUByte(&img.tiles[0].tileComps[comp].vSep)) {
+         error(getPos(), "Error in JPX SIZ marker segment");
+         return gFalse;
+       }
+       img.tiles[0].tileComps[comp].sgned =
+           (img.tiles[0].tileComps[comp].prec & 0x80) ? gTrue : gFalse;
+       img.tiles[0].tileComps[comp].prec =
+           (img.tiles[0].tileComps[comp].prec & 0x7f) + 1;
+       for (i = 1; i < img.nXTiles * img.nYTiles; ++i) {
+         img.tiles[i].tileComps[comp] = img.tiles[0].tileComps[comp];
+       }
+      }
+      haveSIZ = gTrue;
+      break;
+    case 0x52:                 // COD - coding style default
+      if (!readUByte(&img.tiles[0].tileComps[0].style) ||
+         !readUByte(&img.tiles[0].progOrder) ||
+         !readUWord(&img.tiles[0].nLayers) ||
+         !readUByte(&img.tiles[0].multiComp) ||
+         !readUByte(&img.tiles[0].tileComps[0].nDecompLevels) ||
+         !readUByte(&img.tiles[0].tileComps[0].codeBlockW) ||
+         !readUByte(&img.tiles[0].tileComps[0].codeBlockH) ||
+         !readUByte(&img.tiles[0].tileComps[0].codeBlockStyle) ||
+         !readUByte(&img.tiles[0].tileComps[0].transform)) {
+       error(getPos(), "Error in JPX COD marker segment");
+       return gFalse;
+      }
+      img.tiles[0].tileComps[0].codeBlockW += 2;
+      img.tiles[0].tileComps[0].codeBlockH += 2;
+      for (i = 0; i < img.nXTiles * img.nYTiles; ++i) {
+       if (i != 0) {
+         img.tiles[i].progOrder = img.tiles[0].progOrder;
+         img.tiles[i].nLayers = img.tiles[0].nLayers;
+         img.tiles[i].multiComp = img.tiles[0].multiComp;
+       }
+       for (comp = 0; comp < img.nComps; ++comp) {
+         if (!(i == 0 && comp == 0)) {
+           img.tiles[i].tileComps[comp].style =
+               img.tiles[0].tileComps[0].style;
+           img.tiles[i].tileComps[comp].nDecompLevels =
+               img.tiles[0].tileComps[0].nDecompLevels;
+           img.tiles[i].tileComps[comp].codeBlockW =
+               img.tiles[0].tileComps[0].codeBlockW;
+           img.tiles[i].tileComps[comp].codeBlockH =
+               img.tiles[0].tileComps[0].codeBlockH;
+           img.tiles[i].tileComps[comp].codeBlockStyle =
+               img.tiles[0].tileComps[0].codeBlockStyle;
+           img.tiles[i].tileComps[comp].transform =
+               img.tiles[0].tileComps[0].transform;
+         }
+         img.tiles[i].tileComps[comp].resLevels =
+             (JPXResLevel *)gmalloc(
+                    (img.tiles[i].tileComps[comp].nDecompLevels + 1) *
+                    sizeof(JPXResLevel));
+         for (r = 0; r <= img.tiles[i].tileComps[comp].nDecompLevels; ++r) {
+           img.tiles[i].tileComps[comp].resLevels[r].precincts = NULL;
+         }
+       }
+      }
+      for (r = 0; r <= img.tiles[0].tileComps[0].nDecompLevels; ++r) {
+       if (img.tiles[0].tileComps[0].style & 0x01) {
+         if (!readUByte(&precinctSize)) {
+           error(getPos(), "Error in JPX COD marker segment");
+           return gFalse;
+         }
+         img.tiles[0].tileComps[0].resLevels[r].precinctWidth =
+             precinctSize & 0x0f;
+         img.tiles[0].tileComps[0].resLevels[r].precinctHeight =
+             (precinctSize >> 4) & 0x0f;
+       } else {
+         img.tiles[0].tileComps[0].resLevels[r].precinctWidth = 15;
+         img.tiles[0].tileComps[0].resLevels[r].precinctHeight = 15;
+       }
+      }
+      for (i = 0; i < img.nXTiles * img.nYTiles; ++i) {
+       for (comp = 0; comp < img.nComps; ++comp) {
+         if (!(i == 0 && comp == 0)) {
+           for (r = 0; r <= img.tiles[i].tileComps[comp].nDecompLevels; ++r) {
+             img.tiles[i].tileComps[comp].resLevels[r].precinctWidth =
+                 img.tiles[0].tileComps[0].resLevels[r].precinctWidth;
+             img.tiles[i].tileComps[comp].resLevels[r].precinctHeight =
+                 img.tiles[0].tileComps[0].resLevels[r].precinctHeight;
+           }
+         }
+       }
+      }
+      haveCOD = gTrue;
+      break;
+    case 0x53:                 // COC - coding style component
+      if (!haveCOD) {
+       error(getPos(), "JPX COC marker segment before COD segment");
+       return gFalse;
+      }
+      if ((img.nComps > 256 && !readUWord(&comp)) ||
+         (img.nComps <= 256 && !readUByte(&comp)) ||
+         comp >= img.nComps ||
+         !readUByte(&style) ||
+         !readUByte(&img.tiles[0].tileComps[comp].nDecompLevels) ||
+         !readUByte(&img.tiles[0].tileComps[comp].codeBlockW) ||
+         !readUByte(&img.tiles[0].tileComps[comp].codeBlockH) ||
+         !readUByte(&img.tiles[0].tileComps[comp].codeBlockStyle) ||
+         !readUByte(&img.tiles[0].tileComps[comp].transform)) {
+       error(getPos(), "Error in JPX COC marker segment");
+       return gFalse;
+      }
+      img.tiles[0].tileComps[comp].style =
+         (img.tiles[0].tileComps[comp].style & ~1) | (style & 1);
+      img.tiles[0].tileComps[comp].codeBlockW += 2;
+      img.tiles[0].tileComps[comp].codeBlockH += 2;
+      for (i = 0; i < img.nXTiles * img.nYTiles; ++i) {
+       if (i != 0) {
+         img.tiles[i].tileComps[comp].style =
+             img.tiles[0].tileComps[comp].style;
+         img.tiles[i].tileComps[comp].nDecompLevels =
+             img.tiles[0].tileComps[comp].nDecompLevels;
+         img.tiles[i].tileComps[comp].codeBlockW =
+             img.tiles[0].tileComps[comp].codeBlockW;
+         img.tiles[i].tileComps[comp].codeBlockH =
+             img.tiles[0].tileComps[comp].codeBlockH;
+         img.tiles[i].tileComps[comp].codeBlockStyle =
+             img.tiles[0].tileComps[comp].codeBlockStyle;
+         img.tiles[i].tileComps[comp].transform =
+             img.tiles[0].tileComps[comp].transform;
+       }
+       img.tiles[i].tileComps[comp].resLevels =
+           (JPXResLevel *)grealloc(
+                    img.tiles[i].tileComps[comp].resLevels,
+                    (img.tiles[i].tileComps[comp].nDecompLevels + 1) *
+                      sizeof(JPXResLevel));
+       for (r = 0; r <= img.tiles[i].tileComps[comp].nDecompLevels; ++r) {
+         img.tiles[i].tileComps[comp].resLevels[r].precincts = NULL;
+       }
+      }
+      for (r = 0; r <= img.tiles[0].tileComps[comp].nDecompLevels; ++r) {
+       if (img.tiles[0].tileComps[comp].style & 0x01) {
+         if (!readUByte(&precinctSize)) {
+           error(getPos(), "Error in JPX COD marker segment");
+           return gFalse;
+         }
+         img.tiles[0].tileComps[comp].resLevels[r].precinctWidth =
+             precinctSize & 0x0f;
+         img.tiles[0].tileComps[comp].resLevels[r].precinctHeight =
+             (precinctSize >> 4) & 0x0f;
+       } else {
+         img.tiles[0].tileComps[comp].resLevels[r].precinctWidth = 15;
+         img.tiles[0].tileComps[comp].resLevels[r].precinctHeight = 15;
+       }
+      }
+      for (i = 1; i < img.nXTiles * img.nYTiles; ++i) {
+       for (r = 0; r <= img.tiles[i].tileComps[comp].nDecompLevels; ++r) {
+         img.tiles[i].tileComps[comp].resLevels[r].precinctWidth =
+             img.tiles[0].tileComps[comp].resLevels[r].precinctWidth;
+         img.tiles[i].tileComps[comp].resLevels[r].precinctHeight =
+             img.tiles[0].tileComps[comp].resLevels[r].precinctHeight;
+       }
+      }
+      break;
+    case 0x5c:                 // QCD - quantization default
+      if (!readUByte(&img.tiles[0].tileComps[0].quantStyle)) {
+       error(getPos(), "Error in JPX QCD marker segment");
+       return gFalse;
+      }
+      if ((img.tiles[0].tileComps[0].quantStyle & 0x1f) == 0x00) {
+       img.tiles[0].tileComps[0].nQuantSteps = segLen - 3;
+       img.tiles[0].tileComps[0].quantSteps =
+           (Guint *)grealloc(img.tiles[0].tileComps[0].quantSteps,
+                             img.tiles[0].tileComps[0].nQuantSteps *
+                               sizeof(Guint));
+       for (i = 0; i < img.tiles[0].tileComps[0].nQuantSteps; ++i) {
+         if (!readUByte(&img.tiles[0].tileComps[0].quantSteps[i])) {
+           error(getPos(), "Error in JPX QCD marker segment");
+           return gFalse;
+         }
+       }
+      } else if ((img.tiles[0].tileComps[0].quantStyle & 0x1f) == 0x01) {
+       img.tiles[0].tileComps[0].nQuantSteps = 1;
+       img.tiles[0].tileComps[0].quantSteps =
+           (Guint *)grealloc(img.tiles[0].tileComps[0].quantSteps,
+                             img.tiles[0].tileComps[0].nQuantSteps *
+                               sizeof(Guint));
+       if (!readUWord(&img.tiles[0].tileComps[0].quantSteps[0])) {
+         error(getPos(), "Error in JPX QCD marker segment");
+         return gFalse;
+       }
+      } else if ((img.tiles[0].tileComps[0].quantStyle & 0x1f) == 0x02) {
+       img.tiles[0].tileComps[0].nQuantSteps = (segLen - 3) / 2;
+       img.tiles[0].tileComps[0].quantSteps =
+           (Guint *)grealloc(img.tiles[0].tileComps[0].quantSteps,
+                             img.tiles[0].tileComps[0].nQuantSteps *
+                               sizeof(Guint));
+       for (i = 0; i < img.tiles[0].tileComps[0].nQuantSteps; ++i) {
+         if (!readUWord(&img.tiles[0].tileComps[0].quantSteps[i])) {
+           error(getPos(), "Error in JPX QCD marker segment");
+           return gFalse;
+         }
+       }
+      } else {
+       error(getPos(), "Error in JPX QCD marker segment");
+       return gFalse;
+      }
+      for (i = 0; i < img.nXTiles * img.nYTiles; ++i) {
+       for (comp = 0; comp < img.nComps; ++comp) {
+         if (!(i == 0 && comp == 0)) {
+           img.tiles[i].tileComps[comp].quantStyle =
+               img.tiles[0].tileComps[0].quantStyle;
+           img.tiles[i].tileComps[comp].nQuantSteps =
+               img.tiles[0].tileComps[0].nQuantSteps;
+           img.tiles[i].tileComps[comp].quantSteps = 
+               (Guint *)grealloc(img.tiles[i].tileComps[comp].quantSteps,
+                                 img.tiles[0].tileComps[0].nQuantSteps *
+                                   sizeof(Guint));
+           for (j = 0; j < img.tiles[0].tileComps[0].nQuantSteps; ++j) {
+             img.tiles[i].tileComps[comp].quantSteps[j] =
+                 img.tiles[0].tileComps[0].quantSteps[j];
+           }
+         }
+       }
+      }
+      haveQCD = gTrue;
+      break;
+    case 0x5d:                 // QCC - quantization component
+      if (!haveQCD) {
+       error(getPos(), "JPX QCC marker segment before QCD segment");
+       return gFalse;
+      }
+      if ((img.nComps > 256 && !readUWord(&comp)) ||
+         (img.nComps <= 256 && !readUByte(&comp)) ||
+         comp >= img.nComps ||
+         !readUByte(&img.tiles[0].tileComps[comp].quantStyle)) {
+       error(getPos(), "Error in JPX QCC marker segment");
+       return gFalse;
+      }
+      if ((img.tiles[0].tileComps[comp].quantStyle & 0x1f) == 0x00) {
+       img.tiles[0].tileComps[comp].nQuantSteps =
+           segLen - (img.nComps > 256 ? 5 : 4);
+       img.tiles[0].tileComps[comp].quantSteps =
+           (Guint *)grealloc(img.tiles[0].tileComps[comp].quantSteps,
+                             img.tiles[0].tileComps[comp].nQuantSteps *
+                               sizeof(Guint));
+       for (i = 0; i < img.tiles[0].tileComps[comp].nQuantSteps; ++i) {
+         if (!readUByte(&img.tiles[0].tileComps[comp].quantSteps[i])) {
+           error(getPos(), "Error in JPX QCC marker segment");
+           return gFalse;
+         }
+       }
+      } else if ((img.tiles[0].tileComps[comp].quantStyle & 0x1f) == 0x01) {
+       img.tiles[0].tileComps[comp].nQuantSteps = 1;
+       img.tiles[0].tileComps[comp].quantSteps =
+           (Guint *)grealloc(img.tiles[0].tileComps[comp].quantSteps,
+                             img.tiles[0].tileComps[comp].nQuantSteps *
+                               sizeof(Guint));
+       if (!readUWord(&img.tiles[0].tileComps[comp].quantSteps[0])) {
+         error(getPos(), "Error in JPX QCC marker segment");
+         return gFalse;
+       }
+      } else if ((img.tiles[0].tileComps[comp].quantStyle & 0x1f) == 0x02) {
+       img.tiles[0].tileComps[comp].nQuantSteps =
+           (segLen - (img.nComps > 256 ? 5 : 4)) / 2;
+       img.tiles[0].tileComps[comp].quantSteps =
+           (Guint *)grealloc(img.tiles[0].tileComps[comp].quantSteps,
+                             img.tiles[0].tileComps[comp].nQuantSteps *
+                               sizeof(Guint));
+       for (i = 0; i < img.tiles[0].tileComps[comp].nQuantSteps; ++i) {
+         if (!readUWord(&img.tiles[0].tileComps[comp].quantSteps[i])) {
+           error(getPos(), "Error in JPX QCD marker segment");
+           return gFalse;
+         }
+       }
+      } else {
+       error(getPos(), "Error in JPX QCC marker segment");
+       return gFalse;
+      }
+      for (i = 1; i < img.nXTiles * img.nYTiles; ++i) {
+       img.tiles[i].tileComps[comp].quantStyle =
+           img.tiles[0].tileComps[comp].quantStyle;
+       img.tiles[i].tileComps[comp].nQuantSteps =
+           img.tiles[0].tileComps[comp].nQuantSteps;
+       img.tiles[i].tileComps[comp].quantSteps = 
+           (Guint *)grealloc(img.tiles[i].tileComps[comp].quantSteps,
+                             img.tiles[0].tileComps[comp].nQuantSteps *
+                               sizeof(Guint));
+       for (j = 0; j < img.tiles[0].tileComps[comp].nQuantSteps; ++j) {
+         img.tiles[i].tileComps[comp].quantSteps[j] =
+             img.tiles[0].tileComps[comp].quantSteps[j];
+       }
+      }
+      break;
+    case 0x5e:                 // RGN - region of interest
+#if 1 //~ ROI is unimplemented
+      fprintf(stderr, "RGN\n");
+      for (i = 0; i < segLen - 2; ++i) {
+       if (str->getChar() == EOF) {
+         error(getPos(), "Error in JPX PPM marker segment");
+         return gFalse;
+       }
+      }
+#else
+      if ((img.nComps > 256 && !readUWord(&comp)) ||
+         (img.nComps <= 256 && !readUByte(&comp)) ||
+         comp >= img.nComps ||
+         !readUByte(&compInfo[comp].defROI.style) ||
+         !readUByte(&compInfo[comp].defROI.shift)) {
+       error(getPos(), "Error in JPX RGN marker segment");
+       return gFalse;
+      }
+#endif
+      break;
+    case 0x5f:                 // POC - progression order change
+#if 1 //~ progression order changes are unimplemented
+      fprintf(stderr, "POC\n");
+      for (i = 0; i < segLen - 2; ++i) {
+       if (str->getChar() == EOF) {
+         error(getPos(), "Error in JPX PPM marker segment");
+         return gFalse;
+       }
+      }
+#else
+      nProgs = (segLen - 2) / (img.nComps > 256 ? 9 : 7);
+      progs = (JPXProgOrder *)gmalloc(nProgs * sizeof(JPXProgOrder));
+      for (i = 0; i < nProgs; ++i) {
+       if (!readUByte(&progs[i].startRes) ||
+           !(img.nComps > 256 && readUWord(&progs[i].startComp)) ||
+           !(img.nComps <= 256 && readUByte(&progs[i].startComp)) ||
+           !readUWord(&progs[i].endLayer) ||
+           !readUByte(&progs[i].endRes) ||
+           !(img.nComps > 256 && readUWord(&progs[i].endComp)) ||
+           !(img.nComps <= 256 && readUByte(&progs[i].endComp)) ||
+           !readUByte(&progs[i].progOrder)) {
+         error(getPos(), "Error in JPX POC marker segment");
+         return gFalse;
+       }
+      }
+#endif
+      break;
+    case 0x60:                 // PPM - packed packet headers, main header
+#if 1 //~ packed packet headers are unimplemented
+      fprintf(stderr, "PPM\n");
+      for (i = 0; i < segLen - 2; ++i) {
+       if (str->getChar() == EOF) {
+         error(getPos(), "Error in JPX PPM marker segment");
+         return gFalse;
+       }
+      }
+#endif
+      break;
+    case 0x55:                 // TLM - tile-part lengths
+      // skipped
+      for (i = 0; i < segLen - 2; ++i) {
+       if (str->getChar() == EOF) {
+         error(getPos(), "Error in JPX TLM marker segment");
+         return gFalse;
+       }
+      }
+      break;
+    case 0x57:                 // PLM - packet length, main header
+      // skipped
+      for (i = 0; i < segLen - 2; ++i) {
+       if (str->getChar() == EOF) {
+         error(getPos(), "Error in JPX PLM marker segment");
+         return gFalse;
+       }
+      }
+      break;
+    case 0x63:                 // CRG - component registration
+      // skipped
+      for (i = 0; i < segLen - 2; ++i) {
+       if (str->getChar() == EOF) {
+         error(getPos(), "Error in JPX CRG marker segment");
+         return gFalse;
+       }
+      }
+      break;
+    case 0x64:                 // COM - comment
+      // skipped
+      for (i = 0; i < segLen - 2; ++i) {
+       if (str->getChar() == EOF) {
+         error(getPos(), "Error in JPX COM marker segment");
+         return gFalse;
+       }
+      }
+      break;
+    case 0x90:                 // SOT - start of tile
+      haveSOT = gTrue;
+      break;
+    default:
+      error(getPos(), "Unknown marker segment %02x in JPX stream", segType);
+      for (i = 0; i < segLen - 2; ++i) {
+       if (str->getChar() == EOF) {
+         break;
+       }
+      }
+      break;
+    }
+  } while (!haveSOT);
+
+  if (!haveSIZ) {
+    error(getPos(), "Missing SIZ marker segment in JPX stream");
+    return gFalse;
+  }
+  if (!haveCOD) {
+    error(getPos(), "Missing COD marker segment in JPX stream");
+    return gFalse;
+  }
+  if (!haveQCD) {
+    error(getPos(), "Missing QCD marker segment in JPX stream");
+    return gFalse;
+  }
+
+  //----- read the tile-parts
+  while (1) {
+    if (!readTilePart()) {
+      return gFalse;
+    }
+    if (!readMarkerHdr(&segType, &segLen)) {
+      error(getPos(), "Error in JPX codestream");
+      return gFalse;
+    }
+    if (segType != 0x90) {     // SOT - start of tile
+      break;
+    }
+  }
+
+  if (segType != 0xd9) {       // EOC - end of codestream
+    error(getPos(), "Missing EOC marker in JPX codestream");
+    return gFalse;
+  }
+
+  //----- finish decoding the image
+  for (i = 0; i < img.nXTiles * img.nYTiles; ++i) {
+    tile = &img.tiles[i];
+    for (comp = 0; comp < img.nComps; ++comp) {
+      tileComp = &tile->tileComps[comp];
+      inverseTransform(tileComp);
+    }
+    if (!inverseMultiCompAndDC(tile)) {
+      return gFalse;
+    }
+  }
+
+  //~ can free memory below tileComps here, and also tileComp.buf
+
+  return gTrue;
+}
+
+GBool JPXStream::readTilePart() {
+  JPXTile *tile;
+  JPXTileComp *tileComp;
+  JPXResLevel *resLevel;
+  JPXPrecinct *precinct;
+  JPXSubband *subband;
+  JPXCodeBlock *cb;
+  GBool haveSOD;
+  Guint tileIdx, tilePartLen, tilePartIdx, nTileParts;
+  GBool tilePartToEOC;
+  Guint precinctSize, style;
+  Guint n, nSBs, nx, ny, sbx0, sby0, comp, segLen;
+  Guint i, j, k, cbX, cbY, r, pre, sb, cbi;
+  int segType, level;
+
+  // process the SOT marker segment
+  if (!readUWord(&tileIdx) ||
+      !readULong(&tilePartLen) ||
+      !readUByte(&tilePartIdx) ||
+      !readUByte(&nTileParts)) {
+    error(getPos(), "Error in JPX SOT marker segment");
+    return gFalse;
+  }
+
+  if (tileIdx >= img.nXTiles * img.nYTiles) {
+    error(getPos(), "Weird tile index in JPX stream");
+    return gFalse;
+  }
+
+  tilePartToEOC = tilePartLen == 0;
+  tilePartLen -= 12; // subtract size of SOT segment
+
+  haveSOD = gFalse;
+  do {
+    if (!readMarkerHdr(&segType, &segLen)) {
+      error(getPos(), "Error in JPX tile-part codestream");
+      return gFalse;
+    }
+    tilePartLen -= 2 + segLen;
+    switch (segType) {
+    case 0x52:                 // COD - coding style default
+      if (!readUByte(&img.tiles[tileIdx].tileComps[0].style) ||
+         !readUByte(&img.tiles[tileIdx].progOrder) ||
+         !readUWord(&img.tiles[tileIdx].nLayers) ||
+         !readUByte(&img.tiles[tileIdx].multiComp) ||
+         !readUByte(&img.tiles[tileIdx].tileComps[0].nDecompLevels) ||
+         !readUByte(&img.tiles[tileIdx].tileComps[0].codeBlockW) ||
+         !readUByte(&img.tiles[tileIdx].tileComps[0].codeBlockH) ||
+         !readUByte(&img.tiles[tileIdx].tileComps[0].codeBlockStyle) ||
+         !readUByte(&img.tiles[tileIdx].tileComps[0].transform)) {
+       error(getPos(), "Error in JPX COD marker segment");
+       return gFalse;
+      }
+      img.tiles[tileIdx].tileComps[0].codeBlockW += 2;
+      img.tiles[tileIdx].tileComps[0].codeBlockH += 2;
+      for (comp = 0; comp < img.nComps; ++comp) {
+       if (comp != 0) {
+         img.tiles[tileIdx].tileComps[comp].style =
+             img.tiles[tileIdx].tileComps[0].style;
+         img.tiles[tileIdx].tileComps[comp].nDecompLevels =
+             img.tiles[tileIdx].tileComps[0].nDecompLevels;
+         img.tiles[tileIdx].tileComps[comp].codeBlockW =
+             img.tiles[tileIdx].tileComps[0].codeBlockW;
+         img.tiles[tileIdx].tileComps[comp].codeBlockH =
+             img.tiles[tileIdx].tileComps[0].codeBlockH;
+         img.tiles[tileIdx].tileComps[comp].codeBlockStyle =
+             img.tiles[tileIdx].tileComps[0].codeBlockStyle;
+         img.tiles[tileIdx].tileComps[comp].transform =
+             img.tiles[tileIdx].tileComps[0].transform;
+       }
+       img.tiles[tileIdx].tileComps[comp].resLevels =
+           (JPXResLevel *)grealloc(
+                    img.tiles[tileIdx].tileComps[comp].resLevels,
+                    (img.tiles[tileIdx].tileComps[comp].nDecompLevels + 1) *
+                      sizeof(JPXResLevel));
+       for (r = 0;
+            r <= img.tiles[tileIdx].tileComps[comp].nDecompLevels;
+            ++r) {
+         img.tiles[tileIdx].tileComps[comp].resLevels[r].precincts = NULL;
+       }
+      }
+      for (r = 0; r <= img.tiles[tileIdx].tileComps[0].nDecompLevels; ++r) {
+       if (img.tiles[tileIdx].tileComps[0].style & 0x01) {
+         if (!readUByte(&precinctSize)) {
+           error(getPos(), "Error in JPX COD marker segment");
+           return gFalse;
+         }
+         img.tiles[tileIdx].tileComps[0].resLevels[r].precinctWidth =
+             precinctSize & 0x0f;
+         img.tiles[tileIdx].tileComps[0].resLevels[r].precinctHeight =
+             (precinctSize >> 4) & 0x0f;
+       } else {
+         img.tiles[tileIdx].tileComps[0].resLevels[r].precinctWidth = 15;
+         img.tiles[tileIdx].tileComps[0].resLevels[r].precinctHeight = 15;
+       }
+      }
+      for (comp = 1; comp < img.nComps; ++comp) {
+       for (r = 0;
+            r <= img.tiles[tileIdx].tileComps[comp].nDecompLevels;
+            ++r) {
+         img.tiles[tileIdx].tileComps[comp].resLevels[r].precinctWidth =
+             img.tiles[tileIdx].tileComps[0].resLevels[r].precinctWidth;
+         img.tiles[tileIdx].tileComps[comp].resLevels[r].precinctHeight =
+             img.tiles[tileIdx].tileComps[0].resLevels[r].precinctHeight;
+       }
+      }
+      break;
+    case 0x53:                 // COC - coding style component
+      if ((img.nComps > 256 && !readUWord(&comp)) ||
+         (img.nComps <= 256 && !readUByte(&comp)) ||
+         comp >= img.nComps ||
+         !readUByte(&style) ||
+         !readUByte(&img.tiles[tileIdx].tileComps[comp].nDecompLevels) ||
+         !readUByte(&img.tiles[tileIdx].tileComps[comp].codeBlockW) ||
+         !readUByte(&img.tiles[tileIdx].tileComps[comp].codeBlockH) ||
+         !readUByte(&img.tiles[tileIdx].tileComps[comp].codeBlockStyle) ||
+         !readUByte(&img.tiles[tileIdx].tileComps[comp].transform)) {
+       error(getPos(), "Error in JPX COC marker segment");
+       return gFalse;
+      }
+      img.tiles[tileIdx].tileComps[comp].style =
+         (img.tiles[tileIdx].tileComps[comp].style & ~1) | (style & 1);
+      img.tiles[tileIdx].tileComps[comp].codeBlockW += 2;
+      img.tiles[tileIdx].tileComps[comp].codeBlockH += 2;
+      img.tiles[tileIdx].tileComps[comp].resLevels =
+         (JPXResLevel *)grealloc(
+                    img.tiles[tileIdx].tileComps[comp].resLevels,
+                    (img.tiles[tileIdx].tileComps[comp].nDecompLevels + 1) *
+                      sizeof(JPXResLevel));
+      for (r = 0; r <= img.tiles[tileIdx].tileComps[comp].nDecompLevels; ++r) {
+       img.tiles[tileIdx].tileComps[comp].resLevels[r].precincts = NULL;
+      }
+      for (r = 0; r <= img.tiles[tileIdx].tileComps[comp].nDecompLevels; ++r) {
+       if (img.tiles[tileIdx].tileComps[comp].style & 0x01) {
+         if (!readUByte(&precinctSize)) {
+           error(getPos(), "Error in JPX COD marker segment");
+           return gFalse;
+         }
+         img.tiles[tileIdx].tileComps[comp].resLevels[r].precinctWidth =
+             precinctSize & 0x0f;
+         img.tiles[tileIdx].tileComps[comp].resLevels[r].precinctHeight =
+             (precinctSize >> 4) & 0x0f;
+       } else {
+         img.tiles[tileIdx].tileComps[comp].resLevels[r].precinctWidth = 15;
+         img.tiles[tileIdx].tileComps[comp].resLevels[r].precinctHeight = 15;
+       }
+      }
+      break;
+    case 0x5c:                 // QCD - quantization default
+      if (!readUByte(&img.tiles[tileIdx].tileComps[0].quantStyle)) {
+       error(getPos(), "Error in JPX QCD marker segment");
+       return gFalse;
+      }
+      if ((img.tiles[tileIdx].tileComps[0].quantStyle & 0x1f) == 0x00) {
+       img.tiles[tileIdx].tileComps[0].nQuantSteps =
+           segLen - 3;
+       img.tiles[tileIdx].tileComps[0].quantSteps =
+           (Guint *)grealloc(img.tiles[tileIdx].tileComps[0].quantSteps,
+                             img.tiles[tileIdx].tileComps[0].nQuantSteps *
+                               sizeof(Guint));
+       for (i = 0; i < img.tiles[tileIdx].tileComps[0].nQuantSteps; ++i) {
+         if (!readUByte(&img.tiles[tileIdx].tileComps[0].quantSteps[i])) {
+           error(getPos(), "Error in JPX QCD marker segment");
+           return gFalse;
+         }
+       }
+      } else if ((img.tiles[tileIdx].tileComps[0].quantStyle & 0x1f) == 0x01) {
+       img.tiles[tileIdx].tileComps[0].nQuantSteps = 1;
+       img.tiles[tileIdx].tileComps[0].quantSteps =
+           (Guint *)grealloc(img.tiles[tileIdx].tileComps[0].quantSteps,
+                             img.tiles[tileIdx].tileComps[0].nQuantSteps *
+                               sizeof(Guint));
+       if (!readUWord(&img.tiles[tileIdx].tileComps[0].quantSteps[0])) {
+         error(getPos(), "Error in JPX QCD marker segment");
+         return gFalse;
+       }
+      } else if ((img.tiles[tileIdx].tileComps[0].quantStyle & 0x1f) == 0x02) {
+       img.tiles[tileIdx].tileComps[0].nQuantSteps = (segLen - 3) / 2;
+       img.tiles[tileIdx].tileComps[0].quantSteps =
+           (Guint *)grealloc(img.tiles[tileIdx].tileComps[0].quantSteps,
+                             img.tiles[tileIdx].tileComps[0].nQuantSteps *
+                               sizeof(Guint));
+       for (i = 0; i < img.tiles[tileIdx].tileComps[0].nQuantSteps; ++i) {
+         if (!readUWord(&img.tiles[tileIdx].tileComps[0].quantSteps[i])) {
+           error(getPos(), "Error in JPX QCD marker segment");
+           return gFalse;
+         }
+       }
+      } else {
+       error(getPos(), "Error in JPX QCD marker segment");
+       return gFalse;
+      }
+      for (comp = 1; comp < img.nComps; ++comp) {
+       img.tiles[tileIdx].tileComps[comp].quantStyle =
+           img.tiles[tileIdx].tileComps[0].quantStyle;
+       img.tiles[tileIdx].tileComps[comp].nQuantSteps =
+           img.tiles[tileIdx].tileComps[0].nQuantSteps;
+       img.tiles[tileIdx].tileComps[comp].quantSteps = 
+           (Guint *)grealloc(img.tiles[tileIdx].tileComps[comp].quantSteps,
+                             img.tiles[tileIdx].tileComps[0].nQuantSteps *
+                               sizeof(Guint));
+       for (j = 0; j < img.tiles[tileIdx].tileComps[0].nQuantSteps; ++j) {
+         img.tiles[tileIdx].tileComps[comp].quantSteps[j] =
+             img.tiles[tileIdx].tileComps[0].quantSteps[j];
+       }
+      }
+      break;
+    case 0x5d:                 // QCC - quantization component
+      if ((img.nComps > 256 && !readUWord(&comp)) ||
+         (img.nComps <= 256 && !readUByte(&comp)) ||
+         comp >= img.nComps ||
+         !readUByte(&img.tiles[tileIdx].tileComps[comp].quantStyle)) {
+       error(getPos(), "Error in JPX QCC marker segment");
+       return gFalse;
+      }
+      if ((img.tiles[tileIdx].tileComps[comp].quantStyle & 0x1f) == 0x00) {
+       img.tiles[tileIdx].tileComps[comp].nQuantSteps =
+           segLen - (img.nComps > 256 ? 5 : 4);
+       img.tiles[tileIdx].tileComps[comp].quantSteps =
+           (Guint *)grealloc(img.tiles[tileIdx].tileComps[comp].quantSteps,
+                             img.tiles[tileIdx].tileComps[comp].nQuantSteps *
+                               sizeof(Guint));
+       for (i = 0; i < img.tiles[tileIdx].tileComps[comp].nQuantSteps; ++i) {
+         if (!readUByte(&img.tiles[tileIdx].tileComps[comp].quantSteps[i])) {
+           error(getPos(), "Error in JPX QCC marker segment");
+           return gFalse;
+         }
+       }
+      } else if ((img.tiles[tileIdx].tileComps[comp].quantStyle & 0x1f)
+                == 0x01) {
+       img.tiles[tileIdx].tileComps[comp].nQuantSteps = 1;
+       img.tiles[tileIdx].tileComps[comp].quantSteps =
+           (Guint *)grealloc(img.tiles[tileIdx].tileComps[comp].quantSteps,
+                             img.tiles[tileIdx].tileComps[comp].nQuantSteps *
+                               sizeof(Guint));
+       if (!readUWord(&img.tiles[tileIdx].tileComps[comp].quantSteps[0])) {
+         error(getPos(), "Error in JPX QCC marker segment");
+         return gFalse;
+       }
+      } else if ((img.tiles[tileIdx].tileComps[comp].quantStyle & 0x1f)
+                == 0x02) {
+       img.tiles[tileIdx].tileComps[comp].nQuantSteps =
+           (segLen - (img.nComps > 256 ? 5 : 4)) / 2;
+       img.tiles[tileIdx].tileComps[comp].quantSteps =
+           (Guint *)grealloc(img.tiles[tileIdx].tileComps[comp].quantSteps,
+                             img.tiles[tileIdx].tileComps[comp].nQuantSteps *
+                               sizeof(Guint));
+       for (i = 0; i < img.tiles[tileIdx].tileComps[comp].nQuantSteps; ++i) {
+         if (!readUWord(&img.tiles[tileIdx].tileComps[comp].quantSteps[i])) {
+           error(getPos(), "Error in JPX QCD marker segment");
+           return gFalse;
+         }
+       }
+      } else {
+       error(getPos(), "Error in JPX QCC marker segment");
+       return gFalse;
+      }
+      break;
+    case 0x5e:                 // RGN - region of interest
+#if 1 //~ ROI is unimplemented
+      fprintf(stderr, "RGN\n");
+      for (i = 0; i < segLen - 2; ++i) {
+       if (str->getChar() == EOF) {
+         error(getPos(), "Error in JPX PPM marker segment");
+         return gFalse;
+       }
+      }
+#else
+      if ((img.nComps > 256 && !readUWord(&comp)) ||
+         (img.nComps <= 256 && !readUByte(&comp)) ||
+         comp >= img.nComps ||
+         !readUByte(&compInfo[comp].roi.style) ||
+         !readUByte(&compInfo[comp].roi.shift)) {
+       error(getPos(), "Error in JPX RGN marker segment");
+       return gFalse;
+      }
+#endif
+      break;
+    case 0x5f:                 // POC - progression order change
+#if 1 //~ progression order changes are unimplemented
+      fprintf(stderr, "POC\n");
+      for (i = 0; i < segLen - 2; ++i) {
+       if (str->getChar() == EOF) {
+         error(getPos(), "Error in JPX PPM marker segment");
+         return gFalse;
+       }
+      }
+#else
+      nTileProgs = (segLen - 2) / (img.nComps > 256 ? 9 : 7);
+      tileProgs = (JPXProgOrder *)gmalloc(nTileProgs * sizeof(JPXProgOrder));
+      for (i = 0; i < nTileProgs; ++i) {
+       if (!readUByte(&tileProgs[i].startRes) ||
+           !(img.nComps > 256 && readUWord(&tileProgs[i].startComp)) ||
+           !(img.nComps <= 256 && readUByte(&tileProgs[i].startComp)) ||
+           !readUWord(&tileProgs[i].endLayer) ||
+           !readUByte(&tileProgs[i].endRes) ||
+           !(img.nComps > 256 && readUWord(&tileProgs[i].endComp)) ||
+           !(img.nComps <= 256 && readUByte(&tileProgs[i].endComp)) ||
+           !readUByte(&tileProgs[i].progOrder)) {
+         error(getPos(), "Error in JPX POC marker segment");
+         return gFalse;
+       }
+      }
+#endif
+      break;
+    case 0x61:                 // PPT - packed packet headers, tile-part hdr
+#if 1 //~ packed packet headers are unimplemented
+      fprintf(stderr, "PPT\n");
+      for (i = 0; i < segLen - 2; ++i) {
+       if (str->getChar() == EOF) {
+         error(getPos(), "Error in JPX PPT marker segment");
+         return gFalse;
+       }
+      }
+#endif
+    case 0x58:                 // PLT - packet length, tile-part header
+      // skipped
+      for (i = 0; i < segLen - 2; ++i) {
+       if (str->getChar() == EOF) {
+         error(getPos(), "Error in JPX PLT marker segment");
+         return gFalse;
+       }
+      }
+      break;
+    case 0x64:                 // COM - comment
+      // skipped
+      for (i = 0; i < segLen - 2; ++i) {
+       if (str->getChar() == EOF) {
+         error(getPos(), "Error in JPX COM marker segment");
+         return gFalse;
+       }
+      }
+      break;
+    case 0x93:                 // SOD - start of data
+      haveSOD = gTrue;
+      break;
+    default:
+      error(getPos(), "Unknown marker segment %02x in JPX tile-part stream",
+           segType);
+      for (i = 0; i < segLen - 2; ++i) {
+       if (str->getChar() == EOF) {
+         break;
+       }
+      }
+      break;
+    }
+  } while (!haveSOD);
+
+  //----- initialize the tile, precincts, and code-blocks
+  if (tilePartIdx == 0) {
+    tile = &img.tiles[tileIdx];
+    i = tileIdx / img.nXTiles;
+    j = tileIdx % img.nXTiles;
+    if ((tile->x0 = img.xTileOffset + j * img.xTileSize) < img.xOffset) {
+      tile->x0 = img.xOffset;
+    }
+    if ((tile->y0 = img.yTileOffset + i * img.yTileSize) < img.yOffset) {
+      tile->y0 = img.yOffset;
+    }
+    if ((tile->x1 = img.xTileOffset + (j + 1) * img.xTileSize) > img.xSize) {
+      tile->x1 = img.xSize;
+    }
+    if ((tile->y1 = img.yTileOffset + (i + 1) * img.yTileSize) > img.ySize) {
+      tile->y1 = img.ySize;
+    }
+    tile->comp = 0;
+    tile->res = 0;
+    tile->precinct = 0;
+    tile->layer = 0;
+    tile->maxNDecompLevels = 0;
+    for (comp = 0; comp < img.nComps; ++comp) {
+      tileComp = &tile->tileComps[comp];
+      if (tileComp->nDecompLevels > tile->maxNDecompLevels) {
+       tile->maxNDecompLevels = tileComp->nDecompLevels;
+      }
+      tileComp->x0 = jpxCeilDiv(tile->x0, tileComp->hSep);
+      tileComp->y0 = jpxCeilDiv(tile->y0, tileComp->hSep);
+      tileComp->x1 = jpxCeilDiv(tile->x1, tileComp->hSep);
+      tileComp->y1 = jpxCeilDiv(tile->y1, tileComp->hSep);
+      tileComp->cbW = 1 << tileComp->codeBlockW;
+      tileComp->cbH = 1 << tileComp->codeBlockH;
+      tileComp->data = (int *)gmalloc((tileComp->x1 - tileComp->x0) *
+                                     (tileComp->y1 - tileComp->y0) *
+                                     sizeof(int));
+      if (tileComp->x1 - tileComp->x0 > tileComp->y1 - tileComp->y0) {
+       n = tileComp->x1 - tileComp->x0;
+      } else {
+       n = tileComp->y1 - tileComp->y0;
+      }
+      tileComp->buf = (int *)gmalloc((n + 8) * sizeof(int));
+      for (r = 0; r <= tileComp->nDecompLevels; ++r) {
+       resLevel = &tileComp->resLevels[r];
+       k = r == 0 ? tileComp->nDecompLevels
+                  : tileComp->nDecompLevels - r + 1;
+       resLevel->x0 = jpxCeilDivPow2(tileComp->x0, k);
+       resLevel->y0 = jpxCeilDivPow2(tileComp->y0, k);
+       resLevel->x1 = jpxCeilDivPow2(tileComp->x1, k);
+       resLevel->y1 = jpxCeilDivPow2(tileComp->y1, k);
+       if (r == 0) {
+         resLevel->bx0[0] = resLevel->x0;
+         resLevel->by0[0] = resLevel->y0;
+         resLevel->bx1[0] = resLevel->x1;
+         resLevel->by1[0] = resLevel->y1;
+       } else {
+         resLevel->bx0[0] = jpxCeilDivPow2(tileComp->x0 - (1 << (k-1)), k);
+         resLevel->by0[0] = resLevel->y0;
+         resLevel->bx1[0] = jpxCeilDivPow2(tileComp->x1 - (1 << (k-1)), k);
+         resLevel->by1[0] = resLevel->y1;
+         resLevel->bx0[1] = resLevel->x0;
+         resLevel->by0[1] = jpxCeilDivPow2(tileComp->y0 - (1 << (k-1)), k);
+         resLevel->bx1[1] = resLevel->x1;
+         resLevel->by1[1] = jpxCeilDivPow2(tileComp->y1 - (1 << (k-1)), k);
+         resLevel->bx0[2] = jpxCeilDivPow2(tileComp->x0 - (1 << (k-1)), k);
+         resLevel->by0[2] = jpxCeilDivPow2(tileComp->y0 - (1 << (k-1)), k);
+         resLevel->bx1[2] = jpxCeilDivPow2(tileComp->x1 - (1 << (k-1)), k);
+         resLevel->by1[2] = jpxCeilDivPow2(tileComp->y1 - (1 << (k-1)), k);
+       }
+       resLevel->precincts = (JPXPrecinct *)gmalloc(1 * sizeof(JPXPrecinct));
+       for (pre = 0; pre < 1; ++pre) {
+         precinct = &resLevel->precincts[pre];
+         precinct->x0 = resLevel->x0;
+         precinct->y0 = resLevel->y0;
+         precinct->x1 = resLevel->x1;
+         precinct->y1 = resLevel->y1;
+         nSBs = r == 0 ? 1 : 3;
+         precinct->subbands =
+             (JPXSubband *)gmalloc(nSBs * sizeof(JPXSubband));
+         for (sb = 0; sb < nSBs; ++sb) {
+           subband = &precinct->subbands[sb];
+           subband->x0 = resLevel->bx0[sb];
+           subband->y0 = resLevel->by0[sb];
+           subband->x1 = resLevel->bx1[sb];
+           subband->y1 = resLevel->by1[sb];
+           subband->nXCBs = jpxCeilDivPow2(subband->x1,
+                                           tileComp->codeBlockW)
+                            - jpxFloorDivPow2(subband->x0,
+                                              tileComp->codeBlockW);
+           subband->nYCBs = jpxCeilDivPow2(subband->y1,
+                                           tileComp->codeBlockH)
+                            - jpxFloorDivPow2(subband->y0,
+                                              tileComp->codeBlockH);
+           n = subband->nXCBs > subband->nYCBs ? subband->nXCBs
+                                               : subband->nYCBs;
+           for (subband->maxTTLevel = 0, --n;
+                n;
+                ++subband->maxTTLevel, n >>= 1) ;
+           n = 0;
+           for (level = subband->maxTTLevel; level >= 0; --level) {
+             nx = jpxCeilDivPow2(subband->nXCBs, level);
+             ny = jpxCeilDivPow2(subband->nYCBs, level);
+             n += nx * ny;
+           }
+           subband->inclusion =
+               (JPXTagTreeNode *)gmalloc(n * sizeof(JPXTagTreeNode));
+           subband->zeroBitPlane =
+               (JPXTagTreeNode *)gmalloc(n * sizeof(JPXTagTreeNode));
+           for (k = 0; k < n; ++k) {
+             subband->inclusion[k].finished = gFalse;
+             subband->inclusion[k].val = 0;
+             subband->zeroBitPlane[k].finished = gFalse;
+             subband->zeroBitPlane[k].val = 0;
+           }
+           subband->cbs = (JPXCodeBlock *)gmalloc(subband->nXCBs *
+                                                  subband->nYCBs *
+                                                  sizeof(JPXCodeBlock));
+           sbx0 = jpxFloorDivPow2(subband->x0, tileComp->codeBlockW);
+           sby0 = jpxFloorDivPow2(subband->y0, tileComp->codeBlockH);
+           cb = subband->cbs;
+           for (cbY = 0; cbY < subband->nYCBs; ++cbY) {
+             for (cbX = 0; cbX < subband->nXCBs; ++cbX) {
+               cb->x0 = (sbx0 + cbX) << tileComp->codeBlockW;
+               cb->x1 = cb->x0 + tileComp->cbW;
+               if (subband->x0 > cb->x0) {
+                 cb->x0 = subband->x0;
+               }
+               if (subband->x1 < cb->x1) {
+                 cb->x1 = subband->x1;
+               }
+               cb->y0 = (sby0 + cbY) << tileComp->codeBlockH;
+               cb->y1 = cb->y0 + tileComp->cbH;
+               if (subband->y0 > cb->y0) {
+                 cb->y0 = subband->y0;
+               }
+               if (subband->y1 < cb->y1) {
+                 cb->y1 = subband->y1;
+               }
+               cb->seen = gFalse;
+               cb->lBlock = 3;
+               cb->nextPass = jpxPassCleanup;
+               cb->nZeroBitPlanes = 0;
+               cb->coeffs =
+                   (JPXCoeff *)gmalloc((1 << (tileComp->codeBlockW
+                                              + tileComp->codeBlockH))
+                                       * sizeof(JPXCoeff));
+               for (cbi = 0;
+                    cbi < (Guint)(1 << (tileComp->codeBlockW
+                                        + tileComp->codeBlockH));
+                    ++cbi) {
+                 cb->coeffs[cbi].flags = 0;
+                 cb->coeffs[cbi].len = 0;
+                 cb->coeffs[cbi].mag = 0;
+               }
+               cb->stats = new JArithmeticDecoderStats(jpxNContexts);
+               cb->stats->setEntry(jpxContextSigProp, 4, 0);
+               cb->stats->setEntry(jpxContextRunLength, 3, 0);
+               cb->stats->setEntry(jpxContextUniform, 46, 0);
+               ++cb;
+             }
+           }
+         }
+       }
+      }
+    }
+  }
+
+  return readTilePartData(tileIdx, tilePartLen, tilePartToEOC);
+}
+
+GBool JPXStream::readTilePartData(Guint tileIdx,
+                                 Guint tilePartLen, GBool tilePartToEOC) {
+  JPXTile *tile;
+  JPXTileComp *tileComp;
+  JPXResLevel *resLevel;
+  JPXPrecinct *precinct;
+  JPXSubband *subband;
+  JPXCodeBlock *cb;
+  Guint ttVal;
+  Guint bits, cbX, cbY, nx, ny, i, j, n, sb;
+  int level;
+
+  tile = &img.tiles[tileIdx];
+
+  // read all packets from this tile-part
+  while (1) {
+    if (tilePartToEOC) {
+      //~ peek for an EOC marker
+    } else if (tilePartLen == 0) {
+      break;
+    }
+
+    tileComp = &tile->tileComps[tile->comp];
+    resLevel = &tileComp->resLevels[tile->res];
+    precinct = &resLevel->precincts[tile->precinct];
+
+    //----- packet header
+
+    // zero-length flag
+    if (!readBits(1, &bits)) {
+      goto err;
+    }
+    if (!bits) {
+      // packet is empty -- clear all code-block inclusion flags
+      for (sb = 0; sb < (tile->res == 0 ? 1 : 3); ++sb) {
+       subband = &precinct->subbands[sb];
+       for (cbY = 0; cbY < subband->nYCBs; ++cbY) {
+         for (cbX = 0; cbX < subband->nXCBs; ++cbX) {
+           cb = &subband->cbs[cbY * subband->nXCBs + cbX];
+           cb->included = gFalse;
+         }
+       }
+      }
+    } else {
+
+      for (sb = 0; sb < (tile->res == 0 ? 1 : 3); ++sb) {
+       subband = &precinct->subbands[sb];
+       for (cbY = 0; cbY < subband->nYCBs; ++cbY) {
+         for (cbX = 0; cbX < subband->nXCBs; ++cbX) {
+           cb = &subband->cbs[cbY * subband->nXCBs + cbX];
+
+           // skip code-blocks with no coefficients
+           if (cb->x0 >= cb->x1 || cb->y0 >= cb->y1) {
+             cb->included = gFalse;
+             continue;
+           }
+
+           // code-block inclusion
+           if (cb->seen) {
+             if (!readBits(1, &cb->included)) {
+               goto err;
+             }
+           } else {
+             ttVal = 0;
+             i = 0;
+             for (level = subband->maxTTLevel; level >= 0; --level) {
+               nx = jpxCeilDivPow2(subband->nXCBs, level);
+               ny = jpxCeilDivPow2(subband->nYCBs, level);
+               j = i + (cbY >> level) * nx + (cbX >> level);
+               if (!subband->inclusion[j].finished &&
+                   !subband->inclusion[j].val) {
+                 subband->inclusion[j].val = ttVal;
+               } else {
+                 ttVal = subband->inclusion[j].val;
+               }
+               while (!subband->inclusion[j].finished &&
+                      ttVal <= tile->layer) {
+                 if (!readBits(1, &bits)) {
+                   goto err;
+                 }
+                 if (bits == 1) {
+                   subband->inclusion[j].finished = gTrue;
+                 } else {
+                   ++ttVal;
+                 }
+               }
+               subband->inclusion[j].val = ttVal;
+               if (ttVal > tile->layer) {
+                 break;
+               }
+               i += nx * ny;
+             }
+             cb->included = level < 0;
+           }
+
+           if (cb->included) {
+
+             // zero bit-plane count
+             if (!cb->seen) {
+               ttVal = 0;
+               i = 0;
+               for (level = subband->maxTTLevel; level >= 0; --level) {
+                 nx = jpxCeilDivPow2(subband->nXCBs, level);
+                 ny = jpxCeilDivPow2(subband->nYCBs, level);
+                 j = i + (cbY >> level) * nx + (cbX >> level);
+                 if (!subband->zeroBitPlane[j].finished &&
+                     !subband->zeroBitPlane[j].val) {
+                   subband->zeroBitPlane[j].val = ttVal;
+                 } else {
+                   ttVal = subband->zeroBitPlane[j].val;
+                 }
+                 while (!subband->zeroBitPlane[j].finished) {
+                   if (!readBits(1, &bits)) {
+                     goto err;
+                   }
+                   if (bits == 1) {
+                     subband->zeroBitPlane[j].finished = gTrue;
+                   } else {
+                     ++ttVal;
+                   }
+                 }
+                 subband->zeroBitPlane[j].val = ttVal;
+                 i += nx * ny;
+               }
+               cb->nZeroBitPlanes = ttVal;
+             }
+
+             // number of coding passes
+             if (!readBits(1, &bits)) {
+               goto err;
+             }
+             if (bits == 0) {
+               cb->nCodingPasses = 1;
+             } else {
+               if (!readBits(1, &bits)) {
+                 goto err;
+               }
+               if (bits == 0) {
+                 cb->nCodingPasses = 2;
+               } else {
+                 if (!readBits(2, &bits)) {
+                   goto err;
+                 }
+                 if (bits < 3) {
+                   cb->nCodingPasses = 3 + bits;
+                 } else {
+                   if (!readBits(5, &bits)) {
+                     goto err;
+                   }
+                   if (bits < 31) {
+                     cb->nCodingPasses = 6 + bits;
+                   } else {
+                     if (!readBits(7, &bits)) {
+                       goto err;
+                     }
+                     cb->nCodingPasses = 37 + bits;
+                   }
+                 }
+               }
+             }
+
+             // update Lblock
+             while (1) {
+               if (!readBits(1, &bits)) {
+                 goto err;
+               }
+               if (!bits) {
+                 break;
+               }
+               ++cb->lBlock;
+             }
+
+             // length of compressed data
+             //~ deal with multiple codeword segments
+             for (n = cb->lBlock, i = cb->nCodingPasses >> 1;
+                  i;
+                  ++n, i >>= 1) ;
+             if (!readBits(n, &cb->dataLen)) {
+               goto err;
+             }
+           }
+         }
+       }
+      }
+    }
+    tilePartLen -= byteCount;
+    clearBitBuf();
+
+    //----- packet data
+
+    for (sb = 0; sb < (tile->res == 0 ? 1 : 3); ++sb) {
+      subband = &precinct->subbands[sb];
+      for (cbY = 0; cbY < subband->nYCBs; ++cbY) {
+       for (cbX = 0; cbX < subband->nXCBs; ++cbX) {
+         cb = &subband->cbs[cbY * subband->nXCBs + cbX];
+         if (cb->included) {
+           if (!readCodeBlockData(tileComp, resLevel, precinct, subband,
+                                  tile->res, sb, cb)) {
+             return gFalse;
+           }
+           tilePartLen -= cb->dataLen;
+           cb->seen = gTrue;
+         }
+       }
+      }
+    }
+
+    //----- next packet
+
+    switch (tile->progOrder) {
+    case 0: // layer, resolution level, component, precinct
+      if (++tile->comp == img.nComps) {
+       tile->comp = 0;
+       if (++tile->res == tile->maxNDecompLevels + 1) {
+         tile->res = 0;
+         if (++tile->layer == tile->nLayers) {
+           tile->layer = 0;
+         }
+       }
+      }
+      break;
+    case 1: // resolution level, layer, component, precinct
+      if (++tile->comp == img.nComps) {
+       tile->comp = 0;
+       if (++tile->layer == tile->nLayers) {
+         tile->layer = 0;
+         if (++tile->res == tile->maxNDecompLevels + 1) {
+           tile->res = 0;
+         }
+       }
+      }
+      break;
+    case 2: // resolution level, precinct, component, layer
+      //~ this isn't correct -- see B.12.1.3
+      if (++tile->layer == tile->nLayers) {
+       tile->layer = 0;
+       if (++tile->comp == img.nComps) {
+         tile->comp = 0;
+         if (++tile->res == tile->maxNDecompLevels + 1) {
+           tile->res = 0;
+         }
+       }
+      }
+      break;
+    case 3: // precinct, component, resolution level, layer
+      //~ this isn't correct -- see B.12.1.4
+      if (++tile->layer == tile->nLayers) {
+       tile->layer = 0;
+       if (++tile->res == tile->maxNDecompLevels + 1) {
+         tile->res = 0;
+         if (++tile->comp == img.nComps) {
+           tile->comp = 0;
+         }
+       }
+      }
+      break;
+    case 4: // component, precinct, resolution level, layer
+      //~ this isn't correct -- see B.12.1.5
+      if (++tile->layer == tile->nLayers) {
+       tile->layer = 0;
+       if (++tile->res == tile->maxNDecompLevels + 1) {
+         tile->res = 0;
+         if (++tile->comp == img.nComps) {
+           tile->comp = 0;
+         }
+       }
+      }
+      break;
+    }
+  }
+
+  return gTrue;
+
+ err:
+  error(getPos(), "Error in JPX stream");
+  return gFalse;
+}
+
+GBool JPXStream::readCodeBlockData(JPXTileComp *tileComp,
+                                  JPXResLevel *resLevel,
+                                  JPXPrecinct *precinct,
+                                  JPXSubband *subband,
+                                  Guint res, Guint sb,
+                                  JPXCodeBlock *cb) {
+  JPXCoeff *coeff0, *coeff1, *coeff;
+  JArithmeticDecoder *arithDecoder;
+  Guint horiz, vert, diag, all, cx, xorBit;
+  int horizSign, vertSign;
+  Guint i, x, y0, y1, y2;
+
+  arithDecoder = new JArithmeticDecoder();
+  arithDecoder->setStream(str, cb->dataLen);
+  arithDecoder->start();
+
+  for (i = 0; i < cb->nCodingPasses; ++i) {
+    switch (cb->nextPass) {
+
+    //----- significance propagation pass
+    case jpxPassSigProp:
+      for (y0 = cb->y0, coeff0 = cb->coeffs;
+          y0 < cb->y1;
+          y0 += 4, coeff0 += 4 << tileComp->codeBlockW) {
+       for (x = cb->x0, coeff1 = coeff0;
+            x < cb->x1;
+            ++x, ++coeff1) {
+         for (y1 = 0, coeff = coeff1;
+              y1 < 4 && y0+y1 < cb->y1;
+              ++y1, coeff += tileComp->cbW) {
+           if (!(coeff->flags & jpxCoeffSignificant)) {
+             horiz = vert = diag = 0;
+             horizSign = vertSign = 2;
+             if (x > cb->x0) {
+               if (coeff[-1].flags & jpxCoeffSignificant) {
+                 ++horiz;
+                 horizSign += (coeff[-1].flags & jpxCoeffSign) ? -1 : 1;
+               }
+               if (y0+y1 > cb->y0) {
+                 diag += (coeff[-tileComp->cbW - 1].flags
+                          >> jpxCoeffSignificantB) & 1;
+               }
+               if (y0+y1 < cb->y1 - 1) {
+                 diag += (coeff[tileComp->cbW - 1].flags
+                          >> jpxCoeffSignificantB) & 1;
+               }
+             }
+             if (x < cb->x1 - 1) {
+               if (coeff[1].flags & jpxCoeffSignificant) {
+                 ++horiz;
+                 horizSign += (coeff[1].flags & jpxCoeffSign) ? -1 : 1;
+               }
+               if (y0+y1 > cb->y0) {
+                 diag += (coeff[-tileComp->cbW + 1].flags
+                          >> jpxCoeffSignificantB) & 1;
+               }
+               if (y0+y1 < cb->y1 - 1) {
+                 diag += (coeff[tileComp->cbW + 1].flags
+                          >> jpxCoeffSignificantB) & 1;
+               }
+             }
+             if (y0+y1 > cb->y0) {
+               if (coeff[-tileComp->cbW].flags & jpxCoeffSignificant) {
+                 ++vert;
+                 vertSign += (coeff[-tileComp->cbW].flags & jpxCoeffSign)
+                             ? -1 : 1;
+               }
+             }
+             if (y0+y1 < cb->y1 - 1) {
+               if (coeff[tileComp->cbW].flags & jpxCoeffSignificant) {
+                 ++vert;
+                 vertSign += (coeff[tileComp->cbW].flags & jpxCoeffSign)
+                             ? -1 : 1;
+               }
+             }
+             cx = sigPropContext[horiz][vert][diag][res == 0 ? 1 : sb];
+             if (cx != 0) {
+               if (arithDecoder->decodeBit(cx, cb->stats)) {
+                 coeff->flags |= jpxCoeffSignificant | jpxCoeffFirstMagRef;
+                 coeff->mag = (coeff->mag << 1) | 1;
+                 cx = signContext[horizSign][vertSign][0];
+                 xorBit = signContext[horizSign][vertSign][1];
+                 if (arithDecoder->decodeBit(cx, cb->stats) ^ xorBit) {
+                   coeff->flags |= jpxCoeffSign;
+                 }
+               }
+               ++coeff->len;
+               coeff->flags |= jpxCoeffTouched;
+             }
+           }
+         }
+       }
+      }
+      ++cb->nextPass;
+      break;
+
+    //----- magnitude refinement pass
+    case jpxPassMagRef:
+      for (y0 = cb->y0, coeff0 = cb->coeffs;
+          y0 < cb->y1;
+          y0 += 4, coeff0 += 4 << tileComp->codeBlockW) {
+       for (x = cb->x0, coeff1 = coeff0;
+            x < cb->x1;
+            ++x, ++coeff1) {
+         for (y1 = 0, coeff = coeff1;
+              y1 < 4 && y0+y1 < cb->y1;
+              ++y1, coeff += tileComp->cbW) {
+           if ((coeff->flags & jpxCoeffSignificant) &&
+               !(coeff->flags & jpxCoeffTouched)) {
+             if (coeff->flags & jpxCoeffFirstMagRef) {
+               all = 0;
+               if (x > cb->x0) {
+                 all += (coeff[-1].flags >> jpxCoeffSignificantB) & 1;
+                 if (y0+y1 > cb->y0) {
+                   all += (coeff[-tileComp->cbW - 1].flags
+                           >> jpxCoeffSignificantB) & 1;
+                 }
+                 if (y0+y1 < cb->y1 - 1) {
+                   all += (coeff[tileComp->cbW - 1].flags
+                           >> jpxCoeffSignificantB) & 1;
+                 }
+               }
+               if (x < cb->x1 - 1) {
+                 all += (coeff[1].flags >> jpxCoeffSignificantB) & 1;
+                 if (y0+y1 > cb->y0) {
+                   all += (coeff[-tileComp->cbW + 1].flags
+                           >> jpxCoeffSignificantB) & 1;
+                 }
+                 if (y0+y1 < cb->y1 - 1) {
+                   all += (coeff[tileComp->cbW + 1].flags
+                           >> jpxCoeffSignificantB) & 1;
+                 }
+               }
+               if (y0+y1 > cb->y0) {
+                 all += (coeff[-tileComp->cbW].flags
+                         >> jpxCoeffSignificantB) & 1;
+               }
+               if (y0+y1 < cb->y1 - 1) {
+                 all += (coeff[tileComp->cbW].flags
+                         >> jpxCoeffSignificantB) & 1;
+               }
+               cx = all ? 15 : 14;
+             } else {
+               cx = 16;
+             }
+             coeff->mag = (coeff->mag << 1) |
+                          arithDecoder->decodeBit(cx, cb->stats);
+             ++coeff->len;
+             coeff->flags |= jpxCoeffTouched;
+             coeff->flags &= ~jpxCoeffFirstMagRef;
+           }
+         }
+       }
+      }
+      ++cb->nextPass;
+      break;
+
+    //----- cleanup pass
+    case jpxPassCleanup:
+      for (y0 = cb->y0, coeff0 = cb->coeffs;
+          y0 < cb->y1;
+          y0 += 4, coeff0 += 4 << tileComp->codeBlockW) {
+       for (x = cb->x0, coeff1 = coeff0;
+            x < cb->x1;
+            ++x, ++coeff1) {
+         y1 = 0;
+         if (y0 + 3 < cb->y1 &&
+             !(coeff1->flags & jpxCoeffTouched) &&
+             !(coeff1[tileComp->cbW].flags & jpxCoeffTouched) &&
+             !(coeff1[2 * tileComp->cbW].flags & jpxCoeffTouched) &&
+             !(coeff1[3 * tileComp->cbW].flags & jpxCoeffTouched) &&
+             (x == cb->x0 || y0 == cb->y0 ||
+              !(coeff1[-tileComp->cbW - 1].flags
+                & jpxCoeffSignificant)) &&
+             (y0 == cb->y0 ||
+              !(coeff1[-tileComp->cbW].flags & jpxCoeffSignificant)) &&
+             (x == cb->x1 - 1 || y0 == cb->y0 ||
+              !(coeff1[-tileComp->cbW + 1].flags & jpxCoeffSignificant)) &&
+             (x == cb->x0 ||
+              (!(coeff1[-1].flags & jpxCoeffSignificant) &&
+               !(coeff1[tileComp->cbW - 1].flags
+                 & jpxCoeffSignificant) &&
+               !(coeff1[2 * tileComp->cbW - 1].flags
+                 & jpxCoeffSignificant) && 
+               !(coeff1[3 * tileComp->cbW - 1].flags
+                 & jpxCoeffSignificant))) &&
+             (x == cb->x1 - 1 ||
+              (!(coeff1[1].flags & jpxCoeffSignificant) &&
+               !(coeff1[tileComp->cbW + 1].flags
+                 & jpxCoeffSignificant) &&
+               !(coeff1[2 * tileComp->cbW + 1].flags
+                 & jpxCoeffSignificant) &&
+               !(coeff1[3 * tileComp->cbW + 1].flags
+                 & jpxCoeffSignificant))) &&
+             (x == cb->x0 || y0+4 == cb->y1 ||
+              !(coeff1[4 * tileComp->cbW - 1].flags & jpxCoeffSignificant)) &&
+             (y0+4 == cb->y1 ||
+              !(coeff1[4 * tileComp->cbW].flags & jpxCoeffSignificant)) &&
+             (x == cb->x1 - 1 || y0+4 == cb->y1 ||
+              !(coeff1[4 * tileComp->cbW + 1].flags
+                & jpxCoeffSignificant))) {
+           if (arithDecoder->decodeBit(jpxContextRunLength, cb->stats)) {
+             y1 = arithDecoder->decodeBit(jpxContextUniform, cb->stats);
+             y1 = (y1 << 1) |
+                  arithDecoder->decodeBit(jpxContextUniform, cb->stats);
+             for (y2 = 0, coeff = coeff1;
+                  y2 < y1;
+                  ++y2, coeff += tileComp->cbW) {
+               ++coeff->len;
+             }
+             coeff->flags |= jpxCoeffSignificant | jpxCoeffFirstMagRef;
+             coeff->mag = (coeff->mag << 1) | 1;
+             ++coeff->len;
+             cx = signContext[2][2][0];
+             xorBit = signContext[2][2][1];
+             if (arithDecoder->decodeBit(cx, cb->stats) ^ xorBit) {
+               coeff->flags |= jpxCoeffSign;
+             }
+             ++y1;
+           } else {
+             for (y1 = 0, coeff = coeff1;
+                  y1 < 4;
+                  ++y1, coeff += tileComp->cbW) {
+               ++coeff->len;
+             }
+             y1 = 4;
+           }
+         }
+         for (coeff = &coeff1[y1 << tileComp->codeBlockW];
+              y1 < 4 && y0 + y1 < cb->y1;
+              ++y1, coeff += tileComp->cbW) {
+           if (!(coeff->flags & jpxCoeffTouched)) {
+             horiz = vert = diag = 0;
+             horizSign = vertSign = 2;
+             if (x > cb->x0) {
+               if (coeff[-1].flags & jpxCoeffSignificant) {
+                 ++horiz;
+                 horizSign += (coeff[-1].flags & jpxCoeffSign) ? -1 : 1;
+               }
+               if (y0+y1 > cb->y0) {
+                 diag += (coeff[-tileComp->cbW - 1].flags
+                          >> jpxCoeffSignificantB) & 1;
+               }
+               if (y0+y1 < cb->y1 - 1) {
+                 diag += (coeff[tileComp->cbW - 1].flags
+                          >> jpxCoeffSignificantB) & 1;
+               }
+             }
+             if (x < cb->x1 - 1) {
+               if (coeff[1].flags & jpxCoeffSignificant) {
+                 ++horiz;
+                 horizSign += (coeff[1].flags & jpxCoeffSign) ? -1 : 1;
+               }
+               if (y0+y1 > cb->y0) {
+                 diag += (coeff[-tileComp->cbW + 1].flags
+                          >> jpxCoeffSignificantB) & 1;
+               }
+               if (y0+y1 < cb->y1 - 1) {
+                 diag += (coeff[tileComp->cbW + 1].flags
+                          >> jpxCoeffSignificantB) & 1;
+               }
+             }
+             if (y0+y1 > cb->y0) {
+               if (coeff[-tileComp->cbW].flags & jpxCoeffSignificant) {
+                 ++vert;
+                 vertSign += (coeff[-tileComp->cbW].flags & jpxCoeffSign)
+                             ? -1 : 1;
+               }
+             }
+             if (y0+y1 < cb->y1 - 1) {
+               if (coeff[tileComp->cbW].flags & jpxCoeffSignificant) {
+                 ++vert;
+                 vertSign += (coeff[tileComp->cbW].flags & jpxCoeffSign)
+                             ? -1 : 1;
+               }
+             }
+             cx = sigPropContext[horiz][vert][diag][res == 0 ? 1 : sb];
+             if (arithDecoder->decodeBit(cx, cb->stats)) {
+               coeff->flags |= jpxCoeffSignificant | jpxCoeffFirstMagRef;
+               coeff->mag = (coeff->mag << 1) | 1;
+               cx = signContext[horizSign][vertSign][0];
+               xorBit = signContext[horizSign][vertSign][1];
+               if (arithDecoder->decodeBit(cx, cb->stats) ^ xorBit) {
+                 coeff->flags |= jpxCoeffSign;
+               }
+             }
+             ++coeff->len;
+           } else {
+             coeff->flags &= ~jpxCoeffTouched;
+           }
+         }
+       }
+      }
+      cb->nextPass = jpxPassSigProp;
+      break;
+    }
+  }
+
+  delete arithDecoder;
+  return gTrue;
+}
+
+// Inverse quantization, and wavelet transform (IDWT).  This also does
+// the initial shift to convert to fixed point format.
+void JPXStream::inverseTransform(JPXTileComp *tileComp) {
+  JPXResLevel *resLevel;
+  JPXPrecinct *precinct;
+  JPXSubband *subband;
+  JPXCodeBlock *cb;
+  JPXCoeff *coeff0, *coeff;
+  Guint qStyle, guard, eps, shift, shift2;
+  double mu;
+  int val;
+  int *dataPtr;
+  Guint nx0, ny0, nx1, ny1;
+  Guint r, cbX, cbY, x, y;
+
+  //----- (NL)LL subband (resolution level 0)
+
+  resLevel = &tileComp->resLevels[0];
+  precinct = &resLevel->precincts[0];
+  subband = &precinct->subbands[0];
+
+  // i-quant parameters
+  qStyle = tileComp->quantStyle & 0x1f;
+  guard = (tileComp->quantStyle >> 5) & 7;
+  if (qStyle == 0) {
+    eps = (tileComp->quantSteps[0] >> 3) & 0x1f;
+    shift = guard + eps - 1;
+    mu = 0; // make gcc happy
+  } else {
+    shift = guard - 1 + tileComp->prec;
+    mu = (double)(0x800 + (tileComp->quantSteps[0] & 0x7ff)) / 2048.0;
+  }
+  if (tileComp->transform == 0) {
+    shift += fracBits;
+  }
+
+  // copy (NL)LL into the upper-left corner of the data array, doing
+  // the fixed point adjustment and dequantization along the way
+  cb = subband->cbs;
+  for (cbY = 0; cbY < subband->nYCBs; ++cbY) {
+    for (cbX = 0; cbX < subband->nXCBs; ++cbX) {
+      for (y = cb->y0, coeff0 = cb->coeffs;
+          y < cb->y1;
+          ++y, coeff0 += tileComp->cbW) {
+       dataPtr = &tileComp->data[(y - subband->y0)
+                                 * (tileComp->x1 - tileComp->x0)
+                                 + (cb->x0 - subband->x0)];
+       for (x = cb->x0, coeff = coeff0; x < cb->x1; ++x, ++coeff) {
+         val = (int)coeff->mag;
+         if (val != 0) {
+           shift2 = shift - (cb->nZeroBitPlanes + coeff->len);
+           if (shift2 > 0) {
+             val = (val << shift2) + (1 << (shift2 - 1));
+           } else {
+             val >>= -shift2;
+           }
+           if (qStyle == 0) {
+             if (tileComp->transform == 0) {
+               val &= -1 << fracBits;
+             }
+           } else {
+             val = (int)((double)val * mu);
+           }
+           if (coeff->flags & jpxCoeffSign) {
+             val = -val;
+           }
+         }
+         *dataPtr++ = val;
+       }
+      }
+      ++cb;
+    }
+  }
+
+  //----- IDWT for each level
+
+  for (r = 1; r <= tileComp->nDecompLevels; ++r) {
+    resLevel = &tileComp->resLevels[r];
+
+    // (n)LL is already in the upper-left corner of the
+    // tile-component data array -- interleave with (n)HL/LH/HH
+    // and inverse transform to get (n-1)LL, which will be stored
+    // in the upper-left corner of the tile-component data array
+    if (r == tileComp->nDecompLevels) {
+      nx0 = tileComp->x0;
+      ny0 = tileComp->y0;
+      nx1 = tileComp->x1;
+      ny1 = tileComp->y1;
+    } else {
+      nx0 = tileComp->resLevels[r+1].x0;
+      ny0 = tileComp->resLevels[r+1].y0;
+      nx1 = tileComp->resLevels[r+1].x1;
+      ny1 = tileComp->resLevels[r+1].y1;
+    }
+    inverseTransformLevel(tileComp, r, resLevel, nx0, ny0, nx1, ny1);
+  }
+}
+
+// Do one level of the inverse transform:
+// - take (n)LL from the tile-component data array
+// - take (n)HL/LH/HH from <resLevel>
+// - leave the resulting (n-1)LL in the tile-component data array
+void JPXStream::inverseTransformLevel(JPXTileComp *tileComp,
+                                     Guint r, JPXResLevel *resLevel,
+                                     Guint nx0, Guint ny0,
+                                     Guint nx1, Guint ny1) {
+  JPXPrecinct *precinct;
+  JPXSubband *subband;
+  JPXCodeBlock *cb;
+  JPXCoeff *coeff0, *coeff;
+  Guint qStyle, guard, eps, shift, shift2, t;
+  double mu;
+  int val;
+  int *dataPtr;
+  Guint xo, yo;
+  Guint x, y, sb, cbX, cbY;
+  int xx, yy;
+
+  //----- interleave
+
+  // spread out LL
+  for (yy = resLevel->y1 - 1; yy >= (int)resLevel->y0; --yy) {
+    for (xx = resLevel->x1 - 1; xx >= (int)resLevel->x0; --xx) {
+      tileComp->data[(2 * yy - ny0) * (tileComp->x1 - tileComp->x0)
+                    + (2 * xx - nx0)] =
+         tileComp->data[(yy - resLevel->y0) * (tileComp->x1 - tileComp->x0)
+                        + (xx - resLevel->x0)];
+    }
+  }
+
+  // i-quant parameters
+  qStyle = tileComp->quantStyle & 0x1f;
+  guard = (tileComp->quantStyle >> 5) & 7;
+
+  // interleave HL/LH/HH
+  precinct = &resLevel->precincts[0];
+  for (sb = 0; sb < 3; ++sb) {
+
+    // i-quant parameters
+    if (qStyle == 0) {
+      eps = (tileComp->quantSteps[3*r - 2 + sb] >> 3) & 0x1f;
+      shift = guard + eps - 1;
+      mu = 0; // make gcc happy
+    } else {
+      shift = guard + tileComp->prec;
+      if (sb == 2) {
+       ++shift;
+      }
+      t = tileComp->quantSteps[qStyle == 1 ? 0 : (3*r - 2 + sb)];
+      mu = (double)(0x800 + (t & 0x7ff)) / 2048.0;
+    }
+    if (tileComp->transform == 0) {
+      shift += fracBits;
+    }
+
+    // copy the subband coefficients into the data array, doing the
+    // fixed point adjustment and dequantization along the way
+    xo = (sb & 1) ? 0 : 1;
+    yo = (sb > 0) ? 1 : 0;
+    subband = &precinct->subbands[sb];
+    cb = subband->cbs;
+    for (cbY = 0; cbY < subband->nYCBs; ++cbY) {
+      for (cbX = 0; cbX < subband->nXCBs; ++cbX) {
+       for (y = cb->y0, coeff0 = cb->coeffs;
+            y < cb->y1;
+            ++y, coeff0 += tileComp->cbW) {
+         dataPtr = &tileComp->data[(2 * y + yo - ny0)
+                                   * (tileComp->x1 - tileComp->x0)
+                                   + (2 * cb->x0 + xo - nx0)];
+         for (x = cb->x0, coeff = coeff0; x < cb->x1; ++x, ++coeff) {
+           val = (int)coeff->mag;
+           if (val != 0) {
+             shift2 = shift - (cb->nZeroBitPlanes + coeff->len);
+             if (shift2 > 0) {
+               val = (val << shift2) + (1 << (shift2 - 1));
+             } else {
+               val >>= -shift2;
+             }
+             if (qStyle == 0) {
+               if (tileComp->transform == 0) {
+                 val &= -1 << fracBits;
+               }
+             } else {
+               val = (int)((double)val * mu);
+             }
+             if (coeff->flags & jpxCoeffSign) {
+               val = -val;
+             }
+           }
+           *dataPtr = val;
+           dataPtr += 2;
+         }
+       }
+       ++cb;
+      }
+    }
+  }
+
+  //----- horizontal (row) transforms
+  dataPtr = tileComp->data;
+  for (y = 0; y < ny1 - ny0; ++y) {
+    inverseTransform1D(tileComp, dataPtr, 1, nx0, nx1);
+    dataPtr += tileComp->x1 - tileComp->x0;
+  }
+
+  //----- vertical (column) transforms
+  dataPtr = tileComp->data;
+  for (x = 0; x < nx1 - nx0; ++x) {
+    inverseTransform1D(tileComp, dataPtr,
+                      tileComp->x1 - tileComp->x0, ny0, ny1);
+    ++dataPtr;
+  }
+}
+
+void JPXStream::inverseTransform1D(JPXTileComp *tileComp,
+                                  int *data, Guint stride,
+                                  Guint i0, Guint i1) {
+  int *buf;
+  Guint offset, end, i;
+
+  //----- special case for length = 1
+  if (i1 - i0 == 1) {
+    if (i0 & 1) {
+      *data >>= 1;
+    }
+
+  } else {
+
+    // choose an offset: this makes even buf[] indexes correspond to
+    // odd values of i, and vice versa
+    offset = 3 + (i0 & 1);
+    end = offset + i1 - i0;
+
+    //----- gather
+    buf = tileComp->buf;
+    for (i = 0; i < i1 - i0; ++i) {
+      buf[offset + i] = data[i * stride];
+    }
+
+    //----- extend right
+    buf[end] = buf[end - 2];
+    if (i1 - i0 == 2) {
+      buf[end+1] = buf[offset + 1];
+      buf[end+2] = buf[offset];
+      buf[end+3] = buf[offset + 1];
+    } else {
+      buf[end+1] = buf[end - 3];
+      if (i1 - i0 == 3) {
+       buf[end+2] = buf[offset + 1];
+       buf[end+3] = buf[offset + 2];
+      } else {
+       buf[end+2] = buf[end - 4];
+       if (i1 - i0 == 4) {
+         buf[end+3] = buf[offset + 1];
+       } else {
+         buf[end+3] = buf[end - 5];
+       }
+      }
+    }
+
+    //----- extend left
+    buf[offset - 1] = buf[offset + 1];
+    buf[offset - 2] = buf[offset + 2];
+    buf[offset - 3] = buf[offset + 3];
+    if (offset == 4) {
+      buf[0] = buf[offset + 4];
+    }
+
+    //----- 9-7 irreversible filter
+
+    if (tileComp->transform == 0) {
+      // step 1 (even)
+      for (i = 1; i <= end + 2; i += 2) {
+       buf[i] = (int)(idwtKappa * buf[i]);
+      }
+      // step 2 (odd)
+      for (i = 0; i <= end + 3; i += 2) {
+       buf[i] = (int)(idwtIKappa * buf[i]);
+      }
+      // step 3 (even)
+      for (i = 1; i <= end + 2; i += 2) {
+       buf[i] = (int)(buf[i] - idwtDelta * (buf[i-1] + buf[i+1]));
+      }
+      // step 4 (odd)
+      for (i = 2; i <= end + 1; i += 2) {
+       buf[i] = (int)(buf[i] - idwtGamma * (buf[i-1] + buf[i+1]));
+      }
+      // step 5 (even)
+      for (i = 3; i <= end; i += 2) {
+       buf[i] = (int)(buf[i] - idwtBeta * (buf[i-1] + buf[i+1]));
+      }
+      // step 6 (odd)
+      for (i = 4; i <= end - 1; i += 2) {
+       buf[i] = (int)(buf[i] - idwtAlpha * (buf[i-1] + buf[i+1]));
+      }
+
+    //----- 5-3 reversible filter
+
+    } else {
+      // step 1 (even)
+      for (i = 3; i <= end; i += 2) {
+       buf[i] -= (buf[i-1] + buf[i+1] + 2) >> 2;
+      }
+      // step 2 (odd)
+      for (i = 4; i < end; i += 2) {
+       buf[i] += (buf[i-1] + buf[i+1]) >> 1;
+      }
+    }
+
+    //----- scatter
+    for (i = 0; i < i1 - i0; ++i) {
+      data[i * stride] = buf[offset + i];
+    }
+  }
+}
+
+// Inverse multi-component transform and DC level shift.  This also
+// converts fixed point samples back to integers.
+GBool JPXStream::inverseMultiCompAndDC(JPXTile *tile) {
+  JPXTileComp *tileComp;
+  int coeff, d0, d1, d2, minVal, maxVal, zeroVal;
+  int *dataPtr;
+  Guint j, comp, x, y;
+
+  //----- inverse multi-component transform
+
+  if (tile->multiComp == 1) {
+    if (img.nComps < 3 ||
+       tile->tileComps[0].hSep != tile->tileComps[1].hSep ||
+       tile->tileComps[0].vSep != tile->tileComps[1].vSep ||
+       tile->tileComps[1].hSep != tile->tileComps[2].hSep ||
+       tile->tileComps[1].vSep != tile->tileComps[2].vSep) {
+      return gFalse;
+    }
+
+    // inverse irreversible multiple component transform
+    if (tile->tileComps[0].transform == 0) {
+      j = 0;
+      for (y = 0; y < tile->tileComps[0].y1 - tile->tileComps[0].y0; ++y) {
+       for (x = 0; x < tile->tileComps[0].x1 - tile->tileComps[0].x0; ++x) {
+         d0 = tile->tileComps[0].data[j];
+         d1 = tile->tileComps[1].data[j];
+         d2 = tile->tileComps[2].data[j];
+         tile->tileComps[0].data[j] = (int)(d0 + 1.402 * d2 + 0.5);
+         tile->tileComps[1].data[j] =
+             (int)(d0 - 0.34413 * d1 - 0.71414 * d2 + 0.5);
+         tile->tileComps[2].data[j] = (int)(d0 + 1.772 * d1 + 0.5);
+         ++j;
+       }
+      }
+
+    // inverse reversible multiple component transform
+    } else {
+      j = 0;
+      for (y = 0; y < tile->tileComps[0].y1 - tile->tileComps[0].y0; ++y) {
+       for (x = 0; x < tile->tileComps[0].x1 - tile->tileComps[0].x0; ++x) {
+         d0 = tile->tileComps[0].data[j];
+         d1 = tile->tileComps[1].data[j];
+         d2 = tile->tileComps[2].data[j];
+         tile->tileComps[0].data[j] = d0 - ((d2 + d1) >> 2);
+         tile->tileComps[1].data[j] = d2 - d1;
+         tile->tileComps[2].data[j] = d0 - d1;
+         ++j;
+       }
+      }
+    }
+  }
+
+  //----- DC level shift
+  for (comp = 0; comp < img.nComps; ++comp) {
+    tileComp = &tile->tileComps[comp];
+
+    // signed: clip
+    if (tileComp->sgned) {
+      minVal = -(1 << (tileComp->prec - 1));
+      maxVal = (1 << (tileComp->prec - 1)) - 1;
+      dataPtr = tileComp->data;
+      for (y = 0; y < tileComp->y1 - tileComp->y0; ++y) {
+       for (x = 0; x < tileComp->x1 - tileComp->x0; ++x) {
+         coeff = *dataPtr;
+         if (tileComp->transform == 0) {
+           coeff >>= fracBits;
+         }
+         if (coeff < minVal) {
+           coeff = minVal;
+         } else if (coeff > maxVal) {
+           coeff = maxVal;
+         }
+         *dataPtr++ = coeff;
+       }
+      }
+
+    // unsigned: inverse DC level shift and clip
+    } else {
+      maxVal = (1 << tileComp->prec) - 1;
+      zeroVal = 1 << (tileComp->prec - 1);
+      dataPtr = tileComp->data;
+      for (y = 0; y < tileComp->y1 - tileComp->y0; ++y) {
+       for (x = 0; x < tileComp->x1 - tileComp->x0; ++x) {
+         coeff = *dataPtr;
+         if (tileComp->transform == 0) {
+           coeff >>= fracBits;
+         }
+         coeff += zeroVal;
+         if (coeff < 0) {
+           coeff = 0;
+         } else if (coeff > maxVal) {
+           coeff = maxVal;
+         }
+         *dataPtr++ = coeff;
+       }
+      }
+    }
+  }
+
+  return gTrue;
+}
+
+GBool JPXStream::readBoxHdr(Guint *boxType, Guint *boxLen, Guint *dataLen) {
+  Guint len, lenH;
+
+  if (!readULong(&len) ||
+      !readULong(boxType)) {
+    return gFalse;
+  }
+  if (len == 1) {
+    if (!readULong(&lenH) || !readULong(&len)) {
+      return gFalse;
+    }
+    if (lenH) {
+      error(getPos(), "JPX stream contains a box larger than 2^32 bytes");
+      return gFalse;
+    }
+    *boxLen = len;
+    *dataLen = len - 16;
+  } else if (len == 0) {
+    *boxLen = 0;
+    *dataLen = 0;
+  } else {
+    *boxLen = len;
+    *dataLen = len - 8;
+  }
+  return gTrue;
+}
+
+int JPXStream::readMarkerHdr(int *segType, Guint *segLen) {
+  int c;
+
+  do {
+    do {
+      if ((c = str->getChar()) == EOF) {
+       return gFalse;
+      }
+    } while (c != 0xff);
+    do {
+      if ((c = str->getChar()) == EOF) {
+       return gFalse;
+      }
+    } while (c == 0xff);
+  } while (c == 0x00);
+  *segType = c;
+  if ((c >= 0x30 && c <= 0x3f) ||
+      c == 0x4f || c == 0x92 || c == 0x93 || c == 0xd9) {
+    *segLen = 0;
+    return gTrue;
+  }
+  return readUWord(segLen);
+}
+
+GBool JPXStream::readUByte(Guint *x) {
+  int c0;
+
+  if ((c0 = str->getChar()) == EOF) {
+    return gFalse;
+  }
+  *x = (Guint)c0;
+  return gTrue;
+}
+
+GBool JPXStream::readByte(int *x) {
+ int c0;
+
+  if ((c0 = str->getChar()) == EOF) {
+    return gFalse;
+  }
+  *x = c0;
+  if (c0 & 0x80) {
+    *x |= -1 - 0xff;
+  }
+  return gTrue;
+}
+
+GBool JPXStream::readUWord(Guint *x) {
+  int c0, c1;
+
+  if ((c0 = str->getChar()) == EOF ||
+      (c1 = str->getChar()) == EOF) {
+    return gFalse;
+  }
+  *x = (Guint)((c0 << 8) | c1);
+  return gTrue;
+}
+
+GBool JPXStream::readULong(Guint *x) {
+  int c0, c1, c2, c3;
+
+  if ((c0 = str->getChar()) == EOF ||
+      (c1 = str->getChar()) == EOF ||
+      (c2 = str->getChar()) == EOF ||
+      (c3 = str->getChar()) == EOF) {
+    return gFalse;
+  }
+  *x = (Guint)((c0 << 24) | (c1 << 16) | (c2 << 8) | c3);
+  return gTrue;
+}
+
+GBool JPXStream::readNBytes(int nBytes, GBool signd, int *x) {
+  int y, c, i;
+
+  y = 0;
+  for (i = 0; i < nBytes; ++i) {
+    if ((c = str->getChar()) == EOF) {
+      return gFalse;
+    }
+    y = (y << 8) + c;
+  }
+  if (signd) {
+    if (y & (1 << (8 * nBytes - 1))) {
+      y |= -1 << (8 * nBytes);
+    }
+  }
+  *x = y;
+  return gTrue;
+}
+
+GBool JPXStream::readBits(int nBits, Guint *x) {
+  int c;
+
+  while (bitBufLen < nBits) {
+    if ((c = str->getChar()) == EOF) {
+      return gFalse;
+    }
+    ++byteCount;
+    if (bitBufSkip) {
+      bitBuf = (bitBuf << 7) | (c & 0x7f);
+      bitBufLen += 7;
+    } else {
+      bitBuf = (bitBuf << 8) | (c & 0xff);
+      bitBufLen += 8;
+    }
+    bitBufSkip = c == 0xff;
+  }
+  *x = (bitBuf >> (bitBufLen - nBits)) & ((1 << nBits) - 1);
+  bitBufLen -= nBits;
+  return gTrue;
+}
+
+void JPXStream::clearBitBuf() {
+  bitBufLen = 0;
+  bitBufSkip = gFalse;
+  byteCount = 0;
+}
diff --git a/pdf/xpdf/JPXStream.h b/pdf/xpdf/JPXStream.h
new file mode 100644 (file)
index 0000000..eb84fe6
--- /dev/null
@@ -0,0 +1,340 @@
+//========================================================================
+//
+// JPXStream.h
+//
+// Copyright 2002-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef JPXSTREAM_H
+#define JPXSTREAM_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#include "gtypes.h"
+#include "Object.h"
+#include "Stream.h"
+
+class JArithmeticDecoderStats;
+
+//------------------------------------------------------------------------
+
+enum JPXColorSpaceType {
+  jpxCSBiLevel = 0,
+  jpxCSYCbCr1 = 1,
+  jpxCSYCbCr2 = 3,
+  jpxCSYCBCr3 = 4,
+  jpxCSPhotoYCC = 9,
+  jpxCSCMY = 11,
+  jpxCSCMYK = 12,
+  jpxCSYCCK = 13,
+  jpxCSCIELab = 14,
+  jpxCSsRGB = 16,
+  jpxCSGrayscale = 17,
+  jpxCSBiLevel2 = 18,
+  jpxCSCIEJab = 19,
+  jpxCSCISesRGB = 20,
+  jpxCSROMMRGB = 21,
+  jpxCSsRGBYCbCr = 22,
+  jpxCSYPbPr1125 = 23,
+  jpxCSYPbPr1250 = 24
+};
+
+struct JPXColorSpec {
+  Guint meth;                  // method
+  int prec;                    // precedence
+  union {
+    struct {
+      JPXColorSpaceType type;  // color space type
+      union {
+       struct {
+         Guint rl, ol, ra, oa, rb, ob, il;
+       } cieLab;
+      };
+    } enumerated;
+  };
+};
+
+//------------------------------------------------------------------------
+
+struct JPXPalette {
+  Guint nEntries;              // number of entries in the palette
+  Guint nComps;                        // number of components in each entry
+  Guint *bpc;                  // bits per component, for each component
+  int *c;                      // color data:
+                               //   c[i*nComps+j] = entry i, component j
+};
+
+//------------------------------------------------------------------------
+
+struct JPXCompMap {
+  Guint nChannels;             // number of channels
+  Guint *comp;                 // codestream components mapped to each channel
+  Guint *type;                 // 0 for direct use, 1 for palette mapping
+  Guint *pComp;                        // palette components to use
+};
+
+//------------------------------------------------------------------------
+
+struct JPXChannelDefn {
+  Guint nChannels;             // number of channels
+  Guint *idx;                  // channel indexes
+  Guint *type;                 // channel types
+  Guint *assoc;                        // channel associations
+};
+
+//------------------------------------------------------------------------
+
+struct JPXTagTreeNode {
+  GBool finished;              // true if this node is finished
+  Guint val;                   // current value
+};
+
+//------------------------------------------------------------------------
+
+struct JPXCoeff {
+  Gushort flags;               // flag bits
+  Gushort len;                 // number of significant bits in mag
+  Guint mag;                   // magnitude value
+};
+
+// coefficient flags
+#define jpxCoeffSignificantB  0
+#define jpxCoeffTouchedB      1
+#define jpxCoeffFirstMagRefB  2
+#define jpxCoeffSignB         7
+#define jpxCoeffSignificant   (1 << jpxCoeffSignificantB)
+#define jpxCoeffTouched       (1 << jpxCoeffTouchedB)
+#define jpxCoeffFirstMagRef   (1 << jpxCoeffFirstMagRefB)
+#define jpxCoeffSign          (1 << jpxCoeffSignB)
+
+//------------------------------------------------------------------------
+
+struct JPXCodeBlock {
+  //----- size
+  Guint x0, y0, x1, y1;                // bounds
+
+  //----- persistent state
+  GBool seen;                  // true if this code-block has already
+                               //   been seen
+  Guint lBlock;                        // base number of bits used for pkt data length
+  Guint nextPass;              // next coding pass
+
+  //---- info from first packet
+  Guint nZeroBitPlanes;                // number of zero bit planes
+
+  //----- info for the current packet
+  Guint included;              // code-block inclusion in this packet:
+                               //   0=not included, 1=included
+  Guint nCodingPasses;         // number of coding passes in this pkt
+  Guint dataLen;               // pkt data length
+
+  //----- coefficient data
+  JPXCoeff *coeffs;            // the coefficients
+  JArithmeticDecoderStats      // arithmetic decoder stats
+    *stats;
+};
+
+//------------------------------------------------------------------------
+
+struct JPXSubband {
+  //----- computed
+  Guint x0, y0, x1, y1;                // bounds
+  Guint nXCBs, nYCBs;          // number of code-blocks in the x and y
+                               //   directions
+
+  //----- tag trees
+  Guint maxTTLevel;            // max tag tree level
+  JPXTagTreeNode *inclusion;   // inclusion tag tree for each subband
+  JPXTagTreeNode *zeroBitPlane;        // zero-bit plane tag tree for each
+                               //   subband
+
+  //----- children
+  JPXCodeBlock *cbs;           // the code-blocks (len = nXCBs * nYCBs)
+};
+
+//------------------------------------------------------------------------
+
+struct JPXPrecinct {
+  //----- computed
+  Guint x0, y0, x1, y1;                // bounds of the precinct
+
+  //----- children
+  JPXSubband *subbands;                // the subbands
+};
+
+//------------------------------------------------------------------------
+
+struct JPXResLevel {
+  //----- from the COD and COC segments (main and tile)
+  Guint precinctWidth;         // log2(precinct width)
+  Guint precinctHeight;                // log2(precinct height)
+
+  //----- computed
+  Guint x0, y0, x1, y1;                // bounds of the tile-comp (for this res level)
+  Guint bx0[3], by0[3],                // subband bounds
+        bx1[3], by1[3];
+
+  //---- children
+  JPXPrecinct *precincts;      // the precincts
+};
+
+//------------------------------------------------------------------------
+
+struct JPXTileComp {
+  //----- from the SIZ segment
+  GBool sgned;                 // 1 for signed, 0 for unsigned
+  Guint prec;                  // precision, in bits
+  Guint hSep;                  // horizontal separation of samples
+  Guint vSep;                  // vertical separation of samples
+
+  //----- from the COD and COC segments (main and tile)
+  Guint style;                 // coding style parameter (Scod / Scoc)
+  Guint nDecompLevels;         // number of decomposition levels
+  Guint codeBlockW;            // log2(code-block width)
+  Guint codeBlockH;            // log2(code-block height)
+  Guint codeBlockStyle;                // code-block style
+  Guint transform;             // wavelet transformation
+
+  //----- from the QCD and QCC segments (main and tile)
+  Guint quantStyle;            // quantization style
+  Guint *quantSteps;           // quantization step size for each subband
+  Guint nQuantSteps;           // number of entries in quantSteps
+
+  //----- computed
+  Guint x0, y0, x1, y1;                // bounds of the tile-comp, in ref coords
+  Guint cbW;                   // code-block width
+  Guint cbH;                   // code-block height
+
+  //----- image data
+  int *data;                   // the decoded image data
+  int *buf;                    // intermediate buffer for the inverse
+                               //   transform
+
+  //----- children
+  JPXResLevel *resLevels;      // the resolution levels
+                               //   (len = nDecompLevels + 1)
+};
+
+//------------------------------------------------------------------------
+
+struct JPXTile {
+  //----- from the COD segments (main and tile)
+  Guint progOrder;             // progression order
+  Guint nLayers;               // number of layers
+  Guint multiComp;             // multiple component transformation
+
+  //----- computed
+  Guint x0, y0, x1, y1;                // bounds of the tile, in ref coords
+  Guint maxNDecompLevels;      // max number of decomposition levels used
+                               //   in any component in this tile
+
+  //----- progression order loop counters
+  Guint comp;                  //   component
+  Guint res;                   //   resolution level
+  Guint precinct;              //   precinct
+  Guint layer;                 //   layer
+
+  //----- children
+  JPXTileComp *tileComps;      // the tile-components (len = JPXImage.nComps)
+};
+
+//------------------------------------------------------------------------
+
+struct JPXImage {
+  //----- from the SIZ segment
+  Guint xSize, ySize;          // size of reference grid
+  Guint xOffset, yOffset;      // image offset
+  Guint xTileSize, yTileSize;  // size of tiles
+  Guint xTileOffset,           // offset of first tile
+        yTileOffset;
+  Guint nComps;                        // number of components
+
+  //----- computed
+  Guint nXTiles;               // number of tiles in x direction
+  Guint nYTiles;               // number of tiles in y direction
+
+  //----- children
+  JPXTile *tiles;              // the tiles (len = nXTiles * nYTiles)
+};
+
+//------------------------------------------------------------------------
+
+class JPXStream: public FilterStream {
+public:
+
+  JPXStream(Stream *strA);
+  virtual ~JPXStream();
+  virtual StreamKind getKind() { return strJPX; }
+  virtual void reset();
+  virtual int getChar();
+  virtual int lookChar();
+  virtual GString *getPSFilter(int psLevel, char *indent);
+  virtual GBool isBinary(GBool last = gTrue);
+
+private:
+
+  void fillReadBuf();
+  GBool readBoxes();
+  GBool readColorSpecBox(Guint dataLen);
+  GBool readCodestream(Guint len);
+  GBool readTilePart();
+  GBool readTilePartData(Guint tileIdx,
+                        Guint tilePartLen, GBool tilePartToEOC);
+  GBool readCodeBlockData(JPXTileComp *tileComp,
+                         JPXResLevel *resLevel,
+                         JPXPrecinct *precinct,
+                         JPXSubband *subband,
+                         Guint res, Guint sb,
+                         JPXCodeBlock *cb);
+  void inverseTransform(JPXTileComp *tileComp);
+  void inverseTransformLevel(JPXTileComp *tileComp,
+                            Guint r, JPXResLevel *resLevel,
+                            Guint nx0, Guint ny0,
+                            Guint nx1, Guint ny1);
+  void inverseTransform1D(JPXTileComp *tileComp,
+                         int *data, Guint stride,
+                         Guint i0, Guint i1);
+  GBool inverseMultiCompAndDC(JPXTile *tile);
+  GBool readBoxHdr(Guint *boxType, Guint *boxLen, Guint *dataLen);
+  int readMarkerHdr(int *segType, Guint *segLen);
+  GBool readUByte(Guint *x);
+  GBool readByte(int *x);
+  GBool readUWord(Guint *x);
+  GBool readULong(Guint *x);
+  GBool readNBytes(int nBytes, GBool signd, int *x);
+  GBool readBits(int nBits, Guint *x);
+  void clearBitBuf();
+
+  Guint nComps;                        // number of components
+  Guint *bpc;                  // bits per component, for each component
+  Guint width, height;         // image size
+  GBool haveImgHdr;            // set if a JP2/JPX image header has been
+                               //   found
+  JPXColorSpec cs;             // color specification
+  GBool haveCS;                        // set if a color spec has been found
+  JPXPalette palette;          // the palette
+  GBool havePalette;           // set if a palette has been found
+  JPXCompMap compMap;          // the component mapping
+  GBool haveCompMap;           // set if a component mapping has been found
+  JPXChannelDefn channelDefn;  // channel definition
+  GBool haveChannelDefn;       // set if a channel defn has been found
+
+  JPXImage img;                        // JPEG2000 decoder data
+  Guint bitBuf;                        // buffer for bit reads
+  int bitBufLen;               // number of bits in bitBuf
+  GBool bitBufSkip;            // true if next bit should be skipped
+                               //   (for bit stuffing)
+  Guint byteCount;             // number of bytes read since last call
+                               //   to clearBitBuf
+
+  Guint curX, curY, curComp;   // current position for lookChar/getChar
+  Guint readBuf;               // read buffer
+  Guint readBufLen;            // number of valid bits in readBuf
+};
+
+#endif
diff --git a/pdf/xpdf/SplashOutputDev.cc b/pdf/xpdf/SplashOutputDev.cc
new file mode 100644 (file)
index 0000000..0284d82
--- /dev/null
@@ -0,0 +1,1345 @@
+//========================================================================
+//
+// SplashOutputDev.cc
+//
+// Copyright 2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <string.h>
+#include <math.h>
+#include "gfile.h"
+#include "GlobalParams.h"
+#include "Error.h"
+#include "Object.h"
+#include "GfxState.h"
+#include "GfxFont.h"
+#include "Link.h"
+#include "CharCodeToUnicode.h"
+#include "FontEncodingTables.h"
+#include "FoFiTrueType.h"
+#include "SplashBitmap.h"
+#include "SplashGlyphBitmap.h"
+#include "SplashPattern.h"
+#include "SplashScreen.h"
+#include "SplashPath.h"
+#include "SplashState.h"
+#include "SplashErrorCodes.h"
+#include "SplashFontEngine.h"
+#include "SplashFont.h"
+#include "SplashFontFile.h"
+#include "SplashFontFileID.h"
+#include "Splash.h"
+#include "SplashOutputDev.h"
+
+//------------------------------------------------------------------------
+// Font substitutions
+//------------------------------------------------------------------------
+
+struct SplashOutFontSubst {
+  char *name;
+  double mWidth;
+};
+
+// index: {symbolic:12, fixed:8, serif:4, sans-serif:0} + bold*2 + italic
+static SplashOutFontSubst splashOutSubstFonts[16] = {
+  {"Helvetica",             0.833},
+  {"Helvetica-Oblique",     0.833},
+  {"Helvetica-Bold",        0.889},
+  {"Helvetica-BoldOblique", 0.889},
+  {"Times-Roman",           0.788},
+  {"Times-Italic",          0.722},
+  {"Times-Bold",            0.833},
+  {"Times-BoldItalic",      0.778},
+  {"Courier",               0.600},
+  {"Courier-Oblique",       0.600},
+  {"Courier-Bold",          0.600},
+  {"Courier-BoldOblique",   0.600},
+  {"Symbol",                0.576},
+  {"Symbol",                0.576},
+  {"Symbol",                0.576},
+  {"Symbol",                0.576}
+};
+
+//------------------------------------------------------------------------
+
+#define soutRound(x) ((int)(x + 0.5))
+
+//------------------------------------------------------------------------
+// SplashOutFontFileID
+//------------------------------------------------------------------------
+
+class SplashOutFontFileID: public SplashFontFileID {
+public:
+
+  SplashOutFontFileID(Ref *rA) { r = *rA; substIdx = -1; }
+
+  ~SplashOutFontFileID() {}
+
+  GBool matches(SplashFontFileID *id) {
+    return ((SplashOutFontFileID *)id)->r.num == r.num &&
+           ((SplashOutFontFileID *)id)->r.gen == r.gen;
+  }
+
+  void setSubstIdx(int substIdxA) { substIdx = substIdxA; }
+  int getSubstIdx() { return substIdx; }
+
+private:
+
+  Ref r;
+  int substIdx;
+};
+
+//------------------------------------------------------------------------
+// T3FontCache
+//------------------------------------------------------------------------
+
+struct T3FontCacheTag {
+  Gushort code;
+  Gushort mru;                 // valid bit (0x8000) and MRU index
+};
+
+class T3FontCache {
+public:
+
+  T3FontCache(Ref *fontID, double m11A, double m12A,
+             double m21A, double m22A,
+             int glyphXA, int glyphYA, int glyphWA, int glyphHA,
+             GBool aa);
+  ~T3FontCache();
+  GBool matches(Ref *idA, double m11A, double m12A,
+               double m21A, double m22A)
+    { return fontID.num == idA->num && fontID.gen == idA->gen &&
+            m11 == m11A && m12 == m12A && m21 == m21A && m22 == m22A; }
+
+  Ref fontID;                  // PDF font ID
+  double m11, m12, m21, m22;   // transform matrix
+  int glyphX, glyphY;          // pixel offset of glyph bitmaps
+  int glyphW, glyphH;          // size of glyph bitmaps, in pixels
+  int glyphSize;               // size of glyph bitmaps, in bytes
+  int cacheSets;               // number of sets in cache
+  int cacheAssoc;              // cache associativity (glyphs per set)
+  Guchar *cacheData;           // glyph pixmap cache
+  T3FontCacheTag *cacheTags;   // cache tags, i.e., char codes
+};
+
+T3FontCache::T3FontCache(Ref *fontIDA, double m11A, double m12A,
+                        double m21A, double m22A,
+                        int glyphXA, int glyphYA, int glyphWA, int glyphHA,
+                        GBool aa) {
+  int i;
+
+  fontID = *fontIDA;
+  m11 = m11A;
+  m12 = m12A;
+  m21 = m21A;
+  m22 = m22A;
+  glyphX = glyphXA;
+  glyphY = glyphYA;
+  glyphW = glyphWA;
+  glyphH = glyphHA;
+  if (aa) {
+    glyphSize = glyphW * glyphH;
+  } else {
+    glyphSize = ((glyphW + 7) >> 3) * glyphH;
+  }
+  cacheAssoc = 8;
+  if (glyphSize <= 256) {
+    cacheSets = 8;
+  } else if (glyphSize <= 512) {
+    cacheSets = 4;
+  } else if (glyphSize <= 1024) {
+    cacheSets = 2;
+  } else {
+    cacheSets = 1;
+  }
+  cacheData = (Guchar *)gmalloc(cacheSets * cacheAssoc * glyphSize);
+  cacheTags = (T3FontCacheTag *)gmalloc(cacheSets * cacheAssoc *
+                                       sizeof(T3FontCacheTag));
+  for (i = 0; i < cacheSets * cacheAssoc; ++i) {
+    cacheTags[i].mru = i & (cacheAssoc - 1);
+  }
+}
+
+T3FontCache::~T3FontCache() {
+  gfree(cacheData);
+  gfree(cacheTags);
+}
+
+struct T3GlyphStack {
+  Gushort code;                        // character code
+  double x, y;                 // position to draw the glyph
+
+  //----- cache info
+  T3FontCache *cache;          // font cache for the current font
+  T3FontCacheTag *cacheTag;    // pointer to cache tag for the glyph
+  Guchar *cacheData;           // pointer to cache data for the glyph
+
+  //----- saved state
+  SplashBitmap *origBitmap;
+  Splash *origSplash;
+  double origCTM4, origCTM5;
+
+  T3GlyphStack *next;          // next object on stack
+};
+
+//------------------------------------------------------------------------
+// SplashOutputDev
+//------------------------------------------------------------------------
+
+SplashOutputDev::SplashOutputDev(SplashColorMode colorModeA,
+                                GBool reverseVideoA,
+                                SplashColor paperColorA) {
+  colorMode = colorModeA;
+  reverseVideo = reverseVideoA;
+  paperColor = paperColorA;
+
+  xref = NULL;
+
+  bitmap = new SplashBitmap(1, 1, colorMode);
+  splash = new Splash(bitmap);
+  splash->clear(paperColor);
+
+  fontEngine = NULL;
+
+  nT3Fonts = 0;
+  t3GlyphStack = NULL;
+
+  font = NULL;
+  needFontUpdate = gFalse;
+  textClipPath = NULL;
+
+  underlayCbk = NULL;
+  underlayCbkData = NULL;
+}
+
+SplashOutputDev::~SplashOutputDev() {
+  int i;
+
+  for (i = 0; i < nT3Fonts; ++i) {
+    delete t3FontCache[i];
+  }
+  if (fontEngine) {
+    delete fontEngine;
+  }
+  if (splash) {
+    delete splash;
+  }
+  if (bitmap) {
+    delete bitmap;
+  }
+}
+
+void SplashOutputDev::startDoc(XRef *xrefA) {
+  int i;
+
+  xref = xrefA;
+  if (fontEngine) {
+    delete fontEngine;
+  }
+  fontEngine = new SplashFontEngine(
+#if HAVE_T1LIB_H
+                                   globalParams->getEnableT1lib(),
+#endif
+#if HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H
+                                   globalParams->getEnableFreeType(),
+#endif
+                                   globalParams->getAntialias());
+  for (i = 0; i < nT3Fonts; ++i) {
+    delete t3FontCache[i];
+  }
+  nT3Fonts = 0;
+}
+
+void SplashOutputDev::startPage(int pageNum, GfxState *state) {
+  int w, h;
+  SplashColor color;
+
+  w = state ? (int)(state->getPageWidth() + 0.5) : 1;
+  h = state ? (int)(state->getPageHeight() + 0.5) : 1;
+  if (splash) {
+    delete splash;
+  }
+  if (!bitmap || w != bitmap->getWidth() || h != bitmap->getHeight()) {
+    if (bitmap) {
+      delete bitmap;
+    }
+    bitmap = new SplashBitmap(w, h, colorMode);
+  }
+  splash = new Splash(bitmap);
+  switch (colorMode) {
+  case splashModeMono1: color.mono1 = 0; break;
+  case splashModeMono8: color.mono8 = 0; break;
+  case splashModeRGB8: color.rgb8 = splashMakeRGB8(0, 0, 0); break;
+  case splashModeBGR8Packed: color.bgr8 = splashMakeBGR8(0, 0, 0); break;
+  }
+  splash->setStrokePattern(new SplashSolidColor(color));
+  splash->setFillPattern(new SplashSolidColor(color));
+  splash->setLineCap(splashLineCapButt);
+  splash->setLineJoin(splashLineJoinMiter);
+  splash->setLineDash(NULL, 0, 0);
+  splash->setMiterLimit(10);
+  splash->setFlatness(1);
+  splash->clear(paperColor);
+
+  if (underlayCbk) {
+    (*underlayCbk)(underlayCbkData);
+  }
+}
+
+void SplashOutputDev::endPage() {
+}
+
+void SplashOutputDev::drawLink(Link *link, Catalog *catalog) {
+  double x1, y1, x2, y2;
+  LinkBorderStyle *borderStyle;
+  GfxRGB rgb;
+  double gray;
+  double *dash;
+  int dashLength;
+  SplashCoord dashList[20];
+  SplashPath *path;
+  int x, y, i;
+
+  link->getRect(&x1, &y1, &x2, &y2);
+  borderStyle = link->getBorderStyle();
+  if (borderStyle->getWidth() > 0) {
+    borderStyle->getColor(&rgb.r, &rgb.g, &rgb.b);
+    gray = 0.299 * rgb.r + 0.587 * rgb.g + 0.114 * rgb.b;
+    if (gray > 1) {
+      gray = 1;
+    }
+    splash->setStrokePattern(getColor(gray, &rgb));
+    splash->setLineWidth((SplashCoord)borderStyle->getWidth());
+    borderStyle->getDash(&dash, &dashLength);
+    if (borderStyle->getType() == linkBorderDashed && dashLength > 0) {
+      if (dashLength > 20) {
+       dashLength = 20;
+      }
+      for (i = 0; i < dashLength; ++i) {
+       dashList[i] = (SplashCoord)dash[i];
+      }
+      splash->setLineDash(dashList, dashLength, 0);
+    }
+    path = new SplashPath();
+    if (borderStyle->getType() == linkBorderUnderlined) {
+      cvtUserToDev(x1, y1, &x, &y);
+      path->moveTo((SplashCoord)x, (SplashCoord)y);
+      cvtUserToDev(x2, y1, &x, &y);
+      path->lineTo((SplashCoord)x, (SplashCoord)y);
+    } else {
+      cvtUserToDev(x1, y1, &x, &y);
+      path->moveTo((SplashCoord)x, (SplashCoord)y);
+      cvtUserToDev(x2, y1, &x, &y);
+      path->lineTo((SplashCoord)x, (SplashCoord)y);
+      cvtUserToDev(x2, y2, &x, &y);
+      path->lineTo((SplashCoord)x, (SplashCoord)y);
+      cvtUserToDev(x1, y2, &x, &y);
+      path->lineTo((SplashCoord)x, (SplashCoord)y);
+      path->close();
+    }
+    splash->stroke(path);
+    delete path;
+  }
+}
+
+void SplashOutputDev::saveState(GfxState *state) {
+  splash->saveState();
+}
+
+void SplashOutputDev::restoreState(GfxState *state) {
+  splash->restoreState();
+  needFontUpdate = gTrue;
+}
+
+void SplashOutputDev::updateAll(GfxState *state) {
+  updateLineDash(state);
+  updateLineJoin(state);
+  updateLineCap(state);
+  updateLineWidth(state);
+  updateFlatness(state);
+  updateMiterLimit(state);
+  updateFillColor(state);
+  updateStrokeColor(state);
+  needFontUpdate = gTrue;
+}
+
+void SplashOutputDev::updateCTM(GfxState *state, double m11, double m12,
+                               double m21, double m22,
+                               double m31, double m32) {
+  updateLineDash(state);
+  updateLineJoin(state);
+  updateLineCap(state);
+  updateLineWidth(state);
+}
+
+void SplashOutputDev::updateLineDash(GfxState *state) {
+  double *dashPattern;
+  int dashLength;
+  double dashStart;
+  SplashCoord dash[20];
+  SplashCoord phase;
+  int i;
+
+  state->getLineDash(&dashPattern, &dashLength, &dashStart);
+  if (dashLength > 20) {
+    dashLength = 20;
+  }
+  for (i = 0; i < dashLength; ++i) {
+    dash[i] =  (SplashCoord)state->transformWidth(dashPattern[i]);
+    if (dash[i] < 1) {
+      dash[i] = 1;
+    }
+  }
+  phase = (SplashCoord)state->transformWidth(dashStart);
+  splash->setLineDash(dash, dashLength, phase);
+}
+
+void SplashOutputDev::updateFlatness(GfxState *state) {
+  splash->setFlatness(state->getFlatness());
+}
+
+void SplashOutputDev::updateLineJoin(GfxState *state) {
+  splash->setLineJoin(state->getLineJoin());
+}
+
+void SplashOutputDev::updateLineCap(GfxState *state) {
+  splash->setLineCap(state->getLineCap());
+}
+
+void SplashOutputDev::updateMiterLimit(GfxState *state) {
+  splash->setMiterLimit(state->getMiterLimit());
+}
+
+void SplashOutputDev::updateLineWidth(GfxState *state) {
+  splash->setLineWidth(state->getTransformedLineWidth());
+}
+
+void SplashOutputDev::updateFillColor(GfxState *state) {
+  double gray;
+  GfxRGB rgb;
+
+  state->getFillGray(&gray);
+  state->getFillRGB(&rgb);
+  splash->setFillPattern(getColor(gray, &rgb));
+}
+
+void SplashOutputDev::updateStrokeColor(GfxState *state) {
+  double gray;
+  GfxRGB rgb;
+
+  state->getStrokeGray(&gray);
+  state->getStrokeRGB(&rgb);
+  splash->setStrokePattern(getColor(gray, &rgb));
+}
+
+SplashPattern *SplashOutputDev::getColor(double gray, GfxRGB *rgb) {
+  SplashPattern *pattern;
+  SplashColor color0, color1;
+  double r, g, b;
+
+  if (reverseVideo) {
+    gray = 1 - gray;
+    r = 1 - rgb->r;
+    g = 1 - rgb->g;
+    b = 1 - rgb->b;
+  } else {
+    r = rgb->r;
+    g = rgb->g;
+    b = rgb->b;
+  }
+
+  pattern = NULL; // make gcc happy
+  switch (colorMode) {
+  case splashModeMono1:
+    color0.mono1 = 0;
+    color1.mono1 = 1;
+    pattern = new SplashHalftone(color0, color1,
+                                splash->getScreen()->copy(),
+                                (SplashCoord)gray);
+    break;
+  case splashModeMono8:
+    color1.mono8 = soutRound(255 * gray);
+    pattern = new SplashSolidColor(color1);
+    break;
+  case splashModeRGB8:
+    color1.rgb8 = splashMakeRGB8(soutRound(255 * r),
+                                soutRound(255 * g),
+                                soutRound(255 * b));
+    pattern = new SplashSolidColor(color1);
+    break;
+  case splashModeBGR8Packed:
+    color1.bgr8 = splashMakeBGR8(soutRound(255 * r),
+                                soutRound(255 * g),
+                                soutRound(255 * b));
+    pattern = new SplashSolidColor(color1);
+    break;
+  }
+
+  return pattern;
+}
+
+void SplashOutputDev::updateFont(GfxState *state) {
+  GfxFont *gfxFont;
+  GfxFontType fontType;
+  SplashOutFontFileID *id;
+  SplashFontFile *fontFile;
+  FoFiTrueType *ff;
+  Ref embRef;
+  Object refObj, strObj;
+  GString *tmpFileName, *fileName, *substName;
+  FILE *tmpFile;
+  Gushort *codeToGID;
+  DisplayFontParam *dfp;
+  double m11, m12, m21, m22, w1, w2;
+  SplashCoord mat[4];
+  char *name;
+  int c, substIdx, n, code;
+
+  needFontUpdate = gFalse;
+  font = NULL;
+  tmpFileName = NULL;
+  substIdx = -1;
+
+  if (!(gfxFont = state->getFont())) {
+    goto err1;
+  }
+  fontType = gfxFont->getType();
+  if (fontType == fontType3) {
+    goto err1;
+  }
+
+  // check the font file cache
+  id = new SplashOutFontFileID(gfxFont->getID());
+  if ((fontFile = fontEngine->getFontFile(id))) {
+    delete id;
+
+  } else {
+
+    // if there is an embedded font, write it to disk
+    if (gfxFont->getEmbeddedFontID(&embRef)) {
+      if (!openTempFile(&tmpFileName, &tmpFile, "wb", NULL)) {
+       error(-1, "Couldn't create temporary font file");
+       goto err2;
+      }
+      refObj.initRef(embRef.num, embRef.gen);
+      refObj.fetch(xref, &strObj);
+      refObj.free();
+      strObj.streamReset();
+      while ((c = strObj.streamGetChar()) != EOF) {
+       fputc(c, tmpFile);
+      }
+      strObj.streamClose();
+      strObj.free();
+      fclose(tmpFile);
+      fileName = tmpFileName;
+
+    // if there is an external font file, use it
+    } else if (!(fileName = gfxFont->getExtFontFile())) {
+
+      // look for a display font mapping or a substitute font
+      dfp = NULL;
+      if (gfxFont->isCIDFont()) {
+       if (((GfxCIDFont *)gfxFont)->getCollection()) {
+         dfp = globalParams->
+                 getDisplayCIDFont(gfxFont->getName(),
+                                   ((GfxCIDFont *)gfxFont)->getCollection());
+       }
+      } else {
+       if (gfxFont->getName()) {
+         dfp = globalParams->getDisplayFont(gfxFont->getName());
+       }
+       if (!dfp) {
+         // 8-bit font substitution
+         if (gfxFont->isFixedWidth()) {
+           substIdx = 8;
+         } else if (gfxFont->isSerif()) {
+           substIdx = 4;
+         } else {
+           substIdx = 0;
+         }
+         if (gfxFont->isBold()) {
+           substIdx += 2;
+         }
+         if (gfxFont->isItalic()) {
+           substIdx += 1;
+         }
+         substName = new GString(splashOutSubstFonts[substIdx].name);
+         dfp = globalParams->getDisplayFont(substName);
+         delete substName;
+         id->setSubstIdx(substIdx);
+       }
+      }
+      if (!dfp) {
+       error(-1, "Couldn't find a font for '%s'",
+             gfxFont->getName() ? gfxFont->getName()->getCString()
+                                : "(unnamed)");
+       goto err2;
+      }
+      switch (dfp->kind) {
+      case displayFontT1:
+       fileName = dfp->t1.fileName;
+       fontType = gfxFont->isCIDFont() ? fontCIDType0 : fontType1;
+       break;
+      case displayFontTT:
+       fileName = dfp->tt.fileName;
+       fontType = gfxFont->isCIDFont() ? fontCIDType2 : fontTrueType;
+       break;
+      }
+    }
+
+    // load the font file
+    switch (fontType) {
+    case fontType1:
+      if (!(fontFile = fontEngine->loadType1Font(
+                          id,
+                          fileName->getCString(),
+                          fileName == tmpFileName,
+                          ((Gfx8BitFont *)gfxFont)->getEncoding()))) {
+       error(-1, "Couldn't create a font for '%s'",
+             gfxFont->getName() ? gfxFont->getName()->getCString()
+                                : "(unnamed)");
+       goto err2;
+      }
+      break;
+    case fontType1C:
+      if (!(fontFile = fontEngine->loadType1CFont(
+                          id,
+                          fileName->getCString(),
+                          fileName == tmpFileName,
+                          ((Gfx8BitFont *)gfxFont)->getEncoding()))) {
+       error(-1, "Couldn't create a font for '%s'",
+             gfxFont->getName() ? gfxFont->getName()->getCString()
+                                : "(unnamed)");
+       goto err2;
+      }
+      break;
+    case fontTrueType:
+      if (!(ff = FoFiTrueType::load(fileName->getCString()))) {
+       goto err2;
+      }
+      codeToGID = ((Gfx8BitFont *)gfxFont)->getCodeToGIDMap(ff);
+      delete ff;
+      if (!(fontFile = fontEngine->loadTrueTypeFont(
+                          id,
+                          fileName->getCString(),
+                          fileName == tmpFileName,
+                          codeToGID, 256))) {
+       error(-1, "Couldn't create a font for '%s'",
+             gfxFont->getName() ? gfxFont->getName()->getCString()
+                                : "(unnamed)");
+       goto err2;
+      }
+      break;
+    case fontCIDType0:
+    case fontCIDType0C:
+      if (!(fontFile = fontEngine->loadCIDFont(
+                          id,
+                          fileName->getCString(),
+                          fileName == tmpFileName))) {
+       error(-1, "Couldn't create a font for '%s'",
+             gfxFont->getName() ? gfxFont->getName()->getCString()
+                                : "(unnamed)");
+       goto err2;
+      }
+      break;
+    case fontCIDType2:
+      n = ((GfxCIDFont *)gfxFont)->getCIDToGIDLen();
+      codeToGID = (Gushort *)gmalloc(n * sizeof(Gushort));
+      memcpy(codeToGID, ((GfxCIDFont *)gfxFont)->getCIDToGID(),
+            n * sizeof(Gushort));
+      if (!(fontFile = fontEngine->loadTrueTypeFont(
+                          id,
+                          fileName->getCString(),
+                          fileName == tmpFileName,
+                          codeToGID, n))) {
+       error(-1, "Couldn't create a font for '%s'",
+             gfxFont->getName() ? gfxFont->getName()->getCString()
+                                : "(unnamed)");
+       goto err2;
+      }
+      break;
+    default:
+      // this shouldn't happen
+      goto err2;
+    }
+  }
+
+  // get the font matrix
+  state->getFontTransMat(&m11, &m12, &m21, &m22);
+  m11 *= state->getHorizScaling();
+  m12 *= state->getHorizScaling();
+
+  // for substituted fonts: adjust the font matrix -- compare the
+  // width of 'm' in the original font and the substituted font
+  substIdx = ((SplashOutFontFileID *)fontFile->getID())->getSubstIdx();
+  if (substIdx >= 0) {
+    for (code = 0; code < 256; ++code) {
+      if ((name = ((Gfx8BitFont *)gfxFont)->getCharName(code)) &&
+         name[0] == 'm' && name[1] == '\0') {
+       break;
+      }
+    }
+    if (code < 256) {
+      w1 = ((Gfx8BitFont *)gfxFont)->getWidth(code);
+      w2 = splashOutSubstFonts[substIdx].mWidth;
+      if (!gfxFont->isSymbolic()) {
+       // if real font is substantially narrower than substituted
+       // font, reduce the font size accordingly
+       if (w1 > 0.01 && w1 < 0.9 * w2) {
+         w1 /= w2;
+         m11 *= w1;
+         m21 *= w1;
+       }
+      }
+    }
+  }
+
+  // create the scaled font
+  mat[0] = m11;  mat[1] = -m12;
+  mat[2] = m21;  mat[3] = -m22;
+  font = fontEngine->getFont(fontFile, mat);
+
+  if (tmpFileName) {
+    delete tmpFileName;
+  }
+  return;
+
+ err2:
+  delete id;
+ err1:
+  if (tmpFileName) {
+    delete tmpFileName;
+  }
+  return;
+}
+
+void SplashOutputDev::stroke(GfxState *state) {
+  SplashPath *path;
+
+  path = convertPath(state, state->getPath());
+  splash->stroke(path);
+  delete path;
+}
+
+void SplashOutputDev::fill(GfxState *state) {
+  SplashPath *path;
+
+  path = convertPath(state, state->getPath());
+  splash->fill(path, gFalse);
+  delete path;
+}
+
+void SplashOutputDev::eoFill(GfxState *state) {
+  SplashPath *path;
+
+  path = convertPath(state, state->getPath());
+  splash->fill(path, gTrue);
+  delete path;
+}
+
+void SplashOutputDev::clip(GfxState *state) {
+  SplashPath *path;
+
+  path = convertPath(state, state->getPath());
+  splash->clipToPath(path, gFalse);
+  delete path;
+}
+
+void SplashOutputDev::eoClip(GfxState *state) {
+  SplashPath *path;
+
+  path = convertPath(state, state->getPath());
+  splash->clipToPath(path, gTrue);
+  delete path;
+}
+
+SplashPath *SplashOutputDev::convertPath(GfxState *state, GfxPath *path) {
+  SplashPath *sPath;
+  GfxSubpath *subpath;
+  double x1, y1, x2, y2, x3, y3;
+  int i, j;
+
+  sPath = new SplashPath();
+  for (i = 0; i < path->getNumSubpaths(); ++i) {
+    subpath = path->getSubpath(i);
+    if (subpath->getNumPoints() > 0) {
+      state->transform(subpath->getX(0), subpath->getY(0), &x1, &y1);
+      sPath->moveTo((SplashCoord)x1, (SplashCoord)y1);
+      j = 1;
+      while (j < subpath->getNumPoints()) {
+       if (subpath->getCurve(j)) {
+         state->transform(subpath->getX(j), subpath->getY(j), &x1, &y1);
+         state->transform(subpath->getX(j+1), subpath->getY(j+1), &x2, &y2);
+         state->transform(subpath->getX(j+2), subpath->getY(j+2), &x3, &y3);
+         sPath->curveTo((SplashCoord)x1, (SplashCoord)y1,
+                        (SplashCoord)x2, (SplashCoord)y2,
+                        (SplashCoord)x3, (SplashCoord)y3);
+         j += 3;
+       } else {
+         state->transform(subpath->getX(j), subpath->getY(j), &x1, &y1);
+         sPath->lineTo((SplashCoord)x1, (SplashCoord)y1);
+         ++j;
+       }
+      }
+      if (subpath->isClosed()) {
+       sPath->close();
+      }
+    }
+  }
+  return sPath;
+}
+
+void SplashOutputDev::drawChar(GfxState *state, double x, double y,
+                              double dx, double dy,
+                              double originX, double originY,
+                              CharCode code, Unicode *u, int uLen) {
+  double x1, y1;
+  SplashPath *path;
+  int render;
+
+  if (needFontUpdate) {
+    updateFont(state);
+  }
+  if (!font) {
+    return;
+  }
+
+  // check for invisible text -- this is used by Acrobat Capture
+  render = state->getRender();
+  if (render == 3) {
+    return;
+  }
+
+  x -= originX;
+  y -= originY;
+  state->transform(x, y, &x1, &y1);
+
+  // fill
+  if (!(render & 1)) {
+    splash->fillChar((SplashCoord)x1, (SplashCoord)y1, code, font);
+  }
+
+  // stroke
+  if ((render & 3) == 1 || (render & 3) == 2) {
+    if ((path = font->getGlyphPath(code))) {
+      path->offset((SplashCoord)x1, (SplashCoord)y1);
+      splash->stroke(path);
+      delete path;
+    }
+  }
+
+  // clip
+  if (render & 4) {
+    path = font->getGlyphPath(code);
+    path->offset((SplashCoord)x1, (SplashCoord)y1);
+    if (textClipPath) {
+      textClipPath->append(path);
+      delete path;
+    } else {
+      textClipPath = path;
+    }
+  }
+}
+
+GBool SplashOutputDev::beginType3Char(GfxState *state, double x, double y,
+                                     double dx, double dy,
+                                     CharCode code, Unicode *u, int uLen) {
+  GfxFont *gfxFont;
+  Ref *fontID;
+  double *ctm, *bbox;
+  T3FontCache *t3Font;
+  T3GlyphStack *t3gs;
+  double x1, y1, xMin, yMin, xMax, yMax, xt, yt;
+  int i, j;
+
+  if (!(gfxFont = state->getFont())) {
+    return gFalse;
+  }
+  fontID = gfxFont->getID();
+  ctm = state->getCTM();
+  state->transform(0, 0, &xt, &yt);
+
+  // is it the first (MRU) font in the cache?
+  if (!(nT3Fonts > 0 &&
+       t3FontCache[0]->matches(fontID, ctm[0], ctm[1], ctm[2], ctm[3]))) {
+
+    // is the font elsewhere in the cache?
+    for (i = 1; i < nT3Fonts; ++i) {
+      if (t3FontCache[i]->matches(fontID, ctm[0], ctm[1], ctm[2], ctm[3])) {
+       t3Font = t3FontCache[i];
+       for (j = i; j > 0; --j) {
+         t3FontCache[j] = t3FontCache[j - 1];
+       }
+       t3FontCache[0] = t3Font;
+       break;
+      }
+    }
+    if (i >= nT3Fonts) {
+
+      // create new entry in the font cache
+      if (nT3Fonts == splashOutT3FontCacheSize) {
+       delete t3FontCache[nT3Fonts - 1];
+       --nT3Fonts;
+      }
+      for (j = nT3Fonts; j > 0; --j) {
+       t3FontCache[j] = t3FontCache[j - 1];
+      }
+      ++nT3Fonts;
+      bbox = gfxFont->getFontBBox();
+      if (bbox[0] == 0 && bbox[1] == 0 && bbox[2] == 0 && bbox[3] == 0) {
+       // broken bounding box -- just take a guess
+       xMin = xt - 5;
+       xMax = xMin + 30;
+       yMax = yt + 15;
+       yMin = yMax - 45;
+      } else {
+       state->transform(bbox[0], bbox[1], &x1, &y1);
+       xMin = xMax = x1;
+       yMin = yMax = y1;
+       state->transform(bbox[0], bbox[3], &x1, &y1);
+       if (x1 < xMin) {
+         xMin = x1;
+       } else if (x1 > xMax) {
+         xMax = x1;
+       }
+       if (y1 < yMin) {
+         yMin = y1;
+       } else if (y1 > yMax) {
+         yMax = y1;
+       }
+       state->transform(bbox[2], bbox[1], &x1, &y1);
+       if (x1 < xMin) {
+         xMin = x1;
+       } else if (x1 > xMax) {
+         xMax = x1;
+       }
+       if (y1 < yMin) {
+         yMin = y1;
+       } else if (y1 > yMax) {
+         yMax = y1;
+       }
+       state->transform(bbox[2], bbox[3], &x1, &y1);
+       if (x1 < xMin) {
+         xMin = x1;
+       } else if (x1 > xMax) {
+         xMax = x1;
+       }
+       if (y1 < yMin) {
+         yMin = y1;
+       } else if (y1 > yMax) {
+         yMax = y1;
+       }
+      }
+      t3FontCache[0] = new T3FontCache(fontID, ctm[0], ctm[1], ctm[2], ctm[3],
+                                      (int)floor(xMin - xt),
+                                      (int)floor(yMin - yt),
+                                      (int)ceil(xMax) - (int)floor(xMin) + 3,
+                                      (int)ceil(yMax) - (int)floor(yMin) + 3,
+                                      colorMode != splashModeMono1);
+    }
+  }
+  t3Font = t3FontCache[0];
+
+  // is the glyph in the cache?
+  i = (code & (t3Font->cacheSets - 1)) * t3Font->cacheAssoc;
+  for (j = 0; j < t3Font->cacheAssoc; ++j) {
+    if ((t3Font->cacheTags[i+j].mru & 0x8000) &&
+       t3Font->cacheTags[i+j].code == code) {
+      drawType3Glyph(t3Font, &t3Font->cacheTags[i+j],
+                    t3Font->cacheData + (i+j) * t3Font->glyphSize,
+                    xt, yt);
+      return gTrue;
+    }
+  }
+
+  // push a new Type 3 glyph record
+  t3gs = new T3GlyphStack();
+  t3gs->next = t3GlyphStack;
+  t3GlyphStack = t3gs;
+  t3GlyphStack->code = code;
+  t3GlyphStack->x = xt;
+  t3GlyphStack->y = yt;
+  t3GlyphStack->cache = t3Font;
+  t3GlyphStack->cacheTag = NULL;
+  t3GlyphStack->cacheData = NULL;
+
+  return gFalse;
+}
+
+void SplashOutputDev::endType3Char(GfxState *state) {
+  T3GlyphStack *t3gs;
+  double *ctm;
+
+  if (t3GlyphStack->cacheTag) {
+    memcpy(t3GlyphStack->cacheData, bitmap->getDataPtr().mono8,
+          t3GlyphStack->cache->glyphSize);
+    delete bitmap;
+    delete splash;
+    bitmap = t3GlyphStack->origBitmap;
+    splash = t3GlyphStack->origSplash;
+    ctm = state->getCTM();
+    state->setCTM(ctm[0], ctm[1], ctm[2], ctm[3],
+                 t3GlyphStack->origCTM4, t3GlyphStack->origCTM5);
+    drawType3Glyph(t3GlyphStack->cache,
+                  t3GlyphStack->cacheTag, t3GlyphStack->cacheData,
+                  t3GlyphStack->x, t3GlyphStack->y);
+  }
+  t3gs = t3GlyphStack;
+  t3GlyphStack = t3gs->next;
+  delete t3gs;
+}
+
+void SplashOutputDev::type3D0(GfxState *state, double wx, double wy) {
+}
+
+void SplashOutputDev::type3D1(GfxState *state, double wx, double wy,
+                             double llx, double lly, double urx, double ury) {
+  double *ctm;
+  T3FontCache *t3Font;
+  SplashColor color;
+  double xt, yt, xMin, xMax, yMin, yMax, x1, y1;
+  int i, j;
+
+  t3Font = t3GlyphStack->cache;
+
+  // check for a valid bbox
+  state->transform(0, 0, &xt, &yt);
+  state->transform(llx, lly, &x1, &y1);
+  xMin = xMax = x1;
+  yMin = yMax = y1;
+  state->transform(llx, ury, &x1, &y1);
+  if (x1 < xMin) {
+    xMin = x1;
+  } else if (x1 > xMax) {
+    xMax = x1;
+  }
+  if (y1 < yMin) {
+    yMin = y1;
+  } else if (y1 > yMax) {
+    yMax = y1;
+  }
+  state->transform(urx, lly, &x1, &y1);
+  if (x1 < xMin) {
+    xMin = x1;
+  } else if (x1 > xMax) {
+    xMax = x1;
+  }
+  if (y1 < yMin) {
+    yMin = y1;
+  } else if (y1 > yMax) {
+    yMax = y1;
+  }
+  state->transform(urx, ury, &x1, &y1);
+  if (x1 < xMin) {
+    xMin = x1;
+  } else if (x1 > xMax) {
+    xMax = x1;
+  }
+  if (y1 < yMin) {
+    yMin = y1;
+  } else if (y1 > yMax) {
+    yMax = y1;
+  }
+  if (xMin - xt < t3Font->glyphX ||
+      yMin - yt < t3Font->glyphY ||
+      xMax - xt > t3Font->glyphX + t3Font->glyphW ||
+      yMax - yt > t3Font->glyphY + t3Font->glyphH) {
+    error(-1, "Bad bounding box in Type 3 glyph");
+    return;
+  }
+
+  // allocate a cache entry
+  i = (t3GlyphStack->code & (t3Font->cacheSets - 1)) * t3Font->cacheAssoc;
+  for (j = 0; j < t3Font->cacheAssoc; ++j) {
+    if ((t3Font->cacheTags[i+j].mru & 0x7fff) == t3Font->cacheAssoc - 1) {
+      t3Font->cacheTags[i+j].mru = 0x8000;
+      t3Font->cacheTags[i+j].code = t3GlyphStack->code;
+      t3GlyphStack->cacheTag = &t3Font->cacheTags[i+j];
+      t3GlyphStack->cacheData = t3Font->cacheData + (i+j) * t3Font->glyphSize;
+    } else {
+      ++t3Font->cacheTags[i+j].mru;
+    }
+  }
+
+  // save state
+  t3GlyphStack->origBitmap = bitmap;
+  t3GlyphStack->origSplash = splash;
+  ctm = state->getCTM();
+  t3GlyphStack->origCTM4 = ctm[4];
+  t3GlyphStack->origCTM5 = ctm[5];
+
+  // create the temporary bitmap
+  if (colorMode == splashModeMono1) {
+    bitmap = new SplashBitmap(t3Font->glyphW, t3Font->glyphH, splashModeMono1);
+    splash = new Splash(bitmap);
+    color.mono1 = 0;
+    splash->clear(color);
+    color.mono1 = 1;
+  } else {
+    bitmap = new SplashBitmap(t3Font->glyphW, t3Font->glyphH, splashModeMono8);
+    splash = new Splash(bitmap);
+    color.mono8 = 0x00;
+    splash->clear(color);
+    color.mono8 = 0xff;
+  }
+  splash->setFillPattern(new SplashSolidColor(color));
+  splash->setStrokePattern(new SplashSolidColor(color));
+  //~ this should copy other state from t3GlyphStack->origSplash?
+  state->setCTM(ctm[0], ctm[1], ctm[2], ctm[3],
+               -t3Font->glyphX, -t3Font->glyphY);
+}
+
+void SplashOutputDev::drawType3Glyph(T3FontCache *t3Font,
+                                    T3FontCacheTag *tag, Guchar *data,
+                                    double x, double y) {
+  SplashGlyphBitmap glyph;
+
+  glyph.x = -t3Font->glyphX;
+  glyph.y = -t3Font->glyphY;
+  glyph.w = t3Font->glyphW;
+  glyph.h = t3Font->glyphH;
+  glyph.aa = colorMode != splashModeMono1;
+  glyph.data = data;
+  glyph.freeData = gFalse;
+  splash->fillGlyph((SplashCoord)x, (SplashCoord)y, &glyph);
+}
+
+void SplashOutputDev::endTextObject(GfxState *state) {
+  if (textClipPath) {
+    splash->clipToPath(textClipPath, gFalse);
+    delete textClipPath;
+    textClipPath = NULL;
+  }
+}
+
+struct SplashOutImageMaskData {
+  ImageStream *imgStr;
+  int nPixels, idx;
+  GBool invert;
+};
+
+GBool SplashOutputDev::imageMaskSrc(void *data, SplashMono1 *pixel) {
+  SplashOutImageMaskData *imgMaskData = (SplashOutImageMaskData *)data;
+  Guchar pix;
+
+  if (imgMaskData->idx >= imgMaskData->nPixels) {
+    return gFalse;
+  }
+  //~ use getLine
+  imgMaskData->imgStr->getPixel(&pix);
+  if (!imgMaskData->invert) {
+    pix ^= 1;
+  }
+  *pixel = pix;
+  ++imgMaskData->idx;
+  return gTrue;
+}
+
+void SplashOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
+                                   int width, int height, GBool invert,
+                                   GBool inlineImg) {
+  double *ctm;
+  SplashCoord mat[6];
+  SplashOutImageMaskData imgMaskData;
+  Guchar pix;
+
+  ctm = state->getCTM();
+  mat[0] = ctm[0];
+  mat[1] = ctm[1];
+  mat[2] = -ctm[2];
+  mat[3] = -ctm[3];
+  mat[4] = ctm[2] + ctm[4];
+  mat[5] = ctm[3] + ctm[5];
+
+  imgMaskData.imgStr = new ImageStream(str, width, 1, 1);
+  imgMaskData.imgStr->reset();
+  imgMaskData.nPixels = width * height;
+  imgMaskData.idx = 0;
+  imgMaskData.invert = invert;
+
+  splash->fillImageMask(&imageMaskSrc, &imgMaskData, width, height, mat);
+  if (inlineImg) {
+    while (imageMaskSrc(&imgMaskData, &pix)) ;
+  }
+
+  delete imgMaskData.imgStr;
+}
+
+struct SplashOutImageData {
+  ImageStream *imgStr;
+  GfxImageColorMap *colorMap;
+  int *maskColors;
+  SplashOutputDev *out;
+  int nPixels, idx;
+};
+
+GBool SplashOutputDev::imageSrc(void *data, SplashColor *pixel,
+                               Guchar *alpha) {
+  SplashOutImageData *imgData = (SplashOutImageData *)data;
+  Guchar pix[gfxColorMaxComps];
+  GfxRGB rgb;
+  double gray;
+  int i;
+
+  if (imgData->idx >= imgData->nPixels) {
+    return gFalse;
+  }
+
+  //~ use getLine
+  imgData->imgStr->getPixel(pix);
+  switch (imgData->out->colorMode) {
+  case splashModeMono1:
+  case splashModeMono8:
+    imgData->colorMap->getGray(pix, &gray);
+    pixel->mono8 = soutRound(255 * gray);
+    break;
+  case splashModeRGB8:
+    imgData->colorMap->getRGB(pix, &rgb);
+    pixel->rgb8 = splashMakeRGB8(soutRound(255 * rgb.r),
+                                soutRound(255 * rgb.g),
+                                soutRound(255 * rgb.b));
+    break;
+  case splashModeBGR8Packed:
+    imgData->colorMap->getRGB(pix, &rgb);
+    pixel->bgr8 = splashMakeBGR8(soutRound(255 * rgb.r),
+                                soutRound(255 * rgb.g),
+                                soutRound(255 * rgb.b));
+    break;
+  }
+
+  if (imgData->maskColors) {
+    *alpha = 0;
+    for (i = 0; i < imgData->colorMap->getNumPixelComps(); ++i) {
+      if (pix[i] < imgData->maskColors[2*i] ||
+         pix[i] > imgData->maskColors[2*i+1]) {
+       *alpha = 1;
+       break;
+      }
+    }
+  } else {
+    *alpha = 1;
+  }
+
+  ++imgData->idx;
+  return gTrue;
+}
+
+void SplashOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
+                               int width, int height,
+                               GfxImageColorMap *colorMap,
+                               int *maskColors, GBool inlineImg) {
+  double *ctm;
+  SplashCoord mat[6];
+  SplashOutImageData imgData;
+  SplashColor pix;
+  Guchar alpha;
+
+  ctm = state->getCTM();
+  mat[0] = ctm[0];
+  mat[1] = ctm[1];
+  mat[2] = -ctm[2];
+  mat[3] = -ctm[3];
+  mat[4] = ctm[2] + ctm[4];
+  mat[5] = ctm[3] + ctm[5];
+
+  imgData.imgStr = new ImageStream(str, width,
+                                  colorMap->getNumPixelComps(),
+                                  colorMap->getBits());
+  imgData.imgStr->reset();
+  imgData.colorMap = colorMap;
+  imgData.maskColors = maskColors;
+  imgData.out = this;
+  imgData.nPixels = width * height;
+  imgData.idx = 0;
+
+  splash->drawImage(&imageSrc, &imgData,
+                   (colorMode == splashModeMono1) ? splashModeMono8
+                                                  : colorMode,
+                   width, height, mat);
+  if (inlineImg) {
+    while (imageSrc(&imgData, &pix, &alpha)) ;
+  }
+
+  delete imgData.imgStr;
+}
+
+int SplashOutputDev::getBitmapWidth() {
+  return bitmap->getWidth();
+}
+
+int SplashOutputDev::getBitmapHeight() {
+  return bitmap->getHeight();
+}
+
+void SplashOutputDev::xorRectangle(int x0, int y0, int x1, int y1,
+                                  SplashPattern *pattern) {
+  SplashPath *path;
+
+  path = new SplashPath();
+  path->moveTo((SplashCoord)x0, (SplashCoord)y0);
+  path->lineTo((SplashCoord)x1, (SplashCoord)y0);
+  path->lineTo((SplashCoord)x1, (SplashCoord)y1);
+  path->lineTo((SplashCoord)x0, (SplashCoord)y1);
+  path->close();
+  splash->setFillPattern(pattern);
+  splash->xorFill(path, gTrue);
+  delete path;
+}
+
+void SplashOutputDev::setFillColor(int r, int g, int b) {
+  GfxRGB rgb;
+  double gray;
+
+  rgb.r = r / 255.0;
+  rgb.g = g / 255.0;
+  rgb.b = b / 255.0;
+  gray = 0.299 * rgb.r + 0.587 * rgb.g + 0.114 * rgb.g;
+  splash->setFillPattern(getColor(gray, &rgb));
+}
+
+SplashFont *SplashOutputDev::getFont(GString *name, double *mat) {
+  DisplayFontParam *dfp;
+  Ref ref;
+  SplashOutFontFileID *id;
+  SplashFontFile *fontFile;
+  SplashFont *fontObj;
+  int i;
+
+  for (i = 0; i < 16; ++i) {
+    if (!name->cmp(splashOutSubstFonts[i].name)) {
+      break;
+    }
+  }
+  if (i == 16) {
+    return NULL;
+  }
+  ref.num = i;
+  ref.gen = -1;
+  id = new SplashOutFontFileID(&ref);
+
+  // check the font file cache
+  if ((fontFile = fontEngine->getFontFile(id))) {
+    delete id;
+
+  // load the font file
+  } else {
+    dfp = globalParams->getDisplayFont(name);
+    if (dfp->kind != displayFontT1) {
+      return NULL;
+    }
+    fontFile = fontEngine->loadType1Font(id, dfp->t1.fileName->getCString(),
+                                        gFalse, winAnsiEncoding);
+  }
+
+  // create the scaled font
+  fontObj = fontEngine->getFont(fontFile, (SplashCoord *)mat);
+
+  return fontObj;
+}
diff --git a/pdf/xpdf/SplashOutputDev.h b/pdf/xpdf/SplashOutputDev.h
new file mode 100644 (file)
index 0000000..9300fe4
--- /dev/null
@@ -0,0 +1,195 @@
+//========================================================================
+//
+// SplashOutputDev.h
+//
+// Copyright 2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef SPLASHOUTPUTDEV_H
+#define SPLASHOUTPUTDEV_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#include "gtypes.h"
+#include "SplashTypes.h"
+#include "config.h"
+#include "OutputDev.h"
+
+class GfxState;
+class GfxPath;
+class Gfx8BitFont;
+class SplashBitmap;
+class Splash;
+class SplashPath;
+class SplashPattern;
+class SplashFontEngine;
+class SplashFont;
+class T3FontCache;
+struct T3FontCacheTag;
+struct T3GlyphStack;
+struct GfxRGB;
+
+//------------------------------------------------------------------------
+
+// number of Type 3 fonts to cache
+#define splashOutT3FontCacheSize 8
+
+//------------------------------------------------------------------------
+// SplashOutputDev
+//------------------------------------------------------------------------
+
+class SplashOutputDev: public OutputDev {
+public:
+
+  // Constructor.
+  SplashOutputDev(SplashColorMode colorModeA, GBool reverseVideoA,
+                 SplashColor paperColorA);
+
+  // Destructor.
+  virtual ~SplashOutputDev();
+
+  //----- get info about output device
+
+  // Does this device use upside-down coordinates?
+  // (Upside-down means (0,0) is the top left corner of the page.)
+  virtual GBool upsideDown() { return gTrue; }
+
+  // Does this device use drawChar() or drawString()?
+  virtual GBool useDrawChar() { return gTrue; }
+
+  // Does this device use beginType3Char/endType3Char?  Otherwise,
+  // text in Type 3 fonts will be drawn with drawChar/drawString.
+  virtual GBool interpretType3Chars() { return gTrue; }
+
+  //----- initialization and control
+
+  // Start a page.
+  virtual void startPage(int pageNum, GfxState *state);
+
+  // End a page.
+  virtual void endPage();
+
+  //----- link borders
+  virtual void drawLink(Link *link, Catalog *catalog);
+
+  //----- save/restore graphics state
+  virtual void saveState(GfxState *state);
+  virtual void restoreState(GfxState *state);
+
+  //----- update graphics state
+  virtual void updateAll(GfxState *state);
+  virtual void updateCTM(GfxState *state, double m11, double m12,
+                        double m21, double m22, double m31, double m32);
+  virtual void updateLineDash(GfxState *state);
+  virtual void updateFlatness(GfxState *state);
+  virtual void updateLineJoin(GfxState *state);
+  virtual void updateLineCap(GfxState *state);
+  virtual void updateMiterLimit(GfxState *state);
+  virtual void updateLineWidth(GfxState *state);
+  virtual void updateFillColor(GfxState *state);
+  virtual void updateStrokeColor(GfxState *state);
+
+  //----- update text state
+  virtual void updateFont(GfxState *state);
+
+  //----- path painting
+  virtual void stroke(GfxState *state);
+  virtual void fill(GfxState *state);
+  virtual void eoFill(GfxState *state);
+
+  //----- path clipping
+  virtual void clip(GfxState *state);
+  virtual void eoClip(GfxState *state);
+
+  //----- text drawing
+  virtual void drawChar(GfxState *state, double x, double y,
+                       double dx, double dy,
+                       double originX, double originY,
+                       CharCode code, Unicode *u, int uLen);
+  virtual GBool beginType3Char(GfxState *state, double x, double y,
+                              double dx, double dy,
+                              CharCode code, Unicode *u, int uLen);
+  virtual void endType3Char(GfxState *state);
+  virtual void endTextObject(GfxState *state);
+
+  //----- image drawing
+  virtual void drawImageMask(GfxState *state, Object *ref, Stream *str,
+                            int width, int height, GBool invert,
+                            GBool inlineImg);
+  virtual void drawImage(GfxState *state, Object *ref, Stream *str,
+                        int width, int height, GfxImageColorMap *colorMap,
+                        int *maskColors, GBool inlineImg);
+
+  //----- Type 3 font operators
+  virtual void type3D0(GfxState *state, double wx, double wy);
+  virtual void type3D1(GfxState *state, double wx, double wy,
+                      double llx, double lly, double urx, double ury);
+
+  //----- special access
+
+  // Called to indicate that a new PDF document has been loaded.
+  void startDoc(XRef *xrefA);
+  GBool isReverseVideo() { return reverseVideo; }
+
+  // Get the bitmap and its size.
+  SplashBitmap *getBitmap() { return bitmap; }
+  int getBitmapWidth();
+  int getBitmapHeight();
+
+  // Get the Splash object.
+  Splash *getSplash() { return splash; }
+
+  // XOR a rectangular region in the bitmap with <pattern>.  <pattern>
+  // is passed to Splash::setFillPattern, so it should not be used
+  // after calling this function.
+  void xorRectangle(int x0, int y0, int x1, int y1, SplashPattern *pattern);
+
+  // Set the Splash fill color.
+  void setFillColor(int r, int g, int b);
+
+  // Get a font object for a Base-14 font, using the Latin-1 encoding.
+  SplashFont *getFont(GString *name, double *mat);
+
+  void setUnderlayCbk(void (*cbk)(void *data), void *data)
+    { underlayCbk = cbk; underlayCbkData = data; }
+
+private:
+
+  SplashPattern *getColor(double gray, GfxRGB *rgb);
+  SplashPath *convertPath(GfxState *state, GfxPath *path);
+  void drawType3Glyph(T3FontCache *t3Font,
+                     T3FontCacheTag *tag, Guchar *data,
+                     double x, double y);
+  static GBool imageMaskSrc(void *data, SplashMono1 *pixel);
+  static GBool imageSrc(void *data, SplashColor *pixel, Guchar *alpha);
+
+  SplashColorMode colorMode;
+  GBool reverseVideo;          // reverse video mode
+  SplashColor paperColor;      // paper color
+
+  XRef *xref;                  // xref table for current document
+
+  SplashBitmap *bitmap;
+  Splash *splash;
+  SplashFontEngine *fontEngine;
+
+  T3FontCache *                        // Type 3 font cache
+    t3FontCache[splashOutT3FontCacheSize];
+  int nT3Fonts;                        // number of valid entries in t3FontCache
+  T3GlyphStack *t3GlyphStack;  // Type 3 glyph context stack
+
+  SplashFont *font;            // current font
+  GBool needFontUpdate;                // set when the font needs to be updated
+  SplashPath *textClipPath;    // clipping path built with text object
+
+  void (*underlayCbk)(void *data);
+  void *underlayCbkData;
+};
+
+#endif
diff --git a/pdf/xpdf/XSplashOutputDev.cc b/pdf/xpdf/XSplashOutputDev.cc
new file mode 100644 (file)
index 0000000..3e2bfc4
--- /dev/null
@@ -0,0 +1,323 @@
+//========================================================================
+//
+// XSplashOutputDev.cc
+//
+// Copyright 2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include "gmem.h"
+#include "SplashTypes.h"
+#include "SplashBitmap.h"
+#include "Object.h"
+#include "GfxState.h"
+#include "TextOutputDev.h"
+#include "XSplashOutputDev.h"
+
+//------------------------------------------------------------------------
+// Constants and macros
+//------------------------------------------------------------------------
+
+#define xoutRound(x) ((int)(x + 0.5))
+
+//------------------------------------------------------------------------
+// XSplashOutputDev
+//------------------------------------------------------------------------
+
+XSplashOutputDev::XSplashOutputDev(Display *displayA, int screenNumA,
+                                  Visual *visualA, Colormap colormapA,
+                                  GBool reverseVideoA,
+                                  SplashColor paperColorA,
+                                  GBool installCmapA, int rgbCubeSizeA,
+                                  GBool incrementalUpdateA,
+                                  void (*redrawCbkA)(void *data),
+                                  void *redrawCbkDataA):
+  SplashOutputDev(splashModeRGB8, reverseVideoA, paperColorA)
+{
+  XVisualInfo visualTempl;
+  XVisualInfo *visualList;
+  Gulong mask;
+  int nVisuals;
+  XColor xcolor;
+  XColor *xcolors;
+  int r, g, b, n, m;
+  GBool ok;
+
+  incrementalUpdate = incrementalUpdateA;
+  redrawCbk = redrawCbkA;
+  redrawCbkData = redrawCbkDataA;
+
+  // create text object
+  text = new TextPage(gFalse);
+
+  //----- set up the X color stuff
+
+  display = displayA;
+  visual = visualA;
+
+  // check for TrueColor visual
+  //~ this should scan the list, not just look at the first one
+  visualTempl.visualid = XVisualIDFromVisual(visual);
+  visualList = XGetVisualInfo(display, VisualIDMask,
+                             &visualTempl, &nVisuals);
+  if (nVisuals < 1) {
+    // this shouldn't happen
+    XFree((XPointer)visualList);
+    visualList = XGetVisualInfo(display, VisualNoMask, &visualTempl,
+                               &nVisuals);
+  }
+  depth = visualList->depth;
+  if (visualList->c_class == TrueColor) {
+    trueColor = gTrue;
+    for (mask = visualList->red_mask, rShift = 0;
+        mask && !(mask & 1);
+        mask >>= 1, ++rShift) ;
+    for (rDiv = 8; mask; mask >>= 1, --rDiv) ;
+    for (mask = visualList->green_mask, gShift = 0;
+        mask && !(mask & 1);
+        mask >>= 1, ++gShift) ;
+    for (gDiv = 8; mask; mask >>= 1, --gDiv) ;
+    for (mask = visualList->blue_mask, bShift = 0;
+        mask && !(mask & 1);
+        mask >>= 1, ++bShift) ;
+    for (bDiv = 8; mask; mask >>= 1, --bDiv) ;
+  } else {
+    trueColor = gFalse;
+  }
+  XFree((XPointer)visualList);
+
+  // allocate a color cube
+  if (!trueColor) {
+
+    // set colors in private colormap
+    if (installCmapA) {
+      for (rgbCubeSize = xOutMaxRGBCube; rgbCubeSize >= 2; --rgbCubeSize) {
+       m = rgbCubeSize * rgbCubeSize * rgbCubeSize;
+       if (XAllocColorCells(display, colormapA, False, NULL, 0, colors, m)) {
+         break;
+       }
+      }
+      if (rgbCubeSize >= 2) {
+       m = rgbCubeSize * rgbCubeSize * rgbCubeSize;
+       xcolors = (XColor *)gmalloc(m * sizeof(XColor));
+       n = 0;
+       for (r = 0; r < rgbCubeSize; ++r) {
+         for (g = 0; g < rgbCubeSize; ++g) {
+           for (b = 0; b < rgbCubeSize; ++b) {
+             xcolors[n].pixel = colors[n];
+             xcolors[n].red = (r * 65535) / (rgbCubeSize - 1);
+             xcolors[n].green = (g * 65535) / (rgbCubeSize - 1);
+             xcolors[n].blue = (b * 65535) / (rgbCubeSize - 1);
+             xcolors[n].flags = DoRed | DoGreen | DoBlue;
+             ++n;
+           }
+         }
+       }
+       XStoreColors(display, colormapA, xcolors, m);
+       gfree(xcolors);
+      } else {
+       rgbCubeSize = 1;
+       colors[0] = BlackPixel(display, screenNumA);
+       colors[1] = WhitePixel(display, screenNumA);
+      }
+
+    // allocate colors in shared colormap
+    } else {
+      if (rgbCubeSize > xOutMaxRGBCube) {
+       rgbCubeSize = xOutMaxRGBCube;
+      }
+      ok = gFalse;
+      for (rgbCubeSize = rgbCubeSizeA; rgbCubeSize >= 2; --rgbCubeSize) {
+       ok = gTrue;
+       n = 0;
+       for (r = 0; r < rgbCubeSize && ok; ++r) {
+         for (g = 0; g < rgbCubeSize && ok; ++g) {
+           for (b = 0; b < rgbCubeSize && ok; ++b) {
+             if (n == 0) {
+               colors[n] = BlackPixel(display, screenNumA);
+               ++n;
+             } else {
+               xcolor.red = (r * 65535) / (rgbCubeSize - 1);
+               xcolor.green = (g * 65535) / (rgbCubeSize - 1);
+               xcolor.blue = (b * 65535) / (rgbCubeSize - 1);
+               if (XAllocColor(display, colormapA, &xcolor)) {
+                 colors[n++] = xcolor.pixel;
+               } else {
+                 ok = gFalse;
+               }
+             }
+           }
+         }
+       }
+       if (ok) {
+         break;
+       }
+       XFreeColors(display, colormapA, &colors[1], n-1, 0);
+      }
+      if (!ok) {
+       rgbCubeSize = 1;
+       colors[0] = BlackPixel(display, screenNumA);
+       colors[1] = WhitePixel(display, screenNumA);
+      }
+    }
+  }
+}
+
+XSplashOutputDev::~XSplashOutputDev() {
+  delete text;
+}
+
+void XSplashOutputDev::drawChar(GfxState *state, double x, double y,
+                               double dx, double dy,
+                               double originX, double originY,
+                               CharCode code, Unicode *u, int uLen) {
+  text->addChar(state, x, y, dx, dy, code, u, uLen);
+  SplashOutputDev::drawChar(state, x, y, dx, dy, originX, originY,
+                           code, u, uLen);
+}
+
+GBool XSplashOutputDev::beginType3Char(GfxState *state, double x, double y,
+                                      double dx, double dy,
+                                      CharCode code, Unicode *u, int uLen) {
+  text->addChar(state, x, y, dx, dy, code, u, uLen);
+  return SplashOutputDev::beginType3Char(state, x, y, dx, dy, code, u, uLen);
+}
+
+void XSplashOutputDev::clear() {
+  startDoc(NULL);
+  startPage(0, NULL);
+}
+
+void XSplashOutputDev::startPage(int pageNum, GfxState *state) {
+  SplashOutputDev::startPage(pageNum, state);
+  text->startPage(state);
+}
+
+void XSplashOutputDev::endPage() {
+  SplashOutputDev::endPage();
+  if (!incrementalUpdate) {
+    (*redrawCbk)(redrawCbkData);
+  }
+  text->coalesce(gTrue);
+}
+
+void XSplashOutputDev::dump() {
+  if (incrementalUpdate) {
+    (*redrawCbk)(redrawCbkData);
+  }
+}
+
+void XSplashOutputDev::updateFont(GfxState *state) {
+  SplashOutputDev::updateFont(state);
+  text->updateFont(state);
+}
+
+void XSplashOutputDev::redraw(int srcX, int srcY,
+                             Drawable destDrawable, GC destGC,
+                             int destX, int destY,
+                             int width, int height) {
+  XImage *image;
+  SplashColorPtr dataPtr;
+  SplashRGB8 *p;
+  SplashRGB8 rgb;
+  Gulong pixel;
+  int bw, x, y, r, g, b, gray;
+
+  //~ allocate this image once (whenever the window changes size)
+  //~ use XShm
+  image = XCreateImage(display, visual, depth, ZPixmap, 0, NULL,
+                      width, height, 8, 0);
+  image->data = (char *)gmalloc(height * image->bytes_per_line);
+
+  //~ optimize for known XImage formats
+  bw = getBitmap()->getWidth();
+  dataPtr = getBitmap()->getDataPtr();
+
+  if (trueColor) {
+    for (y = 0; y < height; ++y) {
+      p = dataPtr.rgb8 + (y + srcY) * bw + srcX;
+      for (x = 0; x < width; ++x) {
+       rgb = *p++;
+       r = splashRGB8R(rgb) >> rDiv;
+       g = splashRGB8G(rgb) >> gDiv;
+       b = splashRGB8B(rgb) >> bDiv;
+       pixel = ((Gulong)r << rShift) +
+               ((Gulong)g << gShift) +
+               ((Gulong)b << bShift);
+       XPutPixel(image, x, y, pixel);
+      }
+    }
+  } else if (rgbCubeSize == 1) {
+    //~ this should really use splashModeMono, with non-clustered dithering
+    for (y = 0; y < height; ++y) {
+      p = dataPtr.rgb8 + (y + srcY) * bw + srcX;
+      for (x = 0; x < width; ++x) {
+       rgb = *p++;
+       gray = xoutRound(0.299 * splashRGB8R(rgb) +
+                        0.587 * splashRGB8G(rgb) +
+                        0.114 * splashRGB8B(rgb));
+       if (gray < 128) {
+         pixel = colors[0];
+       } else {
+         pixel = colors[1];
+       }
+       XPutPixel(image, x, y, pixel);
+      }
+    }
+  } else {
+    for (y = 0; y < height; ++y) {
+      p = dataPtr.rgb8 + (y + srcY) * bw + srcX;
+      for (x = 0; x < width; ++x) {
+       rgb = *p++;
+       r = (splashRGB8R(rgb) * (rgbCubeSize - 1)) / 255;
+       g = (splashRGB8G(rgb) * (rgbCubeSize - 1)) / 255;
+       b = (splashRGB8B(rgb) * (rgbCubeSize - 1)) / 255;
+       pixel = colors[(r * rgbCubeSize + g) * rgbCubeSize + b];
+       XPutPixel(image, x, y, pixel);
+      }
+    }
+  }
+
+  XPutImage(display, destDrawable, destGC, image,
+           0, 0, destX, destY, width, height);
+
+  gfree(image->data);
+  image->data = NULL;
+  XDestroyImage(image);
+}
+
+GBool XSplashOutputDev::findText(Unicode *s, int len,
+                                GBool startAtTop, GBool stopAtBottom,
+                                GBool startAtLast, GBool stopAtLast,
+                                int *xMin, int *yMin,
+                                int *xMax, int *yMax) {
+  double xMin1, yMin1, xMax1, yMax1;
+  
+  xMin1 = (double)*xMin;
+  yMin1 = (double)*yMin;
+  xMax1 = (double)*xMax;
+  yMax1 = (double)*yMax;
+  if (text->findText(s, len, startAtTop, stopAtBottom,
+                    startAtLast, stopAtLast,
+                    &xMin1, &yMin1, &xMax1, &yMax1)) {
+    *xMin = xoutRound(xMin1);
+    *xMax = xoutRound(xMax1);
+    *yMin = xoutRound(yMin1);
+    *yMax = xoutRound(yMax1);
+    return gTrue;
+  }
+  return gFalse;
+}
+
+GString *XSplashOutputDev::getText(int xMin, int yMin, int xMax, int yMax) {
+  return text->getText((double)xMin, (double)yMin,
+                      (double)xMax, (double)yMax);
+}
diff --git a/pdf/xpdf/XSplashOutputDev.h b/pdf/xpdf/XSplashOutputDev.h
new file mode 100644 (file)
index 0000000..71e5b48
--- /dev/null
@@ -0,0 +1,110 @@
+//========================================================================
+//
+// XSplashOutputDev.h
+//
+// Copyright 2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef XSPLASHOUTPUTDEV_H
+#define XSPLASHOUTPUTDEV_H
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#include <X11/Xlib.h>
+#include "SplashTypes.h"
+#include "SplashOutputDev.h"
+
+//------------------------------------------------------------------------
+
+#define xOutMaxRGBCube 6       // max size of RGB color cube
+
+//------------------------------------------------------------------------
+// XSplashOutputDev
+//------------------------------------------------------------------------
+
+class XSplashOutputDev: public SplashOutputDev {
+public:
+
+  XSplashOutputDev(Display *displayA, int screenNumA,
+                  Visual *visualA, Colormap colormapA,
+                  GBool reverseVideoA, SplashColor paperColorA,
+                  GBool installCmapA, int rgbCubeSizeA,
+                  GBool incrementalUpdateA,
+                  void (*redrawCbkA)(void *data),
+                  void *redrawCbkDataA);
+
+  virtual ~XSplashOutputDev();
+
+  //----- initialization and control
+
+  // Start a page.
+  virtual void startPage(int pageNum, GfxState *state);
+
+  // End a page.
+  virtual void endPage();
+
+  // Dump page contents to display.
+  virtual void dump();
+
+  //----- update text state
+  virtual void updateFont(GfxState *state);
+
+  //----- text drawing
+  virtual void drawChar(GfxState *state, double x, double y,
+                       double dx, double dy,
+                       double originX, double originY,
+                       CharCode code, Unicode *u, int uLen);
+  virtual GBool beginType3Char(GfxState *state, double x, double y,
+                              double dx, double dy,
+                              CharCode code, Unicode *u, int uLen);
+
+  //----- special access
+
+  // Clear out the document (used when displaying an empty window).
+  void clear();
+
+  // Copy the rectangle (srcX, srcY, width, height) to (destX, destY)
+  // in destDC.
+  void redraw(int srcX, int srcY,
+             Drawable destDrawable, GC destGC,
+             int destX, int destY,
+             int width, int height);
+
+  // Find a string.  If <startAtTop> is true, starts looking at the
+  // top of the page; else if <startAtLast> is true, starts looking
+  // immediately after the last find result; else starts looking at
+  // <xMin>,<yMin>.  If <stopAtBottom> is true, stops looking at the
+  // bottom of the page; else if <stopAtLast> is true, stops looking
+  // just before the last find result; else stops looking at
+  // <xMax>,<yMax>.
+  GBool findText(Unicode *s, int len,
+                GBool startAtTop, GBool stopAtBottom,
+                GBool startAtLast, GBool stopAtLast,
+                int *xMin, int *yMin,
+                int *xMax, int *yMax);
+
+  // Get the text which is inside the specified rectangle.
+  GString *getText(int xMin, int yMin, int xMax, int yMax);
+
+private:
+
+  GBool incrementalUpdate;      // incrementally update the display?
+  void (*redrawCbk)(void *data);
+  void *redrawCbkData;
+  TextPage *text;               // text from the current page
+
+  Display *display;            // X display pointer
+  Visual *visual;              // X visual
+  Guint depth;                 // visual depth
+  GBool trueColor;             // set if using a TrueColor visual
+  int rDiv, gDiv, bDiv;                // RGB right shifts (for TrueColor)
+  int rShift, gShift, bShift;  // RGB left shifts (for TrueColor)
+  int rgbCubeSize;             // size of color cube (for non-TrueColor)
+  Gulong                       // color cube (for non-TrueColor)
+    colors[xOutMaxRGBCube * xOutMaxRGBCube * xOutMaxRGBCube];
+};
+
+#endif
diff --git a/pdf/xpdf/pdftoppm.cc b/pdf/xpdf/pdftoppm.cc
new file mode 100644 (file)
index 0000000..9be5c64
--- /dev/null
@@ -0,0 +1,189 @@
+//========================================================================
+//
+// pdftoppm.cc
+//
+// Copyright 2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+#include <stdio.h>
+#include "parseargs.h"
+#include "gmem.h"
+#include "GString.h"
+#include "GlobalParams.h"
+#include "Object.h"
+#include "PDFDoc.h"
+#include "SplashBitmap.h"
+#include "Splash.h"
+#include "SplashOutputDev.h"
+#include "config.h"
+
+static int firstPage = 1;
+static int lastPage = 0;
+static int resolution = 150;
+static GBool mono = gFalse;
+static GBool gray = gFalse;
+static char enableT1libStr[16] = "";
+static char enableFreeTypeStr[16] = "";
+static char antialiasStr[16] = "";
+static char ownerPassword[33] = "";
+static char userPassword[33] = "";
+static GBool quiet = gFalse;
+static char cfgFileName[256] = "";
+static GBool printVersion = gFalse;
+static GBool printHelp = gFalse;
+
+static ArgDesc argDesc[] = {
+  {"-f",      argInt,      &firstPage,     0,
+   "first page to print"},
+  {"-l",      argInt,      &lastPage,      0,
+   "last page to print"},
+  {"-r",      argInt,      &resolution,    0,
+   "resolution, in DPI (default is 150)"},
+  {"-mono",   argFlag,     &mono,          0,
+   "generate a monochrome PBM file"},
+  {"-gray",   argFlag,     &gray,          0,
+   "generate a grayscale PGM file"},
+#if HAVE_T1LIB_H
+  {"-t1lib",      argString,      enableT1libStr, sizeof(enableT1libStr),
+   "enable t1lib font rasterizer: yes, no"},
+#endif
+#if HAVE_FREETYPE_FREETYPE_H | HAVE_FREETYPE_H
+  {"-freetype",   argString,      enableFreeTypeStr, sizeof(enableFreeTypeStr),
+   "enable FreeType font rasterizer: yes, no"},
+#endif
+  {"-aa",         argString,      antialiasStr,   sizeof(antialiasStr),
+   "enable font anti-aliasing: yes, no"},
+  {"-opw",    argString,   ownerPassword,  sizeof(ownerPassword),
+   "owner password (for encrypted files)"},
+  {"-upw",    argString,   userPassword,   sizeof(userPassword),
+   "user password (for encrypted files)"},
+  {"-q",      argFlag,     &quiet,         0,
+   "don't print any messages or errors"},
+  {"-cfg",        argString,      cfgFileName,    sizeof(cfgFileName),
+   "configuration file to use in place of .xpdfrc"},
+  {"-v",      argFlag,     &printVersion,  0,
+   "print copyright and version info"},
+  {"-h",      argFlag,     &printHelp,     0,
+   "print usage information"},
+  {"-help",   argFlag,     &printHelp,     0,
+   "print usage information"},
+  {"--help",  argFlag,     &printHelp,     0,
+   "print usage information"},
+  {"-?",      argFlag,     &printHelp,     0,
+   "print usage information"},
+  {NULL}
+};
+
+int main(int argc, char *argv[]) {
+  PDFDoc *doc;
+  GString *fileName;
+  char *ppmRoot;
+  char ppmFile[512];
+  GString *ownerPW, *userPW;
+  SplashColor paperColor;
+  SplashOutputDev *splashOut;
+  GBool ok;
+  int exitCode;
+  int pg;
+
+  exitCode = 99;
+
+  // parse args
+  ok = parseArgs(argDesc, &argc, argv);
+  if (mono && gray) {
+    ok = gFalse;
+  }
+  if (!ok || argc != 3 || printVersion || printHelp) {
+    fprintf(stderr, "pdftoppm version %s\n", xpdfVersion);
+    fprintf(stderr, "%s\n", xpdfCopyright);
+    if (!printVersion) {
+      printUsage("pdftoppm", "<PDF-file> <PPM-root>", argDesc);
+    }
+    goto err0;
+  }
+  fileName = new GString(argv[1]);
+  ppmRoot = argv[2];
+
+  // read config file
+  globalParams = new GlobalParams(cfgFileName);
+  globalParams->setupBaseFonts(NULL);
+  if (enableT1libStr[0]) {
+    if (!globalParams->setEnableT1lib(enableT1libStr)) {
+      fprintf(stderr, "Bad '-t1lib' value on command line\n");
+    }
+  }
+  if (enableFreeTypeStr[0]) {
+    if (!globalParams->setEnableFreeType(enableFreeTypeStr)) {
+      fprintf(stderr, "Bad '-freetype' value on command line\n");
+    }
+  }
+  if (antialiasStr[0]) {
+    if (!globalParams->setAntialias(antialiasStr)) {
+      fprintf(stderr, "Bad '-aa' value on command line\n");
+    }
+  }
+  if (quiet) {
+    globalParams->setErrQuiet(quiet);
+  }
+
+  // open PDF file
+  if (ownerPassword[0]) {
+    ownerPW = new GString(ownerPassword);
+  } else {
+    ownerPW = NULL;
+  }
+  if (userPassword[0]) {
+    userPW = new GString(userPassword);
+  } else {
+    userPW = NULL;
+  }
+  doc = new PDFDoc(fileName, ownerPW, userPW);
+  if (userPW) {
+    delete userPW;
+  }
+  if (ownerPW) {
+    delete ownerPW;
+  }
+  if (!doc->isOk()) {
+    exitCode = 1;
+    goto err1;
+  }
+
+  // get page range
+  if (firstPage < 1)
+    firstPage = 1;
+  if (lastPage < 1 || lastPage > doc->getNumPages())
+    lastPage = doc->getNumPages();
+
+  // write PPM files
+  paperColor.rgb8 = splashMakeRGB8(255, 255, 255);
+  splashOut = new SplashOutputDev(mono ? splashModeMono1 :
+                                   gray ? splashModeMono8 :
+                                            splashModeRGB8,
+                                 gFalse, paperColor);
+  splashOut->startDoc(doc->getXRef());
+  for (pg = firstPage; pg <= lastPage; ++pg) {
+    doc->displayPage(splashOut, pg, resolution, resolution, 0, gTrue, gFalse);
+    sprintf(ppmFile, "%.*s-%06d.%s",
+           (int)sizeof(ppmFile) - 32, ppmRoot, pg,
+           mono ? "pbm" : gray ? "pgm" : "ppm");
+    splashOut->getBitmap()->writePNMFile(ppmFile);
+  }
+  delete splashOut;
+
+  exitCode = 0;
+
+  // clean up
+ err1:
+  delete doc;
+  delete globalParams;
+ err0:
+
+  // check for memory leaks
+  Object::memCheck(stderr);
+  gMemReport(stderr);
+
+  return exitCode;
+}